1 /*
2  *      hptkill - areas killer for high Portable Tosser (hpt)
3  *      by Serguei Revtov 2:5021/11.10 || 2:5021/19.1
4  *      Some code was taken from hpt/src/areafix.c
5  *
6  * This file is part of HPT.
7  *
8  * HPT is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU General Public License as published by the
10  * Free Software Foundation; either version 2, or (at your option) any
11  * later version.
12  *
13  * HPT is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with HPT; see the file COPYING.  If not, write to the Free
20  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
21  *****************************************************************************
22  * $Id$
23  */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <ctype.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <time.h>
33 #include <string.h>
34 
35 #include <huskylib/compiler.h>
36 
37 #ifdef HAS_PROCESS_H
38 #  include <process.h>
39 #endif
40 
41 #ifdef HAS_UNISTD_H
42 #  include <unistd.h>
43 #  include <strings.h>
44 #endif
45 
46 #ifdef HAS_IO_H
47 #  include <io.h>
48 #endif
49 
50 #ifdef HAS_SHARE_H
51 #  include <share.h>
52 #endif
53 
54 #ifdef HAS_DOS_H
55 #  include <dos.h>
56 #endif
57 
58 #include <smapi/msgapi.h>
59 #include <fidoconf/fidoconf.h>
60 #include <fidoconf/common.h>
61 #include <huskylib/xstr.h>
62 #include <huskylib/log.h>
63 #include <fidoconf/afixcmd.h>
64 
65 #include "version.h"
66 
67 s_fidoconfig *config;
68 s_robot *robot;
69 
70 FILE *outlog;
71 char *versionStr;
72 
73 typedef enum senduns { eNobody, eFirstLink, eNodes, eAll} e_senduns;
74 
75 e_senduns sendUnSubscribe = eNodes;
76 int     delFromConfig = 0;
77 int     eraseBase = 1;
78 int     killPass = 0;
79 int     createDupe = 0;
80 static  time_t  tnow;
81 const   long    secInDay = 3600*24;
82 
83 
usage(void)84 void usage(void) {
85 
86     printf(
87     "hptkill is a tool for removing echoareas\n"
88     "Usage: hptkill [options] [areaNameMask ...]\n"
89     "Options:"
90     "\t  -c config-file - specify alternate config file\n"
91     "\t  -1 - send unsubscribe message to the first link only\n"
92     "\t  -n - don't send unsubscribe messages\n"
93     "\t  -a - send unsubscribe messages to all subscribed links\n"
94     "\t  -d - delete area from config file\n"
95     "\t  -s - save (don't erase) message & dupe bases\n"
96     "\t  -f file - read areas list from file in addition to args\n"
97     "\t  -f -    - read areas list from stdin in addition to args\n"
98     "\t  -p - find & kill passthrough echoareas with <=1 links\n"
99     "\t  -pp - same as -p including paused links\n"
100     "\t  -y - find & kill ALL echoareas with <=1 links\n"
101     "\t  -yp - same as -y including paused links\n"
102     "\t  -o num - kill passthrough areas with dupebases older than"
103 	"\t\t\t'num' days\n"
104     "\t  -O num - same as -o but kill areas without dupebases\n"
105     "\t  -l file - with -o/-O write out file with list of areas"
106 	"\t\t\twithout dupebases\n"
107     "\t  -C - create empty dupebase if it doesn't exist\n"
108     "\nDefault settings:\n"
109     " -  send unsubscribe message only to subcribed links\n"
110     " -  leave config unchanged\n"
111     " -  erase message & dupe bases\n");
112     exit(-1);
113 
114 }
115 
changeconfig(char * fileName,s_area * area)116 int changeconfig(char *fileName, s_area *area) {
117     char *cfgline=NULL, *token=NULL, *tmpPtr=NULL, *line=NULL;
118     long strbeg = 0, strend = -1;
119 
120     char *areaName = area->areaName;
121 
122     w_log(LL_FUNC, __FILE__ "::changeconfig(%s,...)", fileName);
123 
124     if (init_conf(fileName))
125 		return -1;
126 
127     while ((cfgline = configline()) != NULL) {
128         line = sstrdup(cfgline);
129         line = trimLine(line);
130         line = stripComment(line);
131         if (line[0] != 0) {
132             line = shell_expand(line);
133             line = tmpPtr = vars_expand(line);
134             token = strseparate(&tmpPtr, " \t");
135             if (stricmp(token, "echoarea")==0) {
136                 token = strseparate(&tmpPtr, " \t");
137                 if (*token=='\"' && token[strlen(token)-1]=='\"' && token[1]) {
138                     token++;
139                     token[strlen(token)-1]='\0';
140                 }
141                 if (stricmp(token, areaName)==0) {
142                     fileName = sstrdup(getCurConfName());
143                     strend = get_hcfgPos();
144                     if(strbeg > strend) strbeg = 0;
145                     break;
146                 }
147             }
148         }
149         strbeg = get_hcfgPos();
150         nfree(line);
151         nfree(cfgline);
152     }
153     close_conf();
154     nfree(line);
155     if (strend == -1) { /* "Never happens" */
156         nfree(cfgline);
157         nfree(fileName);
158         return -1;
159     }
160     nfree(cfgline);
161     InsertCfgLine(fileName, cfgline, strbeg, strend);
162     nfree(fileName);
163     return 0;
164 }
165 
166 
167 
putMsgInArea(s_area * echo,s_message * msg)168 int putMsgInArea(s_area *echo, s_message *msg)
169 {
170     char *ctrlBuff, *textStart, *textWithoutArea;
171     UINT textLength;
172     HAREA harea;
173     HMSG  hmsg;
174     XMSG  msgHeader;
175     int rc = 0;
176 
177     harea = MsgOpenArea((UCHAR *) echo->fileName, MSGAREA_CRIFNEC, (word)(echo->msgbType));
178     if (harea != NULL) {
179 	hmsg = MsgOpenMsg(harea, MOPEN_CREATE, 0);
180 	if (hmsg != NULL) {
181 
182 	    textWithoutArea = msg->text;
183 	    textLength = strlen(textWithoutArea);
184 
185 	    ctrlBuff = (char *) CopyToControlBuf((UCHAR *) textWithoutArea,
186 						 (UCHAR **) &textStart,
187 						 &textLength);
188 
189         msgHeader = createXMSG(config,msg, NULL, MSGLOCAL ,NULL);
190 
191 	    MsgWriteMsg(hmsg, 0, &msgHeader, (UCHAR*)textStart, (dword)textLength, (dword)textLength, (dword)strlen(ctrlBuff), (byte *)ctrlBuff);
192 
193 	    MsgCloseMsg(hmsg);
194 	    nfree(ctrlBuff);
195 	    rc = 1;
196 
197 	} else {
198 	    fprintf(outlog, "Unable to create new message in %s!", echo->fileName);
199 	} /* endif */
200 
201 	MsgCloseArea(harea);
202 
203     } else {
204 	fprintf(outlog, "Unable to open echoarea %s!", echo->fileName);
205     } /* endif */
206     return rc;
207 }
208 
209 
makeRequestToLink(char * areatag,s_link * link)210 int makeRequestToLink (char *areatag, s_link *link) {
211     s_message *msg;
212 
213     if (link->msg == NULL)
214     {
215         msg = makeMessage(link->ourAka, &(link->hisAka), config->sysop,
216             link->areafix.name ? link->areafix.name : "areafix",
217             link->areafix.pwd ? link->areafix.pwd : "\x00", 1,
218             link->areafix.reportsAttr ? link->areafix.reportsAttr : robot->reportsAttr);
219         msg->text = createKludges(config, NULL, link->ourAka, &(link->hisAka),
220             "hptkill");
221         if (link->areafix.reportsFlags)
222             xstrscat(&(msg->text), "\001FLAGS ", link->areafix.reportsFlags, "\r",NULL);
223         else if (robot->reportsFlags)
224             xstrscat(&(msg->text), "\001FLAGS ", robot->reportsFlags, "\r",NULL);
225         link->msg = msg;
226     } else {
227         msg = link->msg;
228     }
229     xscatprintf(&(msg->text), "-%s\r", areatag);
230     return 0;
231 }
232 
createDupeFileName(s_area * area)233 char *createDupeFileName(s_area *area) {
234     char *name=NULL, *ptr, *retname=NULL;
235 
236     if (!area->DOSFile) {
237         name = makeMsgbFileName(config, area->areaName);
238     } else {
239         if (area->fileName) xstrcat(&name, (ptr = strrchr(area->fileName,PATH_DELIM))
240             ? ptr+1 : area->fileName);
241         else xscatprintf(&name, "%X", strcrc32(area->areaName,0xFFFFFFFFUL) );
242     }
243 
244     switch (config->typeDupeBase) {
245     case hashDupes:
246 	xstrcat(&name,".dph");
247 	break;
248     case hashDupesWmsgid:
249 	xstrcat(&name,".dpd");
250 	break;
251     case textDupes:
252 	xstrcat(&name,".dpt");
253 	break;
254     case commonDupeBase:
255 	break;
256     }
257 
258     if (config->areasFileNameCase == eUpper)
259 	name = strUpper(name);
260     else
261 	name = strLower(name);
262 
263     xstrscat(&retname, config->dupeHistoryDir, name, NULL);
264     nfree(name);
265 
266     return retname;
267 }
268 
update_queue(s_area * area)269 void update_queue(s_area *area)
270 {
271     char *line    = NULL;
272     FILE *queryFile;
273     char seps[]   = " \t\n";
274     char upDate   = 0;
275 
276     if(!robot->queueFile)
277         return;
278 
279     if ( !(queryFile = fopen(robot->queueFile,"a+b")) ) /* can't open query file */
280     {
281        w_log(LL_ERR, "Unable to open areafixQueueFile %s: %s", robot->queueFile, strerror(errno) );
282        return;
283     }
284     rewind(queryFile);
285     while ((line = readLine(queryFile)) != NULL)
286     {
287         char* token = strtok( line, seps );
288         if(strcasecmp(token,area->areaName) == 0)
289         {
290             upDate = 1;
291             break;
292         }
293         nfree(line);
294     }
295     nfree(line)
296 
297     if (upDate == 0)
298     {
299         time_t eTime;
300         struct  tm t1,t2;
301         eTime = tnow + robot->killedRequestTimeout*secInDay;
302         t1 = *localtime( &tnow );
303         t2 = *localtime( &eTime );
304         xscatprintf( &line , "%s %s %d-%02d-%02d@%02d:%02d\t%d-%02d-%02d@%02d:%02d" ,
305                 area->areaName,
306                 "kill",
307                 t1.tm_year + 1900,
308                 t1.tm_mon  + 1,
309                 t1.tm_mday,
310                 t1.tm_hour,
311                 t1.tm_min,
312                 t2.tm_year + 1900,
313                 t2.tm_mon  + 1,
314                 t2.tm_mday,
315                 t2.tm_hour,
316                 t2.tm_min   );
317 
318         xstrscat(&line, " ", aka2str(*(area->useAka)), "\n", NULL);
319         fputs(line , queryFile);
320     }
321     fclose(queryFile);
322 }
323 
delete_area(s_area * area)324 void delete_area(s_area *area)
325 {
326     char *an = area->areaName;
327     unsigned int i;
328     int rc;
329 
330     fprintf(outlog, "Killing area %s\n", an);
331 
332     switch (sendUnSubscribe) {
333 
334     case eNobody:
335 	break;
336 
337     case eFirstLink:
338 	makeRequestToLink(an, area->downlinks[0]->link);
339 	break;
340 
341     case eNodes:
342 	for (i=0; i<area->downlinkCount; i++) {
343 	    if (area->downlinks[i]->link->hisAka.point == 0)
344 		makeRequestToLink(an, area->downlinks[i]->link);
345 	}
346 	break;
347 
348     case eAll:
349 	for (i=0; i<area->downlinkCount; i++) {
350 	    makeRequestToLink(an, area->downlinks[i]->link);
351 	}
352 	break;
353     }
354 
355 
356     /* delete msgbase and dupebase for the area */
357     if (eraseBase) {
358 
359 	if (area->msgbType!=MSGTYPE_PASSTHROUGH) {
360 	    fprintf(outlog, "  Removing base of %s...", an);
361 	    rc=MsgDeleteBase(area->fileName, (word) area->msgbType);
362 	    fprintf(outlog, "%s\n", rc ? "ok" : "failed");
363 	}
364 
365 
366 	if (area->dupeCheck != dcOff) {
367 	    char *dupename = createDupeFileName(area);
368 	    if (dupename) {
369 		fprintf(outlog, "  Removing dupebase for %s...", an);
370 		rc=unlink(dupename);
371 		fprintf(outlog, "%s\n", rc==0 ? "ok" : "failed");
372 		nfree(dupename);
373 	    }
374 	}
375     }
376 
377     /* remove area from config-file */
378     if (delFromConfig) {
379 
380     update_queue(area);
381 
382 	fprintf(outlog, "   deleting from config");
383 	if (changeconfig (getConfigFileName(),  area) != 0)
384 	    fprintf(outlog, " ERROR!\n");
385 	else
386 	    fprintf(outlog, " ok\n");
387 
388 	/* delete the area from in-core config */
389 	for (i=0; i<area->downlinkCount; i++)
390 	    nfree(area->downlinks[i]);
391 	nfree(area->downlinks);
392 	area->downlinkCount = 0;
393 	for (i=0; i<config->echoAreaCount; i++)
394 	    if (stricmp(config->echoAreas[i].areaName, an)==0)
395 		break;
396 	if (i<config->echoAreaCount && area==&(config->echoAreas[i])) {
397 	    nfree(area->areaName);
398 	    nfree(area->fileName);
399 	    nfree(area->description);
400 	    nfree(area->group);
401 	    for (; i<config->echoAreaCount-1; i++)
402 		memcpy(&(config->echoAreas[i]), &(config->echoAreas[i+1]),
403 		       sizeof(s_area));
404 	    config->echoAreaCount--;
405 	}
406     }
407     fprintf(outlog, "done\n");
408 }
409 
410 
main(int argc,char ** argv)411 int main(int argc, char **argv) {
412 
413     int i, j;
414     UINT k;
415     struct _minf m;
416     char **areas = NULL;
417     char *needfree = NULL;
418     char *line = NULL;
419     int nareas=0;
420     int found = 0;
421     FILE *f = NULL;
422     s_link *link = NULL;
423     int killed = 0;
424     int checkPaused = 0;
425     int killNoLink = 0;
426     int killLowLink = 0;
427     int killOld = 0;
428     int killWithoutDupes = 0;
429     int delArea = 0;
430     time_t oldest = 0;
431     s_area *area = NULL;
432     struct stat stbuf;
433     char *listNoDupeFile = NULL;
434     FILE *fNoDupe = NULL;
435     char *dupename = NULL;
436     char *cfgfile = NULL;
437 
438 
439     outlog=stdout;
440     setbuf(outlog, NULL);
441 
442     versionStr = GenVersionStr( "hptkill", VER_MAJOR, VER_MINOR, VER_PATCH,
443                                VER_BRANCH, cvs_date );
444     fprintf(outlog,"%s\n\n", versionStr);
445 
446     for (i=1; i<argc; i++) {
447         if ( argv[i][0] == '-' ) {
448             switch (argv[i][1])
449             {
450             case 'c': /* alternate config file */
451                 if ( ++i<argc ) {
452                     cfgfile = argv[i];
453                 } else {
454                     fprintf( stderr, "Option -c requires config file name\n");
455                     usage();
456                 }
457                 break;
458             case '1': /* send unsubscribe message to first link only */
459                 sendUnSubscribe = eFirstLink;
460                 break;
461 
462             case 'n': /* don't send unsubscribe message */
463             case 'N':
464                 sendUnSubscribe = eNobody;
465                 break;
466 
467             case 'a': /* send unsubscribe message all links */
468             case 'A':
469                 sendUnSubscribe = eAll;
470                 break;
471 
472             case 'd': /* delete area from config */
473             case 'D':
474                 delFromConfig = 1;
475                 break;
476 
477             case 's': /* save (don't erase) message & dupe bases */
478             case 'S':
479                 eraseBase = 0;
480                 break;
481 
482             case 'f': /* read areas list from file */
483             case 'F':
484                 i++;
485 
486                 if ( argv[i] == NULL || argv[i][0] == '\0') {
487                     usage();
488                     exit(-1);
489                 }
490 
491                 if (strcmp(argv[i], "-") == 0) {
492                     f=stdin;
493                 } else {
494                     if ( !(f=fopen(argv[i], "r"))) {
495                         fprintf(outlog, "%s: Unable to open file '%s'\n", argv[0], argv[i]);
496                         exit(-1);
497                     }
498                 }
499 
500                 while (!feof(f)) {
501                     line = readLine(f);
502 
503                     if (line) {
504                         char *spacep=strchr(line+1,' ');
505                         if(spacep) { /* Format FILEBONE.NA: "areatag comment" */
506                           *spacep=0; /* First char should be alphanumberic    */
507                           spacep=strdup(line);
508                           free(line);
509                           line=spacep;
510                         }
511                         if( line[0]==0 ) continue; /* skip empty line */
512                         nareas++;
513                         areas = (char **)srealloc ( areas, nareas*sizeof(char *));
514                         areas[nareas-1] = line;
515                         needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
516                         needfree[nareas-1] = 1;
517                     }
518 
519                 }
520 
521                 if (f != stdin) fclose(f);
522                 break;
523 
524             case 'p': /* kill passthrough areas with 1 link*/
525             case 'P':
526                 killNoLink++;
527                 killPass++;
528                 if (argv[i][2]=='p' || argv[i][2]=='P') checkPaused++;
529                 break;
530 
531             case 'y': /* kill ANY areas with <=1 link*/
532             case 'Y':
533                 killLowLink++;
534                 if (argv[i][2]=='p' || argv[i][2]=='P') checkPaused++;
535                 break;
536 
537             case 'o': /* kill passthrough area with dupebase older 'days' days */
538             case 'O':
539                 if (argv[i][1]=='O') killWithoutDupes++;
540                 i++;
541                 if ( argv[i] == NULL || argv[i][0] == '\0') {
542                     usage();
543                     exit(-1);
544                 }
545                 killPass++;
546                 killOld++;
547                 oldest = time(NULL) - atoi(argv[i]) * 60*60*24;
548                 break;
549 
550             case 'l': /* write list of areas without dupebase to file  */
551             case 'L':
552                 i++;
553                 if ( argv[i] == NULL || argv[i][0] == '\0') {
554                     usage();
555                     exit(-1);
556                 }
557                 listNoDupeFile = argv[i];
558                 break;
559 
560             case 'C': /* create empty dupebase if it doesn't exist */
561                 createDupe++;
562                 break;
563 
564             default:
565                 usage();
566                 exit(-1);
567         }
568     } else {
569         /* AreaName(s) specified by args */
570         nareas++;
571         areas = (char **)srealloc ( areas, nareas*sizeof(char *));
572         areas[nareas-1] = argv[i];
573         needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
574         needfree[nareas-1] = 0;
575     }
576     }
577 
578     if (nareas == 0) {
579         if (killPass || killLowLink) {
580             nareas++;
581             areas = (char **)srealloc ( areas, nareas*sizeof(char *));
582             areas[nareas-1] = "*";
583             needfree = (char *)srealloc ( needfree, nareas*sizeof(char));
584             needfree[nareas-1] = 0;
585         } else {
586             if (!createDupe) {
587                 usage();
588                 exit (-1);
589             }
590         }
591     }
592 
593     if( cfgfile && cfgfile[0] )
594         config = readConfig(cfgfile);
595     else   config = readConfig(getConfigFileName());
596 
597     if (!config) {
598         fprintf(outlog, "Unable to read fido config\n");
599         return (1);
600     }
601 
602     robot = getRobot(config, "areafix", -1);
603     if (!robot) {
604         fprintf(outlog, "Unable to find robot 'areafix' in config\n");
605         return (1);
606     }
607 
608     time( &tnow );
609 
610     m.req_version = 0;
611     m.def_zone = (UINT16) config->addr[0].zone;
612     if (MsgOpenApi(&m) != 0) fprintf(outlog, "MsgApiOpen Error");
613 
614     for ( j=0; j<nareas; j++) {
615         found = 0;
616 
617         for (i=0, area = config->echoAreas; (unsigned int)i < config->echoAreaCount; i++, area++) {
618             if (patimat(area->areaName, areas[j])==1){
619 
620                 delArea = 0;
621                 if (killPass==0 && killLowLink==0) delArea++;
622                 else if ((area->msgbType & MSGTYPE_PASSTHROUGH) == MSGTYPE_PASSTHROUGH) {
623                     if (killNoLink) {
624                         if (area->downlinkCount <= 1) delArea++;
625                         else if (checkPaused) {
626                             delArea = 2; /* if two links w/o pause - leave untouched */
627                             for (k=0; (unsigned int)k < area->downlinkCount && delArea; k++) {
628                                 if ((area->downlinks[k]->link->Pause & ECHOAREA)!= ECHOAREA) delArea--;
629                             }
630                         }
631                     }
632                     if (killOld && !delArea && area->dupeCheck != dcOff) {
633                         dupename = createDupeFileName(area);
634                         if (dupename) {
635                             if (stat(dupename, &stbuf)==0) {
636                                 if (stbuf.st_mtime < oldest) delArea++;
637                             } else {
638                                 if (killWithoutDupes) {
639                                     delArea++;
640                                 }
641                                 if (listNoDupeFile) {
642                                     if (!fNoDupe) {
643                                         if (!(fNoDupe=fopen (listNoDupeFile, "a"))) {
644                                             fprintf (stderr, "Unable to open file '%s' for appending\n", listNoDupeFile);
645                                         }
646                                     }
647                                     if (fNoDupe) fprintf (fNoDupe, "%s\n", area->areaName);
648 
649                                 }
650                             }
651                             nfree(dupename);
652                         }
653                     }
654                 }
655                 if (killLowLink) {
656                     if (area->downlinkCount <= 1) delArea++;
657                     else if (checkPaused) {
658                         delArea = 2; /* if two links w/o pause - leave untouched */
659                         for (k=0; k < area->downlinkCount && delArea; k++) {
660                             if ((area->downlinks[k]->link->Pause & ECHOAREA)!= ECHOAREA) delArea--;
661                         }
662                     }
663                 }
664                 if (delArea) {
665                     delete_area(area);
666                     killed++;
667                     found++;
668                     if (delFromConfig) { /* Area is removed from areas array! */
669                         i--;
670                         area--;
671                     }
672 
673                 }
674             }
675         }
676 
677         if (killPass==0 && killLowLink==0) {
678             for (i=0, area=config->localAreas; (unsigned int)i < config->localAreaCount; i++, area++) {
679                 if (patimat(area->areaName, areas[j])==1){
680                     delete_area(area);
681                     killed++;
682                     found++;
683                     if (delFromConfig) { /* Area is removed from areas array! */
684                         i--;
685                         area--;
686                     }
687                 }
688             }
689 
690             if (!found) fprintf(outlog, "Unable to find area \"%s\"\n", areas[j]);
691         }
692     }
693 
694     if (fNoDupe) fclose (fNoDupe);
695 
696     if (killed) fprintf(outlog, "\n");
697     /* Put mail for links to netmail */
698     for (i=0; (unsigned int)i < config->linkCount; i++) {
699         if (config->links[i]->msg) {
700             link = config->links[i];
701             if (link->hisAka.point)
702                 fprintf(outlog, "Wrote message for %u:%u/%u.%u...",
703                 link->hisAka.zone ,
704                 link->hisAka.net  ,
705                 link->hisAka.node ,
706                 link->hisAka.point);
707             else
708                 fprintf(outlog, "Wrote message for %u:%u/%u...",
709                 link->hisAka.zone ,
710                 link->hisAka.net  ,
711                 link->hisAka.node);
712 
713             putMsgInArea(&(config->netMailAreas[0]), config->links[i]->msg);
714             nfree(link->msg);
715             fprintf(outlog, "done\n");
716         }
717     }
718 
719     for ( j=0; j<nareas; j++) if (needfree[nareas-1]) nfree(areas[j]);
720     if (needfree) nfree(needfree);
721     if (areas) nfree(areas);
722 
723     if (killed && config->echotosslog) {
724         f=fopen(config->echotosslog, "a");
725         if (f==NULL) {
726             fprintf(outlog, "\nUnable to open or create EchoTossLogFile\n");
727         } else {
728             fprintf(f,"%s\n", config->netMailAreas[0].areaName);
729             fclose(f);
730         }
731     }
732 
733     if (createDupe) {
734         for (i=0, area = config->echoAreas; (unsigned int)i < config->echoAreaCount; i++, area++) {
735             dupename = createDupeFileName(area);
736             if (stat(dupename, &stbuf)!=0) {
737                 fprintf (outlog, "Creating %s\n", dupename);
738                 f=fopen(dupename, "a");
739                 if (f) {
740                     fclose (f);
741                 } else {
742                     fprintf (outlog, "Unable to create %s\n", dupename);
743                 }
744             }
745             nfree(dupename);
746         }
747     }
748 
749     /* deinit SMAPI */
750     MsgCloseApi();
751 
752     disposeConfig(config);
753     fprintf(outlog,"Done\n");
754     return (0);
755 }
756