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