1 /*:ts=8*/
2 /*****************************************************************************
3  * FIDOGATE --- Gateway UNIX Mail/News <-> FIDO NetMail/EchoMail
4  *
5  *
6  * Toss FTN NetMail/EchoMail
7  *
8  *****************************************************************************
9  * Copyright (C) 1990-2002
10  *  _____ _____
11  * |     |___  |   Martin Junius             <mj@fidogate.org>
12  * | | | |   | |   Radiumstr. 18
13  * |_|_|_|@home|   D-51069 Koeln, Germany
14  *
15  * This file is part of FIDOGATE.
16  *
17  * FIDOGATE is free software; you can redistribute it and/or modify it
18  * under the terms of the GNU General Public License as published by the
19  * Free Software Foundation; either version 2, or (at your option) any
20  * later version.
21  *
22  * FIDOGATE is distributed in the hope that it will be useful, but
23  * WITHOUT ANY WARRANTY; without even the implied warranty of
24  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
25  * General Public License for more details.
26  *
27  * You should have received a copy of the GNU General Public License
28  * along with FIDOGATE; see the file COPYING.  If not, write to the Free
29  * Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
30  *****************************************************************************/
31 
32 #include "fidogate.h"
33 #include "getopt.h"
34 
35 #include <signal.h>
36 #include <sys/wait.h>
37 #include <sys/types.h>
38 
39 #define PROGRAM 	"ftntoss"
40 #define CONFIG		DEFAULT_CONFIG_MAIN
41 
42 /*
43  * Prototypes
44  */
45 void addtoseenby_init(void);
46 void zonegate_init(void);
47 void deleteseenby_init(void);
48 void deletepath_init(void);
49 int lon_to_kludge(Textlist *, char *, LON *);
50 void lon_to_kludge_sorted(Textlist *, char *, LON *);
51 int toss_echomail(Message *, MsgBody *, LON *, LON *, LON *, LON *);
52 void kludge_to_lon(Textlist *, LON *);
53 int node_eq_echo(Node *, Node *);
54 int node_eq_echo_pa(Node *, Node *);
55 int lon_search_echo(LON *, Node *);
56 int is_local_addr(Node *, char);
57 void do_seenby(LON *, LON *, LON *, LON *, int, Node *);
58 void do_path(LON *);
59 int do_check_path(LON *);
60 int do_bad_msg(Message *, MsgBody *);
61 Node *lon_first(LON *);
62 void do_4dpoint(LON *, LON *, Node *, Node *);
63 int do_echomail(Packet *, Message *, MsgBody *);
64 int do_unknown_area(char *, Message *, MsgBody *);
65 #ifdef DO_NOT_TOSS_NETMAIL
66 void add_via(Textlist *, Node *);
67 #endif                          /* DO_NOT_TOSS_NETMAIL */
68 void change_addr(Node *, Node *);
69 void do_rewrite(Message *);
70 int do_remap(Message *);
71 int check_empty(MsgBody *);
72 #ifndef SPYES
73 int do_netmail(Packet *, Message *, MsgBody *);
74 #else
75 int do_netmail(Packet *, Message *, MsgBody *, int);
76 #endif                          /* !SPYES */
77 int unpack(FILE *, Packet *);
78 int unpack_file(char *);
79 void prog_signal(int);
80 void short_usage(void);
81 void usage(void);
82 
83 extern int authorized_new;
84 static char *areas_bbs = NULL;
85 
86 void areafix_init(int);
87 int areafix_auth_check(Node *, char *, char);
88 int cmd_new_int(Node *, char *, char *);
89 int areafix_check_forbidden_area(char *);
90 
91 /*
92  * Command line options
93  */
94 char g_flag = 'n';              /* Processing grade */
95 char t_flag = FALSE;            /* Insecure tossing enabled */
96 char n_flag = FALSE;            /* Accept EchoMail messages not */
97                     /* addressed to our AKA         */
98 char *O_flag = NULL;            /* output packet directory */
99 char s_flag = FALSE;            /* Strip CRASH, HOLD attribute */
100 int maxmsg = 0;                 /* Process maxmsg messages */
101 char x_flag = FALSE;            /* Exit after maxmsg messages */
102 char l_flag = FALSE;            /* Create lock file */
103 char p_flag = FALSE;            /* -p --passthru */
104 
105 static char in_dir[MAXPATH];    /* Input directory */
106 static char must_exit = FALSE;  /* Flag for -x operation */
107 static int msg_count = 0;       /* Counter for -m, -x operation */
108 static int severe_error = OK;   /* ERROR: exit after error */
109 static int signal_exit = FALSE; /* Flag: TRUE if signal received */
110 
111 short int int_uplinks = FALSE;  /* Flag: TRUE if uplinks initialised */
112 
113 /*
114  * Config options
115  */
116 char kill_empty = FALSE;        /* config: KillEmpty */
117 char kill_unknown = FALSE;      /* config: KillUnknown */
118 char kill_routed = FALSE;       /* config: KillRouted */
119 char kill_insecure = FALSE;     /* config: KillInsecure */
120 #ifdef FTN_ACL
121 char kill_readonly = FALSE;     /* config: KillReadonly */
122 #endif                          /* FTN_ACL */
123 char kill_circular = FALSE;     /* config: KillCircular */
124 char log_netmail = FALSE;       /* config: LogNetMail */
125 char check_path = FALSE;        /* config: CheckPath */
126 char dupe_check = FALSE;        /* config: DupeCheck */
127 char kill_dupe = FALSE;         /* config: KillDupe */
128 char kill_nomsgid = FALSE;      /* config: KillNoMSGID */
129 char kill_old = FALSE;          /* config: KillOld */
130 char echomail4d = FALSE;        /* config: EchoMail4D */
131 char no_empty_path = FALSE;     /* config: NoEmptyPath */
132 char add_other_aka = FALSE;     /* config: AddOtherAKAs */
133 #ifdef FTN_ACL
134 char uplink_can_be_readonly = FALSE;    /* config: UplinkCanBeReadonly */
135 #endif                          /* FTN_ACL */
136 char traffic_statistics = FALSE;    /* config: TrafficStatistics */
137 char *traffic_statistics_file = NULL;   /* config: TrafficStatisticsFile */
138 char *autocreate_line = "";     /* config: AutoCreateLine */
139 #ifndef ACTIVE_LOOKUP
140 char autocreate_ng = FALSE;     /* config: AutoCreateNG */
141 #endif                          /* !ACTIVE_LOOKUP */
142 #ifdef DO_NOT_TOSS_NETMAIL
143 char no_rewrite = FALSE;        /* config: NoRewrite */
144 #endif                          /* DO_NOT_TOSS_NETMAIL */
145 
146 short check_point_origin_addr = FALSE;
147 /* Values checking for old messages */
148 static double max_history = 14; /* Max. number of days entry stays
149                                    in MSGID history database */
150 static time_t max_sec = 0;
151 static time_t now_sec = 0;
152 static time_t exp_sec = 0;
153 
154 /*
155  * Global stat counts
156  */
157 static long msgs_in = 0;        /* Input messages */
158 static long msgs_netmail = 0;   /* Output messages NetMail */
159 static long msgs_echomail = 0;  /* Output messages EchoMail */
160 static long msgs_routed = 0;    /* Routed EchoMail messages */
161 static long msgs_insecure = 0;  /* Insecure EchoMail messages */
162 #ifdef FTN_ACL
163 static long msgs_readonly = 0;  /* EchoMail messages to readonly areas */
164 #endif                          /* FTN_ACL */
165 static long msgs_unknown = 0;   /* Unknown EchoMail area messages */
166 static long msgs_empty = 0;     /* Empty NetMail messages */
167 static long msgs_path = 0;      /* Circular path */
168 static long msgs_dupe = 0;      /* Dupe */
169 
170 static long pkts_in = 0;        /* Input packets */
171 static long pkts_bytes = 0;     /* Input bytes */
172 
173 static Textlist notify;
174 
175 /*
176  * AddToSeenBy list
177  */
178 typedef struct st_addtoseenby {
179     char *area;                 /* Area pattern */
180     LON add;                    /* Nodes to add to SEEN-BY */
181     struct st_addtoseenby *next;
182 } AddToSeenBy;
183 
184 static AddToSeenBy *addto_first = NULL;
185 static AddToSeenBy *addto_last = NULL;
186 static bool strict;
187 
188 /*
189  * Init AddToSeenBy list from config file
190  */
addtoseenby_init(void)191 void addtoseenby_init(void)
192 {
193     char *s, *parea, *pnodes;
194     AddToSeenBy *p;
195 
196     for (s = cf_get_string("AddToSeenBy", TRUE);
197          s && *s; s = cf_get_string("AddToSeenBy", FALSE)) {
198         BUF_COPY(buffer, s);
199         parea = xstrtok(buffer, " \t");
200         pnodes = xstrtok(NULL, "\n");
201 
202         if (!parea || !pnodes)
203             continue;
204 
205         p = (AddToSeenBy *) xmalloc(sizeof(AddToSeenBy));
206         p->next = NULL;
207         p->area = strsave(parea);
208         lon_init(&p->add);
209         lon_add_string(&p->add, pnodes);
210 
211         if (addto_first)
212             addto_last->next = p;
213         else
214             addto_first = p;
215         addto_last = p;
216     }
217 }
218 
219 /*
220  * DeleteSeenBy list
221  */
222 typedef struct st_deleteseenby {
223     LON deleteseenby;           /* Nodes to delete from SEEN-BY */
224     struct st_deleteseenby *next;
225 } DeleteSeenBy;
226 
227 static DeleteSeenBy *deleteseenby_first = NULL;
228 static DeleteSeenBy *deleteseenby_last = NULL;
229 
230 /*
231  * Init DeleteSeenBy list from config file
232  */
deleteseenby_init(void)233 void deleteseenby_init(void)
234 {
235     char *s;
236     DeleteSeenBy *p;
237 
238     for (s = cf_get_string("DeleteSeenBy", TRUE);
239          s && *s; s = cf_get_string("DeleteSeenBy", FALSE)) {
240         p = (DeleteSeenBy *) xmalloc(sizeof(DeleteSeenBy));
241         p->next = NULL;
242         lon_init(&p->deleteseenby);
243         lon_add_string(&p->deleteseenby, s);
244 
245         if (deleteseenby_first)
246             deleteseenby_last->next = p;
247         else
248             deleteseenby_first = p;
249         deleteseenby_last = p;
250     }
251 }
252 
253 /*
254  * DeletePath list
255  */
256 typedef struct st_deletepath {
257     LON deletepath;             /* Nodes to delete from PATH */
258     struct st_deletepath *next;
259 } DeletePath;
260 
261 static DeletePath *deletepath_first = NULL;
262 static DeletePath *deletepath_last = NULL;
263 
264 /*
265  * Init DeletePath list from config file
266  */
deletepath_init(void)267 void deletepath_init(void)
268 {
269     char *s;
270     DeletePath *p;
271 
272     for (s = cf_get_string("DeletePath", TRUE);
273          s && *s; s = cf_get_string("DeletePath", FALSE)) {
274         p = (DeletePath *) xmalloc(sizeof(DeletePath));
275         p->next = NULL;
276         lon_init(&p->deletepath);
277         lon_add_string(&p->deletepath, s);
278 
279         if (deletepath_first)
280             deletepath_last->next = p;
281         else
282             deletepath_first = p;
283         deletepath_last = p;
284     }
285 }
286 
287 /*
288  * Zonegate list
289  */
290 
291 static ZoneGate *zonegate_first = NULL;
292 static ZoneGate *zonegate_last = NULL;
293 
294 /*
295  * Init zonegate list
296  */
zonegate_init(void)297 void zonegate_init(void)
298 {
299     char *s;
300     ZoneGate *p;
301 
302     for (s = cf_get_string("ZoneGate", TRUE);
303          s && *s; s = cf_get_string("ZoneGate", FALSE)) {
304         p = (ZoneGate *) xmalloc(sizeof(ZoneGate));
305         p->next = NULL;
306         lon_init(&p->seenby);
307         lon_add_string(&p->seenby, s);
308         if (p->seenby.first) {
309             p->node = p->seenby.first->node;
310             lon_remove(&p->seenby, &p->node);
311         } else
312             node_invalid(&p->node);
313 
314         if (zonegate_first)
315             zonegate_last->next = p;
316         else
317             zonegate_first = p;
318         zonegate_last = p;
319     }
320 }
321 
322 /*
323  * Create new SEEN-BY or ^APATH line from LON
324  */
325 #define MAX_LENGTH 76
326 
lon_to_kludge(Textlist * tl,char * text,LON * lon)327 int lon_to_kludge(Textlist * tl, char *text, LON * lon)
328 {
329     LNode *p;
330     Node old;
331     char *s = NULL;
332     int n = 0;
333 
334     BUF_COPY(buffer, text);
335     node_invalid(&old);
336     old.zone = cf_zone();
337 
338     for (p = lon->first; p; p = p->next)
339         if (p->node.point == 0 || echomail4d) { /* Normally, no 4D Points */
340             p->node.zone = cf_zone();   /* No zone, use current one */
341             s = node_to_asc_diff(&p->node, &old);
342             old = p->node;
343 
344             if (strlen_zero(buffer) + strlen_zero(s) + 1 > MAX_LENGTH) {
345                 BUF_APPEND(buffer, "\r\n");
346                 tl_append(tl, buffer);
347 
348                 node_invalid(&old);
349                 old.zone = cf_zone();
350 
351                 s = node_to_asc_diff(&p->node, &old);
352                 old = p->node;
353                 BUF_COPY3(buffer, text, " ", s);
354             } else
355                 BUF_APPEND2(buffer, " ", s);
356 
357             n = TRUE;
358         }
359 
360     BUF_APPEND(buffer, "\r\n");
361     tl_append(tl, buffer);
362 
363     return n;
364 }
365 
lon_to_kludge_sorted(Textlist * tl,char * text,LON * lon)366 void lon_to_kludge_sorted(Textlist * tl, char *text, LON * lon)
367 {
368     Node old;
369     char *s = NULL;
370     int i;
371 
372     lon_sort(lon, FALSE);
373 
374     BUF_COPY(buffer, text);
375     node_invalid(&old);
376     old.zone = cf_zone();
377 
378     for (i = 0; i < lon->size; i++)
379         if (lon->sorted[i]->point == 0 || echomail4d) { /* Normally, no 4D Points */
380             lon->sorted[i]->zone = cf_zone();   /* No zone, use current one */
381             s = node_to_asc_diff(lon->sorted[i], &old);
382             old = *lon->sorted[i];
383 
384             if (strlen_zero(buffer) + strlen_zero(s) + 1 > MAX_LENGTH) {
385                 BUF_APPEND(buffer, "\r\n");
386                 tl_append(tl, buffer);
387 
388                 node_invalid(&old);
389                 old.zone = cf_zone();
390 
391                 s = node_to_asc_diff(lon->sorted[i], &old);
392                 old = *lon->sorted[i];
393                 BUF_COPY3(buffer, text, " ", s);
394             } else
395                 BUF_APPEND2(buffer, " ", s);
396         }
397 
398     BUF_APPEND(buffer, "\r\n");
399     tl_append(tl, buffer);
400 
401     return;
402 }
403 
404 /*
405  * Toss EchoMail, writing message to packet for each downlink
406  */
toss_echomail(Message * msg,MsgBody * body,LON * seenby,LON * path,LON * nodes,LON * passive)407 int toss_echomail(Message * msg, MsgBody * body, LON * seenby, LON * path,
408                   LON * nodes, LON * passive)
409 {
410     LNode *p;
411     FILE *fp;
412     Textlist save = { NULL, NULL, 0 };
413     char is_saved;
414 
415     for (p = nodes->first; p; p = p->next) {
416         is_saved = FALSE;
417 
418         if (lon_search(passive, &(p->node)))
419             continue;
420 
421         debug(7, "toss_echomail(): message for %s", znfp1(&p->node));
422 
423         /* Check for msg addressed to zonegate */
424         if (zonegate_first) {
425             ZoneGate *pz;
426 
427             for (pz = zonegate_first; pz; pz = pz->next)
428                 if (node_eq(&p->node, &pz->node)) {
429                     debug(7, "toss_echomail(): message is for zonegate, "
430                           "stripping SEEN-BYs");
431                     save = body->seenby;
432                     tl_init(&body->seenby);
433                     is_saved = TRUE;
434                     lon_to_kludge_sorted(&body->seenby,
435                                          "SEEN-BY:", &pz->seenby);
436                     break;
437                 }
438         }
439 
440         /* Rewrite message header */
441         msg->node_to = p->node;
442 #ifdef BEST_AKA
443         cf_set_best(msg->node_to.zone, msg->node_to.net, msg->node_to.node);
444 #endif                          /* BEST_AKA */
445         msg->node_from = cf_n_addr();
446         /* Open packet file, EchoMail, Normal */
447         fp = outpkt_open(cf_addr(), &p->node, g_flag, 'e', 'n', FALSE);
448         if (fp == NULL)
449             return severe_error = ERROR;
450         /* Write message header and body */
451         if (pkt_put_msg_hdr(fp, msg, FALSE) != OK)
452             return severe_error = ERROR;
453         if (msg_put_msgbody(fp, body) != OK)
454             return severe_error = ERROR;
455 
456         if (is_saved) {
457             tl_clear(&body->seenby);
458             body->seenby = save;
459         }
460 
461         msgs_echomail++;
462     }
463 
464     return OK;
465 }
466 
467 /*
468  * Convert SEEN-BY or ^APATH lines to LON
469  */
kludge_to_lon(Textlist * tl,LON * lon)470 void kludge_to_lon(Textlist * tl, LON * lon)
471 {
472     Node old, node;
473     Textline *l;
474     char *p;
475 
476     for (l = tl->first; l; l = l->next) {
477         p = l->line;
478         /* Skip SEEN-BY:, ^APATH: or whatever, copy to buffer[] */
479         while (*p && !is_space(*p) && *p != ':')
480             p++;
481         if (*p == ':')
482             p++;
483         while (*p && is_space(*p))
484             p++;
485         BUF_COPY(buffer, p);
486         strip_crlf(buffer);
487         /* Tokenize and convert to node addresses */
488         node_invalid(&old);
489         old.zone = cf_zone();
490         for (p = strtok(buffer, " \t"); p; p = strtok(NULL, " \t"))
491             if (asc_to_node_diff(p, &node, &old) == OK) {
492                 lon_add(lon, &node);
493                 old = node;
494             }
495     }
496 
497     return;
498 }
499 
500 /*
501  * node_eq_echo() --- compare node adresses, ignoring zone
502  */
node_eq_echo(Node * a,Node * b)503 int node_eq_echo(Node * a, Node * b)
504 {
505     if (a == NULL || b == NULL)
506         return FALSE;
507 
508     return a->net == b->net && a->node == b->node && a->point == b->point;
509 }
510 
511 /*
512  * node_eq_echo_pa() --- compare node adresses, ignoring zone and point
513  *                  if a->point == 0.
514  */
node_eq_echo_pa(Node * a,Node * b)515 int node_eq_echo_pa(Node * a, Node * b)
516 {
517     if (a == NULL || b == NULL)
518         return FALSE;
519 
520     return
521         a->net == b->net && a->node == b->node &&
522         (a->point == 0 || a->point == b->point);
523 }
524 
525 /*
526  * Search node in SEEN-BY list, ignoring zone
527  */
lon_search_echo(LON * lon,Node * node)528 int lon_search_echo(LON * lon, Node * node)
529 {
530     LNode *p;
531 
532     for (p = lon->first; p; p = p->next)
533         if (node_eq_echo(&p->node, node))
534             return TRUE;
535 
536     return FALSE;
537 }
538 
539 /*
540  * Check for local address
541  */
is_local_addr(Node * node,char only3d)542 int is_local_addr(Node * node, char only3d)
543 {
544     Node *n;
545     char found = FALSE;
546 
547     for (n = cf_addr_trav(TRUE); n; n = cf_addr_trav(FALSE))
548         if (only3d ? node_eq_echo(node, n) : node_eq(node, n)) {
549             found = TRUE;
550             break;
551         }
552 
553     return found;
554 }
555 
556 /*
557  * Add nodes to SEEN-BY
558  */
do_seenby(LON * seenby,LON * nodes,LON * new,LON * passive,int cup,Node * from)559 void do_seenby(LON * seenby, LON * nodes, LON * new, LON * passive, int cup,
560                Node * from)
561     /* seenby --- nodes in SEEN-BY lines */
562     /* nodes  --- nodes in AREAS.BBS */
563     /* new    --- new nodes added */
564 {
565     LNode *p;
566     int j = 1;
567 
568     for (p = nodes->first; p; p = p->next, j++)
569         if (!lon_search_echo(seenby, &p->node)) {
570             if (j < cup || node_eq(from, &p->node))
571                 continue;
572 
573             if ((NULL != passive) && !lon_search(passive, &(p->node))) {
574                 lon_add(seenby, &p->node);
575                 if (new)
576                     lon_add(new, &p->node);
577             }
578         }
579 
580     return;
581 }
582 
583 /*
584  * Add current address to ^APATH
585  */
do_path(LON * path)586 void do_path(LON * path)
587 {
588     if (path->last && node_eq_echo(&path->last->node, cf_addr()))
589         /* Already there */
590         return;
591 
592     lon_add(path, cf_addr());
593     return;
594 }
595 
596 /*
597  * Check ^APATH for circular path
598  */
do_check_path(LON * path)599 int do_check_path(LON * path)
600 {
601     LNode *p;
602 
603     for (p = path->first; p; p = p->next)
604         if (p->next)            /* Don't check last node in list */
605             if (is_local_addr(&p->node, TRUE))
606                 return ERROR;
607 
608     return OK;
609 }
610 
611 /*
612  * Save bad message
613  */
do_bad_msg(Message * msg,MsgBody * body)614 int do_bad_msg(Message * msg, MsgBody * body)
615 {
616     FILE *fp;
617 
618     /* Open packet file, EchoMail, Normal */
619     fp = outpkt_open(&msg->node_from, &msg->node_to, 'b', 'a', 'd', TRUE);
620     if (fp == NULL)
621         return severe_error = ERROR;
622     /* Write message header and body */
623     if (pkt_put_msg_hdr(fp, msg, FALSE) != OK)
624         return severe_error = ERROR;
625     if (msg_put_msgbody(fp, body) != OK)
626         return severe_error = ERROR;
627 
628     return OK;
629 }
630 
631 /*
632  * First node in LON
633  */
lon_first(LON * lon)634 Node *lon_first(LON * lon)
635 {
636     if (lon->first)
637         return &lon->first->node;
638 
639     return NULL;
640 }
641 
642 /*
643  * Special SEEN-BY / PATH processing if message originates from a 4d point
644  */
do_4dpoint(LON * seenby,LON * path,Node * from,Node * addr)645 void do_4dpoint(LON * seenby, LON * path, Node * from, Node * addr)
646 {
647     /*
648      * If there is only one node in SEEN-BY, which is the same address
649      * (2d) as the sender, then replace it with the sender 4d address.
650      */
651     if (seenby->size == 1 && node_eq_echo_pa(lon_first(seenby), from))
652         *lon_first(seenby) = *from;
653 
654     /*
655      * Same for PATH
656      */
657     if (path->size == 1 && node_eq_echo_pa(lon_first(path), from))
658         *lon_first(path) = *from;
659 
660     /*
661      * Now make sure that also our current AKA is in SEEN-BY
662      */
663     if (!lon_search_echo(seenby, cf_addr()))
664         lon_add(seenby, cf_addr());
665 
666     return;
667 }
668 
669 /*
670  * SEEN-BY remove processing
671  */
do_deleteseenby(LON * seenby)672 void do_deleteseenby(LON * seenby)
673 {
674     LNode *p;
675     LON *ln;
676 
677     ln = &deleteseenby_first->deleteseenby;
678 
679     lon_debug(9, "do_deleteseenby(): List of SEEN-BYs for removing: ",
680               ln, FALSE);
681 
682     for (p = ln->first; p; p = p->next) {
683         if (lon_search(seenby, &p->node)) {
684             debug(7, "do_deleteseenby(): Found SEEN-BY for removing: %s",
685                   znf1(&p->node));
686             lon_remove(seenby, &p->node);
687         }
688     }
689 }
690 
691 /*
692  * PATH remove processing
693  */
do_deletepath(LON * path)694 void do_deletepath(LON * path)
695 {
696     LNode *p;
697     LON *ln;
698 
699     ln = &deleteseenby_first->deleteseenby;
700 
701     lon_debug(9, "do_deletepath(): List of PATH for removing: ", ln, FALSE);
702 
703     for (p = ln->first; p; p = p->next) {
704         if (lon_search(path, &p->node)) {
705             debug(7, "do_deletepath(): Found PATH for removing: %s",
706                   znf1(&p->node));
707             lon_remove(path, &p->node);
708         }
709     }
710 }
711 
712 /*
713  * Unknown area
714  */
do_unknown_area(char * areaname,Message * msg,MsgBody * body)715 int do_unknown_area(char *areaname, Message * msg, MsgBody * body)
716 {
717 
718     fglog("unknown area %s from %s", areaname, znfp1(&msg->node_from));
719     msgs_unknown++;
720     if (!kill_unknown)
721         return do_bad_msg(msg, body);
722     return OK;
723 }
724 
725 /*
726  * Process EchoMail message
727  */
do_echomail(Packet * pkt,Message * msg,MsgBody * body)728 int do_echomail(Packet * pkt, Message * msg, MsgBody * body)
729 {
730     AreasBBS *area;
731     AddToSeenBy *addto;
732     LON seenby, path, new;
733     int ret;
734     Node *node;
735 #ifdef SECURITY
736     Node node2;
737 #endif                          /* SECURITY */
738     char areaname[BUFSIZ];
739     char autocreate_cmd[BUFSIZ];
740 #ifndef ACTIVE_LOOKUP
741     char autocreate_ng_cmd[MAXPATH];
742     Area *autocreate_area;
743 #endif                          /* !ACTIVE_LOOKUP */
744     Passwd *pwd;
745 
746     /*
747      * Lookup area and set zone
748      */
749     BUF_COPY(areaname, body->area + 5);
750     strip_crlf(areaname);
751     debug(5, "EchoMail AREA: %s", areaname);
752 
753 #ifdef SECURITY
754     /*
755      * Security checks
756      */
757     node_invalid(&node2);
758 
759     /* No Origin or MSGID address => bad */
760     if (node_eq(&node2, &msg->node_orig)) {
761         /* No origin line address => bad message */
762         fglog("bad echomail (no origin addr) area %s from %s",
763               areaname, znfp1(&msg->node_from));
764         ++msgs_insecure;
765         if (!kill_insecure)
766             return do_bad_msg(msg, body);
767         return OK;
768     }
769 
770     if (check_point_origin_addr == TRUE &&
771         0 != pkt->from.point && !node_eq(&msg->node_orig, &pkt->from)) {
772         /* forged origin line address => bad mesage */
773         fglog("bad echomail (forged origin addr) area %s from %s",
774               areaname, znfp1(&msg->node_from));
775         ++msgs_insecure;
776         if (!kill_insecure)
777             return do_bad_msg(msg, body);
778         return OK;
779     }
780 #endif                          /* !SECURITY */
781 
782     if (NULL != (pwd = passwd_lookup("packet", &msg->node_from)) &&
783         stricmp(pkt->passwd, pwd->passwd)) {
784         fglog("Insecure echomail packet from %s, area %s (%s pkt password)",
785               znfp1(&msg->node_from), areaname,
786               ('\0' == *(pkt->passwd)) ? "no" : "bad");
787         ++msgs_insecure;
788         if (!kill_insecure)
789             return do_bad_msg(msg, body);
790         return OK;
791     }
792 
793     if (NULL == (area = areasbbs_lookup(areaname))) {
794 
795         AreaUplink *a;
796 
797         areafix_init(TRUE);
798 
799         areafix_auth_check(&msg->node_from, NULL, FALSE);
800 
801         if (!authorized_new) {
802             fglog
803                 ("node %s not authorized to create area %s (config restriction)",
804                  znfp1(&msg->node_from), areaname);
805             /* Unknown area */
806             do_unknown_area(areaname, msg, body);
807             return OK;
808         }
809 
810         if (areafix_check_forbidden_area(areaname)) {
811             fglog("area %s is forbidden to create", areaname);
812             /* Unknown area */
813             do_unknown_area(areaname, msg, body);
814             return OK;
815         }
816 
817         if (!int_uplinks) {
818             uplinks_init();
819             int_uplinks = TRUE;
820         }
821         a = uplinks_line_get(TRUE, &msg->node_from);
822         if (a != NULL && a->options != NULL)
823             BUF_COPY5(autocreate_cmd, areaname, " ", autocreate_line, " ",
824                       a->options);
825         else
826             BUF_COPY3(autocreate_cmd, areaname, " ", autocreate_line);
827 
828         if (OK != cmd_new_int(&msg->node_from, autocreate_cmd, NULL)) {
829             fglog("can't create area %s from %s (cmd_new() returned ERROR)",
830                   areaname, znfp1(&msg->node_from));
831             do_unknown_area(areaname, msg, body);
832             return OK;
833         }
834 
835         if (NULL == (area = areasbbs_lookup(areaname))) {
836             fglog("can't create area %s from %s (not found after creation)",
837                   areaname, znfp1(&msg->node_from));
838             do_unknown_area(areaname, msg, body);
839             return OK;
840         }
841 
842         fglog("created area %s from %s", areaname, znfp1(&msg->node_from));
843 
844         /*
845          * Create newsgroup if needed.
846          */
847 
848 #ifndef ACTIVE_LOOKUP
849         if (autocreate_ng) {
850             areas_init();
851 
852             if (NULL !=
853                 (autocreate_area = areas_lookup(areaname, NULL, &pkt->to))) {
854                 BUF_COPY2(autocreate_ng_cmd, "%N/ngoper create ",
855                           autocreate_area->group);
856                 ret = run_system(autocreate_ng_cmd);
857                 if (0 != ret)
858                     fglog("can't create newsgroup (rc != 0)");
859             } else {
860                 fglog("can't create newsgroup (not found in areas)");
861             }
862         }
863 #endif                          /* !ACTIVE_LOOKUP */
864     }
865 
866     /* Set address for this area */
867     if (area->zone != -1)
868         cf_set_zone(area->zone);
869     if (area->addr.zone != -1)
870         cf_set_curr(&area->addr);
871 
872     (area->msgs_in)++;
873 
874     /*
875      * Dupe check
876      */
877     if (dupe_check) {
878         char *p, *msgid;
879         Textline *tl;
880 
881         if ((p = kludge_get(&body->kludge, "MSGID", &tl))) {
882             /* ^AMSGID */
883             p = tl->line;
884             if (*p == '\001')
885                 p++;
886             BUF_COPY3(buffer, area->area, " ", p);
887             strip_crlf(buffer);
888             msgid = buffer;
889             /* Replace char illegal for DBZ */
890             for (p = msgid; *p; p++)
891                 if (*p == '\t' || *p == '\r' || *p == '\n')
892                     *p = '_';
893         } else {
894             /* If KillNoMSGID ... */
895             if (kill_nomsgid) {
896                 fglog("no ^AMSGID treated as dupe from %s(%s): %s",
897                       znfp1(&msg->node_from), znfp2(&pkt->from), area->area);
898                 msgs_dupe++;
899                 (area->msgs_dupe)++;
900 
901                 if (!kill_dupe)
902                     return do_bad_msg(msg, body);
903                 return OK;
904             }
905 
906             /* No ^AMSGID, use sender, date and checksum */
907             if (msg_parse_origin(body->origin, &msg->node_orig) == ERROR) {
908                 fglog("invalid * Origin treated as dupe from %s(%s): %s",
909                       znfp1(&msg->node_from), znfp2(&pkt->from), area->area);
910                 msgs_dupe++;
911                 (area->msgs_dupe)++;
912                 if (!kill_dupe)
913                     return do_bad_msg(msg, body);
914                 return OK;
915             }
916 
917             /* Compute CRC for strings from, to, subject */
918             crc32_init();
919             crc32_compute((unsigned char *)msg->name_from,
920                           strlen(msg->name_from));
921             crc32_compute((unsigned char *)msg->name_to, strlen(msg->name_to));
922             crc32_compute((unsigned char *)msg->subject, strlen(msg->subject));
923 
924             str_printf(buffer, sizeof(buffer), "%s NOMSGID: %s %s %08lx",
925                        area->area, znfp1(&msg->node_orig),
926                        date("%y%m%d %H%M%S", &msg->date), crc32_value());
927             msgid = buffer;
928         }
929 
930         /* Check for old message (date < NOW - MaxHistory) */
931         if (kill_old) {
932             if (msg->date < exp_sec) {
933                 fglog
934                     ("message too old, treated as dupe: %s origin %s(%s) date %s",
935                      area->area, znfp1(&msg->node_orig), znfp2(&pkt->from),
936                      date(DATE_FTS_0001, &msg->date));
937                 msgs_dupe++;
938                 (area->msgs_dupe)++;
939                 if (!kill_dupe)
940                     return do_bad_msg(msg, body);
941                 return OK;
942             }
943         }
944 
945         /* Check for existence in MSGID history */
946         if (hi_test(msgid)) {
947             /* Dupe! */
948             fglog("dupe from %s(%s): %s", znfp1(&msg->node_from),
949                   znfp2(&pkt->from), msgid);
950             msgs_dupe++;
951             (area->msgs_dupe)++;
952             if (!kill_dupe)
953                 return do_bad_msg(msg, body);
954             return OK;
955         } else {
956             /* Put into MSGID history */
957 #ifdef INSECURE_DONT_PUT_INTO_DUPE_DB
958             if (node_eq(cf_addr(), &msg->node_from) ||
959                 lon_search(&area->nodes, &msg->node_from))
960 #endif                          /* INSECURE_DONT_PUT_INTO_DUPE_DB */
961                 if (hi_write(msg->date, msgid) == ERROR)
962                     return ERROR;
963         }
964     }
965 
966     /*
967      * Lookup area in AddToSeenBy list
968      */
969     for (addto = addto_first; addto; addto = addto->next)
970         if (wildmatch(area->area, addto->area, TRUE))
971             break;
972 
973     /*
974      * Check that this message is addressed to one of our AKAs
975      */
976     if (!n_flag) {
977         if (!node_eq(&msg->node_to, cf_addr()) &&
978             !is_local_addr(&msg->node_to, FALSE)) {
979             /* Routed EchoMail */
980             fglog("routed echomail area %s from %s to %s", area->area,
981                   znfp1(&msg->node_from), znfp2(&msg->node_to));
982             msgs_routed++;
983             (area->msgs_routed)++;
984             if (!kill_routed)
985                 return do_bad_msg(msg, body);
986             return OK;
987         }
988     }
989 
990     /*
991      * Check that origin is listed in AREAS.BBS
992      */
993     if (!t_flag) {
994         if (!node_eq(cf_addr(), &msg->node_from) &&
995             !lon_search(&area->nodes, &msg->node_from)) {
996             /* Insecure EchoMail */
997             fglog("insecure echomail area %s from %s", area->area,
998                   znfp1(&msg->node_from));
999             msgs_insecure++;
1000             (area->msgs_insecure)++;
1001             if (!kill_insecure)
1002                 return do_bad_msg(msg, body);
1003             return OK;
1004         }
1005     }
1006 
1007 #ifdef FTN_ACL
1008     /*
1009      * Check if link is read only
1010      */
1011     if (uplink_can_be_readonly || !lon_is_uplink(&(area->nodes), area->uplinks,
1012                                                  &(msg->node_from)))
1013         if (ftnacl_isreadonly(&msg->node_from, area->area, TYPE_ECHO)) {
1014             fglog("echomail to read only area %s from %s", area->area,
1015                   znfp1(&msg->node_from));
1016 
1017             tl_appendf(&notify, "%s,Sysop,Toss Daemon,insecure echomail,\r\n\
1018 \r\n\tYour message to area %s was delete. This\r\n\
1019 area have status read-only\r\n\r\n\
1020     Your Message:\r\n\r\n\
1021 		From: %s\r\n\
1022 		To: %s\r\n\
1023 		Subject: %s\r\n\r\n", znfp1(&msg->node_from), area->area, nf1(&msg->node_from), znfp1(&pkt->to), msg->subject);
1024 
1025             msgs_readonly++;
1026             (area->msgs_readonly)++;
1027 #ifdef FTN_ACL
1028             if (!kill_readonly)
1029 #endif                          /* FTN_ACL */
1030                 return do_bad_msg(msg, body);
1031 #ifdef FTN_ACL
1032             return OK;
1033 #endif                          /* FTN_ACL */
1034         }
1035 #endif                          /* FTN_ACL */
1036 
1037     /*
1038      * SEEN-BY / ^APATH processing
1039      */
1040     lon_init(&seenby);
1041     lon_init(&path);
1042     lon_init(&new);
1043     kludge_to_lon(&body->seenby, &seenby);
1044     kludge_to_lon(&body->path, &path);
1045 
1046     /* Before */
1047     lon_debug(9, "SEEN-BY: ", &seenby, FALSE);
1048     lon_debug(9, "Path   : ", &path, FALSE);
1049 
1050     /* Special processing if message is from a 4d point */
1051     do_4dpoint(&seenby, &path, &msg->node_from, cf_addr());
1052 
1053     /* Make sure that sender/recipient are in SEEN-BY */
1054     if (!lon_search_echo(&seenby, &msg->node_from))
1055         lon_add(&seenby, &msg->node_from);
1056     if (!lon_search_echo(&seenby, &msg->node_to))
1057         lon_add(&seenby, &msg->node_to);
1058 
1059 #ifdef SECURITY
1060     /* Make sure that sender is in PATH */
1061     if (NULL == path.last) {
1062         fglog("WARNING: packet hasn't ^APATH, was added %s", nf1(&pkt->from));
1063         lon_add(&path, &pkt->from);
1064     } else if (!node_eq_echo(&path.last->node, &pkt->from)) {
1065         fglog
1066             ("WARNING: last addr ^APATH (%s) isn't eq to addr in pkt header(%s)",
1067              znfp1(&path.last->node), znfp2(&pkt->from));
1068         lon_add(&path, &pkt->from);
1069     }
1070 #endif                          /* SECURITY */
1071 
1072     /* Add nodes not already in SEEN-BY to seenby and new */
1073     do_seenby(&seenby, &area->nodes, &new, &(area->passive), area->uplinks,
1074               &msg->node_from);
1075 
1076     /* Add extra nodes to SEEN-BY */
1077     if (addto)
1078         do_seenby(&seenby, &addto->add, NULL, NULL, 0, &msg->node_from);
1079 
1080     /* Add all AKAs for the current zone, if AddOtherAKA is set */
1081     if (add_other_aka) {
1082         short z;
1083 
1084         z = cf_zone();
1085         for (node = cf_addr_trav(FIRST); node; node = cf_addr_trav(NEXT))
1086             if (node->zone == z && !lon_search_echo(&seenby, node))
1087                 lon_add(&seenby, node);
1088     }
1089 
1090     /* Delete nodes from SEEN-BY */
1091     if (deleteseenby_first)
1092         do_deleteseenby(&seenby);
1093 
1094     /* If not passthru area and not from our own address (point gateway
1095      * setup with Address==Uplink), add our own address to new          */
1096     if (!p_flag &&
1097         !(area->flags & AREASBBS_PASSTHRU) &&
1098         !node_eq(&msg->node_from, cf_addr()))
1099         lon_add(&new, cf_addr());
1100     else if (!p_flag &&
1101              !node_eq(&msg->node_from, cf_addr()) &&
1102              lon_search(&area->nodes, cf_addr()))
1103         lon_add(&new, cf_addr());
1104 
1105     /* Add our address to end of ^APATH, if not already there */
1106     do_path(&path);
1107 
1108     /* Delete nodes from PATH */
1109     if (deletepath_first)
1110         do_deletepath(&path);
1111 
1112     /* After */
1113     lon_debug(9, "SEEN-BY: ", &seenby, FALSE);
1114     lon_debug(9, "Path   : ", &path, FALSE);
1115     lon_debug(9, "New    : ", &new, FALSE);
1116 
1117     /*
1118      * Check for circular ^APATH
1119      */
1120     if (check_path && do_check_path(&path) == ERROR) {
1121         /* Circular ^APATH EchoMail */
1122         fglog("circular path echomail area %s from %s to %s",
1123               area->area, znfp1(&msg->node_from), znfp2(&msg->node_to));
1124         msgs_path++;
1125         (area->msgs_path)++;
1126 
1127         lon_delete(&seenby);
1128         lon_delete(&path);
1129         lon_delete(&new);
1130 
1131         if (!kill_circular)
1132             return do_bad_msg(msg, body);
1133         return OK;
1134     }
1135 
1136     if (!lon_is_uplink(&(area->nodes), area->uplinks, &(msg->node_from)) &&
1137         areasbbs_isstate(area->state, 'W')) {
1138         fglog("Area %s have status W, rename to bad", area->area);
1139         lon_delete(&seenby);
1140         lon_delete(&path);
1141         lon_delete(&new);
1142 
1143         tl_appendf(&notify, "%s,Sysop,Toss Daemon,insecure echomail,\r\n\
1144 \r\n\tYour message to area %s was delete. This\r\n\
1145 area have status 'W' (no traffic from uplink)\r\n\r\n\
1146     Your Message:\r\n\r\n\
1147 	From: %s\r\n\
1148 	To: %s\r\n\
1149 	Subject: %s\r\n\r\n", znfp1(&msg->node_from), area->area, nf1(&msg->node_from), znfp1(&pkt->to), msg->subject);
1150 
1151         if (!kill_circular)
1152             return do_bad_msg(msg, body);
1153         return OK;
1154 
1155     }
1156 
1157     if (NULL != area->state) {
1158         if (areasbbs_isstate(area->state, 'U') ||
1159             areasbbs_isstate(area->state, 'W') ||
1160             areasbbs_isstate(area->state, 'F')) {
1161             fglog("setting state 'S' for area %s", area->area);
1162             areasbbs_chstate(&(area->state), "UWF", 'S');
1163         } else if (areasbbs_isstate(area->state, 'P'))
1164             return do_bad_msg(msg, body);
1165     }
1166     area->time = time(NULL);
1167     areasbbs_changed();
1168 
1169     /*
1170      * Create new SEEN-BY and ^APATH lines
1171      */
1172     tl_clear(&body->seenby);
1173     tl_clear(&body->path);
1174     lon_to_kludge_sorted(&body->seenby, "SEEN-BY:", &seenby);
1175     ret = lon_to_kludge(&body->path, "\001PATH:", &path);
1176 
1177     if (no_empty_path && ret == 0)
1178         tl_clear(&body->path);
1179 
1180     /*
1181      * Send message to all downlinks in new
1182      */
1183     ret = toss_echomail(msg, body, &seenby, &path, &new, &(area->passive));
1184 
1185     (area->msgs_out)++;
1186     (area->msgs_size) +=
1187         sizeof(body) + sizeof(msg) + sizeof(seenby) + sizeof(path);
1188 
1189     lon_delete(&seenby);
1190     lon_delete(&path);
1191     lon_delete(&new);
1192 
1193     return ret;
1194 }
1195 
1196 /*
1197  * Add our ^AVia line
1198  */
1199 #ifndef DO_NOT_TOSS_NETMAIL
add_via(Textlist * list,Node * gate)1200 void add_via(Textlist * list, Node * gate)
1201 {
1202 #ifndef FTS_VIA
1203     BUF_COPY5(buffer, "Via FIDOGATE/", PROGRAM, znf1(gate),
1204               date(DATE_VIA, NULL), "\r");
1205     tl_append(list, buffer);
1206 #else
1207     BUF_COPY5(buffer, "Via ", znf1(gate), " @", date(DATE_VIA, NULL),
1208               " FIDOGATE/");
1209     BUF_APPEND2(buffer, PROGRAM, "\r");
1210     tl_append(list, buffer);
1211 #endif                          /* !FTS_VIA */
1212 }
1213 
1214 #endif                          /* !DO_NOT_TOSS_NETMAIL */
1215 
1216 /*
1217  * Change address according to rewrite pattern
1218  */
change_addr(Node * node,Node * newpat)1219 void change_addr(Node * node, Node * newpat)
1220 {
1221     if (newpat->zone != -1)
1222         node->zone = newpat->zone;
1223     if (newpat->net != -1)
1224         node->net = newpat->net;
1225     if (newpat->node != -1)
1226         node->node = newpat->node;
1227     if (newpat->point != -1)
1228         node->point = newpat->point;
1229 }
1230 
1231 /*
1232  * Perform REWRITE commands
1233  */
do_rewrite(Message * msg)1234 void do_rewrite(Message * msg)
1235 {
1236     Rewrite *r;
1237 
1238     for (r = rewrite_first; r; r = r->next) {
1239         /* From */
1240         if (node_match(&msg->node_from, &r->from))
1241             if (r->type == CMD_REWRITE || (r->type == CMD_REWRITE_FROM &&
1242                                            wildmatch(msg->name_from, r->name,
1243                                                      TRUE))) {
1244                 fglog("rewrite(from): %s @ %s -> %s", msg->name_from,
1245                       znfp1(&msg->node_from), znfp2(&r->to));
1246 
1247                 change_addr(&msg->node_from, &r->to);
1248                 break;
1249             }
1250         /* To */
1251         if (node_match(&msg->node_to, &r->from))
1252             if (r->type == CMD_REWRITE || (r->type == CMD_REWRITE_TO &&
1253                                            wildmatch(msg->name_to, r->name,
1254                                                      TRUE))) {
1255                 fglog("rewrite(to): %s @ %s -> %s", msg->name_from,
1256                       znfp1(&msg->node_from), znfp2(&r->to));
1257 
1258                 change_addr(&msg->node_from, &r->to);
1259                 break;
1260             }
1261     }
1262 }
1263 
1264 /*
1265  * Perform REMAP commands
1266  *
1267  * Return == TRUE: remapped to 0:0/0.0, kill message
1268  */
do_remap(Message * msg)1269 int do_remap(Message * msg)
1270 {
1271     Remap *r;
1272     Node node;
1273     char kill = FALSE;
1274 
1275     for (r = remap_first; r; r = r->next)
1276         if ((r->type == CMD_REMAP_TO &&
1277              node_match(&msg->node_to, &r->from) &&
1278              wildmatch(msg->name_to, r->name, TRUE))
1279             ||
1280             (r->type == CMD_REMAP_FROM &&
1281              node_match(&msg->node_from, &r->from) &&
1282              wildmatch(msg->name_from, r->name, TRUE))
1283             ) {
1284             node = msg->node_to;
1285             change_addr(&msg->node_to, &r->to);
1286 
1287             if (msg->node_to.zone == 0 && msg->node_to.net == 0 &&
1288                 msg->node_to.node == 0 && msg->node_to.point == 0)
1289                 kill = TRUE;
1290 
1291             if (!node_eq(&node, &msg->node_to)) {
1292                 if (r->type == CMD_REMAP_TO)
1293                     fglog("remapto: %s @ %s -> %s", msg->name_to,
1294                           znfp1(&node),
1295                           !kill ? znfp2(&msg->node_to) : "KILLED");
1296                 if (r->type == CMD_REMAP_FROM)
1297                     fglog("remapfrom: %s @ %s -> %s", msg->name_from,
1298                           znfp1(&msg->node_from),
1299                           !kill ? znfp2(&msg->node_to) : "KILLED");
1300             }
1301 
1302             break;
1303         }
1304 
1305     return kill;
1306 }
1307 
1308 /*
1309  * Check for empty NetMail
1310  *
1311  * An empty NetMail is a message comprising only ^A kludges and empty lines.
1312  */
check_empty(MsgBody * body)1313 int check_empty(MsgBody * body)
1314 {
1315     Textline *pl;
1316 
1317     if (body->rfc.n)
1318         return FALSE;
1319     if (body->tear || body->origin)
1320         return FALSE;
1321     if (body->seenby.n)
1322         return FALSE;
1323 
1324     for (pl = body->body.first; pl; pl = pl->next)
1325         if (pl->line[0] && pl->line[0] != '\r')
1326             return FALSE;
1327 
1328     return TRUE;
1329 }
1330 
1331 /*
1332  * Process NetMail message
1333  */
1334 #ifndef SPYES
do_netmail(Packet * pkt,Message * msg,MsgBody * body)1335 int do_netmail(Packet * pkt, Message * msg, MsgBody * body)
1336 #else
1337 int do_netmail(Packet * pkt, Message * msg, MsgBody * body, int forwarded)
1338 #endif                          /* !SPYES */
1339 {
1340     FILE *fp;
1341     char flav;
1342 
1343     if (log_netmail)
1344 #ifndef SPYES
1345         fglog("MAIL: %s @ %s -> %s @ %s",
1346               msg->name_from, znfp1(&msg->node_from),
1347               msg->name_to, znfp2(&msg->node_to));
1348 #else
1349     {
1350         if (forwarded)
1351             fglog("FORWARDED MAIL: %s @ %s -> %s @ %s",
1352                   msg->name_from, znfp1(&msg->node_from),
1353                   msg->name_to, znfp2(&msg->node_to));
1354         else
1355             fglog("MAIL: %s @ %s -> %s @ %s",
1356                   msg->name_from, znfp1(&msg->node_from),
1357                   msg->name_to, znfp2(&msg->node_to));
1358     }
1359 #endif                          /* !SPYES */
1360 
1361     /*
1362      * Check for file attach
1363      */
1364     if (msg->attr & MSG_FILE) {
1365         fglog("file attach %s", msg->subject);
1366     }
1367 
1368     /*
1369      * Check for empty NetMail message addressed to one of our AKAs
1370      */
1371     if (kill_empty && check_empty(body)) {
1372         if (is_local_addr(&msg->node_to, FALSE)) {
1373             fglog("killing empty msg from %s @ %s",
1374                   msg->name_from, znfp1(&msg->node_from));
1375             msgs_empty++;
1376 
1377             return OK;
1378         }
1379     }
1380 
1381     /*
1382      * Rewrite from/to addresses according to ROUTING rules
1383      */
1384 #ifdef DO_NOT_TOSS_NETMAIL
1385     if (!no_rewrite)
1386         do_rewrite(msg);
1387 #else
1388     do_rewrite(msg);
1389 #endif                          /* DO_NOT_TOSS_NETMAIL */
1390 
1391     /*
1392      * Remap to address according to ROUTING rules
1393      */
1394 #ifdef DO_NOT_TOSS_NETMAIL
1395     if (!no_rewrite && do_remap(msg))
1396 #else
1397     if (do_remap(msg))
1398 #endif                          /* DO_NOT_TOSS_NETMAIL */
1399     {
1400         /* Kill this message, remapped to 0:0/0.0 */
1401         return OK;
1402     }
1403 
1404     /*
1405      * Write to output packet
1406      */
1407     cf_set_best(msg->node_to.zone, msg->node_to.net, msg->node_to.node);
1408 
1409     /* Get outbound flavor from msg attributes */
1410     flav = 'n';
1411     if (!s_flag) {
1412         if (msg->attr & MSG_HOLD)
1413             flav = 'h';
1414         if (msg->attr & MSG_CRASH)
1415             flav = 'c';
1416     }
1417 
1418 #ifdef DO_NOT_TOSS_NETMAIL
1419     pkt_outdir(cf_p_netmaildir(), NULL);
1420 
1421     /* dirty hack for more compatible with other soft */
1422     fp = outpkt_open(cf_addr(), &msg->node_to, 'a', 'a', 'a', FALSE);
1423 #else
1424     /* Open output packet */
1425     fp = outpkt_open(cf_addr(), &msg->node_to, g_flag, 'n', flav, FALSE);
1426 #endif                          /* DO_NOT_TOSS_NETMAIL */
1427 
1428     if (fp == NULL)
1429         return severe_error = ERROR;
1430 
1431     /* Add ftntoss ^AVia line */
1432 #ifndef DO_NOT_TOSS_NETMAIL
1433     add_via(&body->via, cf_addr());
1434 #endif                          /* !DO_NOT_TOSS_NETMAIL */
1435 
1436     /* Write message header and body */
1437     if (pkt_put_msg_hdr(fp, msg, TRUE) != OK)
1438         return severe_error = ERROR;
1439     if (msg_put_msgbody(fp, body) != OK)
1440         return severe_error = ERROR;
1441 
1442 #ifdef DO_NOT_TOSS_NETMAIL
1443     pkt_outdir(O_flag ? O_flag : cf_p_toss_route(), NULL);
1444 #endif                          /* DO_NOT_TOSS_NETMAIL */
1445 
1446     msgs_netmail++;
1447 
1448     return OK;
1449 }
1450 
1451 /*
1452  * Read and process FTN packets
1453  */
unpack(FILE * pkt_file,Packet * pkt)1454 int unpack(FILE * pkt_file, Packet * pkt)
1455 {
1456     Message msg = { 0 };        /* Message header */
1457     Textlist tl;                /* Textlist for message body */
1458     MsgBody body;               /* Message body of FTN message */
1459 #ifdef SPYES
1460     Spy *spy;                   /* Spy info if avaliable */
1461     char old_subject[MSG_MAXSUBJ];
1462 #endif                          /* SPYES */
1463     int type;
1464 
1465     /*
1466      * Initialize
1467      */
1468     tl_init(&tl);
1469     msg_body_init(&body);
1470 
1471     /*
1472      * Read packet
1473      */
1474     type = pkt_get_int16(pkt_file);
1475     if (type == ERROR) {
1476         if (feof(pkt_file)) {
1477             fglog("$WARNING: premature EOF reading input packet");
1478             TMPS_RETURN(OK);
1479         }
1480 
1481         fglog("ERROR: reading input packet");
1482         TMPS_RETURN(ERROR);
1483     }
1484 
1485     while ((type == MSG_TYPE) && !xfeof(pkt_file)) {
1486         /*
1487          * Read message header
1488          */
1489         msg.node_from = pkt->from;
1490         msg.node_to = pkt->to;
1491 
1492         if (pkt_get_msg_hdr(pkt_file, &msg, strict) == ERROR) {
1493             fglog("ERROR: reading input packet");
1494             TMPS_RETURN(ERROR);
1495         }
1496 
1497         /*
1498          * Read message body
1499          */
1500 #ifdef OLD_TOSS
1501 
1502         type = pkt_get_body(pkt_file, &tl);
1503 
1504         if (type == ERROR) {
1505             if (feof(pkt_file)) {
1506                 fglog("$WARNING: premature EOF reading input packet");
1507             } else {
1508                 fglog("ERROR: reading input packet");
1509                 TMPS_RETURN(ERROR);
1510             }
1511         }
1512         msgs_in++;
1513         msg_count++;
1514 
1515         /*
1516          * Parse message body
1517          */
1518         if (msg_body_parse(&tl, &body) == -2)
1519             fglog("ERROR: parsing message body");
1520 #else
1521         if (pkt_get_body_parse(pkt_file, &body, &msg.node_from, &msg.node_to) !=
1522             OK) {
1523             fglog("ERROR: parsing message body");
1524             return ERROR;
1525         }
1526         msgs_in++;
1527         msg_count++;
1528 #endif
1529         /* Retrieve address information from kludges for NetMail */
1530         if (body.area == NULL) {
1531             /* Don't use point address from packet for Netmail */
1532             msg.node_from.point = 0;
1533             msg.node_to.point = 0;
1534             /* Retrieve complete address from kludges */
1535             kludge_pt_intl(&body, &msg, TRUE);
1536             msg.node_orig = msg.node_from;
1537 
1538 #ifndef SPYES
1539             debug(5, "NetMail: %s -> %s",
1540                   znfp1(&msg.node_from), znfp2(&msg.node_to));
1541             if (do_netmail(pkt, &msg, &body) == ERROR)
1542                 TMPS_RETURN(ERROR);
1543 #else
1544             if (((spy = spyes_lookup(&msg.node_from)) != NULL) ||
1545                 ((spy = spyes_lookup(&msg.node_to)) != NULL)) {
1546                 debug(5, "NetMail spyes: %s -> %s, forwarded to %s",
1547                       znfp1(&msg.node_from),
1548                       znfp2(&msg.node_to), znfp3(&spy->forward_node));
1549                 if (do_netmail(pkt, &msg, &body, FALSE) == ERROR)
1550                     return ERROR;
1551                 BUF_COPY(old_subject, msg.subject);
1552                 str_printf(msg.subject, MSG_MAXSUBJ, "[FWD] from %s to %s: %s",
1553                            znfp1(&msg.node_from),
1554                            znfp2(&msg.node_to), old_subject);
1555                 msg.node_to = spy->forward_node;
1556                 if (do_netmail(pkt, &msg, &body, TRUE) == ERROR)
1557                     return ERROR;
1558             } else {
1559                 debug(5, "NetMail: %s -> %s",
1560                       znfp1(&msg.node_from), znfp2(&msg.node_to));
1561                 if (do_netmail(pkt, &msg, &body, FALSE) == ERROR)
1562                     return ERROR;
1563             }
1564 #endif                          /* !SPYES */
1565         } else {
1566             /* Specially for echomail */
1567             msg.node_from = pkt->from;
1568             msg.node_to = pkt->to;
1569 
1570             /* Try to get address from Origin or MSGID */
1571             if (OK != msg_parse_origin(body.origin, &msg.node_orig) &&
1572                 OK != msg_parse_msgid(kludge_get(&body.kludge, "MSGID", NULL),
1573                                       &msg.node_orig)) {
1574                 node_invalid(&msg.node_orig);
1575             }
1576 
1577             debug(5, "EchoMail: %s -> %s (orig: %s)",
1578                   znfp1(&msg.node_from),
1579                   znfp2(&msg.node_to), znfp3(&msg.node_orig));
1580             if (do_echomail(pkt, &msg, &body) == ERROR)
1581                 TMPS_RETURN(ERROR);
1582         }
1583 
1584         /*
1585          * Exit if signal received
1586          */
1587         if (signal_exit) {
1588             outpkt_close();
1589             msg_count = 0;
1590             TMPS_RETURN(severe_error = ERROR);
1591         }
1592 
1593         /*
1594          * Check for number of messages exceeding maxmsg
1595          */
1596         if (maxmsg && msg_count >= maxmsg) {
1597             if (x_flag)
1598                 must_exit = TRUE;
1599             else
1600                 outpkt_close();
1601             msg_count = 0;
1602         }
1603 
1604         tmps_freeall();
1605     } /**while(type == MSG_TYPE)**/
1606 
1607     TMPS_RETURN(OK);
1608 }
1609 
1610 /*
1611  * Unpack one packet file
1612  */
unpack_file(char * pkt_name)1613 int unpack_file(char *pkt_name)
1614 {
1615     Packet pkt;
1616     FILE *pkt_file;
1617     TIMEINFO ti;
1618     long pkt_size;
1619 
1620     /* Update time info for old messages */
1621     GetTimeInfo(&ti);
1622     now_sec = ti.time;
1623     max_sec = 24L * 3600L * max_history;
1624     exp_sec = now_sec - max_sec;
1625     if (exp_sec < 0)
1626         exp_sec = 0;
1627     debug(4, "now=%ld max=%ld, old < %ld",
1628           (long)now_sec, (long)max_sec, (long)exp_sec);
1629 
1630     /* Open packet and read header */
1631     pkt_file = fopen(pkt_name, R_MODE);
1632     if (!pkt_file) {
1633         fglog("$ERROR: can't open packet %s", pkt_name);
1634         rename_bad(pkt_name);
1635         TMPS_RETURN(OK);
1636     }
1637     if (pkt_get_hdr(pkt_file, &pkt) == ERROR) {
1638         fglog("ERROR: reading header from %s", pkt_name);
1639         fclose(pkt_file);
1640         rename_bad(pkt_name);
1641         TMPS_RETURN(OK);
1642     }
1643 
1644     /* Unpack it */
1645     pkt_size = check_size(pkt_name);
1646     fglog("packet %s (%ldb) from %s to %s", pkt_name, pkt_size,
1647           znfp1(&pkt.from), znfp2(&pkt.to));
1648     pkts_in++;
1649     pkts_bytes += pkt_size;
1650 
1651     if (unpack(pkt_file, &pkt) == ERROR) {
1652         fglog("ERROR: processing %s", pkt_name);
1653         fclose(pkt_file);
1654         rename_bad(pkt_name);
1655         TMPS_RETURN(severe_error);
1656     }
1657 
1658     fclose(pkt_file);
1659 
1660     if (unlink(pkt_name)) {
1661         fglog("$ERROR: can't unlink %s", pkt_name);
1662         rename_bad(pkt_name);
1663         TMPS_RETURN(ERROR);
1664     }
1665 
1666     TMPS_RETURN(OK);
1667 }
1668 
1669 /*
1670  * Function called on SIGINT
1671  */
prog_signal(int signum)1672 void prog_signal(int signum)
1673 {
1674     char *name = "";
1675 
1676     signal_exit = TRUE;
1677 
1678     switch (signum) {
1679     case SIGHUP:
1680         name = " by SIGHUP";
1681         break;
1682     case SIGINT:
1683         name = " by SIGINT";
1684         break;
1685     case SIGQUIT:
1686         name = " by SIGQUIT";
1687         break;
1688     default:
1689         name = "";
1690         break;
1691     }
1692 
1693     fglog("KILLED%s: exit forced", name);
1694 }
1695 
1696 /*
1697  * Usage messages
1698  */
short_usage(void)1699 void short_usage(void)
1700 {
1701     fprintf(stderr, "usage: %s [-options] [packet ...]\n", PROGRAM);
1702     fprintf(stderr, "       %s --help  for more information\n", PROGRAM);
1703 }
1704 
usage(void)1705 void usage(void)
1706 {
1707     fprintf(stderr, "FIDOGATE %s  %s %s\n\n",
1708             version_global(), PROGRAM, version_local(VERSION));
1709 
1710     fprintf(stderr, "usage:   %s [-options] [packet ...]\n\n", PROGRAM);
1711     fprintf(stderr, "\
1712 options: -d --no-dupecheck            disable dupe check\n\
1713          -g --grade G                 processing grade\n\
1714          -I --in-dir DIR              set input packet directory\n\
1715          -O --out-dir DIR             set output packet directory\n\
1716          -l --lock-file               create lock file while processing\n\
1717          -t --insecure                insecure tossing (no AREAS.BBS check)\n\
1718          -n --toss-all                toss all EchoMail messages\n\
1719          -p --passthru                make all areas passthru\n\
1720          -r --routing-file NAME       read routing file\n\
1721          -s --strip-attribute         strip crash, hold message attribute\n\
1722          -m --maxmsg N                close output after N msgs\n\
1723          -x --maxmsg-exit             close output and exit after -m msgs\n\
1724          -M --maxopen N               set max # of open packet files\n\
1725          -b --areas-bbs NAME          use alternate AREAS.BBS\n\
1726 \n\
1727 	 -v --verbose                 more verbose\n\
1728 	 -h --help                    this help\n\
1729          -c --config NAME             read config file (\"\" = none)\n\
1730 	 -a --addr Z:N/F.P            set FTN address\n\
1731 	 -u --uplink-addr Z:N/F.P     set FTN uplink address\n\
1732 	 -w --wait [TIME]             wait for areas.bbs and history\n\
1733 	                              lock to be released\n");
1734 }
1735 
1736 /***** main() ****************************************************************/
1737 
main(int argc,char ** argv)1738 int main(int argc, char **argv)
1739 {
1740     int c;
1741     char *p;
1742     char *I_flag = NULL, *r_flag = NULL, *M_flag = NULL;
1743     char *c_flag = NULL;
1744     char *a_flag = NULL, *u_flag = NULL;
1745     char d_flag = FALSE;
1746     char *pkt_name;
1747     int w_flag = FALSE;
1748     time_t toss_start, toss_delta;
1749     AreasBBS *area;
1750     char bbslock[MAXPATH];
1751 
1752     int option_index;
1753     static struct option long_options[] = {
1754         {"no-dupecheck", 1, 0, 'd'},    /* Disable dupe check */
1755         {"grade", 1, 0, 'g'},   /* grade */
1756         {"in-dir", 1, 0, 'I'},  /* Set inbound packets directory */
1757         {"lock-file", 0, 0, 'l'},   /* Create lock file while processing */
1758         {"out-dir", 1, 0, 'O'}, /* Set packet directory */
1759         {"insecure", 0, 0, 't'},    /* Insecure */
1760         {"toss-all", 0, 0, 'n'},    /* Toss all EchoMail */
1761         {"routing-file", 1, 0, 'r'},    /* Set routing file */
1762         {"strip-attribute", 0, 0, 's'}, /* Strip attribute */
1763         {"maxmsg", 1, 0, 'm'},  /* Close after N messages */
1764         {"maxmsg-exit", 0, 0, 'x'}, /* Exit after maxmsg messages */
1765         {"maxopen", 1, 0, 'M'}, /* Set max # open packet files */
1766         {"areas-bbs", 1, 0, 'b'},
1767         {"passthru", 0, 0, 'p'},
1768 
1769         {"verbose", 0, 0, 'v'}, /* More verbose */
1770         {"help", 0, 0, 'h'},    /* Help */
1771         {"config", 1, 0, 'c'},  /* Config file */
1772         {"addr", 1, 0, 'a'},    /* Set FIDO address */
1773         {"uplink-addr", 1, 0, 'u'}, /* Set FIDO uplink address */
1774         {"wait", 1, 0, 'w'},
1775         {0, 0, 0, 0}
1776     };
1777 
1778     /* Log name */
1779 
1780     log_program(PROGRAM);
1781 
1782     /* Init configuration */
1783     cf_initialize();
1784 
1785     /* Parse options */
1786     while ((c = getopt_long(argc, argv, "dg:O:I:ltnr:sm:xM:b:pvhc:w:a:u:",
1787                             long_options, &option_index)) != EOF)
1788         switch (c) {
1789     /***** ftntoss options *****/
1790         case 'd':
1791             d_flag = TRUE;
1792             break;
1793         case 'g':
1794             g_flag = *optarg;
1795             break;
1796         case 'I':
1797             I_flag = optarg;
1798             break;
1799         case 'l':
1800             l_flag = TRUE;
1801             break;
1802         case 'O':
1803             O_flag = optarg;
1804             break;
1805         case 't':
1806             t_flag = TRUE;
1807             break;
1808         case 'n':
1809             n_flag = TRUE;
1810             break;
1811         case 'r':
1812             r_flag = optarg;
1813             break;
1814         case 's':
1815             s_flag = TRUE;
1816             break;
1817         case 'm':
1818             maxmsg = atoi(optarg);
1819             break;
1820         case 'x':
1821             x_flag = TRUE;
1822             break;
1823         case 'M':
1824             M_flag = optarg;
1825             break;
1826         case 'b':
1827             areas_bbs = optarg;
1828             break;
1829         case 'p':
1830             p_flag = TRUE;
1831             break;
1832 
1833     /***** Common options *****/
1834         case 'v':
1835             verbose++;
1836             break;
1837         case 'h':
1838             usage();
1839             return 0;
1840             break;
1841         case 'c':
1842             c_flag = optarg;
1843             break;
1844         case 'a':
1845             a_flag = optarg;
1846             break;
1847         case 'u':
1848             u_flag = optarg;
1849             break;
1850         case 'w':
1851             if (optarg)
1852                 w_flag = atoi(optarg);
1853             else
1854                 w_flag = WAIT;
1855             break;
1856         default:
1857             short_usage();
1858             return EX_USAGE;
1859             break;
1860         }
1861 
1862     /*
1863      * Read config file
1864      */
1865     cf_read_config_file(c_flag ? c_flag : CONFIG);
1866 
1867     /*
1868      * Process config options
1869      */
1870     if (a_flag)
1871         cf_set_addr(a_flag);
1872     if (u_flag)
1873         cf_set_uplink(u_flag);
1874 
1875     cf_debug();
1876 
1877     /*
1878      * Process optional config statements
1879      */
1880     if (cf_get_string("KillEmpty", TRUE) || cf_get_string("KillBlank", TRUE)) {
1881         debug(8, "actual: killempty true");
1882         kill_empty = TRUE;
1883     }
1884     if (cf_get_string("KillUnknown", TRUE)) {
1885         kill_unknown = TRUE;
1886     }
1887     if (cf_get_string("KillRouted", TRUE)) {
1888         kill_routed = TRUE;
1889     }
1890     if (cf_get_string("KillInsecure", TRUE)) {
1891         kill_insecure = TRUE;
1892     }
1893 #ifdef FTN_ACL
1894     if (cf_get_string("KillReadonly", TRUE)) {
1895         kill_readonly = TRUE;
1896     }
1897 #endif                          /* FTN_ACL */
1898     if (cf_get_string("KillCircular", TRUE)) {
1899         kill_circular = TRUE;
1900     }
1901     if (cf_get_string("LogNetMail", TRUE) || cf_get_string("Track", TRUE)) {
1902         debug(8, "actual lognetmail true");
1903         log_netmail = TRUE;
1904     }
1905     if (cf_get_string("CheckPath", TRUE)) {
1906         check_path = TRUE;
1907     }
1908     if (cf_get_string("DupeCheck", TRUE)) {
1909         if (d_flag) {
1910             debug(8, "DupeCheck disabled from command line!");
1911             dupe_check = FALSE;
1912         } else {
1913             dupe_check = TRUE;
1914         }
1915     }
1916     if (cf_get_string("KillNoMSGID", TRUE)) {
1917         kill_nomsgid = TRUE;
1918     }
1919     if (cf_get_string("KillDupe", TRUE) || cf_get_string("KillDupes", TRUE)) {
1920         debug(8, "actual KillDupe true");
1921         kill_dupe = TRUE;
1922     }
1923     if (!maxmsg && (p = cf_get_string("MaxMsg", TRUE))) {
1924         maxmsg = atoi(p);
1925         debug(8, "actual: MaxMsg %d", maxmsg);
1926 
1927     }
1928     if (!M_flag && (p = cf_get_string("MaxOpenFiles", TRUE))) {
1929         M_flag = p;
1930         debug(8, "actual MaxOpenFiles %s", M_flag);
1931     }
1932     if (M_flag)
1933         outpkt_set_maxopen(atoi(M_flag));
1934     if (cf_get_string("KillOld", TRUE)) {
1935         kill_old = TRUE;
1936     }
1937     if ((p = cf_get_string("MaxHistory", TRUE))) {
1938         max_history = atof(p);
1939         if (max_history < 0)
1940             max_history = 0;
1941         debug(8, "actual MaxHistory %g", max_history);
1942     }
1943     if (cf_get_string("TossEchoMail4D", TRUE)) {
1944         echomail4d = TRUE;
1945     }
1946     if (cf_get_string("NoEmptyPath", TRUE)) {
1947         no_empty_path = TRUE;
1948     }
1949     if (cf_get_string("AddOtherAKA", TRUE) ||
1950         cf_get_string("AddOtherAKAs", TRUE)) {
1951         debug(8, "actual AddOtherAKA true");
1952         add_other_aka = TRUE;
1953     }
1954 #ifdef FTN_ACL
1955     if (cf_get_string("UplinkCanBeReadonly", TRUE)) {
1956         uplink_can_be_readonly = TRUE;
1957     }
1958 #endif                          /* FTN_ACL */
1959     if (cf_get_string("TrafficStatistics", TRUE)) {
1960         traffic_statistics = TRUE;
1961     }
1962     if ((p = cf_get_string("TrafficStatisticsFile", TRUE))) {
1963         traffic_statistics_file = p;
1964     }
1965     if ((p = cf_get_string("AutoCreateLine", TRUE))) {
1966         autocreate_line = p;
1967     }
1968 #ifndef ACTIVE_LOOKUP
1969     if (cf_get_string("AutoCreateNG", TRUE)) {
1970         autocreate_ng = TRUE;
1971     }
1972 #endif                          /* !ACTIVE_LOOKUP */
1973     if (cf_get_string("CheckPointOriginAddr", TRUE)) {
1974         check_point_origin_addr = FALSE;
1975     }
1976 #ifdef DO_NOT_TOSS_NETMAIL
1977     if (cf_get_string("NoRewrite", TRUE)) {
1978         no_rewrite = TRUE;
1979     }
1980 #endif                          /* DO_NOT_TOSS_NETMAIL */
1981     strict = (cf_get_string("FTNStrictPktCheck", TRUE) != NULL);
1982 
1983     zonegate_init();
1984     addtoseenby_init();
1985     deleteseenby_init();
1986     deletepath_init();
1987 #ifdef SPYES
1988     spyes_init();
1989 #endif                          /* SPYES */
1990 #ifdef FTN_ACL
1991     ftnacl_init();
1992 #endif                          /* FTN_ACL */
1993 
1994     tl_init(&notify);
1995 
1996     /*
1997      * Process local options
1998      */
1999     BUF_EXPAND(in_dir, I_flag ? I_flag : cf_p_pinbound());
2000     pkt_outdir(O_flag ? O_flag : cf_p_toss_toss(), NULL);
2001     pkt_baddir(O_flag ? O_flag : cf_p_toss_bad(), NULL);
2002 
2003     /*
2004      * Get name of areas.bbs file from config file
2005      */
2006     if (!areas_bbs)
2007         areas_bbs = cf_get_string("AreasBBS", TRUE);
2008     if (!areas_bbs) {
2009         fprintf(stderr, "%s: no areas.bbs specified\n", PROGRAM);
2010         exit_free();
2011         return EX_USAGE;
2012     }
2013 
2014     routing_init(r_flag ? r_flag : cf_p_routing());
2015     BUF_COPY2(bbslock, areas_bbs, ".lock");
2016     if (lock_path(bbslock, w_flag ? w_flag : NOWAIT) == ERROR) {
2017         exit_free();
2018         return EXIT_BUSY;
2019     }
2020     areasbbs_init(areas_bbs);
2021     passwd_init();
2022 
2023     /* Install signal/exit handlers */
2024     signal(SIGHUP, prog_signal);
2025     signal(SIGINT, prog_signal);
2026     signal(SIGQUIT, prog_signal);
2027 
2028     /* Start time */
2029     toss_start = time(NULL);
2030 
2031     c = EXIT_OK;
2032 
2033     if (optind >= argc) {
2034         /* process packet files in directory */
2035         dir_sortmode(DIR_SORTMTIME);
2036         if (dir_open(in_dir, "*.pkt", TRUE) == ERROR) {
2037             fglog("$ERROR: can't open directory %s", in_dir);
2038             unlock_path(bbslock);
2039             exit_free();
2040             return EX_OSERR;
2041         }
2042 
2043         /* Lock file */
2044         if (l_flag)
2045             if (lock_program(PROGRAM, NOWAIT) == ERROR) {
2046                 /* Already busy */
2047                 exit_free();
2048                 return EXIT_BUSY;
2049             }
2050 
2051         /* Open history */
2052         if (dupe_check) {
2053             if (lock_program(cf_p_lock_history(), w_flag ? w_flag : NOWAIT) ==
2054                 ERROR) {
2055 
2056                 /* Already busy, exit */
2057                 if (l_flag) {
2058                     unlock_program(PROGRAM);
2059                     unlock_path(bbslock);
2060                 }
2061                 exit_free();
2062                 return EXIT_BUSY;
2063             }
2064             hi_init_history();
2065         }
2066 
2067         for (pkt_name = dir_get(TRUE); pkt_name; pkt_name = dir_get(FALSE)) {
2068             if (unpack_file(pkt_name) == ERROR) {
2069                 c = EXIT_ERROR;
2070                 break;
2071             }
2072             if (must_exit) {
2073                 c = EXIT_CONTINUE;
2074                 break;
2075             }
2076         }
2077 
2078         /* Close history */
2079         if (dupe_check) {
2080             unlock_program(cf_p_lock_history());
2081             hi_close();
2082         }
2083 
2084         dir_close();
2085 
2086         /* Lock file */
2087         if (l_flag)
2088             unlock_program(PROGRAM);
2089     } else {
2090         /* Lock file */
2091         if (l_flag)
2092             if (lock_program(PROGRAM, NOWAIT) == ERROR) {
2093                 /* Already busy */
2094                 exit_free();
2095                 return EXIT_BUSY;
2096             }
2097 
2098         /* Open history */
2099         if (dupe_check) {
2100             if (lock_program(cf_p_lock_history(), w_flag ? w_flag : NOWAIT) ==
2101                 ERROR) {
2102                 /* Already busy, exit */
2103                 if (l_flag) {
2104                     unlock_program(PROGRAM);
2105                     unlock_path(bbslock);
2106                 }
2107 
2108                 exit_free();
2109                 return EXIT_BUSY;
2110             }
2111             hi_init_history();
2112         }
2113 
2114         /* Process packet files on command line */
2115         for (; optind < argc; optind++) {
2116             if (unpack_file(argv[optind]) == ERROR) {
2117                 c = EXIT_ERROR;
2118                 break;
2119             }
2120             if (must_exit) {
2121                 c = EXIT_CONTINUE;
2122                 break;
2123             }
2124         }
2125 
2126         /* Close history */
2127         if (dupe_check) {
2128             unlock_program(cf_p_lock_history());
2129             hi_close();
2130         }
2131 
2132         /* Lock file */
2133         if (l_flag)
2134             unlock_program(PROGRAM);
2135     }
2136 
2137     outpkt_close();
2138 
2139     /* Stop time */
2140     toss_delta = time(NULL) - toss_start;
2141     if (toss_delta <= 0)
2142         toss_delta = 1;
2143 
2144     if (traffic_statistics) {
2145         area = areasbbs_first();
2146         for (area = areasbbs_first(); NULL != area; area = area->next) {
2147             if (0 != area->msgs_in) {
2148                 if (traffic_statistics_file)
2149                     log_file(traffic_statistics_file);
2150 
2151                 if (area->msgs_in == area->msgs_out) {
2152                     fglog("area %-35s: msgs in: %-3u out: %-3u size: %li",
2153                           area->area, area->msgs_in, area->msgs_out,
2154                           area->msgs_size);
2155                 } else {
2156 #ifndef FTN_ACL
2157                     fglog
2158                         ("area %-35s: msgs in: %-3u out: %-3u size: %li killed: %u/%u/%u/%u",
2159                          area->area, area->msgs_in, area->msgs_out,
2160                          area->msgs_size, area->msgs_routed,
2161                          area->msgs_insecure, area->msgs_dupe, area->msgs_path);
2162 #else
2163                     fglog
2164                         ("area %-35s: msgs in: %-3u out: %-3u size: %li killed: %u/%u/%u/%u/%u",
2165                          area->area, area->msgs_in, area->msgs_out,
2166                          area->msgs_size, area->msgs_routed,
2167                          area->msgs_insecure, area->msgs_dupe, area->msgs_path,
2168                          area->msgs_readonly);
2169 #endif                          /* !FTN_ACL */
2170                 }
2171                 log_program(PROGRAM);
2172             }
2173         }
2174     }
2175 
2176     if (pkts_in)
2177         fglog("pkts processed: %ld, %ld Kbyte in %ld s, %.2f Kbyte/s",
2178               pkts_in, pkts_bytes / 1024, (long)toss_delta,
2179               (double)pkts_bytes / 1024. / toss_delta);
2180 
2181     if (msgs_in) {
2182         fglog("msgs processed: %ld in, %ld out (%ld mail, %ld echo)",
2183               msgs_in, msgs_netmail + msgs_echomail, msgs_netmail,
2184               msgs_echomail);
2185         fglog("msgs processed: %ld in %ld s, %.6f msgs/s", msgs_in,
2186               (long)toss_delta, (double)msgs_in / toss_delta);
2187     }
2188 
2189     if (msgs_unknown || msgs_routed || msgs_insecure || msgs_empty)
2190         fglog
2191             ("msgs killed:    %ld empty, %ld unknown, %ld routed, %ld insecure",
2192              msgs_empty, msgs_unknown, msgs_routed, msgs_insecure);
2193 #ifdef FTN_ACL
2194     if (msgs_readonly)
2195         fglog("msgs killed:    %ld to read only areas", msgs_readonly);
2196 #endif                          /* FTN_ACL */
2197     if (dupe_check && msgs_dupe)
2198         fglog("msgs killed:    %ld dupe", msgs_dupe);
2199     if (check_path && msgs_path)
2200         fglog("msgs killed:    %ld circular path", msgs_path);
2201 
2202     if (notify.n > 0)
2203         send_request(&notify);
2204 
2205     if (ERROR == areasbbs_rewrite())
2206         fglog("error while rewriting areas.bbs");
2207 
2208     unlock_path(bbslock);
2209 
2210     exit_free();
2211     return c;
2212 }
2213