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