d88898be7213f8090ed15dff64dbb28dcd63325f
[citadel.git] / citadel / netproc.c
1 /*
2  * $Id$
3  *
4  * Citadel/UX Intelligent Network Processor for IGnet/Open networks
5  * See copyright.txt for copyright information
6  *
7  */
8
9 /* How long it takes for an old node to drop off the network map */
10 #define EXPIRY_TIME     (2592000L)
11
12 /* How long we keep recently arrived messages in the use table */
13 #define USE_TIME        (604800L)
14
15 /* Where do we keep our lock file? */
16 #define LOCKFILE        "/tmp/netproc.LCK"
17
18 /* Path to the 'uudecode' utility (needed for network file transfers) */
19 #define UUDECODE        "/usr/bin/uudecode"
20
21 /* Files used by the networker */
22 #define MAILSYSINFO     "./network/mail.sysinfo"
23
24 /* Uncomment the DEBUG def to see noisy traces */
25 #define DEBUG 1
26
27
28 #include "sysdep.h"
29 #include <stdlib.h>
30 #include <unistd.h>
31 #include <sys/types.h>
32 #include <sys/wait.h>
33 #include <string.h>
34 #include <fcntl.h>
35 #include <stdio.h>
36 #include <ctype.h>
37
38 #if TIME_WITH_SYS_TIME
39 # include <sys/time.h>
40 # include <time.h>
41 #else
42 # if HAVE_SYS_TIME_H
43 #  include <sys/time.h>
44 # else
45 #  include <time.h>
46 # endif
47 #endif
48
49 #include <signal.h>
50 #include <errno.h>
51 #include <syslog.h>
52 #include "citadel.h"
53 #include "tools.h"
54 #include "ipc.h"
55
56 /* A list of users you wish to filter out of incoming traffic can be kept
57  * in ./network/filterlist -- messages from these users will be automatically
58  * moved to FILTERROOM.  Normally this will be the same as TWITROOM (the
59  * room problem user messages are moved to) but you can override this by
60  * specifying a different room name here.
61  */
62 #ifndef FILTERROOM
63 #define FILTERROOM TWITROOM
64 #endif
65
66 struct msglist {
67         struct msglist *next;
68         long m_num;
69         char m_rmname[ROOMNAMELEN];
70 };
71
72 struct rmlist {
73         struct rmlist *next;
74         char rm_name[ROOMNAMELEN];
75         long rm_lastsent;
76 };
77
78 struct filterlist {
79         struct filterlist *next;
80         char f_person[64];
81         char f_room[64];
82         char f_system[64];
83 };
84
85 struct syslist {
86         struct syslist *next;
87         char s_name[16];
88         char s_type[4];
89         char s_nexthop[128];
90         time_t s_lastcontact;
91         char s_humannode[64];
92         char s_phonenum[32];
93         char s_gdom[64];
94 };
95
96
97
98 /*
99  * This structure is used to hold all of the fields of a message
100  * during conversion, processing, or whatever.
101  */
102 struct minfo {
103         char A[512];
104         char B[512];
105         char C[512];
106         char D[512];
107         char E[512];
108         char G[512];
109         char H[512];
110         char I[512];
111         char N[512];
112         char O[512];
113         char P[512];
114         char R[512];
115         char S[512];
116         long T;
117         char U[512];
118         char Z[512];
119         char nexthop[512];
120         };
121
122
123 struct usetable {
124         struct usetable *next;
125         char msgid[256];
126         time_t timestamp;
127 };
128
129
130
131
132 void serv_read(char *buf, int bytes);
133 void serv_write(char *buf, int nbytes);
134 void get_config(void);
135
136 struct filterlist *filter = NULL;
137 struct syslist *slist = NULL;
138 struct msglist *purgelist = NULL;
139 struct usetable *usetable = NULL;
140
141 struct config config;
142 extern char bbs_home_directory[];
143 extern int home_specified;
144
145
146 #ifndef HAVE_STRERROR
147 /*
148  * replacement strerror() for systems that don't have it
149  */
150 char *strerror(int e)
151 {
152         static char buf[32];
153
154         sprintf(buf, "errno = %d", e);
155         return (buf);
156 }
157 #endif
158
159
160 void strip_trailing_whitespace(char *buf)
161 {
162         while (isspace(buf[strlen(buf) - 1]))
163                 buf[strlen(buf) - 1] = 0;
164 }
165
166
167 /*
168  * we also load the mail.sysinfo table into memory, make changes
169  * as we learn more about the network from incoming messages, and write
170  * the table back to disk when we're done.
171  */
172 int load_syslist(void)
173 {
174         FILE *fp;
175         struct syslist *stemp;
176         char insys = 0;
177         char buf[128];
178
179         fp = fopen(MAILSYSINFO, "r");
180         if (fp == NULL)
181                 return (1);
182
183         while (1) {
184                 if (fgets(buf, 128, fp) == NULL) {
185                         fclose(fp);
186                         return (0);
187                 }
188                 buf[strlen(buf) - 1] = 0;
189                 while (isspace(buf[0]))
190                         strcpy(buf, &buf[1]);
191                 if (buf[0] == '#')
192                         buf[0] = 0;
193                 if ((insys == 0) && (strlen(buf) != 0)) {
194                         insys = 1;
195                         stemp = (struct syslist *) malloc(sizeof(struct syslist));
196                         stemp->next = slist;
197                         slist = stemp;
198                         strcpy(slist->s_name, buf);
199                         strcpy(slist->s_type, "bin");
200                         strcpy(slist->s_nexthop, "Mail");
201                         slist->s_lastcontact = 0L;
202                         strcpy(slist->s_humannode, "");
203                         strcpy(slist->s_phonenum, "");
204                         strcpy(slist->s_gdom, "");
205                 } else if ((insys == 1) && (strlen(buf) == 0)) {
206                         insys = 0;
207                 } else if ((insys == 1) && (!strncasecmp(buf, "bin", 3))) {
208                         strcpy(slist->s_type, "bin");
209                         strcpy(slist->s_nexthop, &buf[4]);
210                 } else if ((insys == 1) && (!strncasecmp(buf, "use", 3))) {
211                         strcpy(slist->s_type, "use");
212                         strcpy(slist->s_nexthop, &buf[4]);
213                 } else if ((insys == 1) && (!strncasecmp(buf, "uum", 3))) {
214                         strcpy(slist->s_type, "uum");
215                         strcpy(slist->s_nexthop, &buf[4]);
216                 } else if ((insys == 1) && (!strncasecmp(buf, "lastcontact", 11))) {
217                         long foo;
218                         sscanf(&buf[12], "%ld", &foo);
219                         slist->s_lastcontact = foo;
220                 } else if ((insys == 1) && (!strncasecmp(buf, "humannode", 9))) {
221                         strcpy(slist->s_humannode, &buf[10]);
222                 } else if ((insys == 1) && (!strncasecmp(buf, "phonenum", 8))) {
223                         strcpy(slist->s_phonenum, &buf[9]);
224                 } else if ((insys == 1) && (!strncasecmp(buf, "gdom", 4))) {
225                         strcpy(slist->s_gdom, &buf[5]);
226                 }
227         }
228 }
229
230 /* now we have to set up two "special" nodes on the list: one
231  * for the local node, and one for an Internet gateway
232  */
233 void setup_special_nodes(void)
234 {
235         struct syslist *stemp, *slocal;
236
237         slocal = NULL;
238         for (stemp = slist; stemp != NULL; stemp = stemp->next) {
239                 if (!strcasecmp(stemp->s_name, config.c_nodename))
240                         slocal = stemp;
241         }
242         if (slocal == NULL) {
243                 slocal = (struct syslist *) malloc(sizeof(struct syslist));
244                 slocal->next = slist;
245                 slist = slocal;
246         }
247         strcpy(slocal->s_name, config.c_nodename);
248         strcpy(slocal->s_type, "bin");
249         strcpy(slocal->s_nexthop, "Mail");
250         time(&slocal->s_lastcontact);
251         strcpy(slocal->s_humannode, config.c_humannode);
252         strcpy(slocal->s_phonenum, config.c_phonenum);
253
254         slocal = NULL;
255         for (stemp = slist; stemp != NULL; stemp = stemp->next) {
256                 if (!strcasecmp(stemp->s_name, "internet"))
257                         slocal = stemp;
258         }
259         if (slocal == NULL) {
260                 slocal = (struct syslist *) malloc(sizeof(struct syslist));
261                 slocal->next = slist;
262                 slist = slocal;
263         }
264         strcpy(slocal->s_name, "internet");
265         strcpy(slocal->s_type, "uum");
266         strcpy(slocal->s_nexthop, "%s");
267         time(&slocal->s_lastcontact);
268         strcpy(slocal->s_humannode, "Internet Gateway");
269         strcpy(slocal->s_phonenum, "");
270         strcpy(slocal->s_gdom, "");
271
272 }
273
274 /*
275  * here's the routine to write the table back to disk.
276  */
277 void rewrite_syslist(void)
278 {
279         struct syslist *stemp;
280         FILE *newfp;
281         time_t now;
282
283         time(&now);
284         newfp = fopen(MAILSYSINFO, "w");
285         for (stemp = slist; stemp != NULL; stemp = stemp->next) {
286                 if (!strcasecmp(stemp->s_name, config.c_nodename)) {
287                         time(&stemp->s_lastcontact);
288                         strcpy(stemp->s_type, "bin");
289                         strcpy(stemp->s_humannode, config.c_humannode);
290                         strcpy(stemp->s_phonenum, config.c_phonenum);
291                 }
292                 /* remove systems we haven't heard from in a while */
293                 if ((stemp->s_lastcontact == 0L)
294                     || (now - stemp->s_lastcontact < EXPIRY_TIME)) {
295                         fprintf(newfp, "%s\n%s %s\n",
296                          stemp->s_name, stemp->s_type, stemp->s_nexthop);
297                         if (strlen(stemp->s_phonenum) > 0)
298                                 fprintf(newfp, "phonenum %s\n", stemp->s_phonenum);
299                         if (strlen(stemp->s_gdom) > 0)
300                                 fprintf(newfp, "gdom %s\n", stemp->s_gdom);
301                         if (strlen(stemp->s_humannode) > 0)
302                                 fprintf(newfp, "humannode %s\n", stemp->s_humannode);
303                         if (stemp->s_lastcontact > 0L)
304                                 fprintf(newfp, "lastcontact %ld %s",
305                                         (long) stemp->s_lastcontact,
306                                         asctime(localtime(&stemp->s_lastcontact)));
307                         fprintf(newfp, "\n");
308                 }
309         }
310         fclose(newfp);
311         /* now free the list */
312         while (slist != NULL) {
313                 stemp = slist;
314                 slist = slist->next;
315                 free(stemp);
316         }
317 }
318
319
320 /* call this function with the node name of a system and it returns a pointer
321  * to its syslist structure.
322  */
323 struct syslist *get_sys_ptr(char *sysname)
324 {
325         static char sysnambuf[16];
326         static struct syslist *sysptrbuf = NULL;
327         struct syslist *stemp;
328
329         if ((!strcmp(sysname, sysnambuf))
330             && (sysptrbuf != NULL))
331                 return (sysptrbuf);
332
333         strcpy(sysnambuf, sysname);
334         for (stemp = slist; stemp != NULL; stemp = stemp->next) {
335                 if (!strcmp(sysname, stemp->s_name)) {
336                         sysptrbuf = stemp;
337                         return (stemp);
338                 }
339         }
340         sysptrbuf = NULL;
341         return (NULL);
342 }
343
344
345 /*
346  * make sure only one copy of netproc runs at a time, using lock files
347  */
348 int set_lockfile(void)
349 {
350         FILE *lfp;
351         int onppid;
352
353         if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
354                 fscanf(lfp, "%d", &onppid);
355                 fclose(lfp);
356                 if (!kill(onppid, 0) || errno == EPERM)
357                         return 1;
358         }
359         lfp = fopen(LOCKFILE, "w");
360         if (lfp == NULL) {
361                 syslog(LOG_NOTICE, "Cannot create %s: %s", LOCKFILE,
362                         strerror(errno));
363                 return(1);
364         }
365         fprintf(lfp, "%ld\n", (long) getpid());
366         fclose(lfp);
367         return (0);
368 }
369
370 void remove_lockfile(void)
371 {
372         unlink(LOCKFILE);
373 }
374
375 /*
376  * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
377  * cleanup() .  If for some reason netproc hangs waiting for the server
378  * to clean up, the alarm clock goes off and the program exits anyway.
379  * The cleanup() routine makes a check to ensure it's not reentering, in
380  * case the ipc module looped it somehow.
381  */
382 void nq_cleanup(int e)
383 {
384         remove_lockfile();
385         closelog();
386         exit(e);
387 }
388
389 void cleanup(int e)
390 {
391         static int nested = 0;
392
393         alarm(30);
394         signal(SIGALRM, nq_cleanup);
395         if (nested++ < 1)
396                 serv_puts("QUIT");
397         nq_cleanup(e);
398 }
399
400 /*
401  * This is implemented as a function rather than as a macro because the
402  * client-side IPC modules expect logoff() to be defined.  They call logoff()
403  * when a problem connecting or staying connected to the server occurs.
404  */
405 void logoff(int e)
406 {
407         cleanup(e);
408 }
409
410 /*
411  * If there is a kill file in place, this function will process it.
412  */
413 void load_filterlist(void)
414 {
415         FILE *fp;
416         struct filterlist *fbuf;
417         char sbuf[256];
418         int a, p;
419         fp = fopen("./network/filterlist", "r");
420         if (fp == NULL)
421                 return;
422         while (fgets(sbuf, sizeof sbuf, fp) != NULL) {
423                 if (sbuf[0] != '#') {
424                         sbuf[strlen(sbuf) - 1] = 0;
425                         fbuf = (struct filterlist *)
426                             malloc((long) sizeof(struct filterlist));
427                         fbuf->next = filter;
428                         filter = fbuf;
429                         strcpy(fbuf->f_person, "*");
430                         strcpy(fbuf->f_room, "*");
431                         strcpy(fbuf->f_system, "*");
432                         p = (-1);
433                         for (a = strlen(sbuf); a >= 0; --a)
434                                 if (sbuf[a] == ',')
435                                         p = a;
436                         if (p >= 0) {
437                                 sbuf[p] = 0;
438                                 strcpy(fbuf->f_person, sbuf);
439                                 strcpy(sbuf, &sbuf[p + 1]);
440                         }
441                         for (a = strlen(sbuf); a >= 0; --a)
442                                 if (sbuf[a] == ',')
443                                         p = a;
444                         if (p >= 0) {
445                                 sbuf[p] = 0;
446                                 strcpy(fbuf->f_room, sbuf);
447                                 strcpy(sbuf, &sbuf[p + 1]);
448                         }
449                         strcpy(fbuf->f_system, sbuf);
450                 }
451         }
452         fclose(fp);
453 }
454
455 /* returns 1 if user/message/room combination is in the kill file */
456 int is_banned(char *k_person, char *k_room, char *k_system)
457 {
458         struct filterlist *fptr;
459
460         for (fptr = filter; fptr != NULL; fptr = fptr->next)
461                 if (
462                            ((!strcasecmp(fptr->f_person, k_person)) || (!strcmp(fptr->f_person, "*")))
463                            &&
464                            ((!strcasecmp(fptr->f_room, k_room)) || (!strcmp(fptr->f_room, "*")))
465                            &&
466                            ((!strcasecmp(fptr->f_system, k_system)) || (!strcmp(fptr->f_system, "*")))
467                     )
468                         return (1);
469
470         return (0);
471 }
472
473 /*
474  * Determine routing from sysinfo file
475  */
476 int get_sysinfo_type(char *name) {
477         struct syslist *stemp;
478
479 GETSN:  for (stemp = slist; stemp != NULL; stemp = stemp->next) {
480                 if (!strcasecmp(stemp->s_name, name)) {
481                         if (!strcasecmp(stemp->s_type, "use")) {
482                                 strcpy(name, stemp->s_nexthop);
483                                 goto GETSN;
484                         }
485                         if (!strcasecmp(stemp->s_type, "bin")) {
486                                 return (MES_BINARY);
487                         }
488                         if (!strcasecmp(stemp->s_type, "uum")) {
489                                 return (MES_INTERNET);
490                         }
491                 }
492         }
493         syslog(LOG_ERR, "cannot find system '%s' in mail.sysinfo", name);
494         return (-1);
495 }
496
497
498 void fpgetfield(FILE *fp, char *string, int limit)
499 {
500         int a, b;
501
502         strcpy(string, "");
503         a = 0;
504         do {
505                 b = getc(fp);
506                 if ((b < 1) || (a >= limit)) {
507                         string[a] = 0;
508                         return;
509                 }
510                 string[a] = b;
511                 ++a;
512         } while (b != 0);
513 }
514
515
516
517 /*
518  * Load all of the fields of a message, except the actual text, into a
519  * table in memory (so we know how to process the message).
520  */
521 void fpmsgfind(FILE *fp, struct minfo *buffer)
522 {
523         int b, e, mtype, aflag;
524         char bbb[1024];
525         char userid[1024];
526
527         strcpy(userid, "");
528         e = getc(fp);
529         if (e != 255) {
530                 syslog(LOG_ERR, "Magic number check failed for this message");
531                 goto END;
532         }
533
534         memset(buffer, 0, sizeof(struct minfo));
535         mtype = getc(fp);
536         aflag = getc(fp);
537
538 BONFGM: b = getc(fp);
539         if (b < 0)
540                 goto END;
541         if (b == 'M')
542                 goto END;
543         fpgetfield(fp, bbb, sizeof bbb);
544         while ((bbb[0] == ' ') && (strlen(bbb) > 1))
545                 strcpy(bbb, &bbb[1]);
546         if (b == 'A') {
547                 strcpy(buffer->A, bbb);
548                 if (strlen(userid) == 0) {
549                         strcpy(userid, bbb);
550                         for (e = 0; e < strlen(userid); ++e)
551                                 if (userid[e] == ' ')
552                                         userid[e] = '_';
553                 }
554         }
555         if (b == 'O')
556                 strcpy(buffer->O, bbb);
557         if (b == 'C')
558                 strcpy(buffer->C, bbb);
559         if (b == 'N')
560                 strcpy(buffer->N, bbb);
561         if (b == 'S')
562                 strcpy(buffer->S, bbb);
563         if (b == 'P') {
564                 /* extract the user id from the path */
565                 for (e = 0; e < strlen(bbb); ++e)
566                         if (bbb[e] == '!')
567                                 strcpy(userid, &bbb[e + 1]);
568
569                 /* now find the next hop */
570                 for (e = 0; e < strlen(bbb); ++e)
571                         if (bbb[e] == '!')
572                                 bbb[e] = 0;
573                 strcpy(buffer->nexthop, bbb);
574         }
575         if (b == 'R') {
576                 for (e = 0; e < strlen(bbb); ++e)
577                         if (bbb[e] == '_')
578                                 bbb[e] = ' ';
579                 strcpy(buffer->R, bbb);
580         }
581         if (b == 'D')
582                 strcpy(buffer->D, bbb);
583         if (b == 'T')
584                 buffer->T = atol(bbb);
585         if (b == 'I')
586                 strcpy(buffer->I, bbb);
587         if (b == 'H')
588                 strcpy(buffer->H, bbb);
589         if (b == 'B')
590                 strcpy(buffer->B, bbb);
591         if (b == 'G')
592                 strcpy(buffer->G, bbb);
593         if (b == 'E')
594                 strcpy(buffer->E, bbb);
595         if (b == 'Z')
596                 strcpy(buffer->Z, bbb);
597         goto BONFGM;
598
599 END:;
600
601 }
602
603
604 /*
605  * msgfind() is the same as fpmsgfind() except it accepts a filename
606  * instead of a file handle.
607  */
608 void msgfind(char *msgfile, struct minfo *buffer) {
609         FILE *fp;
610
611         fp = fopen(msgfile, "rb");
612         if (fp == NULL) {
613                 syslog(LOG_ERR, "can't open %s: %s", msgfile, strerror(errno));
614                 return;
615         }
616
617         fpmsgfind(fp, buffer);
618         fclose(fp);
619 }
620
621
622
623
624
625 void ship_to(char *filenm, char *sysnm)
626 {                               /* send spool file filenm to system sysnm */
627         char sysflnm[100];
628         char commbuf1[100];
629         char commbuf2[100];
630         FILE *sysflfd;
631
632 #ifdef DEBUG
633         syslog(LOG_NOTICE, "shipping %s to %s", filenm, sysnm);
634 #endif
635         sprintf(sysflnm, "./network/systems/%s", sysnm);
636         sysflfd = fopen(sysflnm, "r");
637         if (sysflfd == NULL)
638                 syslog(LOG_ERR, "cannot open %s", sysflnm);
639         fgets(commbuf1, 99, sysflfd);
640         commbuf1[strlen(commbuf1) - 1] = 0;
641         fclose(sysflfd);
642         sprintf(commbuf2, commbuf1, filenm);
643         system(commbuf2);
644 }
645
646 /*
647  * proc_file_transfer()  -  handle a simple file transfer packet
648  *
649  */
650 void proc_file_transfer(char *tname)
651 {                               /* name of temp file containing the whole message */
652         char buf[256];
653         char dest_room[ROOMNAMELEN];
654         char subdir_name[256];
655         FILE *tfp, *uud;
656         int a;
657
658         syslog(LOG_NOTICE, "processing network file transfer...");
659
660         tfp = fopen(tname, "rb");
661         if (tfp == NULL)
662                 syslog(LOG_ERR, "cannot open %s", tname);
663         getc(tfp);
664         getc(tfp);
665         getc(tfp);
666         do {
667                 a = getc(tfp);
668                 if (a != 'M') {
669                         fpgetfield(tfp, buf, sizeof buf);
670                         if (a == 'O') {
671                                 strcpy(dest_room, buf);
672                         }
673                 }
674         } while ((a != 'M') && (a >= 0));
675         if (a != 'M') {
676                 fclose(tfp);
677                 syslog(LOG_ERR, "no message text for file transfer");
678                 return;
679         }
680         strcpy(subdir_name, "---xxx---");
681         sprintf(buf, "GOTO %s", dest_room);
682         serv_puts(buf);
683         serv_gets(buf);
684         if (buf[0] == '2') {
685                 extract(subdir_name, &buf[4], 2);
686                 if (strlen(subdir_name) == 0)
687                         strcpy(subdir_name, "--xxx--");
688         }
689         /* Change to the room's directory; if that fails, change to the
690          * bitbucket directory.  Then run uudecode.
691          */
692         sprintf(buf, "(cd %s/files/%s || cd %s/files/%s ) ; exec %s",
693                 bbs_home_directory, subdir_name,
694                 bbs_home_directory, config.c_bucket_dir,
695                 UUDECODE);
696
697         uud = (FILE *) popen(buf, "w");
698         if (uud == NULL) {
699                 syslog(LOG_ERR, "cannot open uudecode pipe");
700                 fclose(tfp);
701                 return;
702         }
703         fgets(buf, 128, tfp);
704         buf[strlen(buf) - 1] = 0;
705         for (a = 0; a < strlen(buf); ++a)
706                 if (buf[a] == '/')
707                         buf[a] = '_';
708         fprintf(uud, "%s\n", buf);
709         printf("netproc: %s\n", buf);
710         while (a = getc(tfp), a > 0)
711                 putc(a, uud);
712         fclose(tfp);
713         pclose(uud);
714         return;
715 }
716
717
718 /* send a bounce message */
719 void bounce(struct minfo *bminfo)
720 {
721
722         FILE *bounce;
723         char bfilename[64];
724         static int bseq = 1;
725         time_t now;
726
727         sprintf(bfilename, "./network/spoolin/bounce.%ld.%d", (long) getpid(),
728                 bseq++);
729         bounce = fopen(bfilename, "wb");
730         time(&now);
731
732         fprintf(bounce, "%c%c%c", 0xFF, MES_NORMAL, 0);
733         fprintf(bounce, "Ppostmaster%c", 0);
734         fprintf(bounce, "T%ld%c", (long) now, 0);
735         fprintf(bounce, "APostmaster%c", 0);
736         fprintf(bounce, "OMail%c", 0);
737         fprintf(bounce, "N%s%c", config.c_nodename, 0);
738         fprintf(bounce, "H%s%c", config.c_humannode, 0);
739
740         if (strlen(bminfo->E) > 0) {
741                 fprintf(bounce, "R%s%c", bminfo->E, 0);
742         } else {
743                 fprintf(bounce, "R%s%c", bminfo->A, 0);
744         }
745
746         fprintf(bounce, "D%s%c", bminfo->N, 0);
747         fprintf(bounce, "M%s could not deliver your mail to:\n",
748                 config.c_humannode);
749         fprintf(bounce, " \n %s\n \n", bminfo->R);
750         fprintf(bounce, " because there is no such user on this system.\n");
751         fprintf(bounce, " (Unsent message does *not* follow.  ");
752         fprintf(bounce, "Help to conserve bandwidth.)\n%c", 0);
753         fclose(bounce);
754 }
755
756
757
758
759 /*
760  * Generate a Message-ID string for the use table
761  */
762 void strmsgid(char *buf, struct minfo *msginfo) {
763         int i;
764
765         strcpy(buf, msginfo->I);
766         if (strchr(buf, '@') == NULL) {
767                 strcat(buf, "@");
768                 strcat(buf, msginfo->N);
769         }
770
771         for (i=0; i<strlen(buf); ++i) {
772                 if (isspace(buf[i])) {
773                         strcpy(&buf[i], &buf[i+1]);
774                 }
775                 buf[i] = tolower(buf[i]);
776         }
777 }
778
779
780
781 /*
782  * Check the use table to see if a message has been here before.
783  * Returns 1 if the message is a duplicate; otherwise, it returns
784  * 0 and the message ID is added to the use table.
785  */
786 int already_received(struct minfo *msginfo) {
787         char buf[256];
788         struct usetable *u;
789         time_t now;
790
791         /* We can't check for dups on a zero msgid, so just pass them through */
792         if (strlen(msginfo->I)==0) {
793                 return 0;
794         }
795
796         strmsgid(buf, msginfo);
797         now = time(NULL);
798
799         /* Set return value to 1 if message exists */
800         for (u=usetable; u!=NULL; u=u->next) {
801                 if (!strcasecmp(buf, u->msgid)) {
802                         u->timestamp = time(NULL);      /* keep it fresh */
803                         return(1);
804                 }
805         }
806
807         /* Not found, so we're ok, but add it to the use table now */
808         u = (struct usetable *) malloc(sizeof (struct usetable));
809         u->next = usetable;
810         u->timestamp = time(NULL);
811         strncpy(u->msgid, buf, 255);
812         usetable = u;
813
814         return(0);
815 }
816
817
818 /*
819  * Load the use table from disk
820  */
821 void read_use_table(void) {
822         struct usetable *u;
823         struct usetable ubuf;
824         FILE *fp;
825
826         unlink("data/usetable.gdbm");   /* we don't use this anymore */
827
828         fp = fopen("usetable", "rb");
829         if (fp == NULL) return;
830
831         while (fread(&ubuf, sizeof (struct usetable), 1, fp) > 0) {
832                 u = (struct usetable *) malloc(sizeof (struct usetable));
833                 memcpy(u, &ubuf, sizeof (struct usetable));
834                 u->next = usetable;
835                 usetable = u;
836         }
837
838         fclose(fp);
839 }
840
841
842
843 /*
844  * Purge any old entries out of the use table as we write them back to disk.
845  * 
846  */
847 void write_use_table(void) {
848         struct usetable *u;
849         time_t now;
850         FILE *fp;
851
852         now = time(NULL);
853         fp = fopen("usetable", "wb");
854         if (fp == NULL) return;
855         for (u=usetable; u!=NULL; u=u->next) {
856                 if ((now - u->timestamp) <= USE_TIME) {
857                         fwrite(u, sizeof(struct usetable), 1, fp);
858                 }
859         }
860         fclose(fp);
861 }
862
863
864
865
866
867 /*
868  * process incoming files in ./network/spoolin
869  */
870 void inprocess(void)
871 {
872         FILE *fp, *message, *testfp, *ls, *duplist;
873         static struct minfo minfo;
874         char tname[128], aaa[1024], iname[256], sfilename[256], pfilename[256];
875         int a, b;
876         int FieldID;
877         struct syslist *stemp;
878         char *ptr = NULL;
879         char buf[256];
880         long msglen;
881         int bloklen;
882         int valid_msg;
883
884         /* temp file names */
885         sprintf(tname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
886         sprintf(iname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
887
888         load_filterlist();
889
890         /* Make sure we're in the right directory */
891         chdir(bbs_home_directory);
892
893         /* temporary file to contain a log of rejected dups */
894         duplist = tmpfile();
895
896         /* Let the shell do the dirty work. Get all data from spoolin */
897         do {
898                 sprintf(aaa, "cd %s/network/spoolin; ls", bbs_home_directory);
899                 ls = popen(aaa, "r");
900                 if (ls == NULL) {
901                         syslog(LOG_ERR, "could not open dir cmd: %s", strerror(errno));
902                 }
903                 if (ls != NULL) {
904                         do {
905 SKIP:                           ptr = fgets(sfilename, sizeof sfilename, ls);
906                                 if (ptr != NULL) {
907                                         sfilename[strlen(sfilename) - 1] = 0;
908 #ifdef DEBUG
909                                         syslog(LOG_DEBUG,
910                                                 "Trying <%s>", sfilename);
911 #endif
912                                         if (!strcmp(sfilename, ".")) goto SKIP;
913                                         if (!strcmp(sfilename, "..")) goto SKIP;
914                                         if (!strcmp(sfilename, "CVS")) goto SKIP;
915                                         goto PROCESS_IT;
916                                 }
917                         } while (ptr != NULL);
918 PROCESS_IT:             pclose(ls);
919                 }
920                 if (ptr != NULL) {
921                         sprintf(pfilename, "%s/network/spoolin/%s", bbs_home_directory, sfilename);
922                         syslog(LOG_NOTICE, "processing <%s>", pfilename);
923
924                         fp = fopen(pfilename, "rb");
925                         if (fp == NULL) {
926                                 syslog(LOG_ERR, "cannot open %s: %s", pfilename, strerror(errno));
927                                 fp = fopen("/dev/null", "rb");
928                         }
929 NXMSG:  /* Seek to the beginning of the next message */
930                         do {
931                                 a = getc(fp);
932                         } while ((a != 255) && (a >= 0));
933                         if (a < 0)
934                                 goto ENDSTR;
935
936                         /* This crates the temporary file. */
937                         valid_msg = 1;
938                         message = fopen(tname, "wb");
939                         if (message == NULL) {
940                                 syslog(LOG_ERR, "error creating %s: %s",
941                                         tname, strerror(errno));
942                                 goto ENDSTR;
943                         }
944                         putc(255, message);     /* 0xFF (start-of-message) */
945                         a = getc(fp);
946                         putc(a, message);       /* type */
947                         a = getc(fp);
948                         putc(a, message);       /* mode */
949                         do {
950                                 FieldID = getc(fp); /* Header field ID */
951                                 if (isalpha(FieldID)) {
952                                         putc(FieldID, message);
953                                         do {
954                                                 a = getc(fp);
955                                                 if (a < 127) putc(a, message);
956                                         } while (a > 0);
957                                         if (a != 0) putc(0, message);
958                                 }
959                                 else {  /* Invalid field ID; flush it */
960                                         do {
961                                                 a = getc(fp);
962                                         } while (a > 0);
963                                         valid_msg = 0;
964                                 }
965                         } while ((FieldID != 'M') && (a >= 0));
966                         /* M is always last */
967                         if (FieldID != 'M') valid_msg = 0;
968
969                         msglen = ftell(message);
970                         fclose(message);
971
972                         if (!valid_msg) {
973                                 unlink(tname);
974                                 goto NXMSG;
975                         }
976
977                         /* process the individual mesage */
978                         msgfind(tname, &minfo);
979                         syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
980                                minfo.I, minfo.A, minfo.O, minfo.N);
981                         if (strlen(minfo.R) > 0) {
982                                 syslog(LOG_NOTICE, "     to <%s>", minfo.R);
983                                 if (strlen(minfo.D) > 0) {
984                                         syslog(LOG_NOTICE, "     @ <%s>",
985                                                 minfo.D);
986                                 }
987                         }
988                         if (!strcasecmp(minfo.D, FQDN))
989                                 strcpy(minfo.D, NODENAME);
990
991 /* this routine updates our info on the system that sent the message */
992                         stemp = get_sys_ptr(minfo.N);
993                         if ((stemp == NULL) && (get_sys_ptr(minfo.nexthop) != NULL)) {
994                                 /* add non-neighbor system to map */
995                                 syslog(LOG_NOTICE, "Adding non-neighbor system <%s> to map",
996                                        slist->s_name);
997                                 stemp = (struct syslist *) malloc((long) sizeof(struct syslist));
998                                 stemp->next = slist;
999                                 slist = stemp;
1000                                 strcpy(slist->s_name, minfo.N);
1001                                 strcpy(slist->s_type, "use");
1002                                 strcpy(slist->s_nexthop, minfo.nexthop);
1003                                 time(&slist->s_lastcontact);
1004                         } else if ((stemp == NULL) && (!strcasecmp(minfo.N, minfo.nexthop))) {
1005                                 /* add neighbor system to map */
1006                                 syslog(LOG_NOTICE, "Adding neighbor system <%s> to map",
1007                                        slist->s_name);
1008                                 sprintf(aaa, "%s/network/systems/%s", bbs_home_directory, minfo.N);
1009                                 testfp = fopen(aaa, "r");
1010                                 if (testfp != NULL) {
1011                                         fclose(testfp);
1012                                         stemp = (struct syslist *)
1013                                             malloc((long) sizeof(struct syslist));
1014                                         stemp->next = slist;
1015                                         slist = stemp;
1016                                         strcpy(slist->s_name, minfo.N);
1017                                         strcpy(slist->s_type, "bin");
1018                                         strcpy(slist->s_nexthop, "Mail");
1019                                         time(&slist->s_lastcontact);
1020                                 }
1021                         }
1022                         /* now update last contact and long node name if we can */
1023                         if (stemp != NULL) {
1024                                 time(&stemp->s_lastcontact);
1025                                 if (strlen(minfo.H) > 0)
1026                                         strcpy(stemp->s_humannode, minfo.H);
1027                                 if (strlen(minfo.B) > 0)
1028                                         strcpy(stemp->s_phonenum, minfo.B);
1029                                 if (strlen(minfo.G) > 0)
1030                                         strcpy(stemp->s_gdom, minfo.G);
1031                         }
1032
1033                         /* Check the use table; reject message if it's been here before */
1034                         if (already_received(&minfo)) {
1035                                 syslog(LOG_NOTICE, "rejected duplicate message");
1036                                 fprintf(duplist, "#<%s> fm <%s> in <%s> @ <%s>\n",
1037                                         minfo.I, minfo.A, minfo.O, minfo.N);
1038                         }
1039
1040
1041                         /* route the message if necessary */
1042                         else if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
1043                                 a = get_sysinfo_type(minfo.D);
1044                                 syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
1045                                 fflush(stdout);
1046                                 if (a == MES_INTERNET) {
1047                                         if (fork() == 0) {
1048                                                 syslog(LOG_NOTICE, "netmailer %s", tname);
1049                                                 fflush(stdout);
1050                                                 execlp("./netmailer", "netmailer",
1051                                                        tname, NULL);
1052                                                 syslog(LOG_ERR, "error running netmailer: %s",
1053                                                        strerror(errno));
1054                                                 exit(errno);
1055                                         } else
1056                                                 while (wait(&b) != (-1));
1057                                 } else if (a == MES_BINARY) {
1058                                         ship_to(tname, minfo.D);
1059                                 } else {
1060                                         /* message falls into the bit bucket? */
1061                                 }
1062                         }
1063
1064                         /* check to see if it's a file transfer */
1065                         else if (!strncasecmp(minfo.S, "FILE", 4)) {
1066                                 proc_file_transfer(tname);
1067                         }
1068
1069                         /* otherwise process it as a normal message */
1070                         else {
1071                                 if (!strcasecmp(minfo.R, "postmaster")) {
1072                                         strcpy(minfo.R, "");
1073                                         strcpy(minfo.C, "Aide");
1074                                 }
1075                                 if (strlen(minfo.R) > 0) {
1076                                         sprintf(buf, "GOTO _MAIL_");
1077                                 }
1078                                 if (is_banned(minfo.A, minfo.C, minfo.N)) {
1079                                         sprintf(buf, "GOTO %s", FILTERROOM);
1080                                 } else {
1081                                         if (strlen(minfo.C) > 0) {
1082                                                 sprintf(buf, "GOTO %s", minfo.C);
1083                                         } else {
1084                                                 sprintf(buf, "GOTO %s", minfo.O);
1085                                         }
1086                                 }
1087                                 serv_puts(buf);
1088                                 serv_gets(buf);
1089                                 if (buf[0] != '2') {
1090                                         syslog(LOG_ERR, "%s", buf);
1091                                         sprintf(buf, "GOTO _BITBUCKET_");
1092                                         serv_puts(buf);
1093                                         serv_gets(buf);
1094                                 }
1095                                 /* Open the temporary file containing the message */
1096                                 message = fopen(tname, "rb");
1097                                 if (message == NULL) {
1098                                         syslog(LOG_ERR, "cannot open %s: %s",
1099                                                tname, strerror(errno));
1100                                         unlink(tname);
1101                                         goto NXMSG;
1102                                 }
1103                                 /* Transmit the message to the server */
1104                                 sprintf(buf, "ENT3 1|%s|%ld", minfo.R, msglen);
1105                                 serv_puts(buf);
1106                                 serv_gets(buf);
1107                                 if (!strncmp(buf, "570", 3)) {
1108                                         /* no such user, do a bounce */
1109                                         bounce(&minfo);
1110                                 }
1111                                 if (buf[0] == '7') {
1112                                         /* Always use the server's idea of the message length,
1113                                          * even though they should both be identical */
1114                                         msglen = atol(&buf[4]);
1115                                         while (msglen > 0L) {
1116                                                 bloklen = ((msglen >= 255L) ? 255 : ((int) msglen));
1117                                                 if (fread(buf, bloklen, 1, message) < 1) {
1118                                                         syslog(LOG_ERR,
1119                                                                "error trying to read %d bytes: %s",
1120                                                                bloklen, strerror(errno));
1121                                                 }
1122                                                 serv_write(buf, bloklen);
1123                                                 msglen = msglen - (long) bloklen;
1124                                         }
1125                                         serv_puts("NOOP");
1126                                         serv_gets(buf);
1127                                 } else {
1128                                         syslog(LOG_ERR, "%s", buf);
1129                                 }
1130
1131                                 fclose(message);
1132                                 
1133                         }
1134
1135                         unlink(tname);
1136                         goto NXMSG;
1137
1138 ENDSTR:                 fclose(fp);
1139                         unlink(pfilename);
1140                 }
1141         } while (ptr != NULL);
1142         unlink(iname);
1143
1144
1145         /*
1146          * If dups were rejected, post a message saying so
1147          */
1148         if (ftell(duplist)!=0L) {
1149                 fp = fopen("./network/spoolin/ctdl_rejects", "ab");
1150                 if (fp != NULL) {
1151                         fprintf(fp, "%cA%c", 255, 1);
1152                         fprintf(fp, "T%ld%c", time(NULL), 0);
1153                         fprintf(fp, "ACitadel%c", 0);
1154                         fprintf(fp, "OAide%cM", 0);
1155                         fprintf(fp, "The following duplicate messages"
1156                                 " were rejected:\n \n");
1157                         rewind(duplist);
1158                         while (fgets(buf, sizeof(buf), duplist) != NULL) {
1159                                 buf[strlen(buf)-1] = 0;
1160                                 fprintf(fp, " %s\n", buf);
1161                         }
1162                         fprintf(fp, "%c", 0);
1163                         pclose(fp);
1164                 }
1165         }
1166
1167         fclose(duplist);
1168
1169 }
1170
1171
1172 /* Checks to see whether its ok to send */
1173 /* Returns 1 for ok, send message       */
1174 /* Returns 0 if message already there   */
1175 int checkpath(char *path, char *sys)
1176 {
1177         int a;
1178         char sys2[512];
1179         strcpy(sys2, sys);
1180         strcat(sys2, "!");
1181
1182 #ifdef DEBUG
1183         syslog(LOG_NOTICE, "checkpath <%s> <%s> ... ", path, sys);
1184 #endif
1185         for (a = 0; a < strlen(path); ++a) {
1186                 if (!strncmp(&path[a], sys2, strlen(sys2)))
1187                         return (0);
1188         }
1189         return (1);
1190 }
1191
1192 /*
1193  * Implement split horizon algorithm (prevent infinite spooling loops
1194  * by refusing to send any node a message which already contains its
1195  * nodename in the path).
1196  */
1197 int ismsgok(FILE *mmfp, char *sysname)
1198 {
1199         int a;
1200         int ok = 0;             /* fail safe - no path, don't send it */
1201         char fbuf[256];
1202
1203         fseek(mmfp, 0L, 0);
1204         if (getc(mmfp) != 255)
1205                 return (0);
1206         getc(mmfp);
1207         getc(mmfp);
1208
1209         while (a = getc(mmfp), ((a != 'M') && (a != 0))) {
1210                 fpgetfield(mmfp, fbuf, sizeof fbuf);
1211                 if (a == 'P') {
1212                         ok = checkpath(fbuf, sysname);
1213                 }
1214         }
1215 #ifdef DEBUG
1216         syslog(LOG_NOTICE, "%s", ((ok) ? "SEND" : "(no)"));
1217 #endif
1218         return (ok);
1219 }
1220
1221
1222
1223
1224 /*
1225  * Add a message to the list of messages to be deleted off the local server
1226  * at the end of this run.
1227  */
1228 void delete_locally(long msgid, char *roomname) {
1229         struct msglist *mptr;
1230
1231         mptr = (struct msglist *) malloc(sizeof(struct msglist));
1232         mptr->next = purgelist;
1233         mptr->m_num = msgid;
1234         strcpy(mptr->m_rmname, roomname);
1235         purgelist = mptr;
1236 }
1237
1238
1239
1240 /*
1241  * Delete all messages on the purge list from the local server.
1242  */
1243 void process_purgelist(void) {
1244         char curr_rm[ROOMNAMELEN];
1245         char buf[256];
1246         struct msglist *mptr;
1247
1248
1249         strcpy(curr_rm, "__nothing__");
1250         while (purgelist != NULL) {
1251                 if (strcasecmp(curr_rm, purgelist->m_rmname)) {
1252                         sprintf(buf, "GOTO %s", purgelist->m_rmname);
1253                         serv_puts(buf);
1254                         serv_gets(buf);
1255                         if (buf[0] == '2') {
1256                                 extract(curr_rm, &buf[4], 0);
1257                         }
1258                         else {
1259                                 syslog(LOG_ERR, "%s", buf);
1260                         }
1261                 }
1262                 if (!strcasecmp(curr_rm, purgelist->m_rmname)) {
1263                         syslog(LOG_NOTICE, "Purging <%ld> in <%s>",
1264                                 purgelist->m_num, purgelist->m_rmname);
1265                         sprintf(buf, "DELE %ld", purgelist->m_num);
1266                         serv_puts(buf);
1267                         serv_gets(buf);
1268                         if (buf[0] != '2') {
1269                                 syslog(LOG_ERR, "%s", buf);
1270                         }
1271
1272                 }
1273                 mptr = purgelist->next;
1274                 free(purgelist);
1275                 purgelist = mptr;
1276         }
1277 }
1278
1279
1280
1281
1282 /* spool list of messages to a file */
1283 /* returns # of msgs spooled */
1284 int spool_out(struct msglist *cmlist, FILE * destfp, char *sysname)
1285 {
1286         struct msglist *cmptr;
1287         FILE *mmfp;
1288         char fbuf[1024];
1289         int a;
1290         int msgs_spooled = 0;
1291         long msg_len;
1292         int blok_len;
1293         static struct minfo minfo;
1294
1295         char buf[256];
1296         char curr_rm[256];
1297
1298         strcpy(curr_rm, "");
1299
1300         /* for each message in the list... */
1301         for (cmptr = cmlist; cmptr != NULL; cmptr = cmptr->next) {
1302
1303                 /* make sure we're in the correct room... */
1304                 if (strcasecmp(curr_rm, cmptr->m_rmname)) {
1305                         sprintf(buf, "GOTO %s", cmptr->m_rmname);
1306                         serv_puts(buf);
1307                         serv_gets(buf);
1308                         if (buf[0] == '2') {
1309                                 strcpy(curr_rm, cmptr->m_rmname);
1310                         } else {
1311                                 syslog(LOG_ERR, "%s", buf);
1312                         }
1313                 }
1314                 /* download the message from the server... */
1315                 mmfp = tmpfile();
1316                 if (mmfp == NULL) {
1317                         syslog(LOG_NOTICE, "tmpfile() failed: %s\n",
1318                                 strerror(errno) );
1319                 }
1320                 sprintf(buf, "MSG3 %ld", cmptr->m_num);
1321                 serv_puts(buf);
1322                 serv_gets(buf);
1323                 if (buf[0] == '6') {    /* read the msg */
1324                         msg_len = atol(&buf[4]);
1325                         while (msg_len > 0L) {
1326                                 blok_len = ((msg_len >= 256L) ? 256 : (int) msg_len);
1327                                 serv_read(buf, blok_len);
1328                                 fwrite(buf, blok_len, 1, mmfp);
1329                                 msg_len = msg_len - (long) blok_len;
1330                         }
1331                 } else {        /* or print the err */
1332                         syslog(LOG_ERR, "%s", buf);
1333                 }
1334
1335                 rewind(mmfp);
1336
1337                 if (ismsgok(mmfp, sysname)) {
1338                         ++msgs_spooled;
1339                         fflush(stdout);
1340                         fseek(mmfp, 0L, 0);
1341                         fread(fbuf, 3, 1, mmfp);
1342                         fwrite(fbuf, 3, 1, destfp);
1343                         while (a = getc(mmfp), ((a != 0) && (a != 'M'))) {
1344                                 if (a != 'C') {
1345                                         putc(a, destfp);
1346                                 }
1347                                 fpgetfield(mmfp, fbuf, sizeof fbuf);
1348                                 if (a == 'P') {
1349                                         fprintf(destfp, "%s!", NODENAME);
1350                                 }
1351                                 if (a != 'C') {
1352                                         fwrite(fbuf, strlen(fbuf) + 1, 1, destfp);
1353                                 }
1354                                 if (a == 'S') if (!strcasecmp(fbuf, "CANCEL")) {
1355                                         delete_locally(cmptr->m_num, cmptr->m_rmname);
1356                                 }
1357                         }
1358                         if (a == 'M') {
1359                                 fprintf(destfp, "C%s%c",
1360                                         cmptr->m_rmname, 0);
1361                                 putc('M', destfp);
1362                                 do {
1363                                         a = getc(mmfp);
1364                                         putc(a, destfp);
1365                                 } while (a > 0);
1366                         }
1367
1368                 /* Get this message into the use table, so we can reject it
1369                  * if a misconfigured remote system sends it back to us.
1370                  */
1371                 fseek(mmfp, 0L, 0);
1372                 fpmsgfind(mmfp, &minfo);
1373                 already_received(&minfo);
1374
1375                 }
1376                 fclose(mmfp);
1377         }
1378
1379         return (msgs_spooled);
1380 }
1381
1382 void outprocess(char *sysname)
1383 {                               /* send new room messages to sysname */
1384         char sysflnm[64];
1385         char srmname[32];
1386         char shiptocmd[128];
1387         char lbuf[64];
1388         char tempflnm[64];
1389         char buf[256];
1390         struct msglist *cmlist = NULL;
1391         struct msglist *cmlast = NULL;
1392         struct rmlist *crmlist = NULL;
1393         struct rmlist *rmptr, *rmptr2;
1394         struct msglist *cmptr;
1395         FILE *sysflfp, *tempflfp;
1396         int outgoing_msgs = 0;
1397         long thismsg;
1398
1399         sprintf(tempflnm, "%s.netproc.%d", tmpnam(NULL), __LINE__);
1400         tempflfp = fopen(tempflnm, "w");
1401         if (tempflfp == NULL)
1402                 return;
1403
1404
1405 /*
1406  * Read system file for node in question and put together room list
1407  */
1408         sprintf(sysflnm, "%s/network/systems/%s", bbs_home_directory, sysname);
1409         sysflfp = fopen(sysflnm, "r");
1410         if (sysflfp == NULL)
1411                 return;
1412         fgets(shiptocmd, 128, sysflfp);
1413         shiptocmd[strlen(shiptocmd) - 1] = 0;
1414         while (!feof(sysflfp)) {
1415                 if (fgets(srmname, 32, sysflfp) == NULL)
1416                         break;
1417                 srmname[strlen(srmname) - 1] = 0;
1418                 fgets(lbuf, 32, sysflfp);
1419                 rmptr = (struct rmlist *) malloc(sizeof(struct rmlist));
1420                 rmptr->next = NULL;
1421                 strcpy(rmptr->rm_name, srmname);
1422                 strip_trailing_whitespace(rmptr->rm_name);
1423                 rmptr->rm_lastsent = atol(lbuf);
1424                 if (crmlist == NULL)
1425                         crmlist = rmptr;
1426                 else if (!strcasecmp(rmptr->rm_name, "control")) {
1427                         /* control has to be first in room list */
1428                         rmptr->next = crmlist;
1429                         crmlist = rmptr;
1430                 } else {
1431                         rmptr2 = crmlist;
1432                         while (rmptr2->next != NULL)
1433                                 rmptr2 = rmptr2->next;
1434                         rmptr2->next = rmptr;
1435                 }
1436         }
1437         fclose(sysflfp);
1438
1439 /*
1440  * Assemble list of messages to be spooled
1441  */
1442         for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next) {
1443
1444                 sprintf(buf, "GOTO %s", rmptr->rm_name);
1445                 serv_puts(buf);
1446                 serv_gets(buf);
1447                 if (buf[0] != '2') {
1448                         syslog(LOG_ERR, "%s", buf);
1449                 } else {
1450                         sprintf(buf, "MSGS GT|%ld", rmptr->rm_lastsent);
1451                         serv_puts(buf);
1452                         serv_gets(buf);
1453                         if (buf[0] == '1')
1454                                 while (serv_gets(buf), strcmp(buf, "000")) {
1455                                         thismsg = atol(buf);
1456                                         if (thismsg > (rmptr->rm_lastsent)) {
1457                                                 rmptr->rm_lastsent = thismsg;
1458
1459                                                 cmptr = (struct msglist *)
1460                                                     malloc(sizeof(struct msglist));
1461                                                 cmptr->next = NULL;
1462                                                 cmptr->m_num = thismsg;
1463                                                 strcpy(cmptr->m_rmname, rmptr->rm_name);
1464
1465                                                 if (cmlist == NULL) {
1466                                                         cmlist = cmptr;
1467                                                 }
1468                                                 else {
1469                                                         cmlast->next = cmptr;
1470                                                 }
1471                                                 cmlast = cmptr;
1472                                                 ++outgoing_msgs;
1473                                         }
1474                         } else {        /* print error from "msgs all" */
1475                                 syslog(LOG_ERR, "%s", buf);
1476                         }
1477                 }
1478         }
1479
1480         syslog(LOG_NOTICE, "%d messages to be spooled to %s",
1481                outgoing_msgs, sysname);
1482
1483 /*
1484  * Spool out the messages, but only if there are any.
1485  */
1486         if (outgoing_msgs != 0) {
1487                 outgoing_msgs = spool_out(cmlist, tempflfp, sysname);
1488         }
1489
1490         syslog(LOG_NOTICE, "%d messages actually spooled", outgoing_msgs);
1491
1492 /*
1493  * Deallocate list of spooled messages.
1494  */
1495         while (cmlist != NULL) {
1496                 cmptr = cmlist->next;
1497                 free(cmlist);
1498                 cmlist = cmptr;
1499         }
1500
1501 /*
1502  * Rewrite system file and deallocate room list.
1503  */
1504         syslog(LOG_NOTICE, "Spooling...");
1505         sysflfp = fopen(sysflnm, "w");
1506         fprintf(sysflfp, "%s\n", shiptocmd);
1507         for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next)
1508                 fprintf(sysflfp, "%s\n%ld\n", rmptr->rm_name, rmptr->rm_lastsent);
1509         fclose(sysflfp);
1510         while (crmlist != NULL) {
1511                 rmptr = crmlist->next;
1512                 free(crmlist);
1513                 crmlist = rmptr;
1514         }
1515
1516 /* 
1517  * Close temporary file, ship it out, and return
1518  */
1519         fclose(tempflfp);
1520         if (outgoing_msgs != 0)
1521                 ship_to(tempflnm, sysname);
1522         unlink(tempflnm);
1523 }
1524
1525
1526 /*
1527  * Connect netproc to the Citadel server running on this computer.
1528  */
1529 void np_attach_to_server(void)
1530 {
1531         char buf[256];
1532         char *args[] =
1533         { "netproc", NULL };
1534
1535         syslog(LOG_NOTICE, "Attaching to server...");
1536         attach_to_server(1, args, NULL, NULL);
1537         serv_gets(buf);
1538         syslog(LOG_NOTICE, "%s", &buf[4]);
1539         sprintf(buf, "IPGM %d", config.c_ipgm_secret);
1540         serv_puts(buf);
1541         serv_gets(buf);
1542         syslog(LOG_NOTICE, "%s", &buf[4]);
1543         if (buf[0] != '2') {
1544                 cleanup(2);
1545         }
1546 }
1547
1548
1549
1550 /*
1551  * main
1552  */
1553 int main(int argc, char **argv)
1554 {
1555         char allst[32];
1556         FILE *allfp;
1557         int a;
1558         int import_only = 0;    /* if set to 1, don't export anything */
1559
1560         openlog("netproc", LOG_PID, LOG_USER);
1561         strcpy(bbs_home_directory, BBSDIR);
1562
1563         /*
1564          * Change directories if specified
1565          */
1566         for (a = 1; a < argc; ++a) {
1567                 if (!strncmp(argv[a], "-h", 2)) {
1568                         strcpy(bbs_home_directory, argv[a]);
1569                         strcpy(bbs_home_directory, &bbs_home_directory[2]);
1570                         home_specified = 1;
1571                 } else if (!strcmp(argv[a], "-i")) {
1572                         import_only = 1;
1573                 } else {
1574                         fprintf(stderr, "netproc: usage: ");
1575                         fprintf(stderr, "netproc [-hHomeDir] [-i]\n");
1576                         exit(1);
1577                 }
1578         }
1579
1580 #ifdef DEBUG
1581         syslog(LOG_DEBUG, "Calling get_config()");
1582 #endif
1583         get_config();
1584
1585 #ifdef DEBUG
1586         syslog(LOG_DEBUG, "Creating lock file");
1587 #endif
1588         if (set_lockfile() != 0) {
1589                 syslog(LOG_NOTICE, "lock file exists: already running");
1590                 cleanup(1);
1591         }
1592         signal(SIGINT, cleanup);
1593         signal(SIGQUIT, cleanup);
1594         signal(SIGHUP, cleanup);
1595         signal(SIGTERM, cleanup);
1596
1597         syslog(LOG_NOTICE, "started.  pid=%d", getpid());
1598         fflush(stdout);
1599         np_attach_to_server();
1600         fflush(stdout);
1601
1602         if (load_syslist() != 0)
1603                 syslog(LOG_ERR, "cannot load sysinfo");
1604         setup_special_nodes();
1605
1606         /* Open the use table */
1607         read_use_table();
1608
1609         /* first collect incoming stuff */
1610         inprocess();
1611
1612         /* Now process outbound messages, but NOT if this is just a
1613          * quick import-only run (i.e. the -i command-line option
1614          * was specified)
1615          */
1616         if (import_only != 1) {
1617                 allfp = (FILE *) popen("cd ./network/systems; ls", "r");
1618                 if (allfp != NULL) {
1619                         while (fgets(allst, 32, allfp) != NULL) {
1620                                 allst[strlen(allst) - 1] = 0;
1621                                 if (strcmp(allst, "CVS"))
1622                                         outprocess(allst);
1623                         }
1624                         pclose(allfp);
1625                 }
1626                 /* import again in case anything new was generated */
1627                 inprocess();
1628         }
1629
1630         /* Update mail.sysinfo with new information we learned */
1631         rewrite_syslist();
1632
1633         /* Delete any messages which need to be purged locally */
1634         syslog(LOG_NOTICE, "calling process_purgelist()");
1635         process_purgelist();
1636
1637         /* Close the use table */
1638         write_use_table();
1639
1640         syslog(LOG_NOTICE, "processing ended.");
1641         cleanup(0);
1642         return 0;
1643 }