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