1 /*
2  *      hptlink - areas linker for Highly Portable Tosser (hpt)
3  *      by Serguei Revtov 2:5021/11.10 || 2:5021/19.1
4  *      Some code was taken from hpt/src/link.c by Kolya Nesterov
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 <ctype.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <fcntl.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 
33 
34 #include <huskylib/compiler.h>
35 
36 #ifdef HAS_IO_H
37 #include <io.h>
38 #endif
39 
40 #ifdef HAS_UNISTD_H
41 #include <unistd.h>
42 #endif
43 
44 #ifdef HAS_SHARE_H
45 #include <share.h>
46 #endif
47 
48 #include <smapi/msgapi.h>
49 #include <fidoconf/common.h>
50 #include <huskylib/xstr.h>
51 
52 #include <version.h>
53 #include <areafix/areafix.h>
54 #include <global.h>
55 #include "cvsdate.h"
56 
57 
58 /* internal structure holding msg's related to link information */
59 /* used by linkArea */
60 struct msginfo {
61    char *replyId;
62    char *msgId;
63    char *subject;
64    UMSGID replyToPos;
65    UMSGID replies[MAX_REPLY];
66    UMSGID treeId;
67    int freeReply;
68    UMSGID msgPos;
69 
70 };
71 typedef struct msginfo s_msginfo;
72 
73 #define reply1st replies[0]
74 #define replyNxt replies[1]
75 
76 
77 struct origlinks {
78    UMSGID replyToPos;
79    UMSGID replies[MAX_REPLY];
80 };
81 typedef struct origlinks s_origlinks;
82 
83 #define LOGFILENAME "hptlink.log"
84 
85 s_log        *hptlink_log = NULL;
86 s_fidoconfig *config;
87 
88 int singleRepl = 1;
89 int hardSearch = 0;
90 int useSubj = 1;
91 int useReplyId = 1;
92 int loglevel = 10;
93 int linkNew = 0;
94 HAREA harea;
95 int maxreply;
96 
97 
98 long links_msgid=0L;
99 long links_replid=0L;
100 long links_subj=0L;
101 long links_revmsgid=0L;
102 long links_total=0L;
103 long links_ignored=0L;
104 
105 char *versionStr;
106 
skipReSubj(char * subjstr)107 char *skipReSubj ( char *subjstr )
108 {
109    char *ptr;
110 
111    if ( !subjstr ) return (NULL);
112 
113    if ( *subjstr != 'R' && *subjstr != 'r' ) return (NULL);
114    subjstr++;
115 
116    if ( *subjstr != 'e' && *subjstr != 'E' ) return (NULL);
117    subjstr++;
118 
119    if ( *subjstr == '^' ) {
120       subjstr++;
121       while ( isdigit (*subjstr) ) subjstr++;
122    }
123 
124    if ( *subjstr != ':' ) return (NULL);
125    subjstr++;
126 
127    while ( *subjstr == ' ' ) subjstr++;
128 
129    if ( (ptr = skipReSubj ( subjstr )) != NULL ) subjstr = ptr;
130 
131    return (subjstr);
132 
133 }
134 
cmpMsgIdReply(register char * str1,register char * str2)135 int cmpMsgIdReply (register char *str1, register char *str2)
136 {
137     while (*str1==*str2 && *str1) {
138     if (*str1=='@') while (*str1 && *str1!=' ') str1++; /*  skip domain */
139     if (*str1) str1++;
140     if (*str2=='@') while (*str2 && *str2!=' ') str2++; /*  skip domain */
141     if (*str2) str2++;
142     }
143     if (*str1=='\0' && *str2=='\0') return 0;
144     return 1;
145 }
146 
linkMsgs(s_msginfo * crepl,s_msginfo * srepl,dword i,dword j,s_msginfo * replmap)147 void linkMsgs ( s_msginfo *crepl, s_msginfo *srepl, dword i, dword j, s_msginfo *replmap )
148 {
149     dword  linkTo;
150 
151     if (crepl -> msgId && srepl -> msgId &&
152         strcmp ( crepl -> msgId, srepl -> msgId) == 0) {
153         w_log( LL_WARN, "Warning: msg %ld is dupe to %ld", (long)i, (long)j);
154         links_ignored++;
155         return;
156     }
157 
158     if (maxreply == MAX_REPLY) { /*  Squish */
159       if (crepl -> freeReply >= maxreply)
160       {
161         w_log( LL_WARN, "replies count for msg %ld exceeds %d, rest of the replies won't be linked", (long)j, maxreply);
162         links_ignored++;
163       } else {
164           links_total++;
165           (crepl -> replies)[(crepl -> freeReply)++] = srepl->msgPos;
166           srepl -> replyToPos = crepl->msgPos;
167       }
168 
169     } else { /*  Jam, maybe something else? */
170 
171         if(srepl -> replyToPos) {
172            w_log( LL_WARN, "Thread linking broken because of dupes");
173            links_ignored++;
174            return;
175         }
176 
177         srepl -> replyToPos = crepl->msgPos;
178         links_total++;
179         if (crepl->reply1st == 0) {
180             crepl->reply1st = srepl->msgPos;
181             (crepl -> freeReply)++;
182         } else {
183             linkTo = MsgUidToMsgn(harea, crepl->reply1st, UID_EXACT) - 1;
184         if(linkTo == (dword)-1) {
185         w_log( LL_WARN, "Thread linking broken. MsgUidToMsgn() returned -1");
186         links_ignored++;
187         return;
188         }
189 
190             while (replmap[linkTo].replyNxt) {
191         linkTo = MsgUidToMsgn(harea, replmap[linkTo].replyNxt, UID_EXACT) - 1;
192         if(linkTo == (dword)-1) {
193             w_log( LL_WARN, "Thread linking broken. MsgUidToMsgn() returned -1");
194             links_ignored++;
195             return;
196         }
197         }
198             replmap[linkTo].replyNxt = srepl->msgPos;
199             replmap[linkTo].freeReply++;
200         }
201     }
202 }
203 
GetCtrlValue(char * ctl,char * kludge)204 static char *GetCtrlValue (char *ctl, char *kludge)
205 {
206    char *value, *end, *out, *p;
207 
208    if ( !ctl || !kludge ) return (NULL);
209 
210    if ( (value = strstr( ctl, kludge)) == NULL ) return (NULL);
211 
212    if ( value[-1] != '\001') return (NULL);
213 
214    value += strlen(kludge); /* skip kludge name (i.e. MSGID: or REPLY:) */
215 
216    for (end = value; *end != '\001' && *end; end++);
217 
218    if ((end - value) <= 0) return (NULL);
219 
220    out = (char *) smalloc((size_t) (end - value) + 1 );
221    if (out == NULL) return (NULL);
222 
223    memcpy(out, value, (size_t) (end - value));
224    out[(size_t) (end - value)] = '\0';
225 
226    /*  fix for upper case msgids */
227    strLower(out);
228    /*  remove .0 from node address */
229    if (NULL!=(p=strstr(out,".0 "))) memmove(p,p+2,strlen(p+2)+1);
230 
231    return out;
232 
233 }
234 
235 
linkArea(s_area * area)236 void linkArea(s_area *area)
237 {
238    byte *ctl = NULL;
239    dword ctlen_curr=0;
240    dword ctlen;
241    dword highMsg;
242    dword  i, j, linkTo;
243    dword newStart=0;
244    HMSG  hmsg;
245    XMSG  xmsg;
246    s_msginfo *replmap;
247    s_msginfo *crepl, *srepl;
248    s_origlinks *links;
249    s_origlinks *linksptr;
250    dword treeLinks=0;
251    int replFound;
252    int replDone;
253    char *ptr;
254 
255    if ((area->msgbType & MSGTYPE_PASSTHROUGH) == MSGTYPE_PASSTHROUGH) {
256      w_log( LL_LINKING, "PASSTHROUGH area %s, skip", area->areaName);
257      return;
258    }
259 
260    if (area->nolink) {
261      w_log( LL_LINKING, "area %s has nolink option, skip", area->areaName);
262      return;
263    }
264 
265    w_log( LL_LINKING, "linking area %s...", area->areaName);
266 
267    if (area->msgbType & MSGTYPE_JAM || area->msgbType & MSGTYPE_SDM) {
268       maxreply = 2;
269    } else {
270       maxreply = MAX_REPLY;
271    }
272 
273    harea = MsgOpenArea((byte *) area->fileName, MSGAREA_NORMAL, (word)area->msgbType);
274 
275    if (harea)
276    {
277        highMsg = MsgGetHighMsg(harea);
278 
279        if ( highMsg < 2 ) {
280           w_log( LL_LINKING, "nothing to link (%ld messages)", (long)highMsg);
281           MsgCloseArea(harea);
282           return;
283        }
284 
285        if ( (replmap = (s_msginfo *) scalloc (highMsg, sizeof(s_msginfo))) == NULL){
286           w_log( LL_CRIT,"Out of memory. Want %ld bytes",  (long) sizeof(s_msginfo)*highMsg);
287           MsgCloseArea(harea);
288           closeLog();
289           disposeConfig(config);
290           exit(EX_SOFTWARE);
291        }
292 
293        if ( (links = (s_origlinks *) scalloc (highMsg, sizeof(s_origlinks))) == NULL){
294           w_log( LL_CRIT, "Out of memory: can't get %ld bytes",  (long) sizeof(s_origlinks)*highMsg);
295           MsgCloseArea(harea);
296           closeLog();
297           disposeConfig(config);
298           exit(EX_SOFTWARE);
299        }
300 
301        /* Pass 1: read all message information in memory */
302        w_log( LL_LINKPASS, "Pass 1 - reading");
303 
304        for (i = 1, crepl=replmap, linksptr=links; i <= highMsg; i++, crepl++, linksptr++)
305        {
306          hmsg  = MsgOpenMsg(harea, MOPEN_READ, i);
307          if (hmsg){
308          ctlen = MsgGetCtrlLen(hmsg);
309          if( ctlen == 0 )
310          {
311             w_log( LL_WARN, "msg %ld has no control information", (long) i);
312             MsgReadMsg(hmsg, &xmsg, 0, 0, NULL, 0, NULL);
313 
314          } else {
315            if( ctl==NULL || ctlen_curr < ctlen + 1) {
316               ctl = (byte *) srealloc(ctl, ctlen + 1);
317               if (ctl == NULL) {
318             w_log( LL_CRIT,"out of memory while linking on msg %ld", (long) i);
319             MsgCloseArea(harea);
320                         closeLog();
321                         disposeConfig(config);
322             exit(EX_SOFTWARE);
323               }
324 
325               ctlen_curr = ctlen + 1;
326            }
327            MsgReadMsg(hmsg, &xmsg, 0, 0, NULL, ctlen, ctl);
328 
329            ctl[ctlen] = '\0';
330 
331            if ( useReplyId ) {
332               crepl -> replyId = (char *) GetCtrlValue( (char *)ctl, "REPLY:");
333               crepl -> msgId = (char *) GetCtrlValue( (char *)ctl, "MSGID:");
334            }
335 
336          }
337 
338          if ( useSubj && xmsg.subj != NULL) {
339             if ( (ptr=skipReSubj((char*)xmsg.subj)) == NULL)
340                   ptr = (char*) xmsg.subj;
341             crepl -> subject = sstrdup(ptr);
342          }
343 
344                  crepl->msgPos = MsgMsgnToUid(harea, i);
345 
346          /*  Save data for comparing */
347                  if (area->msgbType & MSGTYPE_JAM || area->msgbType & MSGTYPE_SDM) {
348                     linksptr->reply1st = xmsg.xmreply1st;
349                     linksptr->replyNxt = xmsg.xmreplynext;
350                  } else {
351                     memcpy(linksptr->replies, xmsg.replies, sizeof(UMSGID) * MAX_REPLY);
352                  }
353          linksptr->replyToPos = xmsg.replyto;
354 
355                  if (linkNew) {
356                     if (area->msgbType & MSGTYPE_JAM || area->msgbType & MSGTYPE_SDM) {
357                        if (xmsg.replyto || xmsg.xmreply1st || xmsg.xmreplynext) {
358                           newStart = i+1;
359                           crepl->replyToPos = xmsg.replyto;
360                           crepl->reply1st = xmsg.xmreply1st;
361                           crepl->replyNxt = xmsg.xmreplynext;
362                        }
363 
364                     } else {
365 
366                        if (xmsg.replyto || xmsg.replies[0]) {
367                           newStart = i+1;
368                           memcpy(crepl->replies, xmsg.replies, sizeof(UMSGID) * MAX_REPLY);
369                           crepl->replyToPos = xmsg.replyto;
370                           for (j=0; j < MAX_REPLY && xmsg.replies[j]; j++);
371                           crepl->freeReply = j;
372                        }
373                     }
374                  }
375 
376          MsgCloseMsg(hmsg);
377           }
378        }
379 
380        /* Pass 2: building relations tree, & filling tree IDs */
381        if ( loglevel >= 11 ) {
382               if (linkNew)
383                  w_log(LL_LINKPASS, "Pass 2: building relations for %ld messages, new from %ld", (long) i-1, (long) newStart);
384               else
385                  w_log(LL_LINKPASS, "Pass 2: building relations for %ld messages", (long) i-1);
386            }
387 
388        for (i = 1, crepl=replmap; i < highMsg; i++, crepl++) {
389          if (
390           crepl -> replyId ||
391           crepl -> msgId   ||
392           crepl -> subject
393         ) {
394 
395         replDone = 0;
396 
397                 j=i+1;
398                 srepl=crepl+1;
399                 if (newStart > j) {
400                    j=newStart;
401                    srepl = &(replmap[j-1]);
402                 }
403         for (; j <= highMsg && !replDone; j++, srepl++ ) {
404 
405           replFound = 0;
406 
407           if (!replFound &&
408               (crepl -> msgId) && (srepl -> replyId) &&
409               cmpMsgIdReply (crepl -> msgId, srepl -> replyId) == 0 ) {
410 
411                replFound++;
412                links_msgid++;
413 
414                if ( ! crepl -> treeId ) { /*  *crepl isn't linked */
415               if (srepl -> treeId ) { /*  *srepl linked already */
416                  crepl -> treeId = srepl -> treeId;
417               } else {
418                              crepl -> treeId = i; /*  top of new tree */
419               }
420                }
421                srepl -> treeId = crepl -> treeId;
422 
423                if (singleRepl) {
424               treeLinks++;
425                } else {
426               linkMsgs ( crepl, srepl, i, j, replmap );
427                }
428           }
429 
430           if ( !replFound &&
431                (crepl -> treeId == 0 || srepl -> treeId == 0) &&
432                crepl -> replyId && srepl -> replyId) {
433               if ( cmpMsgIdReply (crepl -> replyId, srepl -> replyId) == 0 &&
434                strcmp(crepl -> replyId, "  ffffffff")) {
435 
436               replFound++;
437               links_replid++;
438 
439               if ( ! crepl -> treeId ) { /*  *crepl isn't linked */
440                   if (srepl -> treeId ) { /*  *srepl linked already */
441                   crepl -> treeId = srepl -> treeId;
442                   } else {
443                   crepl -> treeId = i; /*  top of new tree */
444                   }
445               }
446               srepl -> treeId = crepl -> treeId;
447 
448               treeLinks++;
449               }
450           }
451 
452           if (!replFound && (srepl -> msgId) && (crepl -> replyId)) {
453               if ( cmpMsgIdReply (srepl -> msgId, crepl -> replyId) == 0 ) {
454               replFound++;
455               links_revmsgid++;
456 
457                if ( ! crepl -> treeId ) { /*  *crepl isn't linked */
458               if (srepl -> treeId ) { /*  *srepl linked already */
459                  crepl -> treeId = srepl -> treeId;
460               } else {
461                              crepl -> treeId = i; /*  top of new tree */
462               }
463                }
464                srepl -> treeId = crepl -> treeId;
465 
466                if (singleRepl) {
467               treeLinks++;
468                } else {
469               linkMsgs ( srepl, crepl, j, i, replmap );
470                }
471              }
472           }
473 
474           if ( !replFound &&
475                (srepl -> treeId == 0) &&
476                crepl -> subject && srepl -> subject ) {
477 
478              if ( strcmp ( crepl -> subject, srepl -> subject ) == 0 ) {
479 
480                replFound++;
481                links_subj++;
482 
483                if ( ! crepl -> treeId ) { /*  *crepl isn't linked */
484               if (srepl -> treeId ) { /*  *srepl linked already */
485                  crepl -> treeId = srepl -> treeId;
486               } else {
487                              crepl -> treeId = i; /*  top of new tree */
488               }
489                }
490                srepl -> treeId = crepl -> treeId;
491 
492                treeLinks++;
493 
494              }
495           }
496 
497           if (replFound && singleRepl && !hardSearch ) replDone++;
498 
499         }
500          }
501        }
502 
503        /* Pass 3: finding unlinked messages with filled tree IDs, and link
504         * them to the tree where possible
505         */
506        w_log(LL_LINKPASS, "Pass 3: buildng relations by treeIds");
507 
508        for (i = 1, crepl=replmap; i <= highMsg && treeLinks; i++, crepl++) {
509           if ( crepl->replyToPos == 0 && crepl->freeReply == 0 &&
510                    crepl->treeId && i != crepl->treeId ) {
511          /*  Link unlinked message */
512 
513          linkTo = (replmap[crepl -> treeId -1 ]).treeId;
514          if (linkTo > highMsg || linkTo <= 0 ) {
515             w_log(LL_CRIT,"Programming error 1 while linking linkTo=%ld", (long)linkTo);
516             closeLog();
517                     disposeConfig(config);
518             exit(EX_SOFTWARE);
519          }
520 
521                  if (maxreply == MAX_REPLY) { /*  Find place to put link for Squish */
522                     while ( (replmap[linkTo-1]).freeReply >= maxreply) {
523                        linkTo = MsgUidToMsgn(harea,(replmap[linkTo-1]).replies[0], UID_EXACT );
524                        if (linkTo > highMsg || linkTo <= 0 ) {
525                           w_log(LL_CRIT,"Programming error 2 while linking linkTo=%ld", (long)linkTo);
526                             closeLog();
527                           disposeConfig(config);
528                           exit(EX_SOFTWARE);
529                        }
530                     }
531                  }
532          linkMsgs ( &(replmap[linkTo-1]), crepl, linkTo, i , replmap );
533          (replmap[crepl -> treeId - 1]).treeId = i; /*  where to link next message */
534          treeLinks--;
535           }
536        }
537 
538 
539        /* Pass 4: write information back to msgbase */
540        w_log(LL_LINKPASS, "Pass 4: writing");
541 
542        for (i = 1, crepl=replmap, linksptr=links; i <= highMsg; i++, crepl++, linksptr++) {
543 
544               if (area->msgbType & MSGTYPE_JAM || area->msgbType & MSGTYPE_SDM) {
545 
546                  if( (linksptr->replyToPos != crepl->replyToPos) ||
547                      (linksptr->reply1st  != crepl->reply1st) ||
548                      (linksptr->replyNxt  != crepl->replyNxt) ) {
549 
550                     hmsg  = MsgOpenMsg(harea, MOPEN_RW, i);
551 
552                     if (hmsg) {
553 
554                MsgReadMsg(hmsg, &xmsg, 0, 0, NULL, 0, NULL);
555                        xmsg.replyto = crepl->replyToPos;
556                        xmsg.xmreply1st = crepl->reply1st;
557                        xmsg.xmreplynext = crepl->replyNxt;
558                if( 0!=MsgWriteMsg(hmsg, 0, &xmsg, NULL, 0, 0, 0, NULL) )
559                            w_log(LL_ERR, "Could not update msg in area %s! Check the wholeness of messagebase, please.", area->areaName);
560                MsgCloseMsg(hmsg);
561             }
562                  }
563 
564               } else { /*  Not Jam */
565 
566                  if ((linksptr->replyToPos != crepl->replyToPos) ||
567                      memcmp(linksptr->replies, crepl->replies, sizeof(UMSGID) * maxreply)) {
568 
569                     hmsg  = MsgOpenMsg(harea, MOPEN_RW, i);
570 
571                     if (hmsg) {
572 
573                        MsgReadMsg(hmsg, &xmsg, 0, 0, NULL, 0, NULL);
574                        memcpy(xmsg.replies, crepl->replies, sizeof(UMSGID) * maxreply);
575                        xmsg.replyto = crepl->replyToPos;
576                        if( 0!=MsgWriteMsg(hmsg, 0, &xmsg, NULL, 0, 0, 0, NULL) )
577                            w_log(LL_ERR, "Could not update msg in area %s! Check the wholeness of messagebase, please.", area->areaName);
578                        MsgCloseMsg(hmsg);
579                     }
580                  }
581           }
582 
583           if(crepl -> replyId) nfree(crepl -> replyId);
584           if(crepl -> subject) nfree(crepl -> subject);
585           if(crepl -> msgId  ) nfree(crepl -> msgId  );
586        }
587 
588        MsgCloseArea(harea);
589 
590        nfree(ctl);
591        nfree(replmap);
592        nfree(links);
593 
594        w_log( LL_LINKING, "Linking area \"%s\" done", area->areaName);
595    } else {
596        w_log( LL_ERR, "Could not open area %s", area->areaName);
597    }
598 }
599 
usage(void)600 void usage(void) {
601 
602    printf( "Usage: hptlink [options] [areaname ...]\n"
603           "Options:  -t\t- build reply TREE\n"
604           "\t  -s\t- do not use Subject\n"
605           "\t  -a\t- search in all messages (for singlethread only)\n"
606           "\t  -r\t- do not use REPLY:/MSGID:\n"
607           "\t  -n\t- link with 'new' messages only ('new' from last linked + 1)\n"
608          );
609 }
610 
main(int argc,char ** argv)611 int main(int argc, char **argv) {
612 
613    unsigned int i;
614    int j;
615    struct _minf m;
616    char **argareas=NULL;
617    char *line=NULL;
618    int nareas=0;
619    int found;
620    FILE *f;
621    s_area *area;
622 
623    setvar("module", "hpt");
624    xscatprintf(&line, "%u.%u.%u", VER_MAJOR, VER_MINOR, VER_PATCH);
625    setvar("version", line);
626    nfree(line);
627    SetAppModule(M_HPT);
628 
629    versionStr = GenVersionStr( "hptlink", VER_MAJOR, VER_MINOR, VER_PATCH,
630                                VER_BRANCH, cvs_date );
631 
632    printf("%s\n\n", versionStr);
633 
634    for (j=1; j<argc; j++) {
635      if ( argv[j][0] == '-' ) {
636     switch (argv[j][1])
637       {
638          case 't': /* Tree mode */
639          case 'T':
640         singleRepl = 0;
641         break;
642 
643          case 's': /* do NOT use Subject field */
644          case 'S':
645         useSubj = 0;
646         break;
647          case 'a': /* search in all messages */
648          case 'A':
649         hardSearch = 1;
650         break;
651          case 'r': /* do NOT use REPLY:/MSGID: fields */
652          case 'R':
653         useReplyId = 0;
654         break;
655          case 'l':
656          case 'L':
657             break; /* obsolete */
658          case 'n': /* link with 'new' messages only */
659          case 'N':
660         linkNew = 1;
661         break;
662          default:
663         usage();
664         exit(EX_USAGE);
665       }
666      } else {
667        /*  AreaName(s) specified by args */
668        nareas++;
669        argareas = (char **)srealloc ( argareas, nareas*sizeof(char *));
670        argareas[nareas-1] = argv[j];
671      }
672    }
673 
674    config = readConfig(NULL);
675 
676    if (!config) {
677       fprintf(stderr, "Could not read fido config!\n");
678       return (1);
679    }
680 
681    if (config->logFileDir) {
682         xstrscat(&line, config->logFileDir, LOGFILENAME, NULLP);
683         initLog(config->logFileDir, config->logEchoToScreen, config->loglevels, config->screenloglevels);
684     hptlink_log = openLog(line, versionStr);
685     nfree(line);
686    }
687 
688    w_log(LL_PRG, "%s", versionStr);
689 
690    m.req_version = 0;
691    m.def_zone = (UINT16) config->addr[0].zone;
692    if (MsgOpenApi(&m)!= 0) {
693       w_log(LL_CRIT, "MsgOpenApi Error.");
694       closeLog();
695       disposeConfig(config);
696       exit(EX_SOFTWARE);
697    }
698 
699    if ( argareas )
700    {
701      /*  link only specified areas */
702      w_log(LL_LINKING, "Link areas specified by args");
703 
704      for ( j=0; j<nareas; j++) {
705 
706     found=0;
707 
708     /*  EchoAreas */
709     for (i=0, area=config->echoAreas;
710          i < config->echoAreaCount && !found;
711          i++, area++) {
712         if (stricmp(area->areaName, argareas[j])==0){
713         if (!area->scn) {
714             linkArea(area);
715             area->scn=1;
716         }
717         found++;
718         }
719     }
720 
721     /*  Local Areas */
722     for (i=0, area=config->localAreas;
723          i < config->localAreaCount && !found;
724          i++, area++) {
725         if (stricmp(area->areaName, argareas[j])==0){
726         if (!area->scn) {
727             linkArea(area);
728             area->scn=1;
729         }
730         found++;
731         }
732     }
733 
734     /*  NetMail areas */
735     for (i=0, area=config->netMailAreas;
736          i < config->netMailAreaCount && !found;
737          i++, area++) {
738         if (stricmp(area->areaName, argareas[j])==0){
739         if (!area->scn) {
740             linkArea(area);
741             area->scn=1;
742         }
743         found++;
744         }
745     }
746 
747     if(!found) w_log(LL_WARN, "Couldn't find area \"%s\"", argareas[j]);
748      }
749 
750    } else {
751 
752       if (config->LinkWithImportlog != lwiNo){
753      f = fopen(config->importlog, "r");
754       } else {
755      f = NULL;
756       }
757 
758       if ( f ) {
759      w_log(LL_INFO, "Using importlogfile -> linking only listed Areas");
760      while (!feof(f)) {
761         line = readLine(f);
762 
763         if (line) {
764 
765            found=0;
766            /*  EchoAreas */
767            for (i=0, area=config->echoAreas;
768             i < config->echoAreaCount && !found;
769             i++, area++) {
770            if (stricmp(area->areaName, line)==0){
771                if (!area->scn) {
772                linkArea(area);
773                area->scn=1;
774                }
775                found++;
776            }
777            }
778            /*  Local Areas */
779            for (i=0, area=config->localAreas;
780             i < config->localAreaCount && !found;
781             i++, area++) {
782            if (stricmp(area->areaName, line)==0){
783                if (!area->scn) {
784                linkArea(area);
785                area->scn=1;
786                }
787                found++;
788            }
789            }
790 
791            /*  NetMail areas */
792            for (i=0, area=config->netMailAreas;
793             i < config->netMailAreaCount && !found;
794             i++, area++) {
795            if (stricmp(area->areaName, line)==0){
796                if (!area->scn) {
797                linkArea(area);
798                area->scn=1;
799                }
800                found++;
801            }
802            }
803 
804            if(!found) w_log(LL_ERR, "Couldn't find area \"%s\"", line);
805            nfree(line);
806         }
807 
808      }
809      fclose(f);
810      if (config->LinkWithImportlog == lwiKill) remove(config->importlog);
811       } else {
812      /*  importlog does not exist link all areas */
813      w_log(LL_INFO, "No ImportLog file, linking all Areas");
814 
815      /*  NetMails */
816      for (i = 0; i < config -> netMailAreaCount; i++)
817         linkArea (&(config->netMailAreas[i]));
818 
819      /*  EchoAreas */
820      for (i=0; i < config->echoAreaCount; i++) linkArea(&(config->echoAreas[i]));
821 
822      /*  Local Areas */
823      for (i=0; i < config->localAreaCount; i++) linkArea(&(config->localAreas[i]));
824       }
825    }
826 
827    w_log(LL_STAT, "Linked by msgid/reply: %ld, replid: %ld, subj: %ld, revmsgid: %ld", (long)links_msgid, (long)links_replid, (long)links_subj, (long)links_revmsgid);
828    if (links_ignored)
829       w_log(LL_SUMMARY, "Linked total: %ld, Ignored: %ld", (long) links_total, (long) links_ignored);
830    else
831       w_log(LL_SUMMARY, "Linked total: %ld", (long) links_total);
832 
833    w_log(LL_STOP, "Done");
834 
835    MsgCloseApi();
836    closeLog();
837    disposeConfig(config);
838    return (0);
839 }
840