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