/***************************************************************************** * AreaFix for HPT (FTN NetMail/EchoMail Tosser) ***************************************************************************** * Copyright (C) 1998-2002 * * Max Levenkov * * Fido: 2:5000/117 * Internet: sackett@mail.ru * Novosibirsk, West Siberia, Russia * * Big thanks to: * * Fedor Lizunkov * * Fido: 2:5020/960 * Moscow, Russia * * This file is part of HPT. * * HPT is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * HPT is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with HPT; see the file COPYING. If not, write to the Free * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. ***************************************************************************** * $Id: areafix.c,v 1.383.2.8 2005/04/08 04:40:35 d_sergienko Exp $ */ #include #include #include #include #include #include #include #ifdef HAS_IO_H #include #endif #ifdef HAS_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include unsigned char RetFix; static int rescanMode = 0; static int rulesCount = 0; static char **rulesList = NULL; char *print_ch(int len, char ch) { static char tmp[256]; if (len <= 0 || len > 255) return ""; memset(tmp, ch, len); tmp[len]=0; return tmp; } /* test area-link pair to mandatory */ int mandatoryCheck(s_area area, s_link *link) { int i; w_log(LL_FUNC, __FILE__ "::mandatoryCheck()"); if (grpInArray(area.group,link->optGrp,link->numOptGrp)&&link->mandatory){ w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1"); return 1; } if (link->numOptGrp==0 && link->mandatory){ w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1"); return 1; } if (area.mandatory){ w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=1"); return 1; } if ((i=isAreaLink(link->hisAka, &area))!=-1){ w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=%d", area.downlinks[i]->mandatory); return area.downlinks[i]->mandatory; } w_log(LL_FUNC, __FILE__ "::mandatoryCheck() rc=0"); return 0; } /* test area-link pair to manual */ int manualCheck(s_area area, s_link *link) { int i; w_log(LL_FUNC, __FILE__ "::manualCheck()"); if (grpInArray(area.group,link->optGrp,link->numOptGrp)&&link->manual){ w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1"); return 1; } if (link->numOptGrp==0 && link->manual){ w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1"); return 1; } if (area.manual){ w_log(LL_FUNC, __FILE__ "::manualCheck() rc=1"); return 1; } if ((i=isAreaLink(link->hisAka, &area))!=-1){ w_log(LL_FUNC, __FILE__ "::manualCheck() rc=%d", area.downlinks[i]->manual); return area.downlinks[i]->manual; } w_log(LL_FUNC, __FILE__ "::manualCheck() rc=0"); return 0; } int subscribeCheck(s_area area, s_link *link) { int found = 0; w_log( LL_FUNC, "%s::subscribeCheck() begin", __FILE__ ); if (isLinkOfArea(link, &area)) return 0; if (area.group) { if (config->numPublicGroup) found = grpInArray(area.group,config->PublicGroup,config->numPublicGroup); if (!found && link->numAccessGrp) found = grpInArray(area.group,link->AccessGrp,link->numAccessGrp); } else found = 1; if (!found){ w_log( LL_FUNC, "%s::subscribeCheck() end, rc=2", __FILE__ ); return 2; } if (area.levelwrite > link->level && area.levelread > link->level){ w_log( LL_FUNC, "%s::subscribeCheck() end, rc=2", __FILE__ ); return 2; } w_log( LL_FUNC, "%s::subscribeCheck() end, rc=1", __FILE__ ); return 1; } int subscribeAreaCheck(s_area *area, char *areaname, s_link *link) { int rc=4; w_log( LL_SRCLINE, "%s::subscribeAreaCheck()", __FILE__ ); if( (!areaname)||(!areaname[0]) ){ w_log( LL_SRCLINE, "%s::subscribeAreaCheck() Failed (areaname empty) rc=%d", __FILE__, rc ); return rc; } if (patimat(area->areaName,areaname)==1) { rc=subscribeCheck(*area, link); /* 0 - already subscribed / linked */ /* 1 - need subscribe / not linked */ /* 2 - no access */ } /* else: this is another area */ w_log( LL_SRCLINE, "%s::subscribeAreaCheck() end rc=%d", __FILE__, rc ); return rc; } char *getPatternFromLine(char *line, int *reversed) { *reversed = 0; if (!line) return NULL; /* original string is like "%list ! *.citycat.*" or withut '!' sign*/ if (line[0] == '%') line++; /* exclude '%' sign */ while((strlen(line) > 0) && isspace(line[0])) line++; /* exclude spaces between '%' and command */ while((strlen(line) > 0) && !isspace(line[0])) line++; /* exclude command */ while((strlen(line) > 0) && isspace(line[0])) line++; /* exclude spaces between command and pattern */ if ((strlen(line) > 2) && (line[0] == '!') && (isspace(line[1]))) { *reversed = 1; line++; /* exclude '!' sign */ while(isspace(line[0])) line++; /* exclude spaces between '!' and pattern */ } if (strlen(line) > 0) return line; else return NULL; } char *list(s_link *link, char *cmdline) { unsigned int i, active, avail, rc = 0; char *report = NULL; char *list = NULL; char *pattern = NULL; int reversed; ps_arealist al; s_area area; pattern = getPatternFromLine(cmdline, &reversed); if ((pattern) && (strlen(pattern)>60 || !isValidConference(pattern))) { w_log(LL_FUNC, "areafix::list() FAILED (error request line)"); return errorRQ(cmdline); } xscatprintf(&report, "Available areas for %s\r\r", aka2str(link->hisAka)); al = newAreaList(); for (i=active=avail=0; i< config->echoAreaCount; i++) { area = config->echoAreas[i]; rc = subscribeCheck(area, link); if (rc < 2 && (!area.hide || (area.hide && rc==0))) { /* add line */ if (pattern) { /* if matches pattern and not reversed (or vise versa) */ if (patimat(area.areaName, pattern)!=reversed) { addAreaListItem(al,rc==0, area.msgbType!=MSGTYPE_PASSTHROUGH, area.areaName,area.description); if (rc==0) active++; avail++; } } else { addAreaListItem(al,rc==0, area.msgbType!=MSGTYPE_PASSTHROUGH, area.areaName,area.description); if (rc==0) active++; avail++; } } /* end add line */ } /* end for */ sortAreaList(al); list = formatAreaList(al,78," *R"); if (list) xstrcat(&report,list); nfree(list); freeAreaList(al); xstrcat(&report, "\r'R' = area rescanable"); xstrcat(&report, "\r'*' = area active"); xscatprintf(&report, "\r %i areas available, %i areas active",avail, active); xscatprintf(&report, "\r for link:%s\r", aka2str(link->hisAka)); if (link->afixEchoLimit) xscatprintf(&report, "\rYour limit is %u areas for subscribe\r", link->afixEchoLimit); w_log(LL_AREAFIX, "areafix: list sent to %s", aka2str(link->hisAka)); return report; } char *linked(s_link *link) { unsigned int i, n, rc; char *report = NULL; xscatprintf(&report, "\r%s areas on %s\r\r", ((link->Pause & EPAUSE) == EPAUSE) ? "Passive" : "Active", aka2str(link->hisAka)); for (i=n=0; iechoAreaCount; i++) { rc=subscribeCheck(config->echoAreas[i], link); if (rc==0) { xscatprintf(&report, " %s\r", config->echoAreas[i].areaName); n++; } } xscatprintf(&report, "\r%u areas linked\r", n); if (link->afixEchoLimit) xscatprintf(&report, "\rYour limit is %u areas for subscribe\r", link->afixEchoLimit); w_log(LL_AREAFIX, "areafix: linked areas list sent to %s", aka2str(link->hisAka)); return report; } char *unlinked(s_link *link) { unsigned int i, rc; char *report = NULL; s_area *areas; areas=config->echoAreas; xscatprintf(&report, "Unlinked areas to %s\r\r", aka2str(link->hisAka)); for (i=0; iechoAreaCount; i++) { rc=subscribeCheck(areas[i], link); if (rc == 1 && !areas[i].hide) { xscatprintf(&report, " %s\r", areas[i].areaName); } } w_log(LL_AREAFIX, "areafix: unlinked areas list sent to %s", aka2str(link->hisAka)); return report; } char *help(s_link *link) { FILE *f; int i=1; char *help; long endpos; if (config->areafixhelp!=NULL) { if ((f=fopen(config->areafixhelp,"r")) == NULL) { w_log (LL_ERR, "areafix: cannot open help file \"%s\": %s", config->areafixhelp, strerror(errno)); if (!quiet) fprintf(stderr,"areafix: cannot open help file \"%s\": %s\n", config->areafixhelp, strerror(errno)); return NULL; } fseek(f,0L,SEEK_END); endpos=ftell(f); help=(char*) safe_malloc((size_t) endpos+1); fseek(f,0L,SEEK_SET); endpos = fread(help,1,(size_t) endpos,f); for (i=0; iname); return help; } return NULL; } int tag_mask(char *tag, char **mask, unsigned num) { unsigned int i; for (i = 0; i < num; i++) { if (patimat(tag,mask[i])) return 1; } return 0; } /* Process %avail command. * */ char *available(s_link *link, char *cmdline) { FILE *f; unsigned int j=0, found; unsigned int k, rc; char *report = NULL, *line, *token, *running, linkAka[SIZE_aka2str]; char *pattern; int reversed; s_link *uplink=NULL; ps_arealist al=NULL, *hal=NULL; unsigned int halcnt=0, isuplink; pattern = getPatternFromLine(cmdline, &reversed); if ((pattern) && (strlen(pattern)>60 || !isValidConference(pattern))) { w_log(LL_FUNC, "areafix::avail() FAILED (error request line)"); return errorRQ(cmdline); } for (j = 0; j < config->linkCount; j++) { uplink = &(config->links[j]); found = 0; isuplink = 0; for (k = 0; k < link->numAccessGrp && uplink->LinkGrp; k++) if (strcmp(link->AccessGrp[k], uplink->LinkGrp) == 0) found = 1; if ((uplink->forwardRequests && uplink->forwardRequestFile) && ((uplink->LinkGrp == NULL) || (found != 0))) { if ((f=fopen(uplink->forwardRequestFile,"r")) == NULL) { w_log(LL_ERR, "areafix: cannot open forwardRequestFile \"%s\": %s", uplink->forwardRequestFile, strerror(errno)); continue; } isuplink = 1; if ((!hal)&&(link->availlist == AVAILLIST_UNIQUEONE)) xscatprintf(&report, "Available Area List from all uplinks:\r"); if ((!halcnt)||(link->availlist != AVAILLIST_UNIQUEONE)) { halcnt++; hal = realloc(hal, sizeof(ps_arealist)*halcnt); hal[halcnt-1] = newAreaList(); al = hal[halcnt-1]; w_log(LL_DEBUGW, __FILE__ ":%u: New item added to hal, halcnt = %u", __LINE__, halcnt); } while ((line = readLine(f)) != NULL) { line = trimLine(line); if (line[0] != '\0') { running = line; token = strseparate(&running, " \t\r\n"); rc = 0; if (uplink->numDfMask) rc |= tag_mask(token, uplink->dfMask, uplink->numDfMask); if (uplink->denyFwdFile) rc |= IsAreaAvailable(token,uplink->denyFwdFile,NULL,0); if (pattern) { /* if matches pattern and not reversed (or vise versa) */ if ((rc==0) &&(patimat(token, pattern)!=reversed)) addAreaListItem(al,0,0,token,running); } else { if (rc==0) addAreaListItem(al,0,0,token,running); } } nfree(line); } fclose(f); /* warning! do not ever use aka2str twice at once! */ sprintf(linkAka, "%s", aka2str(link->hisAka)); w_log( LL_AREAFIX, "areafix: Available Area List from %s %s to %s", aka2str(uplink->hisAka), (link->availlist == AVAILLIST_UNIQUEONE) ? "prepared": "sent", linkAka ); } if ((link->availlist != AVAILLIST_UNIQUEONE)||(j==(config->linkCount-1))) { if((hal)&&((hal[halcnt-1])->count)) if ((link->availlist != AVAILLIST_UNIQUE)||(isuplink)) { sortAreaListNoDupes(halcnt, hal, link->availlist != AVAILLIST_FULL); if ((hal[halcnt-1])->count) { line = formatAreaList(hal[halcnt-1],78,NULL); if (link->availlist != AVAILLIST_UNIQUEONE) xscatprintf(&report, "\rAvailable Area List from %s:\r", aka2str(uplink->hisAka)); if (line) xstrscat(&report, "\r", line,print_ch(77,'-'), "\r", NULL); nfree(line); } } if ((link->availlist != AVAILLIST_UNIQUE)||(j==(config->linkCount-1))) if (hal) { w_log(LL_DEBUGW, __FILE__ ":%u: hal freed, (%u items)", __LINE__, halcnt); for(;halcnt>0;halcnt--) freeAreaList(hal[halcnt-1]); nfree(hal); } } } if (report==NULL) { xstrcat(&report, "\r no links for creating Available Area List\r"); w_log(LL_AREAFIX, "areafix: no links for creating Available Area List"); } return report; } /* subscribe if (act==0), unsubscribe if (act==1), delete if (act==2) */ int forwardRequestToLink (char *areatag, s_link *uplink, s_link *dwlink, int act) { s_message *msg; char *base, pass[]="passthrough"; if (uplink->msg == NULL) { msg = makeMessage(uplink->ourAka, &(uplink->hisAka), config->sysop, uplink->RemoteRobotName ? uplink->RemoteRobotName : "areafix", uplink->areaFixPwd ? uplink->areaFixPwd : "\x00", 1, config->areafixReportsAttr); msg->text = createKludges(config, NULL, uplink->ourAka, &(uplink->hisAka), versionStr); if (config->areafixReportsFlags) xstrscat(&(msg->text), "\001FLAGS ", config->areafixReportsFlags, "\r", NULL); uplink->msg = msg; } else msg = uplink->msg; if (act==0) { if (getArea(config, areatag) == &(config->badArea)) { if(config->areafixQueueFile) { af_CheckAreaInQuery(areatag, &(uplink->hisAka), &(dwlink->hisAka), ADDFREQ); } else { base = uplink->msgBaseDir; if (config->createFwdNonPass==0) uplink->msgBaseDir = pass; /* create from own address */ if (isOurAka(config,dwlink->hisAka)) { uplink->msgBaseDir = base; } strUpper(areatag); autoCreate(areatag, uplink->hisAka, &(dwlink->hisAka)); uplink->msgBaseDir = base; } } xstrscat(&msg->text, "+", areatag, "\r", NULL); } else if (act==1) { xscatprintf(&(msg->text), "-%s\r", areatag); } else { /* delete area */ if (uplink->advancedAreafix) xscatprintf(&(msg->text), "~%s\r", areatag); else xscatprintf(&(msg->text), "-%s\r", areatag); } return 0; } #if 0 int delLinkFromString(char **lineOut, char *line, char *linkAddr) { char *startLink, *ptr, *tmp, *origLine, *comment, *eptr=NULL, *linkStr; unsigned int nodeAddr=0, rc=1, endLen=0; /* length of string where link ends */ origLine = safe_strdup(line); startLink = line; tmp = line; /* find comments */ for (comment = line; (comment = strchr(comment+1, CommentChar)) != NULL;) if (*(comment-1) == ' ' || *(comment-1) == '\t') break; /* make search string */ linkStr = safe_malloc(strlen(linkAddr)+3); strcpy(linkStr, linkAddr); ptr=strchr(linkStr, '.'); if (ptr==NULL) { nodeAddr = 1; ptr=linkStr+strlen(linkStr); } strcpy(ptr, ".*"); tmp = line; do { ptr = strstr(tmp+1, linkAddr); endLen = 0; if (ptr && comment && ptr>=comment) ptr = NULL; if (ptr) tmp = ptr + strlen(linkAddr); if (ptr && isspace(*(ptr-1))) { eptr = ptr+strlen(linkAddr); if (isspace(*eptr) || *eptr=='\0' || (nodeAddr && *eptr=='.' && eptr[1]=='0' && (isspace(eptr[2]) || eptr[2]=='\0'))) { startLink = ptr; endLen = eptr + 1 - line; rc = 0; /* all ok */ } } ptr = strstr(tmp+1, linkStr); if (ptr && comment && ptr>=comment) ptr = NULL; if (ptr) tmp = ptr + strlen(linkAddr); if (ptr && isspace(*(ptr-1))) { eptr = ptr+strlen(linkStr); if (isspace(*eptr) || *eptr=='\0') { startLink = ptr; endLen = eptr + 1 - line; rc = 2; /* found, but cannot unsubscribe */ } } } while (!endLen && tmp); if (rc == 0) { eptr = tmp; if (*eptr == '.' && eptr[1] == '0') eptr+=2; if (*eptr && isspace(*eptr)) eptr++; endLen = eptr - line; ptr = line + endLen; while (ptr) { tmp = strseparate(&ptr, " \t"); /* looking for link options... */ if (tmp == NULL) break; /* nothing found */ if (*tmp != '-') break; /* this is not option */ else { /* found link option */ /* if (!strncasecmp(tmp, "-r", 2) || */ /* !strncasecmp(tmp, "-w", 2) || */ /* !strncasecmp(tmp, "-mn", 3) || */ /* !strncasecmp(tmp, "-def", 4)) */ { endLen = ptr ? (ptr - line) : strlen(origLine); continue; } } } nfree(*lineOut); *lineOut = (char *) safe_calloc(strlen(origLine) + 1, 1); strncpy(*lineOut, origLine, startLink - line); if (endLen < strlen(origLine)) strcpy(*lineOut+(startLink-line), origLine+endLen); } nfree(origLine); nfree(linkStr); return rc; } #endif int changeconfig(char *fileName, s_area *area, s_link *link, int action) { char *cfgline=NULL, *token=NULL, *tmpPtr=NULL, *line=NULL, *buff=0; char *strbegfileName = fileName; long strbeg = 0, strend = -1; int rc=0; e_changeConfigRet nRet = I_ERR; char *areaName = area->areaName; w_log(LL_FUNC, __FILE__ "::changeconfig(%s,...)", fileName); if (init_conf(fileName)) return -1; w_log(LL_SRCLINE, __FILE__ ":%u:changeconfig() action=%i",__LINE__,action); while ((cfgline = configline()) != NULL) { line = sstrdup(cfgline); line = trimLine(line); line = stripComment(line); if (line[0] != 0) { line = shell_expand(line); line = tmpPtr = vars_expand(line); token = strseparate(&tmpPtr, " \t"); if (stricmp(token, "echoarea")==0) { token = strseparate(&tmpPtr, " \t"); if (*token=='\"' && token[strlen(token)-1]=='\"' && token[1]) { token++; token[strlen(token)-1]='\0'; } if (stricmp(token, areaName)==0) { fileName = safe_strdup(getCurConfName()); strend = get_hcfgPos(); if (strcmp(strbegfileName, fileName) != 0) strbeg = 0; break; } } } strbeg = get_hcfgPos(); strbegfileName = safe_strdup(getCurConfName()); w_log(LL_DEBUGF, __FILE__ ":%u:changeconfig() strbeg=%ld", __LINE__, strbeg); nfree(line); nfree(cfgline); } close_conf(); nfree(line); if (strend == -1) { /* impossible error occurred */ nfree(cfgline); nfree(fileName); return -1; } switch (action) { case 0: /* forward Request To Link */ if ((area->msgbType==MSGTYPE_PASSTHROUGH) && (!config->areafixQueueFile) && (area->downlinkCount==1) && (area->downlinks[0]->link->hisAka.point == 0)) { forwardRequestToLink(areaName, area->downlinks[0]->link, NULL, 0); } case 3: /* add link to existing area */ xscatprintf(&cfgline, " %s", aka2str(link->hisAka)); nRet = ADD_OK; break; case 1: /* remove link from area */ if ((area->msgbType==MSGTYPE_PASSTHROUGH) && (area->downlinkCount==1) && (area->downlinks[0]->link->hisAka.point == 0)) { forwardRequestToLink(areaName, area->downlinks[0]->link, NULL, 1); } case 7: if ((rc = DelLinkFromString(cfgline, link->hisAka)) == 1) { w_log(LL_ERR,"areafix: Unlink is not possible for %s from echo area %s", aka2str(link->hisAka), areaName); nRet = O_ERR; } else { nRet = DEL_OK; } break; case 2: /* makepass(f, fileName, areaName); */ case 4: /* delete area */ nfree(cfgline); nRet = DEL_OK; break; case 5: /* subscribe us to passthrough */ if ( fc_stristr(area->downlinks[0]->link->autoAreaCreateDefaults, "passthrough") ) { nRet = O_ERR; break; } w_log(LL_SRCLINE, __FILE__ "::changeconfig():%u",__LINE__); /* get area string */ buff = makeAreaParam(area->downlinks[0]->link , areaName, NULL ); nRet = ADD_OK; case 6: /* make area pass. */ if(action == 6) { buff = makeAreaParam(area->downlinks[0]->link , areaName, "passthrough" ); nRet = DEL_OK; } /* add all links */ token = NULL; token = strrchr(cfgline, '\"'); if(!token) token = cfgline; token = strstr(token, aka2str(area->downlinks[0]->link->hisAka)); if(!testAddr(token,area->downlinks[0]->link->hisAka)) token = strstr(token+1, aka2str(area->downlinks[0]->link->hisAka)); xstrcat( &buff, token-1); nfree(cfgline); cfgline = buff; break; default: break; } /* switch (action) */ w_log(LL_DEBUGF, __FILE__ ":%u:changeconfig() call InsertCfgLine(\"%s\",,%ld,%ld)", __LINE__, fileName, strbeg, strend); InsertCfgLine(fileName, cfgline, strbeg, strend); nfree(cfgline); nfree(fileName); w_log(LL_FUNC, __FILE__ "::changeconfig() rc=%i", nRet); return nRet; } static int compare_links_priority(const void *a, const void *b) { int ia = *((int*)a); int ib = *((int*)b); if(config->links[ia].forwardAreaPriority < config->links[ib].forwardAreaPriority) return -1; else if(config->links[ia].forwardAreaPriority > config->links[ib].forwardAreaPriority) return 1; else return 0; } int forwardRequest(char *areatag, s_link *dwlink, s_link **lastRlink) { unsigned int i, rc = 1; s_link *uplink; int *Indexes; unsigned int Requestable = 0; /* From Lev Serebryakov -- sort Links by priority */ Indexes = safe_malloc(sizeof(int)*config->linkCount); for (i = 0; i < config->linkCount; i++) { if (config->links[i].forwardRequests) Indexes[Requestable++] = i; } qsort(Indexes,Requestable,sizeof(Indexes[0]),compare_links_priority); i = 0; if(lastRlink) { /* try to find next requestable uplink */ for (; i < Requestable; i++) { uplink = &(config->links[Indexes[i]]); if( addrComp(uplink->hisAka, (*lastRlink)->hisAka) == 0) { /* we found lastRequestedlink */ i++; /* let's try next link */ break; } } } for (; i < Requestable; i++) { uplink = &(config->links[Indexes[i]]); if(lastRlink) *lastRlink = uplink; if (uplink->forwardRequests && (uplink->LinkGrp) ? grpInArray(uplink->LinkGrp,dwlink->AccessGrp,dwlink->numAccessGrp) : 1) { if ( (uplink->numDfMask) && (tag_mask(areatag, uplink->dfMask, uplink->numDfMask))) { rc = 2; continue; } if ( (uplink->denyFwdFile!=NULL) && (IsAreaAvailable(areatag,uplink->denyFwdFile,NULL,0))) { rc = 2; continue; } if (uplink->forwardRequestFile!=NULL) { /* first try to find the areatag in forwardRequestFile */ if (tag_mask(areatag, uplink->frMask, uplink->numFrMask) || IsAreaAvailable(areatag,uplink->forwardRequestFile,NULL,0)) { forwardRequestToLink(areatag,uplink,dwlink,0); rc = 0; } else { rc = 2; }/* found link with freqfile, but there is no areatag */ } else { rc = 0; if (uplink->numFrMask) /* found mask */ { if (tag_mask(areatag, uplink->frMask, uplink->numFrMask)) forwardRequestToLink(areatag,uplink,dwlink,0); else rc = 2; } else { /* unconditional forward request */ if (dwlink->denyUFRA==0) forwardRequestToLink(areatag,uplink,dwlink,0); else rc = 2; } }/* (uplink->forwardRequestFile!=NULL) */ if (rc==0) { /* ? */ nfree(Indexes); return rc; } }/* if (uplink->forwardRequests && (uplink->LinkGrp) ? */ }/* for (i = 0; i < Requestable; i++) { */ /* link with "forwardRequests on" not found */ nfree(Indexes); return rc; } /* test link for areas quantity limit exceed * return 0 if not limit exceed * else return not zero */ int limitCheck(s_link *link) { register unsigned int i,n; w_log(LL_FUNC, __FILE__ "::limitCheck()"); if (link->afixEchoLimit==0) return 0; for (i=n=0; iechoAreaCount; i++) if (0==subscribeCheck(config->echoAreas[i], link)) n++; i = n >= link->afixEchoLimit ; w_log(LL_FUNC, __FILE__ "::limitCheck() rc=%u", i); return i; } int isPatternLine(char *s) { if (strchr(s,'*') || strchr(s,'?')) return 1; return 0; } void fixRules (s_link *link, char *area) { char *fileName = NULL; if (!config->rulesDir) return; if (link->noRules) return; xscatprintf(&fileName, "%s%s.rul", config->rulesDir, strLower(makeMsgbFileName(config, area))); if (fexist(fileName)) { rulesCount++; rulesList = safe_realloc (rulesList, rulesCount * sizeof (char*)); rulesList[rulesCount-1] = safe_strdup (area); /* don't simply copy pointer because area may be */ /* removed while processing other commands */ } nfree (fileName); } char *subscribe(s_link *link, char *cmd) { unsigned int i, rc=4, found=0, matched=0; char *line, *an=NULL, *report = NULL; s_area *area=NULL; w_log(LL_FUNC, "%s::subscribe(...,%s)", __FILE__, cmd); line = cmd; if (line[0]=='+') line++; while (*line==' ') line++; if (*line=='+') line++; while (*line==' ') line++; if (strlen(line)>60 || !isValidConference(line)) { report = errorRQ(line); w_log(LL_FUNC, "%s::subscribe() FAILED (error request line) rc=%s", __FILE__, report); return report; } for (i=0; !found && rc!=6 && iechoAreaCount; i++) { area = &(config->echoAreas[i]); an = area->areaName; rc=subscribeAreaCheck(area, line, link); if (rc==4) continue; /* not match areatag, try next */ if (rc==1 && manualCheck(*area, link)) rc = 5; /* manual area/group/link */ if (rc!=0 && limitCheck(link)) rc = 6; /* areas limit exceed for link */ switch (rc) { case 0: /* already linked */ if (isPatternLine(line)) { matched = 1; } else { xscatprintf(&report, " %s %s already linked\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: %s already linked to %s", aka2str(link->hisAka), an); i = config->echoAreaCount; } break; case 1: /* not linked */ if( isOurAka(config,link->hisAka)) { if(area->msgbType==MSGTYPE_PASSTHROUGH) { int state = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,5); if( state == ADD_OK) { af_CheckAreaInQuery(an, NULL, NULL, DELIDLE); xscatprintf(&report," %s %s added\r",an,print_ch(49-strlen(an),'.')); w_log(LL_AREAFIX, "areafix: %s subscribed to %s",aka2str(link->hisAka),an); } else { xscatprintf(&report, " %s %s not subscribed\r",an,print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: %s not subscribed to %s , cause uplink",aka2str(link->hisAka),an); w_log(LL_AREAFIX, "areafix: %s has \"passthrough\" in \"autoAreaCreateDefaults\" for %s", an, aka2str(area->downlinks[0]->link->hisAka)); } } else { /* ??? (not passthrou echo) */ /* non-passthrough area for our aka means */ /* that we already linked to this area */ xscatprintf(&report, " %s %s already linked\r",an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: %s already linked to %s",aka2str(link->hisAka), an); } } else { if (changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,0)==ADD_OK) { Addlink(link, area, NULL); processPermissions(config); fixRules (link, area->areaName); af_CheckAreaInQuery(an, NULL, NULL, DELIDLE); xscatprintf(&report," %s %s added\r",an,print_ch(49-strlen(an),'.')); w_log(LL_AREAFIX, "areafix: %s subscribed to %s",aka2str(link->hisAka),an); if(cmNotifyLink) forwardRequestToLink(area->areaName,link, NULL, 0); } else { xscatprintf(&report," %s %s error. report to sysop!\r",an,print_ch(49-strlen(an),'.')); w_log(LL_AREAFIX, "areafix: %s not subscribed to %s",aka2str(link->hisAka),an); w_log(LL_ERR, "areafix: can't write to config file: %s!", strerror(errno)); }/* if (changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,3)==0) */ } if (!isPatternLine(line)) i = config->echoAreaCount; break; case 6: /* areas limit exceed for link */ break; default : /* rc = 2 not access */ if (!area->hide && !isPatternLine(line)) { w_log(LL_AREAFIX, "areafix: area %s -- no access for %s", an, aka2str(link->hisAka)); xscatprintf(&report," %s %s no access\r", an, print_ch(49-strlen(an), '.')); found=1; } if (area->hide && !isPatternLine(line)) found=1; break; } } if (rc!=0 && limitCheck(link)) rc = 6; /*double!*/ /* areas limit exceed for link */ if (rc==4 && !isPatternLine(line) && !found) { /* rc not equal 4 there! */ if (link->denyFRA==0) { /* try to forward request */ if ((rc=forwardRequest(line, link, NULL))==2) { xscatprintf(&report, " %s %s no uplinks to forward\r", line, print_ch(49-strlen(line), '.')); w_log( LL_AREAFIX, "areafix: %s - no uplinks to forward", line); } else if (rc==0) { xscatprintf(&report, " %s %s request forwarded\r", line, print_ch(49-strlen(line), '.')); w_log( LL_AREAFIX, "areafix: %s - request forwarded", line); if( !config->areafixQueueFile && isOurAka(config,link->hisAka)==0) { area = getArea(config, line); if ( !isLinkOfArea(link, area) ) { if(changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,3)==ADD_OK) { Addlink(link, area, NULL); processPermissions(config); fixRules (link, area->areaName); w_log( LL_AREAFIX, "areafix: %s subscribed to area %s", aka2str(link->hisAka),line); } else { xscatprintf( &report," %s %s error. report to sysop!\r", an, print_ch(49-strlen(an),'.') ); w_log( LL_AREAFIX, "areafix: %s not subscribed to %s", aka2str(link->hisAka),an); w_log(LL_ERR, "areafix: can't change config file: %s!", strerror(errno)); } } else w_log( LL_AREAFIX, "areafix: %s already subscribed to area %s", aka2str(link->hisAka), line ); } else { fixRules (link, line); } } } } if (rc == 6) { /* areas limit exceed for link */ w_log( LL_AREAFIX,"areafix: area %s -- no access (full limit) for %s", line, aka2str(link->hisAka)); xscatprintf(&report," %s %s no access (full limit)\r", line, print_ch(49-strlen(line), '.')); } if (matched) { if (report == NULL) w_log (LL_AREAFIX, "areafix: all areas matching %s are already linked", line); xscatprintf(&report, "All %sareas matching %s are already linked\r", report ? "other " : "", line); } else if ((report == NULL && found==0) || (found && area->hide)) { xscatprintf(&report," %s %s not found\r",line,print_ch(49-strlen(line),'.')); w_log( LL_AREAFIX, "areafix: area %s is not found",line); } w_log(LL_FUNC, "areafix::subscribe() OK"); return report; } char *errorRQ(char *line) { char *report = NULL; if (strlen(line)>48) { xstrscat(&report, " ", line, " .......... error line\r", NULL); } else xscatprintf(&report, " %s %s error line\r", line, print_ch(49-strlen(line),'.')); return report; } char *do_delete(s_link *link, s_area *area) { char *report = NULL, *an = area->areaName; unsigned int i=0; if(!link) { link = getLinkFromAddr(config, *area->useAka); while( !link && i < config->addrCount ) { link = getLinkFromAddr( config, config->addr[i] ); i++; } if(!link) return NULL; } /* unsubscribe from downlinks */ xscatprintf(&report, " %s %s deleted\r", an, print_ch(49-strlen(an), '.')); for (i=0; idownlinkCount; i++) { if (addrComp(area->downlinks[i]->link->hisAka, link->hisAka)) forwardRequestToLink(an, area->downlinks[i]->link, NULL, 2); } /* remove area from config-file */ if( changeconfig ((cfgFile) ? cfgFile : getConfigFileName(), area, link, 4) != DEL_OK) { w_log( LL_AREAFIX, "areafix: can't remove area from config: %s", strerror(errno)); } /* delete msgbase and dupebase for the area */ /* if (area->msgbType!=MSGTYPE_PASSTHROUGH) MsgDeleteBase(area->fileName, (word) area->msgbType); */ if (area->dupeCheck != dcOff && config->typeDupeBase != commonDupeBase) { char *dupename = createDupeFileName(area); if (dupename) { unlink(dupename); nfree(dupename); } } w_log( LL_AREAFIX, "areafix: area %s deleted by %s", an, aka2str(link->hisAka)); /* delete the area from in-core config */ for (i=0; iechoAreaCount; i++) { if (stricmp(config->echoAreas[i].areaName, an)==0) break; } if (iechoAreaCount && area==&(config->echoAreas[i])) { fc_freeEchoArea(area); for (; iechoAreaCount-1; i++) memcpy(&(config->echoAreas[i]), &(config->echoAreas[i+1]), sizeof(s_area)); config->echoAreaCount--; RebuildEchoAreaTree(config); } return report; } char *delete(s_link *link, char *cmd) { int rc; char *line, *report = NULL, *an; s_area *area; for (line = cmd + 1; *line == ' ' || *line == '\t'; line++); if (*line == 0) return errorRQ(cmd); area = getArea(config, line); if (area == &(config->badArea)) { xscatprintf(&report, " %s %s not found\r", line, print_ch(49-strlen(line), '.')); w_log(LL_AREAFIX, "areafix: area %s is not found", line); return report; } rc = subscribeCheck(*area, link); an = area->areaName; switch (rc) { case 0: break; case 1: xscatprintf(&report, " %s %s not linked\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: area %s is not linked to %s", an, aka2str(link->hisAka)); return report; case 2: xscatprintf(&report, " %s %s no access\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: area %s -- no access for %s", an, aka2str(link->hisAka)); return report; } if (link->LinkGrp == NULL || (area->group && strcmp(link->LinkGrp, area->group))) { xscatprintf(&report, " %s %s delete not allowed\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: area %s delete not allowed for %s", an, aka2str(link->hisAka)); return report; } return do_delete(link, area); } char *unsubscribe(s_link *link, char *cmd) { unsigned int i, rc = 2, j=(unsigned int)I_ERR, from_us=0, matched = 0; char *line, *an, *report = NULL; s_area *area; w_log(LL_FUNC,__FILE__ ":%u:unsubscribe() begin", __LINE__); line = cmd; if (line[1]=='-') return NULL; line++; while (*line==' ') line++; for (i = 0; i< config->echoAreaCount; i++) { area = &(config->echoAreas[i]); an = area->areaName; rc = subscribeAreaCheck(area, line, link); if (rc==4) continue; if (rc==0 && mandatoryCheck(*area,link)) rc = 5; if (isOurAka(config,link->hisAka)) { from_us = 1; rc = area->msgbType == MSGTYPE_PASSTHROUGH ? 1 : 0 ; } switch (rc) { case 0: if (from_us == 0) { unsigned int k; for (k=0; kdownlinkCount; k++) if (addrComp(link->hisAka, area->downlinks[k]->link->hisAka)==0 && area->downlinks[k]->defLink) return do_delete(link, area); RemoveLink(link, area, NULL); if ((area->msgbType == MSGTYPE_PASSTHROUGH) && (area->downlinkCount == 1) && (area->downlinks[0]->link->hisAka.point == 0)) { if(config->areafixQueueFile) { af_CheckAreaInQuery(an, &(area->downlinks[0]->link->hisAka), NULL, ADDIDLE); j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,7); } else { j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,1); } } else { j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,7); } if (j != DEL_OK) { w_log(LL_AREAFIX, "areafix: %s doesn't unlinked from %s", aka2str(link->hisAka), an); } else { w_log(LL_AREAFIX,"areafix: %s unlinked from %s",aka2str(link->hisAka),an); if(cmNotifyLink) forwardRequestToLink(area->areaName,link, NULL, 1); } } else { /* unsubscribing from own address - set area passtrough */ if (area->downlinkCount==0) { return do_delete(getLinkFromAddr(config,*(area->useAka)), area); } else if ((area->downlinkCount==1) && (area->downlinks[0]->link->hisAka.point == 0)) { if(config->areafixQueueFile) { af_CheckAreaInQuery(an, &(area->downlinks[0]->link->hisAka), NULL, ADDIDLE); } else { forwardRequestToLink(area->areaName, area->downlinks[0]->link, NULL, 1); } } j = changeconfig(cfgFile?cfgFile:getConfigFileName(),area,link,6); /* if ( (j == DEL_OK) && area->msgbType!=MSGTYPE_PASSTHROUGH ) */ if ( (j == DEL_OK) && area->fileName && area->killMsgBase) MsgDeleteBase(area->fileName, (word) area->msgbType); } if (j == DEL_OK){ xscatprintf(&report," %s %s unlinked\r",an,print_ch(49-strlen(an),'.')); }else xscatprintf(&report," %s %s error. report to sysop!\r", an, print_ch(49-strlen(an),'.') ); break; case 1: if (isPatternLine(line)) { matched = 1; continue; } if (area->hide) { i = config->echoAreaCount; break; } xscatprintf(&report, " %s %s not linked\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: area %s is not linked to %s", area->areaName, aka2str(link->hisAka)); break; case 5: xscatprintf(&report, " %s %s unlink is not possible\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: area %s -- unlink is not possible for %s", area->areaName, aka2str(link->hisAka)); break; default: break; } } if(config->areafixQueueFile) report = af_Req2Idle(line, report, link->hisAka); if (report == NULL) { if (matched) { xscatprintf(&report, " %s %s no areas to unlink\r", line, print_ch(49-strlen(line), '.')); w_log(LL_AREAFIX, "areafix: no areas to unlink"); } else { xscatprintf(&report, " %s %s not found\r", line, print_ch(49-strlen(line), '.')); w_log(LL_AREAFIX, "areafix: area %s is not found", line); } } w_log(LL_FUNC,__FILE__ ":%u:unsubscribe() end", __LINE__); return report; } char *pause_link(s_link *link) { char *tmp, *report = NULL; if ((link->Pause & EPAUSE) != EPAUSE) { if (Changepause((cfgFile) ? cfgFile : getConfigFileName(), link, 0,EPAUSE) == 0) return NULL; } xstrcat(&report, " System switched to passive\r"); tmp = linked (link); xstrcat(&report, tmp); nfree(tmp); return report; } char *resume_link(s_link *link) { char *tmp, *report = NULL; if ((link->Pause & EPAUSE) == EPAUSE) { if (Changepause((cfgFile) ? cfgFile : getConfigFileName(), link,0,EPAUSE) == 0) return NULL; } xstrcat(&report, " System switched to active\r"); tmp = linked (link); xstrcat(&report, tmp); nfree(tmp); return report; } char *info_link(s_link *link) { char *report=NULL, *ptr, linkAka[SIZE_aka2str]; char hisAddr[]="Your address: "; char ourAddr[]="AKA used here: "; char Arch[]="Compression: "; char Rsb[]="Reduced SEEN-BY: "; unsigned int i; sprintf(linkAka,aka2str(link->hisAka)); xscatprintf(&report, "Here is some information about our link:\r\r"); xscatprintf(&report, "%20s%s\r%20s%s\r%20s%s\r%20s", hisAddr, linkAka, ourAddr, aka2str(*link->ourAka), Rsb, link->reducedSeenBy?"on":"off", Arch); if (link->packerDef==NULL) xscatprintf(&report, "No packer ("); else xscatprintf(&report, "%s (", link->packerDef->packer); for (i=0; i < config->packCount; i++) xscatprintf(&report, "%s%s", config->pack[i].packer, (i+1 == config->packCount) ? "" : ", "); xscatprintf(&report, ")\r\r"); xscatprintf(&report, "Your system is %s\r", ((link->Pause & EPAUSE) == EPAUSE)?"passive":"active"); ptr = linked (link); xstrcat(&report, ptr); nfree(ptr); w_log(LL_AREAFIX, "areafix: link information sent to %s", aka2str(link->hisAka)); return report; } char *rescan(s_link *link, char *cmd) { unsigned int i, c, rc = 0; long rescanCount = -1, rcc; char *report = NULL, *line, *countstr, *an, *end; s_area *area; s_arealink *arealink; line = cmd; if (strncasecmp(cmd, "%rescan", 7)==0) line += strlen("%rescan"); if (*line == 0) return errorRQ(cmd); while (*line && (*line == ' ' || *line == '\t')) line++; if (*line == 0) return errorRQ(cmd); countstr = line; while (*countstr && (!isspace(*countstr))) countstr++; /* skip areatag */ while (*countstr && (*countstr == ' ' || *countstr == '\t')) countstr++; if (strncasecmp(countstr, "/R",2)==0) { countstr += 2; if (*countstr == '=') countstr++; } if (*countstr != '\0') { rescanCount = strtol(countstr, NULL, 10); } end = strpbrk(line, " \t"); if (end) *end = 0; if (*line == 0) return errorRQ(cmd); for (i=c=0; iechoAreaCount; i++) { rc=subscribeAreaCheck(&(config->echoAreas[i]), line, link); if (rc == 4) continue; area = &(config->echoAreas[i]); an = area->areaName; switch (rc) { case 0: if (area->msgbType == MSGTYPE_PASSTHROUGH) { xscatprintf(&report," %s %s no rescan possible\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: %s area no rescan possible to %s", an, aka2str(link->hisAka)); } else { arealink = getAreaLink(area, link->hisAka); if (arealink->export) { rcc = rescanEMArea(area, arealink, rescanCount); tossTempOutbound(config->tempOutbound); } else { rcc = 0; xscatprintf(&report," %s %s no access to export\r", an, print_ch(49-strlen(an), '.')); w_log(LL_AREAFIX, "areafix: %s -- no access to export for %s", an, aka2str(link->hisAka)); } xscatprintf(&report," %s %s rescanned %lu mails\r", an, print_ch(49-strlen(an), '.'), rcc); w_log(LL_AREAFIX,"areafix: %s rescanned %lu mails to %s", an, rcc, aka2str(link->hisAka)); } if (!isPatternLine(line)) i = config->echoAreaCount; break; case 1: if (isPatternLine(line)) continue; w_log(LL_AREAFIX, "areafix: %s area not linked for rescan to %s", area->areaName, aka2str(link->hisAka)); xscatprintf(&report, " %s %s not linked for rescan\r", an, print_ch(49-strlen(an), '.')); break; default: w_log(LL_AREAFIX, "areafix: %s area not access for %s", area->areaName, aka2str(link->hisAka)); break; } } if (report == NULL) { xscatprintf(&report," %s %s not linked for rescan\r", line, print_ch(49-strlen(line), '.')); w_log(LL_AREAFIX, "areafix: %s area not linked for rescan", line); } return report; } char *add_rescan(s_link *link, char *line) { char *report=NULL, *line2=NULL, *p; if (*line=='+') line++; while (*line==' ') line++; p = fc_stristr(line, " /R"); *p = '\0'; report = subscribe (link, line); *p = ' '; xstrscat(&line2,"%rescan ", line, NULL); xstrcat(&report, rescan(link, line2)); nfree(line2); *p = '\0'; return report; } char *packer(s_link *link, char *cmdline) { char *report=NULL; char *was=NULL; char *pattern = NULL; int reversed; UINT i; pattern = getPatternFromLine(cmdline, &reversed); if(pattern) { char *packerString=NULL; ps_pack packerDef = NULL; char *confName = NULL; long strbeg=0; long strend=0; for (i=0; i < config->packCount; i++) { if (stricmp(config->pack[i].packer,pattern) == 0) { packerDef = &(config->pack[i]); break; } } if( (i == config->packCount) && (stricmp("none",pattern) != 0) ) { xscatprintf(&report, "Packer '%s' was not found\r", pattern); return report; } if (link->packerDef==NULL) xstrcat(&was, "none"); else xstrcat(&was, link->packerDef->packer); xstrcat(&confName,(cfgFile) ? cfgFile : getConfigFileName()); FindTokenPos4Link(&confName, "Packer", link, &strbeg, &strend); xscatprintf(&packerString,"Packer %s",pattern); if( InsertCfgLine(confName, packerString, strbeg, strend) ) { link->packerDef = packerDef; } nfree(confName); nfree(packerString); } xstrcat( &report, "Here is some information about current & available packers:\r\r"); xstrcat( &report, "Compression: "); if (link->packerDef==NULL) xscatprintf(&report, "none ("); else xscatprintf(&report, "%s (", link->packerDef->packer); for (i=0; i < config->packCount; i++) xscatprintf(&report, "%s%s", config->pack[i].packer,(i+1 == config->packCount) ? "" : ", "); xscatprintf(&report, "%snone)\r", (i == 0) ? "" : ", "); if(was) { xscatprintf(&report, " was: %s\r", was); } return report; } char *rsb(s_link *link, char *cmdline) { int mode; /* 1 = RSB on, 0 - RSB off. */ char *param=NULL; /* RSB value. */ char *report=NULL; char *confName = NULL; long strbeg=0; long strend=0; param = getPatternFromLine(cmdline, &mode); /* extract rsb value (on or off) */ if (param == NULL) { xscatprintf(&report, "Invalid request: %s\rPlease read help.\r\r", cmdline); return report; } param = trimLine(param); if ((!strcmp(param, "0")) || (!strcasecmp(param, "off"))) mode = 0; else { if ((!strcmp(param, "1")) || (!strcasecmp(param, "on"))) mode = 1; else { xscatprintf(&report, "Unknown parameter for areafix %rsb command: %s\r. Please read help.\r\r", param); nfree(param); return report; } } nfree(param); if (link->reducedSeenBy == (UINT)mode) { xscatprintf(&report, "Redused SEEN-BYs had not been changed.\rCurrent value is '%s'\r\r", mode?"on":"off"); return report; } xstrcat(&confName,(cfgFile) ? cfgFile : getConfigFileName()); FindTokenPos4Link(&confName, "reducedSeenBy", link, &strbeg, &strend); xscatprintf(¶m, "reducedSeenBy %s", mode?"on":"off"); if( InsertCfgLine(confName, param, strbeg, strend) ) { xscatprintf(&report, "Redused SEEN-BYs is turned %s now\r\r", mode?"on":"off"); link->reducedSeenBy = mode; } nfree(param); nfree(confName); return report; } int tellcmd(char *cmd) { char *line; if (strncmp(cmd, "* Origin:", 9) == 0) return NOTHING; line = cmd; if (line && *line && (line[1]==' ' || line[1]=='\t')) return AFERROR; switch (line[0]) { case '%': line++; if (*line == '\000') return AFERROR; if (strncasecmp(line,"list",4)==0) return LIST; if (strncasecmp(line,"help",4)==0) return HELP; if (strncasecmp(line,"avail",5)==0) return AVAIL; if (strncasecmp(line,"all",3)==0) return AVAIL; if (strncasecmp(line,"unlinked",8)==0) return UNLINK; if (strncasecmp(line,"linked",6)==0) return QUERY; if (strncasecmp(line,"query",5)==0) return QUERY; if (strncasecmp(line,"pause",5)==0) return PAUSE; if (strncasecmp(line,"resume",6)==0) return RESUME; if (strncasecmp(line,"info",4)==0) return INFO; if (strncasecmp(line,"packer",6)==0) return PACKER; if (strncasecmp(line,"compress",8)==0) return PACKER; if (strncasecmp(line,"rsb",3)==0) return RSB; if (strncasecmp(line,"rescan", 6)==0) { if (line[6] == '\0') { rescanMode=1; return NOTHING; } else { return RESCAN; } } return AFERROR; case '\001': return NOTHING; case '\000': return NOTHING; case '-' : if (line[1]=='-' && line[2]=='-') return DONE; if (line[1]=='\000') return AFERROR; if (strchr(line,' ') || strchr(line,'\t')) return AFERROR; return DEL; case '~' : return REMOVE; case '+': if (line[1]=='\000') return AFERROR; default: if (fc_stristr(line, " /R")!=NULL) return ADD_RSC; /* add & rescan */ return ADD; } return 0;/* - Unreachable */ } char *processcmd(s_link *link, char *line, int cmd) { char *report; w_log(LL_FUNC, __FILE__ "::processcmd()"); switch (cmd) { case NOTHING: return NULL; case DONE: RetFix=DONE; return NULL; case LIST: report = list (link, line); RetFix=LIST; break; case HELP: report = help (link); RetFix=HELP; break; case ADD: report = subscribe (link, line); RetFix=ADD; break; case DEL: report = unsubscribe (link, line); RetFix=STAT; break; case REMOVE: report = delete (link, line); RetFix=STAT; break; case AVAIL: report = available (link, line); RetFix=AVAIL; break; case UNLINK: report = unlinked (link); RetFix=UNLINK; break; case QUERY: report = linked (link); RetFix=QUERY; break; case PAUSE: report = pause_link (link); RetFix=PAUSE; break; case RESUME: report = resume_link (link); RetFix=RESUME; break; case PACKER: report = packer (link, line); RetFix=PACKER; break; case RSB: report = rsb (link, line); RetFix=RSB; break; case INFO: report = info_link(link); RetFix=INFO; break; case RESCAN: report = rescan(link, line); RetFix=STAT; break; case ADD_RSC: report = add_rescan(link, line); RetFix=STAT; break; case AFERROR: report = errorRQ(line); RetFix=STAT; break; default: return NULL; } w_log(LL_FUNC, __FILE__ "::processcmd() OK"); return report; } void preprocText(char *split, s_message *msg) { char *orig = (config->areafixOrigin) ? config->areafixOrigin : config->origin; msg->text = createKludges(config, NULL, &msg->origAddr, &msg->destAddr, versionStr); /* xstrcat(&(msg->text), "\001FLAGS NPD DIR\r"); */ if (config->areafixReportsFlags) xstrscat(&(msg->text), "\001FLAGS ", config->areafixReportsFlags, "\r", NULL); xscatprintf(&split, "\r--- %s areafix\r", versionStr); if (orig && orig[0]) { xscatprintf(&split, " * Origin: %s (%s)\r", orig, aka2str(msg->origAddr)); } xstrcat(&(msg->text), split); msg->textLength=(int)strlen(msg->text); nfree(split); } char *textHead(void) { char *text_head = NULL; xscatprintf(&text_head, " Area%sStatus\r", print_ch(48,' ')); xscatprintf(&text_head, " %s -------------------------\r",print_ch(50, '-')); return text_head; } char *areaStatus(char *report, char *preport) { if (report == NULL) report = textHead(); xstrcat(&report, preport); nfree(preport); return report; } /* report already nfree() after this function */ void RetMsg(s_message *msg, s_link *link, char *report, char *subj) { char *tab = config->intab, *text, *split, *p, *newsubj = NULL; char splitted[]=" > message splitted..."; char *splitStr = config->areafixSplitStr; int len, msgsize = config->areafixMsgSize * 1024, partnum=0; s_message *tmpmsg; config->intab = NULL; text = report; if (msg->text) xstrscat(&text,"\rFollowing is the original message text\r--------------------------------------\r",msg->text,"\r--------------------------------------\r",NULL); else xstrscat(&text,"\r",NULL); while (text) { len = strlen(text); if (msgsize == 0 || len <= msgsize) { split = text; text = NULL; if (partnum) { /* last part of splitted msg */ partnum++; xstrcat(&text,split); split = text; text = NULL; nfree(report); } } else { p = text + msgsize; while (p > text && *p != '\r') p--; if (p == text) { p = text + msgsize; while (p > text && *p != ' ' && *p != '\t') p--; if (p == text) p = text + msgsize; } *p = '\000'; len = p - text; split = (char*)safe_malloc(len+strlen(splitStr ? splitStr : splitted)+3+1); memcpy(split,text,len); strcpy(split+len,"\r\r"); strcat(split, (splitStr) ? splitStr : splitted); strcat(split,"\r"); text = p+1; partnum++; } if (partnum) xscatprintf(&newsubj, "%s (%d)", subj, partnum); else newsubj = subj; if (config->areafixFromName == NULL) tmpmsg = makeMessage(link->ourAka, &(link->hisAka), msg->toUserName, msg->fromUserName, newsubj, 1, config->areafixReportsAttr); else tmpmsg = makeMessage(link->ourAka, &(link->hisAka), config->areafixFromName, msg->fromUserName, newsubj, 1, config->areafixReportsAttr); preprocText(split, tmpmsg); processNMMsg(tmpmsg, NULL, getNetMailArea(config,config->robotsArea), 0, MSGLOCAL); writeEchoTossLogEntry(config->robotsArea?config->robotsArea:config->netMailAreas[0].areaName); closeOpenedPkt(); freeMsgBuffers(tmpmsg); nfree(tmpmsg); if (partnum) nfree(newsubj); } config->intab = tab; } void RetRules (s_message *msg, s_link *link, char *areaName) { FILE *f=NULL; char *fileName = NULL; char *text=NULL, *subj=NULL; char *msg_text; long len=0; int nrul=0; xscatprintf(&fileName, "%s%s.rul", config->rulesDir, strLower(makeMsgbFileName(config, areaName))); for (nrul=0; nrul<=9 && (f = fopen (fileName, "rb")); nrul++) { len = fsize (fileName); text = safe_malloc (len+1); fread (text, len, 1, f); fclose (f); text[len] = '\0'; if (nrul==0) { xscatprintf(&subj, "Rules of %s", areaName); w_log(LL_AREAFIX, "areafix: send '%s' as rules for area '%s'", fileName, areaName); } else { xscatprintf(&subj, "Echo related text #%d of %s", nrul, areaName); w_log(LL_AREAFIX, "areafix: send '%s' as text %d for area '%s'", fileName, nrul, areaName); } /* prevent "Following original message text" in rules msgs */ msg_text = msg->text; msg->text= NULL; RetMsg(msg, link, text, subj); /* preserve original message text */ msg->text= msg_text; nfree (subj); /* nfree (text); don't free text because RetMsg() free it */ fileName[strlen(fileName)-1] = nrul+'1'; } if (nrul==0) { /* couldn't open any rules file while first one exists! */ w_log(LL_ERR, "areafix: can't open file '%s' for reading: %s", fileName, strerror(errno)); } nfree (fileName); } void sendAreafixMessages() { s_link *link = NULL; s_message *linkmsg; unsigned int i; for (i = 0; i < config->linkCount; i++) { if (config->links[i].msg == NULL) continue; link = &(config->links[i]); linkmsg = link->msg; xscatprintf(&(linkmsg->text), " \r--- %s areafix\r", versionStr); linkmsg->textLength = strlen(linkmsg->text); w_log(LL_AREAFIX, "areafix: write netmail msg for %s", aka2str(link->hisAka)); processNMMsg(linkmsg, NULL, getNetMailArea(config,config->robotsArea), 0, MSGLOCAL); closeOpenedPkt(); freeMsgBuffers(linkmsg); nfree(linkmsg); link->msg = NULL; } } /* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- */ int processAreaFix(s_message *msg, s_pktHeader *pktHeader, unsigned force_pwd) { unsigned int security=1, notforme = 0; s_link *link = NULL; s_link *tmplink = NULL; /* s_message *linkmsg; */ s_pktHeader header; char *token, *report=NULL, *preport = NULL; char *textBuff = NULL,*tmp; int nr; w_log(LL_FUNC, __FILE__ "::processAreaFix()"); RetFix = NOTHING; /* 1st security check */ if (pktHeader) security=addrComp(msg->origAddr, pktHeader->origAddr); else { makePktHeader(NULL, &header); pktHeader = &header; pktHeader->origAddr = msg->origAddr; pktHeader->destAddr = msg->destAddr; security = 0; } if (security) security=1; /* different pkt and msg addresses */ /* find link */ link=getLinkFromAddr(config, msg->origAddr); /* if keyword allowPktAddrDiffer for this link is on, */ /* we allow the addresses in PKT and MSG header differ */ if (link!=NULL) if (link->allowPktAddrDiffer == pdOn) security = 0; /* OK */ /* is this for me? */ if (link!=NULL) notforme=addrComp(msg->destAddr, *link->ourAka); else if (!security) security=4; /* link == NULL; unknown system */ if (notforme && !security) security=5; /* message to wrong AKA */ #if 0 /* we're process only our messages here */ /* ignore msg for other link (maybe this is transit...) */ if (notforme || (link==NULL && security==1)) { w_log(LL_FUNC, __FILE__ "::processAreaFix() call processNMMsg() and return"); nr = processNMMsg(msg, pktHeader, NULL, 0, 0); closeOpenedPkt(); return nr; } #endif /* 2nd security check. link, areafixing & password. */ if (!security && !force_pwd) { if (link->AreaFix==1) { if (link->areaFixPwd!=NULL) { if (stricmp(link->areaFixPwd,msg->subjectLine)==0) security=0; else security=3; /* password error */ } } else security=2; /* areafix is turned off */ } /* remove kluges */ tmp = msg->text; token = strseparate (&tmp,"\n\r"); while(token != NULL) { if( !strcmp(token,"---") || !strncmp(token,"--- ",4) ) /* stop on tearline ("---" or "--- text") */ break; if( token[0] != '\001' ) xstrscat(&textBuff,token,"\r",NULL); token = strseparate (&tmp,"\n\r"); } nfree(msg->text); msg->text = textBuff; if (!security) { textBuff = safe_strdup(msg->text); tmp = textBuff; token = strseparate (&tmp, "\n\r"); while(token != NULL) { while ((*token == ' ') || (*token == '\t')) token++; while(isspace(token[strlen(token)-1])) token[strlen(token)-1]='\0'; w_log(LL_AREAFIX, "Process command: %s", token); preport = processcmd( link, token, tellcmd (token) ); if (preport != NULL) { switch (RetFix) { case LIST: RetMsg(msg, link, preport, "Areafix reply: list request"); break; case HELP: RetMsg(msg, link, preport, "Areafix reply: help request"); break; case ADD: report = areaStatus(report, preport); if (rescanMode) { preport = processcmd( link, token, RESCAN ); if (preport != NULL) report = areaStatus(report, preport); } break; case AVAIL: RetMsg(msg, link, preport, "Areafix reply: available areas"); break; case UNLINK: RetMsg(msg, link, preport, "Areafix reply: unlinked request"); break; case QUERY: RetMsg(msg, link, preport, "Areafix reply: linked request"); break; case PAUSE: RetMsg(msg, link, preport, "Areafix reply: pause request"); break; case RESUME: RetMsg(msg, link, preport, "Areafix reply: resume request"); break; case INFO: RetMsg(msg, link, preport, "Areafix reply: link information"); break; case PACKER: RetMsg(msg, link, preport, "Areafix reply: packer change request"); break; case RSB: RetMsg(msg, link, preport, "Areafix reply: redused seen-by change request"); break; case STAT: report = areaStatus(report, preport); break; default: w_log(LL_ERR,"Unknown areafix command:%s", token); break; } } /* end if (preport != NULL) */ token = strseparate (&tmp, "\n\r"); if (RetFix==DONE) token=NULL; } /* end while (token != NULL) */ nfree(textBuff); } else { if (link == NULL) { tmplink = (s_link*) safe_malloc(sizeof(s_link)); memset(tmplink, '\0', sizeof(s_link)); tmplink->ourAka = &(msg->destAddr); tmplink->hisAka.zone = msg->origAddr.zone; tmplink->hisAka.net = msg->origAddr.net; tmplink->hisAka.node = msg->origAddr.node; tmplink->hisAka.point = msg->origAddr.point; link = tmplink; } /* security problem */ switch (security) { case 1: xscatprintf(&report, " \r different pkt and msg addresses\r"); break; case 2: xscatprintf(&report, " \r areafix is turned off\r"); break; case 3: xscatprintf(&report, " \r password error\r"); break; case 4: xscatprintf(&report, " \r your system is unknown\r"); break; case 5: xscatprintf(&report, " \r message sent to wrong AKA\r"); break; default: xscatprintf(&report, " \r unknown error. mail to sysop.\r"); break; } RetMsg(msg, link, report, "Areafix reply: security violation"); w_log(LL_AREAFIX, "areafix: security violation from %s", aka2str(link->hisAka)); nfree(tmplink); w_log(LL_FUNC, __FILE__ ":%u:processAreaFix() rc=1", __LINE__); return 1; } if ( report != NULL ) { if (config->areafixQueryReports) { preport = linked (link); xstrcat(&report, preport); nfree(preport); } RetMsg(msg, link, report, "Areafix reply: node change request"); } if (rulesCount) { for (nr=0; nr < rulesCount; nr++) { if (rulesList && rulesList[nr]) { RetRules (msg, link, rulesList[nr]); nfree (rulesList[nr]); } } nfree (rulesList); rulesCount=0; } w_log(LL_AREAFIX, "Areafix: successfully done for %s",aka2str(link->hisAka)); /* send msg to the links (forward requests to areafix) */ sendAreafixMessages(); w_log(LL_FUNC, __FILE__ "::processAreaFix() end (rc=1)"); return 1; } void MsgToStruct(HMSG SQmsg, XMSG xmsg, s_message *msg) { /* convert header */ msg->attributes = xmsg.attr; msg->origAddr.zone = xmsg.orig.zone; msg->origAddr.net = xmsg.orig.net; msg->origAddr.node = xmsg.orig.node; msg->origAddr.point = xmsg.orig.point; msg->destAddr.zone = xmsg.dest.zone; msg->destAddr.net = xmsg.dest.net; msg->destAddr.node = xmsg.dest.node; msg->destAddr.point = xmsg.dest.point; strcpy((char *)msg->datetime, (char *) xmsg.__ftsc_date); xstrcat(&(msg->subjectLine), (char *) xmsg.subj); xstrcat(&(msg->toUserName), (char *) xmsg.to); xstrcat(&(msg->fromUserName), (char *) xmsg.from); msg->textLength = MsgGetTextLen(SQmsg); xstralloc(&(msg->text),msg->textLength+1); MsgReadMsg(SQmsg, NULL, 0, msg->textLength, (unsigned char *) msg->text, 0, NULL); msg->text[msg->textLength] = '\0'; } void afix(hs_addr addr, char *cmd) { HAREA netmail; HMSG SQmsg; unsigned long highmsg, i; XMSG xmsg; hs_addr dest; s_message msg, *tmpmsg; int k, startarea = 0, endarea = config->netMailAreaCount; s_area *area; char *name = config->robotsArea; s_link *link; w_log(LL_FUNC, __FILE__ "::afix() begin"); w_log(LL_INFO, "Start AreaFix..."); if ((area = getNetMailArea(config, name)) != NULL) { startarea = area - config->netMailAreas; endarea = startarea + 1; } if (cmd) { link = getLinkFromAddr(config, addr); if (link) { if (cmd && strlen(cmd)) { tmpmsg = makeMessage(&addr, link->ourAka, link->name, link->RemoteRobotName ? link->RemoteRobotName : "Areafix", link->areaFixPwd ? link->areaFixPwd : "", 1, config->areafixReportsAttr); tmpmsg->text = safe_strdup(cmd); processAreaFix(tmpmsg, NULL, 1); freeMsgBuffers(tmpmsg); } else w_log(LL_WARN, "areafix: empty areafix command from %s", aka2str(addr)); } else w_log(LL_ERR, "areafix: no such link in config: %s!", aka2str(addr)); } else for (k = startarea; k < endarea; k++) { netmail = MsgOpenArea((unsigned char *) config->netMailAreas[k].fileName, MSGAREA_NORMAL, /*config -> netMailArea.fperm, config -> netMailArea.uid, config -> netMailArea.gid,*/ (word)config -> netMailAreas[k].msgbType); if (netmail != NULL) { highmsg = MsgGetHighMsg(netmail); w_log(LL_INFO,"Scanning %s",config->netMailAreas[k].areaName); /* scan all Messages and test if they are already sent. */ for (i=1; i<= highmsg; i++) { SQmsg = MsgOpenMsg(netmail, MOPEN_RW, i); /* msg does not exist */ if (SQmsg == NULL) continue; MsgReadMsg(SQmsg, &xmsg, 0, 0, NULL, 0, NULL); cvtAddr(xmsg.dest, &dest); /* if not read and for us -> process AreaFix */ striptwhite((char*)xmsg.to); if (((xmsg.attr & MSGREAD) != MSGREAD) && (isOurAka(config,dest)) && (strlen((char*)xmsg.to)>0) && fc_stristr(config->areafixNames,(char*)xmsg.to)) { memset(&msg,'\0',sizeof(s_message)); MsgToStruct(SQmsg, xmsg, &msg); processAreaFix(&msg, NULL, 0); if (config->areafixKillRequests) { MsgCloseMsg(SQmsg); MsgKillMsg(netmail, i--); } else { xmsg.attr |= MSGREAD; MsgWriteMsg(SQmsg, 0, &xmsg, NULL, 0, 0, 0, NULL); MsgCloseMsg(SQmsg); } freeMsgBuffers(&msg); } else MsgCloseMsg(SQmsg); } MsgCloseArea(netmail); } else { w_log(LL_ERR, "Could not open %s", config->netMailAreas[k].areaName); } } w_log(LL_FUNC, __FILE__ "::afix() end"); } int unsubscribeFromPausedEchoAreas(s_link *link) { unsigned i,j; char *text = NULL; s_area *area; s_message *tmpmsg; for (i=0; iechoAreaCount; i++) { area = &(config->echoAreas[i]); if ((area->msgbType & MSGTYPE_PASSTHROUGH) && isLinkOfArea(link,area)) { /* unsubscribe only if uplink & auto-paused downlink presents */ if (area->downlinkCount==2) { if ((j = isAreaLink(link->hisAka, area)) != -1) { /* don't touch mandatory links */ if (area->downlinks[j]->mandatory) continue; /* add area for unsubscribe */ xstrscat(&text,"-",area->areaName,"\r",NULL); } } } } if (text) { tmpmsg = makeMessage(&(link->hisAka), link->ourAka, link->name, "areafix", link->areaFixPwd, 1, config->areafixReportsAttr); tmpmsg->text = text; processAreaFix(tmpmsg, NULL, 0); freeMsgBuffers(tmpmsg); nfree(tmpmsg); } return 0; } void autoPassive() { time_t time_cur, time_test; struct stat stat_file; s_message *msg; FILE *f; char *line, *path; unsigned int i; for (i = 0; i < config->linkCount; i++) { if (config->links[i].autoPause==0 || (config->links[i].Pause == (EPAUSE|FPAUSE)) ) continue; if (createOutboundFileName(&(config->links[i]), config->links[i].echoMailFlavour, FLOFILE) == 0) { f = fopen(config->links[i].floFile, "rt"); if (f) { while ((line = readLine(f)) != NULL) { line = trimLine(line); path = line; if (!isArcMail(path)) { nfree(line); continue; } if (*path && (*path == '^' || *path == '#')) { path++; /* set Pause if files stored only in outbound */ if (*path && strncmp(config->outbound,path,strlen(config->outbound)-1)==0 && stat(path, &stat_file) != -1) { time_cur = time(NULL); if (time_cur > stat_file.st_mtime) { time_test = (time_cur - stat_file.st_mtime)/3600; } else { /* buggly time on file, anyway don't autopause on it */ time_test = 0; } if (time_test >= (time_t)(config->links[i].autoPause*24)) { w_log(LL_AREAFIX, "autopause: the file %s is %d days old", path, time_test/24); if (Changepause((cfgFile) ? cfgFile : getConfigFileName(), &(config->links[i]), 1, config->links[i].Pause^(EPAUSE|FPAUSE))) { msg = makeMessage(config->links[i].ourAka, &(config->links[i].hisAka), versionStr,config->links[i].name, "AutoPassive", 1, config->areafixReportsAttr); msg->text = createKludges(config, NULL, config->links[i].ourAka, &(config->links[i].hisAka), versionStr); if (config->areafixReportsFlags) xstrscat(&msg->text, "\001FLAGS ", config->areafixReportsFlags, "\r", NULL); xstrcat(&msg->text, "\r System switched to passive, your subscription are paused.\r\r" " You are being unsubscribed from echo areas with no downlinks besides you!\r\r" " When you wish to continue receiving echomail, please send requests\r" " to AreaFix containing the %RESUME command."); xscatprintf(&msg->text, "\r\r--- %s autopause\r", versionStr); msg->textLength = strlen(msg->text); processNMMsg(msg, NULL, getNetMailArea(config,config->robotsArea), 0, MSGLOCAL); closeOpenedPkt(); freeMsgBuffers(msg); nfree(msg); /* unsubscribe link from areas without non-paused links */ /* use "hptkill -y" or "hptkill -yp" to fulfill this purpose unsubscribeFromPausedEchoAreas(&(config->links[i])); */ } /* end changepause */ nfree(line); /* fclose(f); file closed after endwhile */ break; } } /* endif */ } /* endif ^# */ nfree(line); } /* endwhile */ fclose(f); } /* endif */ nfree(config->links[i].floFile); remove(config->links[i].bsyFile); nfree(config->links[i].bsyFile); } nfree(config->links[i].pktFile); nfree(config->links[i].packFile); } /* endfor */ } int relink (char *straddr) { s_link *researchLink = NULL; unsigned int count, areasArraySize; s_area **areasIndexArray = NULL; struct _minf m; /* parse config */ if (config==NULL) processConfig(); if ( initSMAPI == -1 ) { /* init SMAPI */ initSMAPI = 0; m.req_version = 0; m.def_zone = (UINT16) config->addr[0].zone; if (MsgOpenApi(&m) != 0) { exit_hpt("MsgApiOpen Error",1); } } w_log(LL_START, "Start relink..."); if (straddr) researchLink = getLink(config, straddr); else { w_log(LL_ERR, "No address"); return 1; } if ( researchLink == NULL ) { w_log(LL_ERR, "Unknown link address %s", straddr); return 1; } areasArraySize = 0; areasIndexArray = (s_area **) safe_malloc (sizeof(s_area *) * (config->echoAreaCount + config->localAreaCount + 1)); for (count = 0; count < config->echoAreaCount; count++) if ( isLinkOfArea(researchLink, &config->echoAreas[count])) { areasIndexArray[areasArraySize] = &config->echoAreas[count]; areasArraySize++; w_log(LL_AREAFIX, "Echo %s from link %s refreshed", config->echoAreas[count].areaName, aka2str(researchLink->hisAka)); } if ( areasArraySize > 0 ) { s_message *msg; msg = makeMessage(researchLink->ourAka, &researchLink->hisAka, versionStr, researchLink->RemoteRobotName ? researchLink->RemoteRobotName : "areafix", researchLink->areaFixPwd ? researchLink->areaFixPwd : "", 1, config->areafixReportsAttr); msg->text = createKludges(config,NULL,researchLink->ourAka, &researchLink->hisAka,versionStr); if (config->areafixReportsFlags) xstrscat(&(msg->text), "\001FLAGS ", config->areafixReportsFlags, "\r",NULL); for ( count = 0 ; count < areasArraySize; count++ ) { if ((areasIndexArray[count]->downlinkCount <= 1) && (areasIndexArray[count]->msgbType & MSGTYPE_PASSTHROUGH)) xscatprintf(&(msg->text), "-%s\r",areasIndexArray[count]->areaName); else xscatprintf(&(msg->text), "+%s\r",areasIndexArray[count]->areaName); } xscatprintf(&(msg->text), " \r--- %s areafix\r", versionStr); msg->textLength = strlen(msg->text); w_log(LL_AREAFIX, "'Refresh' message created to `%s`", researchLink->RemoteRobotName ? researchLink->RemoteRobotName : "areafix"); processNMMsg(msg, NULL, getNetMailArea(config,config->robotsArea), 1, MSGLOCAL|MSGKILL); closeOpenedPkt(); freeMsgBuffers(msg); nfree(msg); w_log(LL_AREAFIX, "Total request relink %i area(s)",areasArraySize); } nfree(areasIndexArray); /* deinit SMAPI */ MsgCloseApi(); return 0; }