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