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 "/var/lock/LCK.netproc"
16 /* Path to the 'uudecode' utility (needed for network file transfers) */
17 #define UUDECODE "/usr/bin/uudecode"
19 /* Uncomment the DEBUG def to see noisy traces */
26 #include <sys/types.h>
42 /* A list of users you wish to filter out of incoming traffic can be kept
43 * in ./network/filterlist -- messages from these users will be automatically
44 * moved to FILTERROOM. Normally this will be the same as TWITROOM (the
45 * room problem user messages are moved to) but you can override this by
46 * specifying a different room name here.
49 #define FILTERROOM TWITROOM
55 char m_rmname[ROOMNAMELEN];
60 char rm_name[ROOMNAMELEN];
65 struct filterlist *next;
83 void attach_to_server(int argc, char **argv);
84 void serv_read(char *buf, int bytes);
85 void serv_write(char *buf, int nbytes);
86 void get_config(void);
88 struct filterlist *filter = NULL;
89 struct syslist *slist = NULL;
92 extern char bbs_home_directory[];
93 extern int home_specified;
98 * replacement strerror() for systems that don't have it
100 char *strerror(int e)
104 sprintf(buf, "errno = %d", e);
110 void strip_trailing_whitespace(char *buf)
112 while (isspace(buf[strlen(buf) - 1]))
113 buf[strlen(buf) - 1] = 0;
118 * we also load the network/mail.sysinfo table into memory, make changes
119 * as we learn more about the network from incoming messages, and write
120 * the table back to disk when we're done.
122 int load_syslist(void)
125 struct syslist *stemp;
129 fp = fopen("network/mail.sysinfo", "r");
134 if (fgets(buf, 128, fp) == NULL) {
138 buf[strlen(buf) - 1] = 0;
139 while (isspace(buf[0]))
140 strcpy(buf, &buf[1]);
143 if ((insys == 0) && (strlen(buf) != 0)) {
145 stemp = (struct syslist *) malloc(sizeof(struct syslist));
148 strcpy(slist->s_name, buf);
149 strcpy(slist->s_type, "bin");
150 strcpy(slist->s_nexthop, "Mail");
151 slist->s_lastcontact = 0L;
152 strcpy(slist->s_humannode, "");
153 strcpy(slist->s_phonenum, "");
154 strcpy(slist->s_gdom, "");
155 } else if ((insys == 1) && (strlen(buf) == 0)) {
157 } else if ((insys == 1) && (!strncasecmp(buf, "bin", 3))) {
158 strcpy(slist->s_type, "bin");
159 strcpy(slist->s_nexthop, &buf[4]);
160 } else if ((insys == 1) && (!strncasecmp(buf, "use", 3))) {
161 strcpy(slist->s_type, "use");
162 strcpy(slist->s_nexthop, &buf[4]);
163 } else if ((insys == 1) && (!strncasecmp(buf, "uum", 3))) {
164 strcpy(slist->s_type, "uum");
165 strcpy(slist->s_nexthop, &buf[4]);
166 } else if ((insys == 1) && (!strncasecmp(buf, "lastcontact", 11))) {
168 sscanf(&buf[12], "%ld", &foo);
169 slist->s_lastcontact = foo;
170 } else if ((insys == 1) && (!strncasecmp(buf, "humannode", 9))) {
171 strcpy(slist->s_humannode, &buf[10]);
172 } else if ((insys == 1) && (!strncasecmp(buf, "phonenum", 8))) {
173 strcpy(slist->s_phonenum, &buf[9]);
174 } else if ((insys == 1) && (!strncasecmp(buf, "gdom", 4))) {
175 strcpy(slist->s_gdom, &buf[5]);
180 /* now we have to set up two "special" nodes on the list: one
181 * for the local node, and one for an Internet gateway
183 void setup_special_nodes(void)
185 struct syslist *stemp, *slocal;
188 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
189 if (!strcasecmp(stemp->s_name, config.c_nodename))
192 if (slocal == NULL) {
193 slocal = (struct syslist *) malloc(sizeof(struct syslist));
194 slocal->next = slist;
197 strcpy(slocal->s_name, config.c_nodename);
198 strcpy(slocal->s_type, "bin");
199 strcpy(slocal->s_nexthop, "Mail");
200 time(&slocal->s_lastcontact);
201 strcpy(slocal->s_humannode, config.c_humannode);
202 strcpy(slocal->s_phonenum, config.c_phonenum);
205 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
206 if (!strcasecmp(stemp->s_name, "internet"))
209 if (slocal == NULL) {
210 slocal = (struct syslist *) malloc(sizeof(struct syslist));
211 slocal->next = slist;
214 strcpy(slocal->s_name, "internet");
215 strcpy(slocal->s_type, "uum");
216 strcpy(slocal->s_nexthop, "%s");
217 time(&slocal->s_lastcontact);
218 strcpy(slocal->s_humannode, "Internet Gateway");
219 strcpy(slocal->s_phonenum, "");
220 strcpy(slocal->s_gdom, "");
225 * here's the routine to write the table back to disk.
227 void rewrite_syslist(void)
229 struct syslist *stemp;
234 newfp = fopen("network/mail.sysinfo", "w");
235 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
236 if (!strcasecmp(stemp->s_name, config.c_nodename)) {
237 time(&stemp->s_lastcontact);
238 strcpy(stemp->s_type, "bin");
239 strcpy(stemp->s_humannode, config.c_humannode);
240 strcpy(stemp->s_phonenum, config.c_phonenum);
242 /* remove systems we haven't heard from in a while */
243 if ((stemp->s_lastcontact == 0L)
244 || (now - stemp->s_lastcontact < EXPIRY_TIME)) {
245 fprintf(newfp, "%s\n%s %s\n",
246 stemp->s_name, stemp->s_type, stemp->s_nexthop);
247 if (strlen(stemp->s_phonenum) > 0)
248 fprintf(newfp, "phonenum %s\n", stemp->s_phonenum);
249 if (strlen(stemp->s_gdom) > 0)
250 fprintf(newfp, "gdom %s\n", stemp->s_gdom);
251 if (strlen(stemp->s_humannode) > 0)
252 fprintf(newfp, "humannode %s\n", stemp->s_humannode);
253 if (stemp->s_lastcontact > 0L)
254 fprintf(newfp, "lastcontact %ld %s",
255 (long) stemp->s_lastcontact,
256 asctime(localtime(&stemp->s_lastcontact)));
257 fprintf(newfp, "\n");
261 /* now free the list */
262 while (slist != NULL) {
270 /* call this function with the node name of a system and it returns a pointer
271 * to its syslist structure.
273 struct syslist *get_sys_ptr(char *sysname)
275 static char sysnambuf[16];
276 static struct syslist *sysptrbuf = NULL;
277 struct syslist *stemp;
279 if ((!strcmp(sysname, sysnambuf))
280 && (sysptrbuf != NULL))
283 strcpy(sysnambuf, sysname);
284 for (stemp = slist; stemp != NULL; stemp = stemp->next) {
285 if (!strcmp(sysname, stemp->s_name)) {
296 * make sure only one copy of netproc runs at a time, using lock files
298 int set_lockfile(void)
303 if ((lfp = fopen(LOCKFILE, "r")) != NULL) {
304 fscanf(lfp, "%d", &onppid);
306 if (!kill(onppid, 0) || errno == EPERM)
309 lfp = fopen(LOCKFILE, "w");
311 syslog(LOG_NOTICE, "Cannot create %s: %s", LOCKFILE,
315 fprintf(lfp, "%ld\n", (long) getpid());
320 void remove_lockfile(void)
326 * Why both cleanup() and nq_cleanup() ? Notice the alarm() call in
327 * cleanup() . If for some reason netproc hangs waiting for the server
328 * to clean up, the alarm clock goes off and the program exits anyway.
329 * The cleanup() routine makes a check to ensure it's not reentering, in
330 * case the ipc module looped it somehow.
332 void nq_cleanup(int e)
341 static int nested = 0;
344 signal(SIGALRM, nq_cleanup);
351 * This is implemented as a function rather than as a macro because the
352 * client-side IPC modules expect logoff() to be defined. They call logoff()
353 * when a problem connecting or staying connected to the server occurs.
361 * If there is a kill file in place, this function will process it.
363 void load_filterlist(void)
366 struct filterlist *fbuf;
369 fp = fopen("./network/filterlist", "r");
372 while (fgets(sbuf, 256, fp) != NULL) {
373 if (sbuf[0] != '#') {
374 sbuf[strlen(sbuf) - 1] = 0;
375 fbuf = (struct filterlist *)
376 malloc((long) sizeof(struct filterlist));
379 strcpy(fbuf->f_person, "*");
380 strcpy(fbuf->f_room, "*");
381 strcpy(fbuf->f_system, "*");
383 for (a = strlen(sbuf); a >= 0; --a)
388 strcpy(fbuf->f_person, sbuf);
389 strcpy(sbuf, &sbuf[p + 1]);
391 for (a = strlen(sbuf); a >= 0; --a)
396 strcpy(fbuf->f_room, sbuf);
397 strcpy(sbuf, &sbuf[p + 1]);
399 strcpy(fbuf->f_system, sbuf);
405 /* returns 1 if user/message/room combination is in the kill file */
406 int is_banned(char *k_person, char *k_room, char *k_system)
408 struct filterlist *fptr;
410 for (fptr = filter; fptr != NULL; fptr = fptr->next)
412 ((!strcasecmp(fptr->f_person, k_person)) || (!strcmp(fptr->f_person, "*")))
414 ((!strcasecmp(fptr->f_room, k_room)) || (!strcmp(fptr->f_room, "*")))
416 ((!strcasecmp(fptr->f_system, k_system)) || (!strcmp(fptr->f_system, "*")))
424 * Determine routing from sysinfo file
426 int get_sysinfo_type(char *name) {
427 struct syslist *stemp;
429 GETSN: for (stemp = slist; stemp != NULL; stemp = stemp->next) {
430 if (!strcasecmp(stemp->s_name, name)) {
431 if (!strcasecmp(stemp->s_type, "use")) {
432 strcpy(name, stemp->s_nexthop);
435 if (!strcasecmp(stemp->s_type, "bin")) {
438 if (!strcasecmp(stemp->s_type, "uum")) {
439 return (MES_INTERNET);
443 syslog(LOG_ERR, "cannot find system '%s' in mail.sysinfo", name);
448 void fpgetfield(FILE * fp, char *string)
468 * Load all of the fields of a message, except the actual text, into a
469 * table in memory (so we know how to process the message).
471 void msgfind(char *msgfile, struct minfo *buffer)
473 int b, e, mtype, aflag;
479 fp = fopen(msgfile, "rb");
481 syslog(LOG_ERR, "can't open message file: %s", strerror(errno));
486 syslog(LOG_ERR, "incorrect message format");
499 BONFGM: b = getc(fp);
505 while ((bbb[0] == ' ') && (strlen(bbb) > 1))
506 strcpy(bbb, &bbb[1]);
508 strcpy(buffer->A, bbb);
509 if (strlen(userid) == 0) {
511 for (e = 0; e < strlen(userid); ++e)
512 if (userid[e] == ' ')
517 strcpy(buffer->O, bbb);
519 strcpy(buffer->C, bbb);
521 strcpy(buffer->N, bbb);
523 strcpy(buffer->S, bbb);
525 /* extract the user id from the path */
526 for (e = 0; e < strlen(bbb); ++e)
528 strcpy(userid, &bbb[e + 1]);
530 /* now find the next hop */
531 for (e = 0; e < strlen(bbb); ++e)
534 strcpy(buffer->nexthop, bbb);
537 for (e = 0; e < strlen(bbb); ++e)
540 strcpy(buffer->R, bbb);
543 strcpy(buffer->D, bbb);
545 buffer->T = atol(bbb);
547 buffer->I = atol(bbb);
549 strcpy(buffer->H, bbb);
551 strcpy(buffer->B, bbb);
553 strcpy(buffer->G, bbb);
555 strcpy(buffer->E, bbb);
560 /* NOTE: we used to use the following two lines of code to assign
561 * the timestamp as a message-ID if there was no message-ID already
562 * in the message. We don't do this anymore because it screws up
566 buffer->I = buffer->T;
574 void ship_to(char *filenm, char *sysnm)
575 { /* send spool file filenm to system sysnm */
582 syslog(LOG_NOTICE, "shipping %s to %s", filenm, sysnm);
584 sprintf(sysflnm, "./network/systems/%s", sysnm);
585 sysflfd = fopen(sysflnm, "r");
587 syslog(LOG_ERR, "cannot open %s", sysflnm);
588 fgets(commbuf1, 99, sysflfd);
589 commbuf1[strlen(commbuf1) - 1] = 0;
591 sprintf(commbuf2, commbuf1, filenm);
596 * proc_file_transfer() - handle a simple file transfer packet
599 void proc_file_transfer(char *tname)
600 { /* name of temp file containing the whole message */
602 char dest_room[ROOMNAMELEN];
603 char subdir_name[256];
607 syslog(LOG_NOTICE, "processing network file transfer...");
609 tfp = fopen(tname, "rb");
611 syslog(LOG_ERR, "cannot open %s", tname);
618 fpgetfield(tfp, buf);
620 strcpy(dest_room, buf);
623 } while ((a != 'M') && (a >= 0));
626 syslog(LOG_ERR, "no message text for file transfer");
629 strcpy(subdir_name, "---xxx---");
630 sprintf(buf, "GOTO %s", dest_room);
634 extract(subdir_name, &buf[4], 2);
635 if (strlen(subdir_name) == 0)
636 strcpy(subdir_name, "--xxx--");
638 /* Change to the room's directory; if that fails, change to the
639 * bitbucket directory. Then run uudecode.
641 sprintf(buf, "(cd %s/files/%s || cd %s/files/%s ) ; exec %s",
642 bbs_home_directory, subdir_name,
643 bbs_home_directory, config.c_bucket_dir,
646 uud = (FILE *) popen(buf, "w");
648 syslog(LOG_ERR, "cannot open uudecode pipe");
652 fgets(buf, 128, tfp);
653 buf[strlen(buf) - 1] = 0;
654 for (a = 0; a < strlen(buf); ++a)
657 fprintf(uud, "%s\n", buf);
658 printf("netproc: %s\n", buf);
659 while (a = getc(tfp), a > 0)
667 /* send a bounce message */
668 void bounce(struct minfo *bminfo)
676 sprintf(bfilename, "./network/spoolin/bounce.%ld.%d", (long) getpid(),
678 bounce = fopen(bfilename, "wb");
681 fprintf(bounce, "%c%c%c", 0xFF, MES_NORMAL, 0);
682 fprintf(bounce, "Ppostmaster%c", 0);
683 fprintf(bounce, "T%ld%c", (long) now, 0);
684 fprintf(bounce, "APostmaster%c", 0);
685 fprintf(bounce, "OMail%c", 0);
686 fprintf(bounce, "N%s%c", config.c_nodename, 0);
687 fprintf(bounce, "H%s%c", config.c_humannode, 0);
689 if (strlen(bminfo->E) > 0) {
690 fprintf(bounce, "R%s%c", bminfo->E, 0);
692 fprintf(bounce, "R%s%c", bminfo->A, 0);
695 fprintf(bounce, "D%s%c", bminfo->N, 0);
696 fprintf(bounce, "M%s could not deliver your mail to:\n",
698 fprintf(bounce, " \n %s\n \n", bminfo->R);
699 fprintf(bounce, " because there is no such user on this system.\n");
700 fprintf(bounce, " (Unsent message does *not* follow. ");
701 fprintf(bounce, "Help to conserve bandwidth.)\n%c", 0);
709 * Generate a Message-ID string for the use table
711 void strmsgid(char *buf, struct minfo *msginfo) {
714 sprintf(buf, "%ld@%s", msginfo->I, msginfo->N);
715 for (i=0; i<strlen(buf); ++i) {
716 if (isspace(buf[i])) {
717 strcpy(&buf[i], &buf[i+1]);
719 buf[i] = tolower(buf[i]);
726 * Check the use table to see if a message has been here before.
727 * Returns 1 if the message is a duplicate; otherwise, it returns
728 * 0 and the message ID is added to the use table.
730 int already_received(GDBM_FILE ut, struct minfo *msginfo) {
736 /* We can't check for dups on a zero msgid, so just pass them through */
737 if ((msginfo->I)==0L) {
741 strmsgid(buf, msginfo);
745 mkey.dsize = strlen(buf);
747 /* Set return value to 1 if message exists */
748 if (gdbm_exists(ut, mkey)) {
752 /* Write a record into the use table for this message.
753 * Replace existing records; this keeps the timestamp fresh.
755 newrec.dptr = (char *)&now;
756 newrec.dsize = sizeof(now);
757 gdbm_store(ut, mkey, newrec, GDBM_REPLACE);
765 * Purge any old entries out of the use table.
767 * Yes, you're reading this correctly: it keeps traversing the table until
768 * it manages to do a complete pass without deleting any records. Read the
769 * gdbm man page to find out why.
772 void purge_use_table(GDBM_FILE ut) {
773 datum mkey, nextkey, therec;
774 int purged_anything = 0;
775 time_t rec_timestamp, now;
781 mkey = gdbm_firstkey(ut);
782 while (mkey.dptr != NULL) {
783 therec = gdbm_fetch(ut, mkey);
784 if (therec.dptr != NULL) {
785 memcpy(&rec_timestamp, therec.dptr,
789 if ((now - rec_timestamp) > USE_TIME) {
790 gdbm_delete(ut, mkey);
795 nextkey = gdbm_nextkey(ut, mkey);
799 } while (purged_anything != 0);
805 * process incoming files in ./network/spoolin
809 FILE *fp, *message, *testfp, *ls, *duplist;
810 static struct minfo minfo;
811 struct recentmsg recentmsg;
812 char tname[128], aaa[1024], iname[256], sfilename[256], pfilename[256];
815 struct syslist *stemp;
822 /* temp file names */
823 sprintf(tname, tmpnam(NULL));
824 sprintf(iname, tmpnam(NULL));
828 /* Make sure we're in the right directory */
829 chdir(bbs_home_directory);
831 /* Open the use table */
832 use_table = gdbm_open("./data/usetable.gdbm", 512,
833 GDBM_WRCREAT, 0600, 0);
834 if (use_table == NULL) {
835 syslog(LOG_ERR, "could not open use table: %s",
840 /* temporary file to contain a log of rejected dups */
843 /* Let the shell do the dirty work. Get all data from spoolin */
845 sprintf(aaa, "cd %s/network/spoolin; ls", bbs_home_directory);
846 ls = popen(aaa, "r");
848 syslog(LOG_ERR, "could not open dir cmd: %s", strerror(errno));
852 SKIP: ptr = fgets(sfilename, sizeof sfilename, ls);
856 "Trying %s", sfilename);
858 sfilename[strlen(sfilename) - 1] = 0;
859 if (!strcmp(sfilename, ".")) goto SKIP;
860 if (!strcmp(sfilename, "..")) goto SKIP;
861 if (!strcmp(sfilename, "CVS")) goto SKIP;
864 } while (ptr != NULL);
865 PROCESS_IT: pclose(ls);
868 sprintf(pfilename, "%s/network/spoolin/%s", bbs_home_directory, sfilename);
869 syslog(LOG_NOTICE, "processing <%s>", pfilename);
871 fp = fopen(pfilename, "rb");
873 syslog(LOG_ERR, "cannot open %s: %s", pfilename, strerror(errno));
874 fp = fopen("/dev/null", "rb");
876 NXMSG: /* Seek to the beginning of the next message */
879 } while ((a != 255) && (a >= 0));
883 /* This crates the temporary file. */
884 message = fopen(tname, "wb");
885 if (message == NULL) {
886 syslog(LOG_ERR, "error creating %s: %s",
887 tname, strerror(errno));
890 putc(255, message); /* 0xFF (start-of-message) */
892 putc(a, message); /* type */
894 putc(a, message); /* mode */
896 FieldID = getc(fp); /* Header field ID */
897 putc(FieldID, message);
902 } while ((FieldID != 'M') && (a >= 0));
903 /* M is always last */
905 msglen = ftell(message);
908 /* process the individual mesage */
914 msgfind(tname, &minfo);
915 strncpy(recentmsg.RMnodename, minfo.N, 9);
916 recentmsg.RMnodename[9] = 0;
917 recentmsg.RMnum = minfo.I;
918 syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
919 minfo.I, minfo.A, minfo.O, minfo.N);
920 if (strlen(minfo.R) > 0) {
921 syslog(LOG_NOTICE, " to <%s>", minfo.R);
922 if (strlen(minfo.D) > 0) {
923 syslog(LOG_NOTICE, " @ <%s>",
927 if (!strcasecmp(minfo.D, FQDN))
928 strcpy(minfo.D, NODENAME);
930 /* this routine updates our info on the system that sent the message */
931 stemp = get_sys_ptr(minfo.N);
932 if ((stemp == NULL) && (get_sys_ptr(minfo.nexthop) != NULL)) {
933 /* add non-neighbor system to map */
934 syslog(LOG_NOTICE, "Adding non-neighbor system <%s> to map",
936 stemp = (struct syslist *) malloc((long) sizeof(struct syslist));
939 strcpy(slist->s_name, minfo.N);
940 strcpy(slist->s_type, "use");
941 strcpy(slist->s_nexthop, minfo.nexthop);
942 time(&slist->s_lastcontact);
943 } else if ((stemp == NULL) && (!strcasecmp(minfo.N, minfo.nexthop))) {
944 /* add neighbor system to map */
945 syslog(LOG_NOTICE, "Adding neighbor system <%s> to map",
947 sprintf(aaa, "%s/network/systems/%s", bbs_home_directory, minfo.N);
948 testfp = fopen(aaa, "r");
949 if (testfp != NULL) {
951 stemp = (struct syslist *)
952 malloc((long) sizeof(struct syslist));
955 strcpy(slist->s_name, minfo.N);
956 strcpy(slist->s_type, "bin");
957 strcpy(slist->s_nexthop, "Mail");
958 time(&slist->s_lastcontact);
961 /* now update last contact and long node name if we can */
963 time(&stemp->s_lastcontact);
964 if (strlen(minfo.H) > 0)
965 strcpy(stemp->s_humannode, minfo.H);
966 if (strlen(minfo.B) > 0)
967 strcpy(stemp->s_phonenum, minfo.B);
968 if (strlen(minfo.G) > 0)
969 strcpy(stemp->s_gdom, minfo.G);
972 /* Check the use table; reject message if it's been here before */
973 if (already_received(use_table, &minfo)) {
974 syslog(LOG_NOTICE, "rejected duplicate message");
975 fprintf(duplist, "#%ld fm <%s> in <%s> @ <%s>\n",
976 minfo.I, minfo.A, minfo.O, minfo.N);
980 /* route the message if necessary */
981 else if ((strcasecmp(minfo.D, NODENAME)) && (minfo.D[0] != 0)) {
982 a = get_sysinfo_type(minfo.D);
983 syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
985 if (a == MES_INTERNET) {
987 syslog(LOG_NOTICE, "netmailer %s", tname);
989 execlp("./netmailer", "netmailer",
991 syslog(LOG_ERR, "error running netmailer: %s",
995 while (wait(&b) != (-1));
996 } else if (a == MES_BINARY) {
997 ship_to(tname, minfo.D);
999 /* message falls into the bit bucket? */
1003 /* check to see if it's a file transfer */
1004 else if (!strncasecmp(minfo.S, "FILE", 4)) {
1005 proc_file_transfer(tname);
1008 /* otherwise process it as a normal message */
1011 if (!strcasecmp(minfo.R, "postmaster")) {
1012 strcpy(minfo.R, "");
1013 strcpy(minfo.C, "Aide");
1015 if (strlen(minfo.R) > 0) {
1016 sprintf(buf, "GOTO _MAIL_");
1018 if (is_banned(minfo.A, minfo.C, minfo.N)) {
1019 sprintf(buf, "GOTO %s", FILTERROOM);
1021 if (strlen(minfo.C) > 0) {
1022 sprintf(buf, "GOTO %s", minfo.C);
1024 sprintf(buf, "GOTO %s", minfo.O);
1029 if (buf[0] != '2') {
1030 syslog(LOG_ERR, "%s", buf);
1031 sprintf(buf, "GOTO _BITBUCKET_");
1035 /* Open the temporary file containing the message */
1036 message = fopen(tname, "rb");
1037 if (message == NULL) {
1038 syslog(LOG_ERR, "cannot open %s: %s",
1039 tname, strerror(errno));
1043 /* Transmit the message to the server */
1044 sprintf(buf, "ENT3 1|%s|%ld", minfo.R, msglen);
1047 if (!strncmp(buf, "570", 3)) {
1048 /* no such user, do a bounce */
1051 if (buf[0] == '7') {
1052 /* Always use the server's idea of the message length,
1053 * even though they should both be identical */
1054 msglen = atol(&buf[4]);
1055 while (msglen > 0L) {
1056 bloklen = ((msglen >= 255L) ? 255 : ((int) msglen));
1057 if (fread(buf, bloklen, 1, message) < 1) {
1059 "error trying to read %d bytes: %s",
1060 bloklen, strerror(errno));
1062 serv_write(buf, bloklen);
1063 msglen = msglen - (long) bloklen;
1068 syslog(LOG_ERR, "%s", buf);
1080 } while (ptr != NULL);
1083 purge_use_table(use_table);
1084 gdbm_close(use_table);
1088 * If dups were rejected, post a message saying so
1090 if (ftell(duplist)!=0L) {
1091 fp = fopen("./network/spoolin/ctdl_rejects", "ab");
1093 fprintf(fp, "%cA%c", 255, 1);
1094 fprintf(fp, "T%ld%c", time(NULL), 0);
1095 fprintf(fp, "ACitadel%c", 0);
1096 fprintf(fp, "OAide%cM", 0);
1097 fprintf(fp, "The following duplicate messages"
1098 " were rejected:\n \n");
1100 while (fgets(buf, sizeof(buf), duplist) != NULL) {
1101 buf[strlen(buf)-1] = 0;
1102 fprintf(fp, " %s\n", buf);
1104 fprintf(fp, "%c", 0);
1114 /* Checks to see whether its ok to send */
1115 /* Returns 1 for ok, send message */
1116 /* Returns 0 if message already there */
1117 int checkpath(char *path, char *sys)
1125 syslog(LOG_NOTICE, "checkpath <%s> <%s> ... ", path, sys);
1127 for (a = 0; a < strlen(path); ++a) {
1128 if (!strncmp(&path[a], sys2, strlen(sys2)))
1135 * implement split horizon algorithm
1137 int ismsgok(long int mpos, FILE * mmfp, char *sysname)
1140 int ok = 0; /* fail safe - no path, don't send it */
1143 fseek(mmfp, mpos, 0);
1144 if (getc(mmfp) != 255)
1149 while (a = getc(mmfp), ((a != 'M') && (a != 0))) {
1150 fpgetfield(mmfp, fbuf);
1152 ok = checkpath(fbuf, sysname);
1156 syslog(LOG_NOTICE, "%s", ((ok) ? "SEND" : "(no)"));
1163 /* spool list of messages to a file */
1164 /* returns # of msgs spooled */
1165 int spool_out(struct msglist *cmlist, FILE * destfp, char *sysname)
1167 struct msglist *cmptr;
1171 int msgs_spooled = 0;
1178 strcpy(curr_rm, "");
1180 /* for each message in the list... */
1181 for (cmptr = cmlist; cmptr != NULL; cmptr = cmptr->next) {
1183 /* make sure we're in the correct room... */
1184 if (strcasecmp(curr_rm, cmptr->m_rmname)) {
1185 sprintf(buf, "GOTO %s", cmptr->m_rmname);
1188 if (buf[0] == '2') {
1189 strcpy(curr_rm, cmptr->m_rmname);
1191 syslog(LOG_ERR, "%s", buf);
1194 /* download the message from the server... */
1196 sprintf(buf, "MSG3 %ld", cmptr->m_num);
1199 if (buf[0] == '6') { /* read the msg */
1200 msg_len = atol(&buf[4]);
1201 while (msg_len > 0L) {
1202 blok_len = ((msg_len >= 256L) ? 256 : (int) msg_len);
1203 serv_read(buf, blok_len);
1204 fwrite(buf, blok_len, 1, mmfp);
1205 msg_len = msg_len - (long) blok_len;
1207 } else { /* or print the err */
1208 syslog(LOG_ERR, "%s", buf);
1213 if (ismsgok(0L, mmfp, sysname)) {
1217 fread(fbuf, 3, 1, mmfp);
1218 fwrite(fbuf, 3, 1, destfp);
1219 while (a = getc(mmfp), ((a != 0) && (a != 'M'))) {
1222 fpgetfield(mmfp, fbuf);
1224 fprintf(destfp, "%s!", NODENAME);
1226 fwrite(fbuf, strlen(fbuf) + 1, 1, destfp);
1229 fprintf(destfp, "C%s%c",
1230 cmptr->m_rmname, 0);
1241 return (msgs_spooled);
1244 void outprocess(char *sysname)
1245 { /* send new room messages to sysname */
1248 char shiptocmd[128];
1252 struct msglist *cmlist = NULL;
1253 struct rmlist *crmlist = NULL;
1254 struct rmlist *rmptr, *rmptr2;
1255 struct msglist *cmptr, *cmptr2;
1256 FILE *sysflfp, *tempflfp;
1260 sprintf(tempflnm, tmpnam(NULL));
1261 tempflfp = fopen(tempflnm, "w");
1262 if (tempflfp == NULL)
1267 * Read system file for node in question and put together room list
1269 sprintf(sysflnm, "%s/network/systems/%s", bbs_home_directory, sysname);
1270 sysflfp = fopen(sysflnm, "r");
1271 if (sysflfp == NULL)
1273 fgets(shiptocmd, 128, sysflfp);
1274 shiptocmd[strlen(shiptocmd) - 1] = 0;
1275 while (!feof(sysflfp)) {
1276 if (fgets(srmname, 32, sysflfp) == NULL)
1278 srmname[strlen(srmname) - 1] = 0;
1279 fgets(lbuf, 32, sysflfp);
1280 rmptr = (struct rmlist *) malloc(sizeof(struct rmlist));
1282 strcpy(rmptr->rm_name, srmname);
1283 strip_trailing_whitespace(rmptr->rm_name);
1284 rmptr->rm_lastsent = atol(lbuf);
1285 if (crmlist == NULL)
1287 else if (!strcasecmp(rmptr->rm_name, "control")) {
1288 /* control has to be first in room list */
1289 rmptr->next = crmlist;
1293 while (rmptr2->next != NULL)
1294 rmptr2 = rmptr2->next;
1295 rmptr2->next = rmptr;
1301 * Assemble list of messages to be spooled
1303 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next) {
1305 sprintf(buf, "GOTO %s", rmptr->rm_name);
1308 if (buf[0] != '2') {
1309 syslog(LOG_ERR, "%s", buf);
1311 sprintf(buf, "MSGS GT|%ld", rmptr->rm_lastsent);
1315 while (serv_gets(buf), strcmp(buf, "000")) {
1316 thismsg = atol(buf);
1317 if (thismsg > (rmptr->rm_lastsent)) {
1318 rmptr->rm_lastsent = thismsg;
1320 cmptr = (struct msglist *)
1321 malloc(sizeof(struct msglist));
1323 cmptr->m_num = thismsg;
1324 strcpy(cmptr->m_rmname, rmptr->rm_name);
1330 while (cmptr2->next != NULL)
1331 cmptr2 = cmptr2->next;
1332 cmptr2->next = cmptr;
1335 } else { /* print error from "msgs all" */
1336 syslog(LOG_ERR, "%s", buf);
1342 cmptr2 = cmlist; /* this loop counts the messages */
1343 while (cmptr2 != NULL) {
1345 cmptr2 = cmptr2->next;
1347 syslog(LOG_NOTICE, "%d messages to be spooled to %s",
1348 outgoing_msgs, sysname);
1351 * Spool out the messages, but only if there are any.
1353 if (outgoing_msgs != 0)
1354 outgoing_msgs = spool_out(cmlist, tempflfp, sysname);
1355 syslog(LOG_NOTICE, "%d messages actually spooled",
1359 * Deallocate list of spooled messages.
1361 while (cmlist != NULL) {
1362 cmptr = cmlist->next;
1368 * Rewrite system file and deallocate room list.
1370 syslog(LOG_NOTICE, "Spooling...");
1371 sysflfp = fopen(sysflnm, "w");
1372 fprintf(sysflfp, "%s\n", shiptocmd);
1373 for (rmptr = crmlist; rmptr != NULL; rmptr = rmptr->next)
1374 fprintf(sysflfp, "%s\n%ld\n", rmptr->rm_name, rmptr->rm_lastsent);
1376 while (crmlist != NULL) {
1377 rmptr = crmlist->next;
1383 * Close temporary file, ship it out, and return
1386 if (outgoing_msgs != 0)
1387 ship_to(tempflnm, sysname);
1393 * Connect netproc to the Citadel server running on this computer.
1395 void np_attach_to_server(void)
1400 {"netproc", "localhost", NULL, NULL};
1402 syslog(LOG_NOTICE, "Attaching to server...");
1403 sprintf(portname, "%d", config.c_port_number);
1405 attach_to_server(3, args);
1407 syslog(LOG_NOTICE, "%s", &buf[4]);
1408 sprintf(buf, "IPGM %d", config.c_ipgm_secret);
1411 syslog(LOG_NOTICE, "%s", &buf[4]);
1412 if (buf[0] != '2') {
1422 int main(int argc, char **argv)
1427 int import_only = 0; /* if set to 1, don't export anything */
1429 openlog("netproc", LOG_PID, LOG_USER);
1430 strcpy(bbs_home_directory, BBSDIR);
1433 * Change directories if specified
1435 for (a = 1; a < argc; ++a) {
1436 if (!strncmp(argv[a], "-h", 2)) {
1437 strcpy(bbs_home_directory, argv[a]);
1438 strcpy(bbs_home_directory, &bbs_home_directory[2]);
1440 } else if (!strcmp(argv[a], "-i")) {
1443 fprintf(stderr, "netproc: usage: ");
1444 fprintf(stderr, "netproc [-hHomeDir] [-i]\n");
1450 syslog(LOG_DEBUG, "Calling get_config()");
1455 syslog(LOG_DEBUG, "Creating lock file");
1457 if (set_lockfile() != 0) {
1458 syslog(LOG_NOTICE, "lock file exists: already running");
1461 signal(SIGINT, cleanup);
1462 signal(SIGQUIT, cleanup);
1463 signal(SIGHUP, cleanup);
1464 signal(SIGTERM, cleanup);
1466 syslog(LOG_NOTICE, "started. pid=%d", getpid());
1468 np_attach_to_server();
1471 if (load_syslist() != 0)
1472 syslog(LOG_ERR, "cannot load sysinfo");
1473 setup_special_nodes();
1475 inprocess(); /* first collect incoming stuff */
1477 if (import_only != 1) {
1478 allfp = (FILE *) popen("cd ./network/systems; ls", "r");
1479 if (allfp != NULL) {
1480 while (fgets(allst, 32, allfp) != NULL) {
1481 allst[strlen(allst) - 1] = 0;
1486 /* import again in case anything new was generated */
1490 syslog(LOG_NOTICE, "processing ended.");