2 * Citadel/UX Intelligent Network Processor for IGnet/Open networks
3 * See copyright.txt for copyright information
7 /* How long it takes for an old node to drop off the network map */
8 #define EXPIRY_TIME (2592000L)
10 /* How long we keep recently arrived messages in the use table */
11 #define USE_TIME (604800L)
13 /* Where do we keep our lock file? */
14 #define LOCKFILE "/tmp/netproc.LCK"
16 /* Path to the 'uudecode' utility (needed for network file transfers) */
17 #define UUDECODE "/usr/bin/uudecode"
19 /* Files used by the networker */
20 #define MAILSYSINFO "./network/mail.sysinfo"
22 /* Uncomment the DEBUG def to see noisy traces */
29 #include <sys/types.h>
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.
53 #define FILTERROOM TWITROOM
59 char m_rmname[ROOMNAMELEN];
64 char rm_name[ROOMNAMELEN];
69 struct filterlist *next;
89 * This structure is used to hold all of the fields of a message
90 * during conversion, processing, or whatever.
114 void serv_read(char *buf, int bytes);
115 void serv_write(char *buf, int nbytes);
116 void get_config(void);
118 struct filterlist *filter = NULL;
119 struct syslist *slist = NULL;
120 struct msglist *purgelist = NULL;
122 struct config config;
123 extern char bbs_home_directory[];
124 extern int home_specified;
129 #ifndef HAVE_STRERROR
131 * replacement strerror() for systems that don't have it
133 char *strerror(int e)
137 sprintf(buf, "errno = %d", e);
143 void strip_trailing_whitespace(char *buf)
145 while (isspace(buf[strlen(buf) - 1]))
146 buf[strlen(buf) - 1] = 0;
151 * we also load the mail.sysinfo table into memory, make changes
152 * as we learn more about the network from incoming messages, and write
153 * the table back to disk when we're done.
155 int load_syslist(void)
158 struct syslist *stemp;
162 fp = fopen(MAILSYSINFO, "r");
167 if (fgets(buf, 128, fp) == NULL) {
171 buf[strlen(buf) - 1] = 0;
172 while (isspace(buf[0]))
173 strcpy(buf, &buf[1]);
176 if ((insys == 0) && (strlen(buf) != 0)) {
178 stemp = (struct syslist *) malloc(sizeof(struct syslist));
181 strcpy(slist->s_name, buf);
182 strcpy(slist->s_type, "bin");
183 strcpy(slist->s_nexthop, "Mail");
184 slist->s_lastcontact = 0L;
185 strcpy(slist->s_humannode, "");
186 strcpy(slist->s_phonenum, "");
187 strcpy(slist->s_gdom, "");
188 } else if ((insys == 1) && (strlen(buf) == 0)) {
190 } else if ((insys == 1) && (!strncasecmp(buf, "bin", 3))) {
191 strcpy(slist->s_type, "bin");
192 strcpy(slist->s_nexthop, &buf[4]);
193 } else if ((insys == 1) && (!strncasecmp(buf, "use", 3))) {
194 strcpy(slist->s_type, "use");
195 strcpy(slist->s_nexthop, &buf[4]);
196 } else if ((insys == 1) && (!strncasecmp(buf, "uum", 3))) {
197 strcpy(slist->s_type, "uum");
198 strcpy(slist->s_nexthop, &buf[4]);
199 } else if ((insys == 1) && (!strncasecmp(buf, "lastcontact", 11))) {
201 sscanf(&buf[12], "%ld", &foo);
202 slist->s_lastcontact = foo;
203 } else if ((insys == 1) && (!strncasecmp(buf, "humannode", 9))) {
204 strcpy(slist->s_humannode, &buf[10]);
205 } else if ((insys == 1) && (!strncasecmp(buf, "phonenum", 8))) {
206 strcpy(slist->s_phonenum, &buf[9]);
207 } else if ((insys == 1) && (!strncasecmp(buf, "gdom", 4))) {
208 strcpy(slist->s_gdom, &buf[5]);
213 /* now we have to set up two "special" nodes on the list: one
214 * for the local node, and one for an Internet gateway
216 void setup_special_nodes(void)
218 struct syslist *stemp, *slocal;
221 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
222 if (!strcasecmp(stemp->s_name, config.c_nodename))
225 if (slocal == NULL) {
226 slocal = (struct syslist *) malloc(sizeof(struct syslist));
227 slocal->next = slist;
230 strcpy(slocal->s_name, config.c_nodename);
231 strcpy(slocal->s_type, "bin");
232 strcpy(slocal->s_nexthop, "Mail");
233 time(&slocal->s_lastcontact);
234 strcpy(slocal->s_humannode, config.c_humannode);
235 strcpy(slocal->s_phonenum, config.c_phonenum);
238 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
239 if (!strcasecmp(stemp->s_name, "internet"))
242 if (slocal == NULL) {
243 slocal = (struct syslist *) malloc(sizeof(struct syslist));
244 slocal->next = slist;
247 strcpy(slocal->s_name, "internet");
248 strcpy(slocal->s_type, "uum");
249 strcpy(slocal->s_nexthop, "%s");
250 time(&slocal->s_lastcontact);
251 strcpy(slocal->s_humannode, "Internet Gateway");
252 strcpy(slocal->s_phonenum, "");
253 strcpy(slocal->s_gdom, "");
258 * here's the routine to write the table back to disk.
260 void rewrite_syslist(void)
262 struct syslist *stemp;
267 newfp = fopen(MAILSYSINFO, "w");
268 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
269 if (!strcasecmp(stemp->s_name, config.c_nodename)) {
270 time(&stemp->s_lastcontact);
271 strcpy(stemp->s_type, "bin");
272 strcpy(stemp->s_humannode, config.c_humannode);
273 strcpy(stemp->s_phonenum, config.c_phonenum);
275 /* remove systems we haven't heard from in a while */
276 if ((stemp->s_lastcontact == 0L)
277 || (now - stemp->s_lastcontact < EXPIRY_TIME)) {
278 fprintf(newfp, "%s\n%s %s\n",
279 stemp->s_name, stemp->s_type, stemp->s_nexthop);
280 if (strlen(stemp->s_phonenum) > 0)
281 fprintf(newfp, "phonenum %s\n", stemp->s_phonenum);
282 if (strlen(stemp->s_gdom) > 0)
283 fprintf(newfp, "gdom %s\n", stemp->s_gdom);
284 if (strlen(stemp->s_humannode) > 0)
285 fprintf(newfp, "humannode %s\n", stemp->s_humannode);
286 if (stemp->s_lastcontact > 0L)
287 fprintf(newfp, "lastcontact %ld %s",
288 (long) stemp->s_lastcontact,
289 asctime(localtime(&stemp->s_lastcontact)));
290 fprintf(newfp, "\n");
294 /* now free the list */
295 while (slist != NULL) {
303 /* call this function with the node name of a system and it returns a pointer
304 * to its syslist structure.
306 struct syslist *get_sys_ptr(char *sysname)
308 static char sysnambuf[16];
309 static struct syslist *sysptrbuf = NULL;
310 struct syslist *stemp;
312 if ((!strcmp(sysname, sysnambuf))
313 && (sysptrbuf != NULL))
316 strcpy(sysnambuf, sysname);
317 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
318 if (!strcmp(sysname, stemp->s_name)) {
329 * make sure only one copy of netproc runs at a time, using lock files
331 int set_lockfile(void)
336 if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
337 fscanf(lfp, "%d", &onppid);
339 if (!kill(onppid, 0) || errno == EPERM)
342 lfp = fopen(LOCKFILE, "w");
344 syslog(LOG_NOTICE, "Cannot create %s: %s", LOCKFILE,
348 fprintf(lfp, "%ld\n", (long) getpid());
353 void remove_lockfile(void)
359 * Why both cleanup() and nq_cleanup() ? Notice the alarm() call in
360 * cleanup() . If for some reason netproc hangs waiting for the server
361 * to clean up, the alarm clock goes off and the program exits anyway.
362 * The cleanup() routine makes a check to ensure it's not reentering, in
363 * case the ipc module looped it somehow.
365 void nq_cleanup(int e)
374 static int nested = 0;
377 signal(SIGALRM, nq_cleanup);
384 * This is implemented as a function rather than as a macro because the
385 * client-side IPC modules expect logoff() to be defined. They call logoff()
386 * when a problem connecting or staying connected to the server occurs.
394 * If there is a kill file in place, this function will process it.
396 void load_filterlist(void)
399 struct filterlist *fbuf;
402 fp = fopen("./network/filterlist", "r");
405 while (fgets(sbuf, 256, fp) != NULL) {
406 if (sbuf[0] != '#') {
407 sbuf[strlen(sbuf) - 1] = 0;
408 fbuf = (struct filterlist *)
409 malloc((long) sizeof(struct filterlist));
412 strcpy(fbuf->f_person, "*");
413 strcpy(fbuf->f_room, "*");
414 strcpy(fbuf->f_system, "*");
416 for (a = strlen(sbuf); a >= 0; --a)
421 strcpy(fbuf->f_person, sbuf);
422 strcpy(sbuf, &sbuf[p + 1]);
424 for (a = strlen(sbuf); a >= 0; --a)
429 strcpy(fbuf->f_room, sbuf);
430 strcpy(sbuf, &sbuf[p + 1]);
432 strcpy(fbuf->f_system, sbuf);
438 /* returns 1 if user/message/room combination is in the kill file */
439 int is_banned(char *k_person, char *k_room, char *k_system)
441 struct filterlist *fptr;
443 for (fptr = filter; fptr != NULL; fptr = fptr->next)
445 ((!strcasecmp(fptr->f_person, k_person)) || (!strcmp(fptr->f_person, "*")))
447 ((!strcasecmp(fptr->f_room, k_room)) || (!strcmp(fptr->f_room, "*")))
449 ((!strcasecmp(fptr->f_system, k_system)) || (!strcmp(fptr->f_system, "*")))
457 * Determine routing from sysinfo file
459 int get_sysinfo_type(char *name) {
460 struct syslist *stemp;
462 GETSN: for (stemp = slist; stemp != NULL; stemp = stemp->next) {
463 if (!strcasecmp(stemp->s_name, name)) {
464 if (!strcasecmp(stemp->s_type, "use")) {
465 strcpy(name, stemp->s_nexthop);
468 if (!strcasecmp(stemp->s_type, "bin")) {
471 if (!strcasecmp(stemp->s_type, "uum")) {
472 return (MES_INTERNET);
476 syslog(LOG_ERR, "cannot find system '%s' in mail.sysinfo", name);
481 void fpgetfield(FILE * fp, char *string)
501 * Load all of the fields of a message, except the actual text, into a
502 * table in memory (so we know how to process the message).
504 void fpmsgfind(FILE *fp, struct minfo *buffer)
506 int b, e, mtype, aflag;
513 syslog(LOG_ERR, "Magic number check failed for this message");
517 memset(buffer, 0, sizeof(struct minfo));
521 BONFGM: b = getc(fp);
527 while ((bbb[0] == ' ') && (strlen(bbb) > 1))
528 strcpy(bbb, &bbb[1]);
530 strcpy(buffer->A, bbb);
531 if (strlen(userid) == 0) {
533 for (e = 0; e < strlen(userid); ++e)
534 if (userid[e] == ' ')
539 strcpy(buffer->O, bbb);
541 strcpy(buffer->C, bbb);
543 strcpy(buffer->N, bbb);
545 strcpy(buffer->S, bbb);
547 /* extract the user id from the path */
548 for (e = 0; e < strlen(bbb); ++e)
550 strcpy(userid, &bbb[e + 1]);
552 /* now find the next hop */
553 for (e = 0; e < strlen(bbb); ++e)
556 strcpy(buffer->nexthop, bbb);
559 for (e = 0; e < strlen(bbb); ++e)
562 strcpy(buffer->R, bbb);
565 strcpy(buffer->D, bbb);
567 buffer->T = atol(bbb);
569 strcpy(buffer->I, bbb);
571 strcpy(buffer->H, bbb);
573 strcpy(buffer->B, bbb);
575 strcpy(buffer->G, bbb);
577 strcpy(buffer->E, bbb);
579 strcpy(buffer->Z, bbb);
588 * msgfind() is the same as fpmsgfind() except it accepts a filename
589 * instead of a file handle.
591 void msgfind(char *msgfile, struct minfo *buffer) {
594 fp = fopen(msgfile, "rb");
596 syslog(LOG_ERR, "can't open %s: %s", msgfile, strerror(errno));
600 fpmsgfind(fp, buffer);
608 void ship_to(char *filenm, char *sysnm)
609 { /* send spool file filenm to system sysnm */
616 syslog(LOG_NOTICE, "shipping %s to %s", filenm, sysnm);
618 sprintf(sysflnm, "./network/systems/%s", sysnm);
619 sysflfd = fopen(sysflnm, "r");
621 syslog(LOG_ERR, "cannot open %s", sysflnm);
622 fgets(commbuf1, 99, sysflfd);
623 commbuf1[strlen(commbuf1) - 1] = 0;
625 sprintf(commbuf2, commbuf1, filenm);
630 * proc_file_transfer() - handle a simple file transfer packet
633 void proc_file_transfer(char *tname)
634 { /* name of temp file containing the whole message */
636 char dest_room[ROOMNAMELEN];
637 char subdir_name[256];
641 syslog(LOG_NOTICE, "processing network file transfer...");
643 tfp = fopen(tname, "rb");
645 syslog(LOG_ERR, "cannot open %s", tname);
652 fpgetfield(tfp, buf);
654 strcpy(dest_room, buf);
657 } while ((a != 'M') && (a >= 0));
660 syslog(LOG_ERR, "no message text for file transfer");
663 strcpy(subdir_name, "---xxx---");
664 sprintf(buf, "GOTO %s", dest_room);
668 extract(subdir_name, &buf[4], 2);
669 if (strlen(subdir_name) == 0)
670 strcpy(subdir_name, "--xxx--");
672 /* Change to the room's directory; if that fails, change to the
673 * bitbucket directory. Then run uudecode.
675 sprintf(buf, "(cd %s/files/%s || cd %s/files/%s ) ; exec %s",
676 bbs_home_directory, subdir_name,
677 bbs_home_directory, config.c_bucket_dir,
680 uud = (FILE *) popen(buf, "w");
682 syslog(LOG_ERR, "cannot open uudecode pipe");
686 fgets(buf, 128, tfp);
687 buf[strlen(buf) - 1] = 0;
688 for (a = 0; a < strlen(buf); ++a)
691 fprintf(uud, "%s\n", buf);
692 printf("netproc: %s\n", buf);
693 while (a = getc(tfp), a > 0)
701 /* send a bounce message */
702 void bounce(struct minfo *bminfo)
710 sprintf(bfilename, "./network/spoolin/bounce.%ld.%d", (long) getpid(),
712 bounce = fopen(bfilename, "wb");
715 fprintf(bounce, "%c%c%c", 0xFF, MES_NORMAL, 0);
716 fprintf(bounce, "Ppostmaster%c", 0);
717 fprintf(bounce, "T%ld%c", (long) now, 0);
718 fprintf(bounce, "APostmaster%c", 0);
719 fprintf(bounce, "OMail%c", 0);
720 fprintf(bounce, "N%s%c", config.c_nodename, 0);
721 fprintf(bounce, "H%s%c", config.c_humannode, 0);
723 if (strlen(bminfo->E) > 0) {
724 fprintf(bounce, "R%s%c", bminfo->E, 0);
726 fprintf(bounce, "R%s%c", bminfo->A, 0);
729 fprintf(bounce, "D%s%c", bminfo->N, 0);
730 fprintf(bounce, "M%s could not deliver your mail to:\n",
732 fprintf(bounce, " \n %s\n \n", bminfo->R);
733 fprintf(bounce, " because there is no such user on this system.\n");
734 fprintf(bounce, " (Unsent message does *not* follow. ");
735 fprintf(bounce, "Help to conserve bandwidth.)\n%c", 0);
743 * Generate a Message-ID string for the use table
745 void strmsgid(char *buf, struct minfo *msginfo) {
748 strcpy(buf, msginfo->I);
749 if (strchr(buf, '@') == NULL) {
751 strcat(buf, msginfo->N);
754 for (i=0; i<strlen(buf); ++i) {
755 if (isspace(buf[i])) {
756 strcpy(&buf[i], &buf[i+1]);
758 buf[i] = tolower(buf[i]);
765 * Check the use table to see if a message has been here before.
766 * Returns 1 if the message is a duplicate; otherwise, it returns
767 * 0 and the message ID is added to the use table.
769 int already_received(GDBM_FILE ut, struct minfo *msginfo) {
775 /* We can't check for dups on a zero msgid, so just pass them through */
776 if (strlen(msginfo->I)==0) {
780 strmsgid(buf, msginfo);
784 mkey.dsize = strlen(buf);
786 /* Set return value to 1 if message exists */
787 if (gdbm_exists(ut, mkey)) {
791 /* Write a record into the use table for this message.
792 * Replace existing records; this keeps the timestamp fresh.
794 newrec.dptr = (char *)&now;
795 newrec.dsize = sizeof(now);
796 gdbm_store(ut, mkey, newrec, GDBM_REPLACE);
804 * Purge any old entries out of the use table.
806 * Yes, you're reading this correctly: it keeps traversing the table until
807 * it manages to do a complete pass without deleting any records. Read the
808 * gdbm man page to find out why.
811 void purge_use_table(GDBM_FILE ut) {
812 datum mkey, nextkey, therec;
813 int purged_anything = 0;
814 time_t rec_timestamp, now;
820 mkey = gdbm_firstkey(ut);
821 while (mkey.dptr != NULL) {
822 therec = gdbm_fetch(ut, mkey);
823 if (therec.dptr != NULL) {
824 memcpy(&rec_timestamp, therec.dptr,
828 if ((now - rec_timestamp) > USE_TIME) {
829 gdbm_delete(ut, mkey);
834 nextkey = gdbm_nextkey(ut, mkey);
838 } while (purged_anything != 0);
846 * process incoming files in ./network/spoolin
850 FILE *fp, *message, *testfp, *ls, *duplist;
851 static struct minfo minfo;
852 char tname[128], aaa[1024], iname[256], sfilename[256], pfilename[256];
855 struct syslist *stemp;
862 /* temp file names */
863 sprintf(tname, tmpnam(NULL));
864 sprintf(iname, tmpnam(NULL));
868 /* Make sure we're in the right directory */
869 chdir(bbs_home_directory);
871 /* temporary file to contain a log of rejected dups */
874 /* Let the shell do the dirty work. Get all data from spoolin */
876 sprintf(aaa, "cd %s/network/spoolin; ls", bbs_home_directory);
877 ls = popen(aaa, "r");
879 syslog(LOG_ERR, "could not open dir cmd: %s", strerror(errno));
883 SKIP: ptr = fgets(sfilename, sizeof sfilename, ls);
885 sfilename[strlen(sfilename) - 1] = 0;
888 "Trying <%s>", sfilename);
890 if (!strcmp(sfilename, ".")) goto SKIP;
891 if (!strcmp(sfilename, "..")) goto SKIP;
892 if (!strcmp(sfilename, "CVS")) goto SKIP;
895 } while (ptr != NULL);
896 PROCESS_IT: pclose(ls);
899 sprintf(pfilename, "%s/network/spoolin/%s", bbs_home_directory, sfilename);
900 syslog(LOG_NOTICE, "processing <%s>", pfilename);
902 fp = fopen(pfilename, "rb");
904 syslog(LOG_ERR, "cannot open %s: %s", pfilename, strerror(errno));
905 fp = fopen("/dev/null", "rb");
907 NXMSG: /* Seek to the beginning of the next message */
910 } while ((a != 255) && (a >= 0));
914 /* This crates the temporary file. */
916 message = fopen(tname, "wb");
917 if (message == NULL) {
918 syslog(LOG_ERR, "error creating %s: %s",
919 tname, strerror(errno));
922 putc(255, message); /* 0xFF (start-of-message) */
924 putc(a, message); /* type */
926 putc(a, message); /* mode */
928 FieldID = getc(fp); /* Header field ID */
929 if (isalpha(FieldID)) {
930 putc(FieldID, message);
933 if (a < 127) putc(a, message);
935 if (a != 0) putc(0, message);
937 else { /* Invalid field ID; flush it */
943 } while ((FieldID != 'M') && (a >= 0));
944 /* M is always last */
945 if (FieldID != 'M') valid_msg = 0;
947 msglen = ftell(message);
955 /* process the individual mesage */
956 msgfind(tname, &minfo);
957 syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
958 minfo.I, minfo.A, minfo.O, minfo.N);
959 if (strlen(minfo.R) > 0) {
960 syslog(LOG_NOTICE, " to <%s>", minfo.R);
961 if (strlen(minfo.D) > 0) {
962 syslog(LOG_NOTICE, " @ <%s>",
966 if (!strcasecmp(minfo.D, FQDN))
967 strcpy(minfo.D, NODENAME);
969 /* this routine updates our info on the system that sent the message */
970 stemp = get_sys_ptr(minfo.N);
971 if ((stemp == NULL) && (get_sys_ptr(minfo.nexthop) != NULL)) {
972 /* add non-neighbor system to map */
973 syslog(LOG_NOTICE, "Adding non-neighbor system <%s> to map",
975 stemp = (struct syslist *) malloc((long) sizeof(struct syslist));
978 strcpy(slist->s_name, minfo.N);
979 strcpy(slist->s_type, "use");
980 strcpy(slist->s_nexthop, minfo.nexthop);
981 time(&slist->s_lastcontact);
982 } else if ((stemp == NULL) && (!strcasecmp(minfo.N, minfo.nexthop))) {
983 /* add neighbor system to map */
984 syslog(LOG_NOTICE, "Adding neighbor system <%s> to map",
986 sprintf(aaa, "%s/network/systems/%s", bbs_home_directory, minfo.N);
987 testfp = fopen(aaa, "r");
988 if (testfp != NULL) {
990 stemp = (struct syslist *)
991 malloc((long) sizeof(struct syslist));
994 strcpy(slist->s_name, minfo.N);
995 strcpy(slist->s_type, "bin");
996 strcpy(slist->s_nexthop, "Mail");
997 time(&slist->s_lastcontact);
1000 /* now update last contact and long node name if we can */
1001 if (stemp != NULL) {
1002 time(&stemp->s_lastcontact);
1003 if (strlen(minfo.H) > 0)
1004 strcpy(stemp->s_humannode, minfo.H);
1005 if (strlen(minfo.B) > 0)
1006 strcpy(stemp->s_phonenum, minfo.B);
1007 if (strlen(minfo.G) > 0)
1008 strcpy(stemp->s_gdom, minfo.G);
1011 /* Check the use table; reject message if it's been here before */
1012 if (already_received(use_table, &minfo)) {
1013 syslog(LOG_NOTICE, "rejected duplicate message");
1014 fprintf(duplist, "#<%s> fm <%s> in <%s> @ <%s>\n",
1015 minfo.I, minfo.A, minfo.O, minfo.N);
1019 /* route the message if necessary */
1020 else if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
1021 a = get_sysinfo_type(minfo.D);
1022 syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
1024 if (a == MES_INTERNET) {
1026 syslog(LOG_NOTICE, "netmailer %s", tname);
1028 execlp("./netmailer", "netmailer",
1030 syslog(LOG_ERR, "error running netmailer: %s",
1034 while (wait(&b) != (-1));
1035 } else if (a == MES_BINARY) {
1036 ship_to(tname, minfo.D);
1038 /* message falls into the bit bucket? */
1042 /* check to see if it's a file transfer */
1043 else if (!strncasecmp(minfo.S, "FILE", 4)) {
1044 proc_file_transfer(tname);
1047 /* otherwise process it as a normal message */
1049 if (!strcasecmp(minfo.R, "postmaster")) {
1050 strcpy(minfo.R, "");
1051 strcpy(minfo.C, "Aide");
1053 if (strlen(minfo.R) > 0) {
1054 sprintf(buf, "GOTO _MAIL_");
1056 if (is_banned(minfo.A, minfo.C, minfo.N)) {
1057 sprintf(buf, "GOTO %s", FILTERROOM);
1059 if (strlen(minfo.C) > 0) {
1060 sprintf(buf, "GOTO %s", minfo.C);
1062 sprintf(buf, "GOTO %s", minfo.O);
1067 if (buf[0] != '2') {
1068 syslog(LOG_ERR, "%s", buf);
1069 sprintf(buf, "GOTO _BITBUCKET_");
1073 /* Open the temporary file containing the message */
1074 message = fopen(tname, "rb");
1075 if (message == NULL) {
1076 syslog(LOG_ERR, "cannot open %s: %s",
1077 tname, strerror(errno));
1081 /* Transmit the message to the server */
1082 sprintf(buf, "ENT3 1|%s|%ld", minfo.R, msglen);
1085 if (!strncmp(buf, "570", 3)) {
1086 /* no such user, do a bounce */
1089 if (buf[0] == '7') {
1090 /* Always use the server's idea of the message length,
1091 * even though they should both be identical */
1092 msglen = atol(&buf[4]);
1093 while (msglen > 0L) {
1094 bloklen = ((msglen >= 255L) ? 255 : ((int) msglen));
1095 if (fread(buf, bloklen, 1, message) < 1) {
1097 "error trying to read %d bytes: %s",
1098 bloklen, strerror(errno));
1100 serv_write(buf, bloklen);
1101 msglen = msglen - (long) bloklen;
1106 syslog(LOG_ERR, "%s", buf);
1119 } while (ptr != NULL);
1124 * If dups were rejected, post a message saying so
1126 if (ftell(duplist)!=0L) {
1127 fp = fopen("./network/spoolin/ctdl_rejects", "ab");
1129 fprintf(fp, "%cA%c", 255, 1);
1130 fprintf(fp, "T%ld%c", time(NULL), 0);
1131 fprintf(fp, "ACitadel%c", 0);
1132 fprintf(fp, "OAide%cM", 0);
1133 fprintf(fp, "The following duplicate messages"
1134 " were rejected:\n \n");
1136 while (fgets(buf, sizeof(buf), duplist) != NULL) {
1137 buf[strlen(buf)-1] = 0;
1138 fprintf(fp, " %s\n", buf);
1140 fprintf(fp, "%c", 0);
1150 /* Checks to see whether its ok to send */
1151 /* Returns 1 for ok, send message */
1152 /* Returns 0 if message already there */
1153 int checkpath(char *path, char *sys)
1161 syslog(LOG_NOTICE, "checkpath <%s> <%s> ... ", path, sys);
1163 for (a = 0; a < strlen(path); ++a) {
1164 if (!strncmp(&path[a], sys2, strlen(sys2)))
1171 * Implement split horizon algorithm (prevent infinite spooling loops
1172 * by refusing to send any node a message which already contains its
1173 * nodename in the path).
1175 int ismsgok(FILE *mmfp, char *sysname)
1178 int ok = 0; /* fail safe - no path, don't send it */
1182 if (getc(mmfp) != 255)
1187 while (a = getc(mmfp), ((a != 'M') && (a != 0))) {
1188 fpgetfield(mmfp, fbuf);
1190 ok = checkpath(fbuf, sysname);
1194 syslog(LOG_NOTICE, "%s", ((ok) ? "SEND" : "(no)"));
1203 * Add a message to the list of messages to be deleted off the local server
1204 * at the end of this run.
1206 void delete_locally(long msgid, char *roomname) {
1207 struct msglist *mptr;
1209 mptr = (struct msglist *) malloc(sizeof(struct msglist));
1210 mptr->next = purgelist;
1211 mptr->m_num = msgid;
1212 strcpy(mptr->m_rmname, roomname);
1219 * Delete all messages on the purge list from the local server.
1221 void process_purgelist(void) {
1222 char curr_rm[ROOMNAMELEN];
1224 struct msglist *mptr;
1227 strcpy(curr_rm, "__nothing__");
1228 while (purgelist != NULL) {
1229 if (strcasecmp(curr_rm, purgelist->m_rmname)) {
1230 sprintf(buf, "GOTO %s", purgelist->m_rmname);
1233 if (buf[0] == '2') {
1234 extract(curr_rm, &buf[4], 0);
1237 syslog(LOG_ERR, "%s", buf);
1240 if (!strcasecmp(curr_rm, purgelist->m_rmname)) {
1241 syslog(LOG_NOTICE, "Purging <%ld> in <%s>",
1242 purgelist->m_num, purgelist->m_rmname);
1243 sprintf(buf, "DELE %ld", purgelist->m_num);
1246 if (buf[0] != '2') {
1247 syslog(LOG_ERR, "%s", buf);
1251 mptr = purgelist->next;
1260 /* spool list of messages to a file */
1261 /* returns # of msgs spooled */
1262 int spool_out(struct msglist *cmlist, FILE * destfp, char *sysname)
1264 struct msglist *cmptr;
1268 int msgs_spooled = 0;
1271 static struct minfo minfo;
1276 strcpy(curr_rm, "");
1278 /* for each message in the list... */
1279 for (cmptr = cmlist; cmptr != NULL; cmptr = cmptr->next) {
1281 /* make sure we're in the correct room... */
1282 if (strcasecmp(curr_rm, cmptr->m_rmname)) {
1283 sprintf(buf, "GOTO %s", cmptr->m_rmname);
1286 if (buf[0] == '2') {
1287 strcpy(curr_rm, cmptr->m_rmname);
1289 syslog(LOG_ERR, "%s", buf);
1292 /* download the message from the server... */
1294 sprintf(buf, "MSG3 %ld", cmptr->m_num);
1297 if (buf[0] == '6') { /* read the msg */
1298 msg_len = atol(&buf[4]);
1299 while (msg_len > 0L) {
1300 blok_len = ((msg_len >= 256L) ? 256 : (int) msg_len);
1301 serv_read(buf, blok_len);
1302 fwrite(buf, blok_len, 1, mmfp);
1303 msg_len = msg_len - (long) blok_len;
1305 } else { /* or print the err */
1306 syslog(LOG_ERR, "%s", buf);
1311 if (ismsgok(mmfp, sysname)) {
1315 fread(fbuf, 3, 1, mmfp);
1316 fwrite(fbuf, 3, 1, destfp);
1317 while (a = getc(mmfp), ((a != 0) && (a != 'M'))) {
1320 fpgetfield(mmfp, fbuf);
1322 fprintf(destfp, "%s!", NODENAME);
1324 fwrite(fbuf, strlen(fbuf) + 1, 1, destfp);
1325 if (a == 'S') if (!strcasecmp(fbuf, "CANCEL")) {
1326 delete_locally(cmptr->m_num, cmptr->m_rmname);
1330 fprintf(destfp, "C%s%c",
1331 cmptr->m_rmname, 0);
1339 /* Get this message into the use table, so we can reject it
1340 * if a misconfigured remote system sends it back to us.
1343 fpmsgfind(mmfp, &minfo);
1344 already_received(use_table, &minfo);
1350 return (msgs_spooled);
1353 void outprocess(char *sysname)
1354 { /* send new room messages to sysname */
1357 char shiptocmd[128];
1361 struct msglist *cmlist = NULL;
1362 struct rmlist *crmlist = NULL;
1363 struct rmlist *rmptr, *rmptr2;
1364 struct msglist *cmptr, *cmptr2;
1365 FILE *sysflfp, *tempflfp;
1369 sprintf(tempflnm, tmpnam(NULL));
1370 tempflfp = fopen(tempflnm, "w");
1371 if (tempflfp == NULL)
1376 * Read system file for node in question and put together room list
1378 sprintf(sysflnm, "%s/network/systems/%s", bbs_home_directory, sysname);
1379 sysflfp = fopen(sysflnm, "r");
1380 if (sysflfp == NULL)
1382 fgets(shiptocmd, 128, sysflfp);
1383 shiptocmd[strlen(shiptocmd) - 1] = 0;
1384 while (!feof(sysflfp)) {
1385 if (fgets(srmname, 32, sysflfp) == NULL)
1387 srmname[strlen(srmname) - 1] = 0;
1388 fgets(lbuf, 32, sysflfp);
1389 rmptr = (struct rmlist *) malloc(sizeof(struct rmlist));
1391 strcpy(rmptr->rm_name, srmname);
1392 strip_trailing_whitespace(rmptr->rm_name);
1393 rmptr->rm_lastsent = atol(lbuf);
1394 if (crmlist == NULL)
1396 else if (!strcasecmp(rmptr->rm_name, "control")) {
1397 /* control has to be first in room list */
1398 rmptr->next = crmlist;
1402 while (rmptr2->next != NULL)
1403 rmptr2 = rmptr2->next;
1404 rmptr2->next = rmptr;
1410 * Assemble list of messages to be spooled
1412 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next) {
1414 sprintf(buf, "GOTO %s", rmptr->rm_name);
1417 if (buf[0] != '2') {
1418 syslog(LOG_ERR, "%s", buf);
1420 sprintf(buf, "MSGS GT|%ld", rmptr->rm_lastsent);
1424 while (serv_gets(buf), strcmp(buf, "000")) {
1425 thismsg = atol(buf);
1426 if (thismsg > (rmptr->rm_lastsent)) {
1427 rmptr->rm_lastsent = thismsg;
1429 cmptr = (struct msglist *)
1430 malloc(sizeof(struct msglist));
1432 cmptr->m_num = thismsg;
1433 strcpy(cmptr->m_rmname, rmptr->rm_name);
1439 while (cmptr2->next != NULL)
1440 cmptr2 = cmptr2->next;
1441 cmptr2->next = cmptr;
1444 } else { /* print error from "msgs all" */
1445 syslog(LOG_ERR, "%s", buf);
1451 cmptr2 = cmlist; /* this loop counts the messages */
1452 while (cmptr2 != NULL) {
1454 cmptr2 = cmptr2->next;
1456 syslog(LOG_NOTICE, "%d messages to be spooled to %s",
1457 outgoing_msgs, sysname);
1460 * Spool out the messages, but only if there are any.
1462 if (outgoing_msgs != 0)
1463 outgoing_msgs = spool_out(cmlist, tempflfp, sysname);
1464 syslog(LOG_NOTICE, "%d messages actually spooled",
1468 * Deallocate list of spooled messages.
1470 while (cmlist != NULL) {
1471 cmptr = cmlist->next;
1477 * Rewrite system file and deallocate room list.
1479 syslog(LOG_NOTICE, "Spooling...");
1480 sysflfp = fopen(sysflnm, "w");
1481 fprintf(sysflfp, "%s\n", shiptocmd);
1482 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next)
1483 fprintf(sysflfp, "%s\n%ld\n", rmptr->rm_name, rmptr->rm_lastsent);
1485 while (crmlist != NULL) {
1486 rmptr = crmlist->next;
1492 * Close temporary file, ship it out, and return
1495 if (outgoing_msgs != 0)
1496 ship_to(tempflnm, sysname);
1502 * Connect netproc to the Citadel server running on this computer.
1504 void np_attach_to_server(void)
1509 { "netproc", NULL };
1511 syslog(LOG_NOTICE, "Attaching to server...");
1512 sprintf(portname, "%d", config.c_port_number);
1514 attach_to_server(1, args, NULL, NULL);
1516 syslog(LOG_NOTICE, "%s", &buf[4]);
1517 sprintf(buf, "IPGM %d", config.c_ipgm_secret);
1520 syslog(LOG_NOTICE, "%s", &buf[4]);
1521 if (buf[0] != '2') {
1531 int main(int argc, char **argv)
1536 int import_only = 0; /* if set to 1, don't export anything */
1538 openlog("netproc", LOG_PID, LOG_USER);
1539 strcpy(bbs_home_directory, BBSDIR);
1542 * Change directories if specified
1544 for (a = 1; a < argc; ++a) {
1545 if (!strncmp(argv[a], "-h", 2)) {
1546 strcpy(bbs_home_directory, argv[a]);
1547 strcpy(bbs_home_directory, &bbs_home_directory[2]);
1549 } else if (!strcmp(argv[a], "-i")) {
1552 fprintf(stderr, "netproc: usage: ");
1553 fprintf(stderr, "netproc [-hHomeDir] [-i]\n");
1559 syslog(LOG_DEBUG, "Calling get_config()");
1564 syslog(LOG_DEBUG, "Creating lock file");
1566 if (set_lockfile() != 0) {
1567 syslog(LOG_NOTICE, "lock file exists: already running");
1570 signal(SIGINT, cleanup);
1571 signal(SIGQUIT, cleanup);
1572 signal(SIGHUP, cleanup);
1573 signal(SIGTERM, cleanup);
1575 syslog(LOG_NOTICE, "started. pid=%d", getpid());
1577 np_attach_to_server();
1580 if (load_syslist() != 0)
1581 syslog(LOG_ERR, "cannot load sysinfo");
1582 setup_special_nodes();
1584 /* Open the use table */
1585 use_table = gdbm_open("./data/usetable.gdbm", 512,
1586 GDBM_WRCREAT, 0600, 0);
1587 if (use_table == NULL) {
1588 syslog(LOG_ERR, "could not open use table: %s",
1592 /* first collect incoming stuff */
1595 /* Now process outbound messages, but NOT if this is just a
1596 * quick import-only run (i.e. the -i command-line option
1599 if (import_only != 1) {
1600 allfp = (FILE *) popen("cd ./network/systems; ls", "r");
1601 if (allfp != NULL) {
1602 while (fgets(allst, 32, allfp) != NULL) {
1603 allst[strlen(allst) - 1] = 0;
1604 if (strcmp(allst, "CVS"))
1609 /* import again in case anything new was generated */
1613 /* Update mail.sysinfo with new information we learned */
1616 /* Delete any messages which need to be purged locally */
1617 syslog(LOG_NOTICE, "calling process_purgelist()");
1618 process_purgelist();
1620 /* Close the use table */
1621 purge_use_table(use_table);
1622 gdbm_close(use_table);
1624 syslog(LOG_NOTICE, "processing ended.");