4 * Citadel/UX Intelligent Network Processor for IGnet/Open networks
5 * See copyright.txt for copyright information
9 /* How long it takes for an old node to drop off the network map */
10 #define EXPIRY_TIME (2592000L)
12 /* How long we keep recently arrived messages in the use table */
13 #define USE_TIME (604800L)
15 /* Where do we keep our lock file? */
16 #define LOCKFILE "/tmp/netproc.LCK"
18 /* Path to the 'uudecode' utility (needed for network file transfers) */
19 #define UUDECODE "/usr/bin/uudecode"
21 /* Files used by the networker */
22 #define MAILSYSINFO "./network/mail.sysinfo"
24 /* Uncomment the DEBUG def to see noisy traces */
31 #include <sys/types.h>
38 #if TIME_WITH_SYS_TIME
39 # include <sys/time.h>
43 # include <sys/time.h>
56 /* A list of users you wish to filter out of incoming traffic can be kept
57 * in ./network/filterlist -- messages from these users will be automatically
58 * moved to FILTERROOM. Normally this will be the same as TWITROOM (the
59 * room problem user messages are moved to) but you can override this by
60 * specifying a different room name here.
63 #define FILTERROOM TWITROOM
69 char m_rmname[ROOMNAMELEN];
74 char rm_name[ROOMNAMELEN];
79 struct filterlist *next;
99 * This structure is used to hold all of the fields of a message
100 * during conversion, processing, or whatever.
124 struct usetable *next;
132 void serv_read(char *buf, int bytes);
133 void serv_write(char *buf, int nbytes);
134 void get_config(void);
136 struct filterlist *filter = NULL;
137 struct syslist *slist = NULL;
138 struct msglist *purgelist = NULL;
139 struct usetable *usetable = NULL;
141 struct config config;
142 extern char bbs_home_directory[];
143 extern int home_specified;
146 #ifndef HAVE_STRERROR
148 * replacement strerror() for systems that don't have it
150 char *strerror(int e)
154 sprintf(buf, "errno = %d", e);
160 void strip_trailing_whitespace(char *buf)
162 while (isspace(buf[strlen(buf) - 1]))
163 buf[strlen(buf) - 1] = 0;
168 * we also load the mail.sysinfo table into memory, make changes
169 * as we learn more about the network from incoming messages, and write
170 * the table back to disk when we're done.
172 int load_syslist(void)
175 struct syslist *stemp;
179 fp = fopen(MAILSYSINFO, "r");
184 if (fgets(buf, 128, fp) == NULL) {
188 buf[strlen(buf) - 1] = 0;
189 while (isspace(buf[0]))
190 strcpy(buf, &buf[1]);
193 if ((insys == 0) && (strlen(buf) != 0)) {
195 stemp = (struct syslist *) malloc(sizeof(struct syslist));
198 strcpy(slist->s_name, buf);
199 strcpy(slist->s_type, "bin");
200 strcpy(slist->s_nexthop, "Mail");
201 slist->s_lastcontact = 0L;
202 strcpy(slist->s_humannode, "");
203 strcpy(slist->s_phonenum, "");
204 strcpy(slist->s_gdom, "");
205 } else if ((insys == 1) && (strlen(buf) == 0)) {
207 } else if ((insys == 1) && (!strncasecmp(buf, "bin", 3))) {
208 strcpy(slist->s_type, "bin");
209 strcpy(slist->s_nexthop, &buf[4]);
210 } else if ((insys == 1) && (!strncasecmp(buf, "use", 3))) {
211 strcpy(slist->s_type, "use");
212 strcpy(slist->s_nexthop, &buf[4]);
213 } else if ((insys == 1) && (!strncasecmp(buf, "uum", 3))) {
214 strcpy(slist->s_type, "uum");
215 strcpy(slist->s_nexthop, &buf[4]);
216 } else if ((insys == 1) && (!strncasecmp(buf, "lastcontact", 11))) {
218 sscanf(&buf[12], "%ld", &foo);
219 slist->s_lastcontact = foo;
220 } else if ((insys == 1) && (!strncasecmp(buf, "humannode", 9))) {
221 strcpy(slist->s_humannode, &buf[10]);
222 } else if ((insys == 1) && (!strncasecmp(buf, "phonenum", 8))) {
223 strcpy(slist->s_phonenum, &buf[9]);
224 } else if ((insys == 1) && (!strncasecmp(buf, "gdom", 4))) {
225 strcpy(slist->s_gdom, &buf[5]);
230 /* now we have to set up two "special" nodes on the list: one
231 * for the local node, and one for an Internet gateway
233 void setup_special_nodes(void)
235 struct syslist *stemp, *slocal;
238 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
239 if (!strcasecmp(stemp->s_name, config.c_nodename))
242 if (slocal == NULL) {
243 slocal = (struct syslist *) malloc(sizeof(struct syslist));
244 slocal->next = slist;
247 strcpy(slocal->s_name, config.c_nodename);
248 strcpy(slocal->s_type, "bin");
249 strcpy(slocal->s_nexthop, "Mail");
250 time(&slocal->s_lastcontact);
251 strcpy(slocal->s_humannode, config.c_humannode);
252 strcpy(slocal->s_phonenum, config.c_phonenum);
255 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
256 if (!strcasecmp(stemp->s_name, "internet"))
259 if (slocal == NULL) {
260 slocal = (struct syslist *) malloc(sizeof(struct syslist));
261 slocal->next = slist;
264 strcpy(slocal->s_name, "internet");
265 strcpy(slocal->s_type, "uum");
266 strcpy(slocal->s_nexthop, "%s");
267 time(&slocal->s_lastcontact);
268 strcpy(slocal->s_humannode, "Internet Gateway");
269 strcpy(slocal->s_phonenum, "");
270 strcpy(slocal->s_gdom, "");
275 * here's the routine to write the table back to disk.
277 void rewrite_syslist(void)
279 struct syslist *stemp;
284 newfp = fopen(MAILSYSINFO, "w");
285 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
286 if (!strcasecmp(stemp->s_name, config.c_nodename)) {
287 time(&stemp->s_lastcontact);
288 strcpy(stemp->s_type, "bin");
289 strcpy(stemp->s_humannode, config.c_humannode);
290 strcpy(stemp->s_phonenum, config.c_phonenum);
292 /* remove systems we haven't heard from in a while */
293 if ((stemp->s_lastcontact == 0L)
294 || (now - stemp->s_lastcontact < EXPIRY_TIME)) {
295 fprintf(newfp, "%s\n%s %s\n",
296 stemp->s_name, stemp->s_type, stemp->s_nexthop);
297 if (strlen(stemp->s_phonenum) > 0)
298 fprintf(newfp, "phonenum %s\n", stemp->s_phonenum);
299 if (strlen(stemp->s_gdom) > 0)
300 fprintf(newfp, "gdom %s\n", stemp->s_gdom);
301 if (strlen(stemp->s_humannode) > 0)
302 fprintf(newfp, "humannode %s\n", stemp->s_humannode);
303 if (stemp->s_lastcontact > 0L)
304 fprintf(newfp, "lastcontact %ld %s",
305 (long) stemp->s_lastcontact,
306 asctime(localtime(&stemp->s_lastcontact)));
307 fprintf(newfp, "\n");
311 /* now free the list */
312 while (slist != NULL) {
320 /* call this function with the node name of a system and it returns a pointer
321 * to its syslist structure.
323 struct syslist *get_sys_ptr(char *sysname)
325 static char sysnambuf[16];
326 static struct syslist *sysptrbuf = NULL;
327 struct syslist *stemp;
329 if ((!strcmp(sysname, sysnambuf))
330 && (sysptrbuf != NULL))
333 strcpy(sysnambuf, sysname);
334 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
335 if (!strcmp(sysname, stemp->s_name)) {
346 * make sure only one copy of netproc runs at a time, using lock files
348 int set_lockfile(void)
353 if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
354 fscanf(lfp, "%d", &onppid);
356 if (!kill(onppid, 0) || errno == EPERM)
359 lfp = fopen(LOCKFILE, "w");
361 syslog(LOG_NOTICE, "Cannot create %s: %s", LOCKFILE,
365 fprintf(lfp, "%ld\n", (long) getpid());
370 void remove_lockfile(void)
376 * Why both cleanup() and nq_cleanup() ? Notice the alarm() call in
377 * cleanup() . If for some reason netproc hangs waiting for the server
378 * to clean up, the alarm clock goes off and the program exits anyway.
379 * The cleanup() routine makes a check to ensure it's not reentering, in
380 * case the ipc module looped it somehow.
382 void nq_cleanup(int e)
391 static int nested = 0;
394 signal(SIGALRM, nq_cleanup);
401 * This is implemented as a function rather than as a macro because the
402 * client-side IPC modules expect logoff() to be defined. They call logoff()
403 * when a problem connecting or staying connected to the server occurs.
411 * If there is a kill file in place, this function will process it.
413 void load_filterlist(void)
416 struct filterlist *fbuf;
419 fp = fopen("./network/filterlist", "r");
422 while (fgets(sbuf, sizeof sbuf, fp) != NULL) {
423 if (sbuf[0] != '#') {
424 sbuf[strlen(sbuf) - 1] = 0;
425 fbuf = (struct filterlist *)
426 malloc((long) sizeof(struct filterlist));
429 strcpy(fbuf->f_person, "*");
430 strcpy(fbuf->f_room, "*");
431 strcpy(fbuf->f_system, "*");
433 for (a = strlen(sbuf); a >= 0; --a)
438 strcpy(fbuf->f_person, sbuf);
439 strcpy(sbuf, &sbuf[p + 1]);
441 for (a = strlen(sbuf); a >= 0; --a)
446 strcpy(fbuf->f_room, sbuf);
447 strcpy(sbuf, &sbuf[p + 1]);
449 strcpy(fbuf->f_system, sbuf);
455 /* returns 1 if user/message/room combination is in the kill file */
456 int is_banned(char *k_person, char *k_room, char *k_system)
458 struct filterlist *fptr;
460 for (fptr = filter; fptr != NULL; fptr = fptr->next)
462 ((!strcasecmp(fptr->f_person, k_person)) || (!strcmp(fptr->f_person, "*")))
464 ((!strcasecmp(fptr->f_room, k_room)) || (!strcmp(fptr->f_room, "*")))
466 ((!strcasecmp(fptr->f_system, k_system)) || (!strcmp(fptr->f_system, "*")))
474 * Determine routing from sysinfo file
476 int get_sysinfo_type(char *name) {
477 struct syslist *stemp;
479 GETSN: for (stemp = slist; stemp != NULL; stemp = stemp->next) {
480 if (!strcasecmp(stemp->s_name, name)) {
481 if (!strcasecmp(stemp->s_type, "use")) {
482 strcpy(name, stemp->s_nexthop);
485 if (!strcasecmp(stemp->s_type, "bin")) {
488 if (!strcasecmp(stemp->s_type, "uum")) {
489 return (MES_INTERNET);
493 syslog(LOG_ERR, "cannot find system '%s' in mail.sysinfo", name);
498 void fpgetfield(FILE *fp, char *string, int limit)
506 if ((b < 1) || (a >= limit)) {
518 * Load all of the fields of a message, except the actual text, into a
519 * table in memory (so we know how to process the message).
521 void fpmsgfind(FILE *fp, struct minfo *buffer)
523 int b, e, mtype, aflag;
530 syslog(LOG_ERR, "Magic number check failed for this message");
534 memset(buffer, 0, sizeof(struct minfo));
538 BONFGM: b = getc(fp);
543 fpgetfield(fp, bbb, sizeof bbb);
544 while ((bbb[0] == ' ') && (strlen(bbb) > 1))
545 strcpy(bbb, &bbb[1]);
547 strcpy(buffer->A, bbb);
548 if (strlen(userid) == 0) {
550 for (e = 0; e < strlen(userid); ++e)
551 if (userid[e] == ' ')
556 strcpy(buffer->O, bbb);
558 strcpy(buffer->C, bbb);
560 strcpy(buffer->N, bbb);
562 strcpy(buffer->S, bbb);
564 /* extract the user id from the path */
565 for (e = 0; e < strlen(bbb); ++e)
567 strcpy(userid, &bbb[e + 1]);
569 /* now find the next hop */
570 for (e = 0; e < strlen(bbb); ++e)
573 strcpy(buffer->nexthop, bbb);
576 for (e = 0; e < strlen(bbb); ++e)
579 strcpy(buffer->R, bbb);
582 strcpy(buffer->D, bbb);
584 buffer->T = atol(bbb);
586 strcpy(buffer->I, bbb);
588 strcpy(buffer->H, bbb);
590 strcpy(buffer->B, bbb);
592 strcpy(buffer->G, bbb);
594 strcpy(buffer->E, bbb);
596 strcpy(buffer->Z, bbb);
605 * msgfind() is the same as fpmsgfind() except it accepts a filename
606 * instead of a file handle.
608 void msgfind(char *msgfile, struct minfo *buffer) {
611 fp = fopen(msgfile, "rb");
613 syslog(LOG_ERR, "can't open %s: %s", msgfile, strerror(errno));
617 fpmsgfind(fp, buffer);
625 void ship_to(char *filenm, char *sysnm)
626 { /* send spool file filenm to system sysnm */
633 syslog(LOG_NOTICE, "shipping %s to %s", filenm, sysnm);
635 sprintf(sysflnm, "./network/systems/%s", sysnm);
636 sysflfd = fopen(sysflnm, "r");
638 syslog(LOG_ERR, "cannot open %s", sysflnm);
639 fgets(commbuf1, 99, sysflfd);
640 commbuf1[strlen(commbuf1) - 1] = 0;
642 sprintf(commbuf2, commbuf1, filenm);
647 * proc_file_transfer() - handle a simple file transfer packet
650 void proc_file_transfer(char *tname)
651 { /* name of temp file containing the whole message */
653 char dest_room[ROOMNAMELEN];
654 char subdir_name[256];
658 syslog(LOG_NOTICE, "processing network file transfer...");
660 tfp = fopen(tname, "rb");
662 syslog(LOG_ERR, "cannot open %s", tname);
669 fpgetfield(tfp, buf, sizeof buf);
671 strcpy(dest_room, buf);
674 } while ((a != 'M') && (a >= 0));
677 syslog(LOG_ERR, "no message text for file transfer");
680 strcpy(subdir_name, "---xxx---");
681 sprintf(buf, "GOTO %s", dest_room);
685 extract(subdir_name, &buf[4], 2);
686 if (strlen(subdir_name) == 0)
687 strcpy(subdir_name, "--xxx--");
689 /* Change to the room's directory; if that fails, change to the
690 * bitbucket directory. Then run uudecode.
692 sprintf(buf, "(cd %s/files/%s || cd %s/files/%s ) ; exec %s",
693 bbs_home_directory, subdir_name,
694 bbs_home_directory, config.c_bucket_dir,
697 uud = (FILE *) popen(buf, "w");
699 syslog(LOG_ERR, "cannot open uudecode pipe");
703 fgets(buf, 128, tfp);
704 buf[strlen(buf) - 1] = 0;
705 for (a = 0; a < strlen(buf); ++a)
708 fprintf(uud, "%s\n", buf);
709 printf("netproc: %s\n", buf);
710 while (a = getc(tfp), a > 0)
718 /* send a bounce message */
719 void bounce(struct minfo *bminfo)
727 sprintf(bfilename, "./network/spoolin/bounce.%ld.%d", (long) getpid(),
729 bounce = fopen(bfilename, "wb");
732 fprintf(bounce, "%c%c%c", 0xFF, MES_NORMAL, 0);
733 fprintf(bounce, "Ppostmaster%c", 0);
734 fprintf(bounce, "T%ld%c", (long) now, 0);
735 fprintf(bounce, "APostmaster%c", 0);
736 fprintf(bounce, "OMail%c", 0);
737 fprintf(bounce, "N%s%c", config.c_nodename, 0);
738 fprintf(bounce, "H%s%c", config.c_humannode, 0);
740 if (strlen(bminfo->E) > 0) {
741 fprintf(bounce, "R%s%c", bminfo->E, 0);
743 fprintf(bounce, "R%s%c", bminfo->A, 0);
746 fprintf(bounce, "D%s%c", bminfo->N, 0);
747 fprintf(bounce, "M%s could not deliver your mail to:\n",
749 fprintf(bounce, " \n %s\n \n", bminfo->R);
750 fprintf(bounce, " because there is no such user on this system.\n");
751 fprintf(bounce, " (Unsent message does *not* follow. ");
752 fprintf(bounce, "Help to conserve bandwidth.)\n%c", 0);
760 * Generate a Message-ID string for the use table
762 void strmsgid(char *buf, struct minfo *msginfo) {
765 strcpy(buf, msginfo->I);
766 if (strchr(buf, '@') == NULL) {
768 strcat(buf, msginfo->N);
771 for (i=0; i<strlen(buf); ++i) {
772 if (isspace(buf[i])) {
773 strcpy(&buf[i], &buf[i+1]);
775 buf[i] = tolower(buf[i]);
782 * Check the use table to see if a message has been here before.
783 * Returns 1 if the message is a duplicate; otherwise, it returns
784 * 0 and the message ID is added to the use table.
786 int already_received(struct minfo *msginfo) {
791 /* We can't check for dups on a zero msgid, so just pass them through */
792 if (strlen(msginfo->I)==0) {
796 strmsgid(buf, msginfo);
799 /* Set return value to 1 if message exists */
800 for (u=usetable; u!=NULL; u=u->next) {
801 if (!strcasecmp(buf, u->msgid)) {
802 u->timestamp = time(NULL); /* keep it fresh */
807 /* Not found, so we're ok, but add it to the use table now */
808 u = (struct usetable *) malloc(sizeof (struct usetable));
810 u->timestamp = time(NULL);
811 strncpy(u->msgid, buf, 255);
819 * Load the use table from disk
821 void read_use_table(void) {
823 struct usetable ubuf;
826 unlink("data/usetable.gdbm"); /* we don't use this anymore */
828 fp = fopen("usetable", "rb");
829 if (fp == NULL) return;
831 while (fread(&ubuf, sizeof (struct usetable), 1, fp) > 0) {
832 u = (struct usetable *) malloc(sizeof (struct usetable));
833 memcpy(u, &ubuf, sizeof (struct usetable));
844 * Purge any old entries out of the use table as we write them back to disk.
847 void write_use_table(void) {
853 fp = fopen("usetable", "wb");
854 if (fp == NULL) return;
855 for (u=usetable; u!=NULL; u=u->next) {
856 if ((now - u->timestamp) <= USE_TIME) {
857 fwrite(u, sizeof(struct usetable), 1, fp);
868 * process incoming files in ./network/spoolin
872 FILE *fp, *message, *testfp, *ls, *duplist;
873 static struct minfo minfo;
874 char tname[128], aaa[1024], iname[256], sfilename[256], pfilename[256];
877 struct syslist *stemp;
884 /* temp file names */
885 sprintf(tname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
886 sprintf(iname, "%s.netproc.%d", tmpnam(NULL), __LINE__);
890 /* Make sure we're in the right directory */
891 chdir(bbs_home_directory);
893 /* temporary file to contain a log of rejected dups */
896 /* Let the shell do the dirty work. Get all data from spoolin */
898 sprintf(aaa, "cd %s/network/spoolin; ls", bbs_home_directory);
899 ls = popen(aaa, "r");
901 syslog(LOG_ERR, "could not open dir cmd: %s", strerror(errno));
905 SKIP: ptr = fgets(sfilename, sizeof sfilename, ls);
907 sfilename[strlen(sfilename) - 1] = 0;
910 "Trying <%s>", sfilename);
912 if (!strcmp(sfilename, ".")) goto SKIP;
913 if (!strcmp(sfilename, "..")) goto SKIP;
914 if (!strcmp(sfilename, "CVS")) goto SKIP;
917 } while (ptr != NULL);
918 PROCESS_IT: pclose(ls);
921 sprintf(pfilename, "%s/network/spoolin/%s", bbs_home_directory, sfilename);
922 syslog(LOG_NOTICE, "processing <%s>", pfilename);
924 fp = fopen(pfilename, "rb");
926 syslog(LOG_ERR, "cannot open %s: %s", pfilename, strerror(errno));
927 fp = fopen("/dev/null", "rb");
929 NXMSG: /* Seek to the beginning of the next message */
932 } while ((a != 255) && (a >= 0));
936 /* This crates the temporary file. */
938 message = fopen(tname, "wb");
939 if (message == NULL) {
940 syslog(LOG_ERR, "error creating %s: %s",
941 tname, strerror(errno));
944 putc(255, message); /* 0xFF (start-of-message) */
946 putc(a, message); /* type */
948 putc(a, message); /* mode */
950 FieldID = getc(fp); /* Header field ID */
951 if (isalpha(FieldID)) {
952 putc(FieldID, message);
955 if (a < 127) putc(a, message);
957 if (a != 0) putc(0, message);
959 else { /* Invalid field ID; flush it */
965 } while ((FieldID != 'M') && (a >= 0));
966 /* M is always last */
967 if (FieldID != 'M') valid_msg = 0;
969 msglen = ftell(message);
977 /* process the individual mesage */
978 msgfind(tname, &minfo);
979 syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
980 minfo.I, minfo.A, minfo.O, minfo.N);
981 if (strlen(minfo.R) > 0) {
982 syslog(LOG_NOTICE, " to <%s>", minfo.R);
983 if (strlen(minfo.D) > 0) {
984 syslog(LOG_NOTICE, " @ <%s>",
988 if (!strcasecmp(minfo.D, FQDN))
989 strcpy(minfo.D, NODENAME);
991 /* this routine updates our info on the system that sent the message */
992 stemp = get_sys_ptr(minfo.N);
993 if ((stemp == NULL) && (get_sys_ptr(minfo.nexthop) != NULL)) {
994 /* add non-neighbor system to map */
995 syslog(LOG_NOTICE, "Adding non-neighbor system <%s> to map",
997 stemp = (struct syslist *) malloc((long) sizeof(struct syslist));
1000 strcpy(slist->s_name, minfo.N);
1001 strcpy(slist->s_type, "use");
1002 strcpy(slist->s_nexthop, minfo.nexthop);
1003 time(&slist->s_lastcontact);
1004 } else if ((stemp == NULL) && (!strcasecmp(minfo.N, minfo.nexthop))) {
1005 /* add neighbor system to map */
1006 syslog(LOG_NOTICE, "Adding neighbor system <%s> to map",
1008 sprintf(aaa, "%s/network/systems/%s", bbs_home_directory, minfo.N);
1009 testfp = fopen(aaa, "r");
1010 if (testfp != NULL) {
1012 stemp = (struct syslist *)
1013 malloc((long) sizeof(struct syslist));
1014 stemp->next = slist;
1016 strcpy(slist->s_name, minfo.N);
1017 strcpy(slist->s_type, "bin");
1018 strcpy(slist->s_nexthop, "Mail");
1019 time(&slist->s_lastcontact);
1022 /* now update last contact and long node name if we can */
1023 if (stemp != NULL) {
1024 time(&stemp->s_lastcontact);
1025 if (strlen(minfo.H) > 0)
1026 strcpy(stemp->s_humannode, minfo.H);
1027 if (strlen(minfo.B) > 0)
1028 strcpy(stemp->s_phonenum, minfo.B);
1029 if (strlen(minfo.G) > 0)
1030 strcpy(stemp->s_gdom, minfo.G);
1033 /* Check the use table; reject message if it's been here before */
1034 if (already_received(&minfo)) {
1035 syslog(LOG_NOTICE, "rejected duplicate message");
1036 fprintf(duplist, "#<%s> fm <%s> in <%s> @ <%s>\n",
1037 minfo.I, minfo.A, minfo.O, minfo.N);
1041 /* route the message if necessary */
1042 else if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
1043 a = get_sysinfo_type(minfo.D);
1044 syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
1046 if (a == MES_INTERNET) {
1048 syslog(LOG_NOTICE, "netmailer %s", tname);
1050 execlp("./netmailer", "netmailer",
1052 syslog(LOG_ERR, "error running netmailer: %s",
1056 while (wait(&b) != (-1));
1057 } else if (a == MES_BINARY) {
1058 ship_to(tname, minfo.D);
1060 /* message falls into the bit bucket? */
1064 /* check to see if it's a file transfer */
1065 else if (!strncasecmp(minfo.S, "FILE", 4)) {
1066 proc_file_transfer(tname);
1069 /* otherwise process it as a normal message */
1071 if (!strcasecmp(minfo.R, "postmaster")) {
1072 strcpy(minfo.R, "");
1073 strcpy(minfo.C, "Aide");
1075 if (strlen(minfo.R) > 0) {
1076 sprintf(buf, "GOTO _MAIL_");
1078 if (is_banned(minfo.A, minfo.C, minfo.N)) {
1079 sprintf(buf, "GOTO %s", FILTERROOM);
1081 if (strlen(minfo.C) > 0) {
1082 sprintf(buf, "GOTO %s", minfo.C);
1084 sprintf(buf, "GOTO %s", minfo.O);
1089 if (buf[0] != '2') {
1090 syslog(LOG_ERR, "%s", buf);
1091 sprintf(buf, "GOTO _BITBUCKET_");
1095 /* Open the temporary file containing the message */
1096 message = fopen(tname, "rb");
1097 if (message == NULL) {
1098 syslog(LOG_ERR, "cannot open %s: %s",
1099 tname, strerror(errno));
1103 /* Transmit the message to the server */
1104 sprintf(buf, "ENT3 1|%s|%ld", minfo.R, msglen);
1107 if (!strncmp(buf, "570", 3)) {
1108 /* no such user, do a bounce */
1111 if (buf[0] == '7') {
1112 /* Always use the server's idea of the message length,
1113 * even though they should both be identical */
1114 msglen = atol(&buf[4]);
1115 while (msglen > 0L) {
1116 bloklen = ((msglen >= 255L) ? 255 : ((int) msglen));
1117 if (fread(buf, bloklen, 1, message) < 1) {
1119 "error trying to read %d bytes: %s",
1120 bloklen, strerror(errno));
1122 serv_write(buf, bloklen);
1123 msglen = msglen - (long) bloklen;
1128 syslog(LOG_ERR, "%s", buf);
1141 } while (ptr != NULL);
1146 * If dups were rejected, post a message saying so
1148 if (ftell(duplist)!=0L) {
1149 fp = fopen("./network/spoolin/ctdl_rejects", "ab");
1151 fprintf(fp, "%cA%c", 255, 1);
1152 fprintf(fp, "T%ld%c", time(NULL), 0);
1153 fprintf(fp, "ACitadel%c", 0);
1154 fprintf(fp, "OAide%cM", 0);
1155 fprintf(fp, "The following duplicate messages"
1156 " were rejected:\n \n");
1158 while (fgets(buf, sizeof(buf), duplist) != NULL) {
1159 buf[strlen(buf)-1] = 0;
1160 fprintf(fp, " %s\n", buf);
1162 fprintf(fp, "%c", 0);
1172 /* Checks to see whether its ok to send */
1173 /* Returns 1 for ok, send message */
1174 /* Returns 0 if message already there */
1175 int checkpath(char *path, char *sys)
1183 syslog(LOG_NOTICE, "checkpath <%s> <%s> ... ", path, sys);
1185 for (a = 0; a < strlen(path); ++a) {
1186 if (!strncmp(&path[a], sys2, strlen(sys2)))
1193 * Implement split horizon algorithm (prevent infinite spooling loops
1194 * by refusing to send any node a message which already contains its
1195 * nodename in the path).
1197 int ismsgok(FILE *mmfp, char *sysname)
1200 int ok = 0; /* fail safe - no path, don't send it */
1204 if (getc(mmfp) != 255)
1209 while (a = getc(mmfp), ((a != 'M') && (a != 0))) {
1210 fpgetfield(mmfp, fbuf, sizeof fbuf);
1212 ok = checkpath(fbuf, sysname);
1216 syslog(LOG_NOTICE, "%s", ((ok) ? "SEND" : "(no)"));
1225 * Add a message to the list of messages to be deleted off the local server
1226 * at the end of this run.
1228 void delete_locally(long msgid, char *roomname) {
1229 struct msglist *mptr;
1231 mptr = (struct msglist *) malloc(sizeof(struct msglist));
1232 mptr->next = purgelist;
1233 mptr->m_num = msgid;
1234 strcpy(mptr->m_rmname, roomname);
1241 * Delete all messages on the purge list from the local server.
1243 void process_purgelist(void) {
1244 char curr_rm[ROOMNAMELEN];
1246 struct msglist *mptr;
1249 strcpy(curr_rm, "__nothing__");
1250 while (purgelist != NULL) {
1251 if (strcasecmp(curr_rm, purgelist->m_rmname)) {
1252 sprintf(buf, "GOTO %s", purgelist->m_rmname);
1255 if (buf[0] == '2') {
1256 extract(curr_rm, &buf[4], 0);
1259 syslog(LOG_ERR, "%s", buf);
1262 if (!strcasecmp(curr_rm, purgelist->m_rmname)) {
1263 syslog(LOG_NOTICE, "Purging <%ld> in <%s>",
1264 purgelist->m_num, purgelist->m_rmname);
1265 sprintf(buf, "DELE %ld", purgelist->m_num);
1268 if (buf[0] != '2') {
1269 syslog(LOG_ERR, "%s", buf);
1273 mptr = purgelist->next;
1282 /* spool list of messages to a file */
1283 /* returns # of msgs spooled */
1284 int spool_out(struct msglist *cmlist, FILE * destfp, char *sysname)
1286 struct msglist *cmptr;
1290 int msgs_spooled = 0;
1293 static struct minfo minfo;
1298 strcpy(curr_rm, "");
1300 /* for each message in the list... */
1301 for (cmptr = cmlist; cmptr != NULL; cmptr = cmptr->next) {
1303 /* make sure we're in the correct room... */
1304 if (strcasecmp(curr_rm, cmptr->m_rmname)) {
1305 sprintf(buf, "GOTO %s", cmptr->m_rmname);
1308 if (buf[0] == '2') {
1309 strcpy(curr_rm, cmptr->m_rmname);
1311 syslog(LOG_ERR, "%s", buf);
1314 /* download the message from the server... */
1317 syslog(LOG_NOTICE, "tmpfile() failed: %s\n",
1320 sprintf(buf, "MSG3 %ld", cmptr->m_num);
1323 if (buf[0] == '6') { /* read the msg */
1324 msg_len = atol(&buf[4]);
1325 while (msg_len > 0L) {
1326 blok_len = ((msg_len >= 256L) ? 256 : (int) msg_len);
1327 serv_read(buf, blok_len);
1328 fwrite(buf, blok_len, 1, mmfp);
1329 msg_len = msg_len - (long) blok_len;
1331 } else { /* or print the err */
1332 syslog(LOG_ERR, "%s", buf);
1337 if (ismsgok(mmfp, sysname)) {
1341 fread(fbuf, 3, 1, mmfp);
1342 fwrite(fbuf, 3, 1, destfp);
1343 while (a = getc(mmfp), ((a != 0) && (a != 'M'))) {
1347 fpgetfield(mmfp, fbuf, sizeof fbuf);
1349 fprintf(destfp, "%s!", NODENAME);
1352 fwrite(fbuf, strlen(fbuf) + 1, 1, destfp);
1354 if (a == 'S') if (!strcasecmp(fbuf, "CANCEL")) {
1355 delete_locally(cmptr->m_num, cmptr->m_rmname);
1359 fprintf(destfp, "C%s%c",
1360 cmptr->m_rmname, 0);
1368 /* Get this message into the use table, so we can reject it
1369 * if a misconfigured remote system sends it back to us.
1372 fpmsgfind(mmfp, &minfo);
1373 already_received(&minfo);
1379 return (msgs_spooled);
1382 void outprocess(char *sysname)
1383 { /* send new room messages to sysname */
1386 char shiptocmd[128];
1390 struct msglist *cmlist = NULL;
1391 struct msglist *cmlast = NULL;
1392 struct rmlist *crmlist = NULL;
1393 struct rmlist *rmptr, *rmptr2;
1394 struct msglist *cmptr;
1395 FILE *sysflfp, *tempflfp;
1396 int outgoing_msgs = 0;
1399 sprintf(tempflnm, "%s.netproc.%d", tmpnam(NULL), __LINE__);
1400 tempflfp = fopen(tempflnm, "w");
1401 if (tempflfp == NULL)
1406 * Read system file for node in question and put together room list
1408 sprintf(sysflnm, "%s/network/systems/%s", bbs_home_directory, sysname);
1409 sysflfp = fopen(sysflnm, "r");
1410 if (sysflfp == NULL)
1412 fgets(shiptocmd, 128, sysflfp);
1413 shiptocmd[strlen(shiptocmd) - 1] = 0;
1414 while (!feof(sysflfp)) {
1415 if (fgets(srmname, 32, sysflfp) == NULL)
1417 srmname[strlen(srmname) - 1] = 0;
1418 fgets(lbuf, 32, sysflfp);
1419 rmptr = (struct rmlist *) malloc(sizeof(struct rmlist));
1421 strcpy(rmptr->rm_name, srmname);
1422 strip_trailing_whitespace(rmptr->rm_name);
1423 rmptr->rm_lastsent = atol(lbuf);
1424 if (crmlist == NULL)
1426 else if (!strcasecmp(rmptr->rm_name, "control")) {
1427 /* control has to be first in room list */
1428 rmptr->next = crmlist;
1432 while (rmptr2->next != NULL)
1433 rmptr2 = rmptr2->next;
1434 rmptr2->next = rmptr;
1440 * Assemble list of messages to be spooled
1442 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next) {
1444 sprintf(buf, "GOTO %s", rmptr->rm_name);
1447 if (buf[0] != '2') {
1448 syslog(LOG_ERR, "%s", buf);
1450 sprintf(buf, "MSGS GT|%ld", rmptr->rm_lastsent);
1454 while (serv_gets(buf), strcmp(buf, "000")) {
1455 thismsg = atol(buf);
1456 if (thismsg > (rmptr->rm_lastsent)) {
1457 rmptr->rm_lastsent = thismsg;
1459 cmptr = (struct msglist *)
1460 malloc(sizeof(struct msglist));
1462 cmptr->m_num = thismsg;
1463 strcpy(cmptr->m_rmname, rmptr->rm_name);
1465 if (cmlist == NULL) {
1469 cmlast->next = cmptr;
1474 } else { /* print error from "msgs all" */
1475 syslog(LOG_ERR, "%s", buf);
1480 syslog(LOG_NOTICE, "%d messages to be spooled to %s",
1481 outgoing_msgs, sysname);
1484 * Spool out the messages, but only if there are any.
1486 if (outgoing_msgs != 0) {
1487 outgoing_msgs = spool_out(cmlist, tempflfp, sysname);
1490 syslog(LOG_NOTICE, "%d messages actually spooled", outgoing_msgs);
1493 * Deallocate list of spooled messages.
1495 while (cmlist != NULL) {
1496 cmptr = cmlist->next;
1502 * Rewrite system file and deallocate room list.
1504 syslog(LOG_NOTICE, "Spooling...");
1505 sysflfp = fopen(sysflnm, "w");
1506 fprintf(sysflfp, "%s\n", shiptocmd);
1507 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next)
1508 fprintf(sysflfp, "%s\n%ld\n", rmptr->rm_name, rmptr->rm_lastsent);
1510 while (crmlist != NULL) {
1511 rmptr = crmlist->next;
1517 * Close temporary file, ship it out, and return
1520 if (outgoing_msgs != 0)
1521 ship_to(tempflnm, sysname);
1527 * Connect netproc to the Citadel server running on this computer.
1529 void np_attach_to_server(void)
1533 { "netproc", NULL };
1535 syslog(LOG_NOTICE, "Attaching to server...");
1536 attach_to_server(1, args, NULL, NULL);
1538 syslog(LOG_NOTICE, "%s", &buf[4]);
1539 sprintf(buf, "IPGM %d", config.c_ipgm_secret);
1542 syslog(LOG_NOTICE, "%s", &buf[4]);
1543 if (buf[0] != '2') {
1553 int main(int argc, char **argv)
1558 int import_only = 0; /* if set to 1, don't export anything */
1560 openlog("netproc", LOG_PID, LOG_USER);
1561 strcpy(bbs_home_directory, BBSDIR);
1564 * Change directories if specified
1566 for (a = 1; a < argc; ++a) {
1567 if (!strncmp(argv[a], "-h", 2)) {
1568 strcpy(bbs_home_directory, argv[a]);
1569 strcpy(bbs_home_directory, &bbs_home_directory[2]);
1571 } else if (!strcmp(argv[a], "-i")) {
1574 fprintf(stderr, "netproc: usage: ");
1575 fprintf(stderr, "netproc [-hHomeDir] [-i]\n");
1581 syslog(LOG_DEBUG, "Calling get_config()");
1586 syslog(LOG_DEBUG, "Creating lock file");
1588 if (set_lockfile() != 0) {
1589 syslog(LOG_NOTICE, "lock file exists: already running");
1592 signal(SIGINT, cleanup);
1593 signal(SIGQUIT, cleanup);
1594 signal(SIGHUP, cleanup);
1595 signal(SIGTERM, cleanup);
1597 syslog(LOG_NOTICE, "started. pid=%d", getpid());
1599 np_attach_to_server();
1602 if (load_syslist() != 0)
1603 syslog(LOG_ERR, "cannot load sysinfo");
1604 setup_special_nodes();
1606 /* Open the use table */
1609 /* first collect incoming stuff */
1612 /* Now process outbound messages, but NOT if this is just a
1613 * quick import-only run (i.e. the -i command-line option
1616 if (import_only != 1) {
1617 allfp = (FILE *) popen("cd ./network/systems; ls", "r");
1618 if (allfp != NULL) {
1619 while (fgets(allst, 32, allfp) != NULL) {
1620 allst[strlen(allst) - 1] = 0;
1621 if (strcmp(allst, "CVS"))
1626 /* import again in case anything new was generated */
1630 /* Update mail.sysinfo with new information we learned */
1633 /* Delete any messages which need to be purged locally */
1634 syslog(LOG_NOTICE, "calling process_purgelist()");
1635 process_purgelist();
1637 /* Close the use table */
1640 syslog(LOG_NOTICE, "processing ended.");