1 /*:ts=8*/
2 /*****************************************************************************
3  * FIDOGATE --- Gateway UNIX Mail/News <-> FTN NetMail/EchoMail
4  *
5  *
6  * Function for processing AREAS.BBS EchoMail distribution file.
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 <fcntl.h>
34 
35 /*
36  * Number of old AREAS.BBS to keep as AREAS.Onn
37  */
38 #define N_HISTORY	5
39 
40 static char *areasbbs_1stline = NULL;
41 static AreasBBS *areasbbs_list = NULL;
42 static AreasBBS *areasbbs_last = NULL;
43 static char *areasbbs_filename = NULL;
44 static int areasbbs_changed_flag = FALSE;
45 
46 /*
47  * Remove area from areas.bbs
48  */
areasbbs_remove(AreasBBS * cur,AreasBBS * prev)49 void areasbbs_remove(AreasBBS * cur, AreasBBS * prev)
50 {
51     if (!cur)
52         return;
53 
54     if (prev)
55         prev->next = cur->next;
56     else
57         areasbbs_list = cur->next;
58     if (areasbbs_last == cur)
59         areasbbs_last = prev;
60 }
61 
areasbbs_remove1(AreasBBS * cur)62 void areasbbs_remove1(AreasBBS * cur)
63 {
64     AreasBBS *p1, *p2;
65 
66     if (!cur)
67         return;
68 
69     p1 = NULL;
70     p2 = areasbbs_list;
71     while (p2) {
72         if (cur == p2) {
73             areasbbs_remove(p2, p1);
74             return;
75         }
76         p1 = p2;
77         p2 = p2->next;
78     }
79 }
80 
81 /*
82  * Alloc and init new AreasBBS struct
83  */
areasbbs_new(void)84 AreasBBS *areasbbs_new(void)
85 {
86     AreasBBS *p;
87 
88     p = (AreasBBS *) xmalloc(sizeof(AreasBBS));
89 
90     /* Init */
91     p->next = NULL;
92     p->flags = 0;
93     p->dir = NULL;
94     p->area = NULL;
95     p->zone = -1;
96     node_invalid(&p->addr);
97     p->lvl = -1;
98     p->key = NULL;
99     p->desc = NULL;
100     p->state = NULL;
101     lon_init(&(p->passive));
102     p->time = 0;
103     p->expire_n = 0;
104     p->expire_t = 0;
105     p->msgs_in = 0;
106     p->msgs_out = 0;
107     p->msgs_dupe = 0;
108     p->msgs_routed = 0;
109     p->msgs_insecure = 0;
110 #ifdef FTN_ACL
111     p->msgs_readonly = 0;
112 #endif                          /* FTN_ACL */
113     p->msgs_path = 0;
114     p->msgs_size = 0;
115     lon_init(&p->nodes);
116     p->uplinks = 1;
117 
118     return p;
119 }
120 
121 /*
122  * Add nodes from string to list of nodes
123  */
areasbbs_add_string(LON * lon,LON * lon_passive,char * p)124 static int areasbbs_add_string(LON * lon, LON * lon_passive, char *p)
125 {
126     Node node, old;
127     int ret;
128 
129     old.zone = cf_zone();
130     old.net = old.node = old.point = -1;
131     lon->size = 0;
132 
133     ret = OK;
134     for (; p; p = xstrtok(NULL, " \t\r\n")) {
135         if ('P' == *p) {
136             lon_add(lon_passive, &node);
137             p++;
138         }
139         if (asc_to_node_diff(p, &node, &old) == OK) {
140             old = node;
141             lon_add(lon, &node);
142         } else {
143             ret = ERROR;
144             break;
145         }
146     }
147 
148     return ret;
149 }
150 
151 /*
152  *
153  */
areasbbs_add_passive(LON * lon,char * p)154 int areasbbs_add_passive(LON * lon, char *p)
155 {
156     Node node, old;
157     int ret;
158     char *p2;
159 
160     old.zone = cf_zone();
161     old.net = old.node = old.point = -1;
162 
163     ret = OK;
164     while (p) {
165         p2 = strchr(p, ',');
166         if (p2)
167             *p2++ = '\0';
168         if (asc_to_node_diff(p, &node, &old) == OK) {
169             old = node;
170             lon_add(lon, &node);
171         } else {
172             ret = ERROR;
173             break;
174         }
175         p = p2;
176     }
177 
178     return ret;
179 }
180 
181 /*
182  * Create new AreasBBS struct for line from AREAS.BBS
183  */
areasbbs_parse_line(char * line)184 static AreasBBS *areasbbs_parse_line(char *line)
185 {
186     AreasBBS *p;
187     char *dir, *tag, *nl, *o2;
188 
189     dir = xstrtok(line, " \t\r\n");
190     tag = xstrtok(NULL, " \t\r\n");
191     if (!dir || !tag)
192         return NULL;
193 
194     /* New areas.bbs entry */
195     p = areasbbs_new();
196 
197     if (*dir == '#') {
198         p->flags |= AREASBBS_PASSTHRU;
199         dir++;
200     }
201     p->dir = strsave(dir);
202     p->area = strsave(tag);
203 
204     /*
205      * Options:
206      *
207      *     -a Z:N/F.P    alternate AKA for this area
208      *     -z ZONE       alternate zone AKA for this area
209      *     -l LVL        Areafix access level
210      *     -k KEY        Areafix access key
211      *     -d DESC       Area description text
212      *     -#            Passthru
213      +     -r            Read-only for new downlinks
214      *     -p LIST       List of write-only (passive) links
215      *     -s STAT   Area status
216      *     -u NUM    Uplinks number
217      */
218     nl = xstrtok(NULL, " \t\r\n");
219     while (nl && *nl == '-') {
220         if (streq(nl, "-a")) {  /* -a Z:N/F.P */
221             o2 = xstrtok(NULL, " \t\r\n");
222             asc_to_node(o2, &p->addr, FALSE);
223         }
224 
225         if (streq(nl, "-z")) {  /* -z ZONE */
226             o2 = xstrtok(NULL, " \t\r\n");
227             p->zone = atoi(o2);
228         }
229 
230         if (streq(nl, "-l")) {  /* -l LVL */
231             o2 = xstrtok(NULL, " \t\r\n");
232             p->lvl = atoi(o2);
233         }
234 
235         if (streq(nl, "-k")) {  /* -k KEY */
236             o2 = xstrtok(NULL, " \t\r\n");
237             p->key = strsave(o2);
238         }
239 
240         if (streq(nl, "-d")) {  /* -d DESC */
241             o2 = xstrtok(NULL, " \t\r\n");
242             p->desc = strsave(o2);
243         }
244 
245         if (streq(nl, "-s")) {  /* -s STATE */
246             o2 = xstrtok(NULL, " \t\r\n");
247             p->state = strsave(o2);
248         }
249 
250         if (streq(nl, "-#")) {  /* -# */
251             p->flags |= AREASBBS_PASSTHRU;
252         }
253 
254         if (streq(nl, "-r")) {  /* -r */
255             p->flags |= AREASBBS_READONLY;
256         }
257 
258         if (streq(nl, "-p")) {  /* -p LIST */
259             o2 = xstrtok(NULL, " \t\r\n");
260             areasbbs_add_passive(&(p->passive), o2);
261         }
262 
263         if (streq(nl, "-t")) {  /* -t TIME */
264             o2 = xstrtok(NULL, " \t\r\n");
265             p->time = atol(o2);
266         }
267 
268         if (streq(nl, "-e")) {  /* -e DAYS */
269             o2 = xstrtok(NULL, " \t\r\n");
270             p->expire_n = atoi(o2);
271         }
272         if (streq(nl, "-n")) {  /* -n DAYS */
273             o2 = xstrtok(NULL, " \t\r\n");
274             p->expire_t = atoi(o2);
275         }
276         if (streq(nl, "-u")) {  /* -u NUM */
277             o2 = xstrtok(NULL, " \t\r\n");
278             p->uplinks = atoi(o2);
279         }
280         nl = xstrtok(NULL, " \t\r\n");
281     }
282 
283     areasbbs_add_string(&(p->nodes), &(p->passive), nl);
284 
285     if (p->zone == -1)
286         p->zone = p->nodes.first ? p->nodes.first->node.zone : 0;
287 
288     return p;
289 }
290 
291 /*
292  * Read area distribution list from AREAS.BBS file
293  *
294  * Format:
295  *    [#$]DIR AREA [-options] Z:N/F.P Z:N/F.P ...
296  */
areasbbs_init(char * name)297 int areasbbs_init(char *name)
298 {
299     FILE *fp;
300     AreasBBS *p;
301 
302     if (!name)
303         return ERROR;
304 
305     debug(14, "Reading %s file", name);
306 
307     areasbbs_filename = name;
308     areasbbs_changed_flag = FALSE;
309 
310     fp = fopen_expand_name(name, R_MODE, FALSE);
311     if (!fp) {
312         if (errno == ENOENT) {
313             debug(14, "No file %s, assuming empty", name);
314             return OK;
315         } else {
316             return ERROR;
317         }
318     }
319 
320     /*
321      * 1st line is special
322      */
323     if (fgets(buffer, BUFFERSIZE, fp)) {
324         strip_crlf(buffer);
325         areasbbs_1stline = strsave(buffer);
326     }
327 
328     /*
329      * The following lines are areas and linked nodes
330      */
331     while (fgets(buffer, BUFFERSIZE, fp)) {
332         strip_crlf(buffer);
333         p = areasbbs_parse_line(buffer);
334         if (!p)
335             continue;
336 
337         debug(15, "areas.bbs: %s %s Z%d", p->dir, p->area, p->zone);
338 
339         /*
340          * Put into linked list
341          */
342         if (areasbbs_list)
343             areasbbs_last->next = p;
344         else
345             areasbbs_list = p;
346         areasbbs_last = p;
347     }
348 
349     fclose(fp);
350 
351     return OK;
352 }
353 
354 /*
355  * Output AREAS.BBS, format short sorted list of downlink
356  */
areasbbs_print(FILE * fp)357 int areasbbs_print(FILE * fp)
358 {
359     AreasBBS *p;
360 
361     fprintf(fp, "%s\n", areasbbs_1stline);
362 
363     for (p = areasbbs_list; p; p = p->next) {
364         if (p->flags & AREASBBS_PASSTHRU)
365             fprintf(fp, "#");
366         fprintf(fp, "%s %s ", p->dir, p->area);
367         if (p->zone != -1)
368             fprintf(fp, "-z %d ", p->zone);
369         if (p->addr.zone != -1)
370             fprintf(fp, "-a %s ", znfp1(&p->addr));
371         if (p->lvl != -1)
372             fprintf(fp, "-l %d ", p->lvl);
373         if (p->key)
374             fprintf(fp, "-k %s ", p->key);
375         if (0 < p->passive.size) {
376             fprintf(fp, "-p ");
377             lon_print_passive(&(p->passive), fp);
378             fprintf(fp, " ");
379         }
380         fprintf(fp, "-t %lu ", (unsigned long)(p->time));
381         if (p->expire_n)
382             fprintf(fp, "-e %d ", p->expire_n);
383         if (p->expire_t)
384             fprintf(fp, "-n %d ", p->expire_t);
385         if (p->desc)
386             fprintf(fp, "-d \"%s\" ", p->desc);
387         if (p->state)
388             fprintf(fp, "-s %s ", p->state);
389         if (p->uplinks > 1)
390             fprintf(fp, "-u %d ", p->uplinks);
391         lon_print_sorted(&p->nodes, fp, p->uplinks);
392         fprintf(fp, "\n");
393     }
394 
395     return ferror(fp);
396 }
397 
398 /*
399  * Return areasbbs_list
400  */
areasbbs_first(void)401 AreasBBS *areasbbs_first(void)
402 {
403     return areasbbs_list;
404 }
405 
406 /*
407  * Rewrite AREAS.BBS if changed
408  */
areasbbs_rewrite(void)409 int areasbbs_rewrite(void)
410 {
411     char old[MAXPATH], new[MAXPATH];
412     int i, ovwr;
413     FILE *fp;
414 
415     if (!areasbbs_changed_flag) {
416         debug(4, "AREAS.BBS not changed");
417         return OK;
418     }
419 
420     /*
421      * Base name
422      */
423     if (!areasbbs_filename) {
424         fglog("$ERROR: unable to rewrite areas.bbs");
425         return ERROR;
426     }
427 
428     str_expand_name(buffer, MAXPATH, areasbbs_filename);
429     ovwr = strlen(buffer) - 3;  /* 3 = extension "bbs" */
430     if (ovwr < 0)               /* Just to be sure */
431         ovwr = 0;
432 
433     /*
434      * Write new one as AREAS.NEW
435      */
436     BUF_COPY(new, buffer);
437     str_copy(new + ovwr, sizeof(new) - ovwr, "new");
438     debug(4, "Writing %s", new);
439 
440     if ((fp = fopen(new, W_MODE)) == NULL) {
441         fglog("$ERROR: can't open %s for writing AREAS.BBS", new);
442         return ERROR;
443     }
444     if (areasbbs_print(fp) == ERROR) {
445         fglog("$ERROR: writing to %s", new);
446         fclose(fp);
447         unlink(new);
448         return ERROR;
449     }
450     if (fclose(fp) == ERROR) {
451         fglog("$ERROR: closing %s", new);
452         unlink(new);
453         return ERROR;
454     }
455 
456     /*
457      * Renumber saved AREAS.Onn
458      */
459     BUF_COPY(old, buffer);
460     sprintf(old + ovwr, "o%02d", N_HISTORY);
461     debug(4, "Removing %s", old);
462     unlink(old);
463     for (i = N_HISTORY - 1; i >= 1; i--) {
464         BUF_COPY(old, buffer);
465         sprintf(old + ovwr, "o%02d", i);
466         BUF_COPY(new, buffer);
467         sprintf(new + ovwr, "o%02d", i + 1);
468         debug(4, "Renaming %s -> %s", old, new);
469         rename(old, new);
470     }
471 
472     /*
473      * Rename AREAS.BBS -> AREAS.O01
474      */
475     BUF_COPY(old, buffer);
476     str_copy(old + ovwr, sizeof(old) - ovwr, "bbs");
477     BUF_COPY(new, buffer);
478     str_copy(new + ovwr, sizeof(new) - ovwr, "o01");
479     debug(4, "Renaming %s -> %s", old, new);
480     rename(old, new);
481 
482     /*
483      * Rename AREAS.NEW -> AREAS.BBS
484      */
485     BUF_COPY(old, buffer);
486     str_copy(old + ovwr, sizeof(old) - ovwr, "new");
487     BUF_COPY(new, buffer);
488     str_copy(new + ovwr, sizeof(new) - ovwr, "bbs");
489     debug(4, "Renaming %s -> %s", old, new);
490     rename(old, new);
491 
492     fglog("%s changed", buffer);
493 
494     return OK;
495 }
496 
areasbbs_changed(void)497 void areasbbs_changed(void)
498 {
499     areasbbs_changed_flag = TRUE;
500 }
501 
areasbbs_not_changed(void)502 void areasbbs_not_changed(void)
503 {
504     areasbbs_changed_flag = FALSE;
505 }
506 
507 /*
508  * Lookup area
509  */
areasbbs_lookup(char * area)510 AreasBBS *areasbbs_lookup(char *area)
511 {
512     AreasBBS *p;
513 
514     /**FIXME: the search method should use hashing or similar**/
515     for (p = areasbbs_list; p; p = p->next) {
516         if (area && !stricmp(area, p->area))
517             return p;
518     }
519 
520     return NULL;
521 }
522 
523 /*
524  * Add areas.bbs entry
525  */
areasbbs_add(AreasBBS * p)526 void areasbbs_add(AreasBBS * p)
527 {
528     /* Put into linked list */
529     if (areasbbs_list)
530         areasbbs_last->next = p;
531     else
532         areasbbs_list = p;
533     areasbbs_last = p;
534 }
535 
areasbbs_isstate(char * state,char st)536 int areasbbs_isstate(char *state, char st)
537 {
538     if ((NULL == state) || ('\0' == st))
539         return FALSE;
540 
541     if (NULL != strchr(state, st))
542         return TRUE;
543     else
544         return FALSE;
545 }
546 
547 /* Returns:
548  *	TRUE  -> state changed
549  *	FALSE -> state not changed
550  */
areasbbs_chstate(char ** state,char * stold,char stnew)551 int areasbbs_chstate(char **state, char *stold, char stnew)
552 {
553     char *p, *p2;
554     int i, j, len;
555 
556     if (NULL == state)
557         return FALSE;
558 
559     /* No state -> just add new state */
560     if ((NULL == *state) || ('\0' == **state)) {
561         p = xmalloc(2);
562         p[0] = stnew;
563         p[1] = '\0';
564         xfree(*state);
565         *state = p;
566         return TRUE;
567     }
568 
569     /* Check if new state already set */
570     if (NULL != strchr(*state, stnew))
571         return FALSE;
572 
573     len = strlen(*state) + 2;   /* '\0' + 1 byte for state */
574     p = xmalloc(len);
575     memset(p, 0, len);
576     p2 = *state;
577 
578     /* "old state" not given */
579     if ((NULL == stold) || ('\0' == *stold)) {
580         BUF_COPY(p, p2);
581         j = len - 2;
582     } else {
583         for (i = 0, j = 0; '\0' != p2[i]; ++i)
584             if (NULL == strchr(stold, p2[i]))
585                 p[j++] = p2[i];
586     }
587 
588     p[j] = stnew;
589     xfree(*state);
590     *state = p;
591     return TRUE;
592 }
593 
areasbbs_free(void)594 void areasbbs_free(void)
595 {
596     AreasBBS *p, *n;
597 
598     for (p = areasbbs_list; p; p = n) {
599         n = p->next;
600         xfree(p->area);
601         xfree(p->dir);
602         xfree(p->key);
603         xfree(p->desc);
604         xfree(p->state);
605 
606         if ((&p->passive)->size > 0)
607             lon_delete(&p->passive);
608         if ((&p->nodes)->size > 0)
609             lon_delete(&p->nodes);
610         xfree(p);
611     }
612     if (areasbbs_1stline)
613         xfree(areasbbs_1stline);
614 }
615