* Various changes to begin work on support for MIME messages
[citadel.git] / citadel / netproc.c
1 /*
2  * Citadel/UX Intelligent Network Processor for IGnet/Open networks v3.6
3  * Designed and written by Art Cancro @ Uncensored Communications Group
4  * See copyright.txt for copyright information
5  * $Id$
6  */
7
8 /* How long it takes for an old node to drop off the network map */
9 #define EXPIRY_TIME     (2592000L)
10
11 /* Where do we keep our lock file? */
12 #define LOCKFILE        "/var/lock/LCK.netproc"
13
14 /* Path to the 'uudecode' utility (needed for network file transfers) */
15 #define UUDECODE        "/usr/bin/uudecode"
16
17 /* Uncomment the DEBUG def to see noisy traces */
18 /* #define DEBUG 1 */
19
20
21 #include "sysdep.h"
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <sys/types.h>
25 #include <sys/wait.h>
26 #include <string.h>
27 #include <fcntl.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <time.h>
31 #include <signal.h>
32 #include <errno.h>
33 #include <syslog.h>
34 #include "citadel.h"
35 #include "tools.h"
36
37 /* A list of users you wish to filter out of incoming traffic can be kept
38  * in ./network/filterlist -- messages from these users will be automatically
39  * moved to FILTERROOM.  Normally this will be the same as TWITROOM (the
40  * room problem user messages are moved to) but you can override this by
41  * specifying a different room name here.
42  */
43 #ifndef FILTERROOM
44 #define FILTERROOM TWITROOM
45 #endif
46
47 struct msglist {
48         struct msglist *next;
49         long m_num;
50         char m_rmname[ROOMNAMELEN];
51         };
52
53 struct rmlist {
54         struct rmlist *next;
55         char rm_name[ROOMNAMELEN];
56         long rm_lastsent;
57         };
58
59 struct filterlist {
60         struct filterlist *next;
61         char f_person[64];
62         char f_room[64];
63         char f_system[64];
64         };
65
66 struct syslist {
67         struct syslist *next;
68         char s_name[16];
69         char s_type[4];
70         char s_nexthop[128];
71         time_t s_lastcontact;
72         char s_humannode[64];
73         char s_phonenum[32];
74         char s_gdom[64];
75         };
76
77
78 void attach_to_server(int argc, char **argv);
79 void serv_read(char *buf, int bytes);
80 void serv_write(char *buf, int nbytes);
81 void get_config(void);
82
83 struct filterlist *filter = NULL;
84 struct syslist *slist = NULL;
85
86 struct config config;
87 extern char bbs_home_directory[];
88 extern int home_specified;
89
90
91 #ifndef HAVE_STRERROR
92 /*
93  * replacement strerror() for systems that don't have it
94  */
95 char *strerror(int e)
96 {
97         static char buf[32];
98
99         sprintf(buf,"errno = %d",e);
100         return(buf);
101         }
102 #endif
103
104
105 void strip_trailing_whitespace(char *buf)
106 {
107         while(isspace(buf[strlen(buf)-1]))
108                 buf[strlen(buf)-1]=0;
109         }
110
111
112 /*
113  * we also load the network/mail.sysinfo table into memory, make changes
114  * as we learn more about the network from incoming messages, and write
115  * the table back to disk when we're done.
116  */
117 int load_syslist(void) {
118         FILE *fp;
119         struct syslist *stemp;
120         char insys = 0;
121         char buf[128];
122
123         fp=fopen("network/mail.sysinfo","rb");
124         if (fp==NULL) return(1);
125
126         while(1) {
127                 if (fgets(buf,128,fp)==NULL) {
128                         fclose(fp);
129                         return(0);
130                         }
131                 buf[strlen(buf)-1] = 0;
132                 while (isspace(buf[0])) strcpy(buf,&buf[1]);
133                 if (buf[0]=='#') buf[0]=0;
134                 if ( (insys==0) && (strlen(buf)!=0) ) {
135                         insys = 1;
136                         stemp =(struct syslist *)malloc(sizeof(struct syslist));
137                         stemp->next = slist;
138                         slist = stemp;
139                         strcpy(slist->s_name,buf);
140                         strcpy(slist->s_type,"bin");
141                         strcpy(slist->s_nexthop,"Mail");
142                         slist->s_lastcontact = 0L;
143                         strcpy(slist->s_humannode,"");
144                         strcpy(slist->s_phonenum,"");
145                         strcpy(slist->s_gdom,"");
146                         }
147                 else if ( (insys==1) && (strlen(buf)==0) ) {
148                         insys = 0;
149                         }
150                 else if ( (insys==1) && (!strncmp(buf,"bin",3)) ) {
151                         strcpy(slist->s_type,"bin");
152                         strcpy(slist->s_nexthop,&buf[4]);
153                         }
154                 else if ( (insys==1) && (!strncmp(buf,"use",3)) ) {
155                         strcpy(slist->s_type,"use");
156                         strcpy(slist->s_nexthop,&buf[4]);
157                         }
158                 else if ( (insys==1) && (!strncmp(buf,"uum",3)) ) {
159                         strcpy(slist->s_type,"uum");
160                         strcpy(slist->s_nexthop,&buf[4]);
161                         }
162                 else if ( (insys==1) && (!strncmp(buf,"lastcontact",11)) ) {
163                         sscanf(&buf[12],"%ld",&slist->s_lastcontact);
164                         }
165                 else if ( (insys==1) && (!strncmp(buf,"humannode",9)) ) {
166                         strcpy(slist->s_humannode,&buf[10]);
167                         }
168                 else if ( (insys==1) && (!strncmp(buf,"phonenum",8)) ) {
169                         strcpy(slist->s_phonenum,&buf[9]);
170                         }
171                 else if ( (insys==1) && (!strncmp(buf,"gdom",4)) ) {
172                         strcpy(slist->s_gdom,&buf[5]);
173                         }
174                 }
175         }
176
177 /* now we have to set up two "special" nodes on the list: one
178  * for the local node, and one for an Internet gateway
179  */
180 void setup_special_nodes(void) {
181         struct syslist *stemp,*slocal;
182
183         slocal = NULL;
184         for (stemp=slist; stemp!=NULL; stemp=stemp->next) {
185                 if (!strcasecmp(stemp->s_name,config.c_nodename)) slocal=stemp;
186                 }
187         if (slocal==NULL) {
188                 slocal =(struct syslist *)malloc(sizeof(struct syslist));
189                 slocal->next = slist;
190                 slist = slocal;
191                 }
192         strcpy(slocal->s_name,config.c_nodename);
193         strcpy(slocal->s_type,"bin");
194         strcpy(slocal->s_nexthop,"Mail");
195         time(&slocal->s_lastcontact);
196         strcpy(slocal->s_humannode,config.c_humannode);
197         strcpy(slocal->s_phonenum,config.c_phonenum);
198
199         slocal = NULL;
200         for (stemp=slist; stemp!=NULL; stemp=stemp->next) {
201                 if (!strcasecmp(stemp->s_name,"internet")) slocal=stemp;
202                 }
203         if (slocal==NULL) {
204                 slocal =(struct syslist *)malloc(sizeof(struct syslist));
205                 slocal->next = slist;
206                 slist = slocal;
207                 }
208         strcpy(slocal->s_name,"internet");
209         strcpy(slocal->s_type,"uum");
210         strcpy(slocal->s_nexthop,"%s");
211         time(&slocal->s_lastcontact);
212         strcpy(slocal->s_humannode,"Internet Gateway");
213         strcpy(slocal->s_phonenum,"");
214         strcpy(slocal->s_gdom,"");
215
216         }
217
218 /*
219  * here's the routine to write the table back to disk.
220  */
221 void rewrite_syslist(void) {
222         struct syslist *stemp;
223         FILE *newfp;
224         time_t now;
225
226         time(&now);
227         newfp=fopen("network/mail.sysinfo","w");
228         for (stemp=slist; stemp!=NULL; stemp=stemp->next) {
229                 if (!strcasecmp(stemp->s_name,config.c_nodename)) {
230                         time(&stemp->s_lastcontact);
231                         strcpy(stemp->s_type,"bin");
232                         strcpy(stemp->s_humannode,config.c_humannode);
233                         strcpy(stemp->s_phonenum,config.c_phonenum);
234                         }
235             /* remove systems we haven't heard from in a while */
236             if ( (stemp->s_lastcontact == 0L) 
237                  || (now - stemp->s_lastcontact < EXPIRY_TIME) ) {
238                 fprintf(newfp,"%s\n%s %s\n",
239                         stemp->s_name,stemp->s_type,stemp->s_nexthop);
240                 if (strlen(stemp->s_phonenum) > 0) 
241                         fprintf(newfp,"phonenum %s\n",stemp->s_phonenum);
242                 if (strlen(stemp->s_gdom) > 0) 
243                         fprintf(newfp,"gdom %s\n",stemp->s_gdom);
244                 if (strlen(stemp->s_humannode) > 0) 
245                         fprintf(newfp,"humannode %s\n",stemp->s_humannode);
246                 if (stemp->s_lastcontact > 0L)
247                         fprintf(newfp,"lastcontact %ld %s",
248                                 stemp->s_lastcontact,
249                                 asctime(localtime(&stemp->s_lastcontact)));
250                 fprintf(newfp,"\n");
251                 }
252             }
253         fclose(newfp);
254         /* now free the list */
255         while (slist!=NULL) {
256                 stemp = slist;
257                 slist = slist->next;
258                 free(stemp);
259                 }
260         }
261
262
263 /* call this function with the node name of a system and it returns a pointer
264  * to its syslist structure.
265  */
266 struct syslist *get_sys_ptr(char *sysname)
267 {
268         static char sysnambuf[16];
269         static struct syslist *sysptrbuf = NULL;
270         struct syslist *stemp;
271
272         if ( (!strcmp(sysname,sysnambuf))
273                 && (sysptrbuf!=NULL) )  return(sysptrbuf);
274
275         strcpy(sysnambuf,sysname);
276         for (stemp=slist; stemp!=NULL; stemp=stemp->next) {
277                 if (!strcmp(sysname,stemp->s_name)) {
278                         sysptrbuf = stemp;
279                         return(stemp);
280                         }
281                 }
282         sysptrbuf = NULL;
283         return(NULL);
284         }
285
286
287 /*
288  * make sure only one copy of netproc runs at a time, using lock files
289  */
290 int set_lockfile(void) {
291         FILE *lfp;
292         int onppid;
293
294         if ((lfp = fopen(LOCKFILE,"r")) != NULL) {
295                 fscanf(lfp,"%d",&onppid);
296                 fclose(lfp);
297                 if (!kill(onppid, 0) || errno == EPERM) return 1;
298                 }
299
300         lfp=fopen(LOCKFILE,"w");
301         fprintf(lfp,"%d\n",getpid());
302         fclose(lfp);
303         return(0);
304         }
305
306 void remove_lockfile(void) {
307         unlink(LOCKFILE);
308         }
309
310 /*
311  * Why both cleanup() and nq_cleanup() ?  Notice the alarm() call in
312  * cleanup() .  If for some reason netproc hangs waiting for the server
313  * to clean up, the alarm clock goes off and the program exits anyway.
314  * The cleanup() routine makes a check to ensure it's not reentering, in
315  * case the ipc module looped it somehow.
316  */
317 void nq_cleanup(int e)
318 {
319         remove_lockfile();
320         closelog();
321         exit(e);
322         }
323
324 void cleanup(int e)
325 {
326         static int nested = 0;
327
328         alarm(30);
329         signal(SIGALRM,nq_cleanup);
330         if (nested++ < 1) serv_puts("QUIT");
331         nq_cleanup(e);
332         }
333
334 /*
335  * This is implemented as a function rather than as a macro because the
336  * client-side IPC modules expect logoff() to be defined.  They call logoff()
337  * when a problem connecting or staying connected to the server occurs.
338  */
339 void logoff(int e)
340 {
341         cleanup(e);
342         }
343
344 /*
345  * If there is a kill file in place, this function will process it.
346  */
347 void load_filterlist(void) {
348         FILE *fp;
349         struct filterlist *fbuf;
350         char sbuf[256];
351         int a,p;
352         fp=fopen("./network/filterlist","r");
353         if (fp==NULL) return;
354         while (fgets(sbuf,256,fp)!=NULL) {
355                 if (sbuf[0]!='#') {
356                         sbuf[strlen(sbuf)-1]=0;
357                         fbuf=(struct filterlist *)
358                                 malloc((long)sizeof(struct filterlist));
359                         fbuf->next = filter;
360                         filter = fbuf;
361                         strcpy(fbuf->f_person,"*");
362                         strcpy(fbuf->f_room,"*");
363                         strcpy(fbuf->f_system,"*");
364                         p = (-1);
365                         for (a=strlen(sbuf); a>=0; --a) if (sbuf[a]==',') p=a;
366                         if (p>=0) {
367                                 sbuf[p] = 0;
368                                 strcpy(fbuf->f_person,sbuf);
369                                 strcpy(sbuf,&sbuf[p+1]);
370                                 }
371                         for (a=strlen(sbuf); a>=0; --a) if (sbuf[a]==',') p=a;
372                         if (p>=0) {
373                                 sbuf[p] = 0;
374                                 strcpy(fbuf->f_room,sbuf);
375                                 strcpy(sbuf,&sbuf[p+1]);
376                                 }
377                         strcpy(fbuf->f_system,sbuf);
378                         }
379                 }
380         fclose(fp);
381         }
382
383 /* returns 1 if user/message/room combination is in the kill file */
384 int is_banned(char *k_person, char *k_room, char *k_system)
385 {
386         struct filterlist *fptr;
387
388         for (fptr=filter; fptr!=NULL; fptr=fptr->next) if (
389          ((!strcasecmp(fptr->f_person,k_person))||(!strcmp(fptr->f_person,"*")))
390         &&
391          ((!strcasecmp(fptr->f_room,k_room))||(!strcmp(fptr->f_room,"*")))
392         &&
393          ((!strcasecmp(fptr->f_system,k_system))||(!strcmp(fptr->f_system,"*")))
394         ) return(1);
395
396         return(0);
397         }
398
399 int get_sysinfo_type(char *name)        /* determine routing from sysinfo file */
400              {
401         struct syslist *stemp;
402 GETSN:  for (stemp=slist; stemp!=NULL; stemp=stemp->next) {
403             if (!strcasecmp(stemp->s_name,name)) {
404                 if (!strcasecmp(stemp->s_type,"use")) {
405                         strcpy(name,stemp->s_nexthop);
406                         goto GETSN;
407                         }
408                 if (!strcasecmp(stemp->s_type,"bin")) {
409                         return(M_BINARY);
410                         }
411                 if (!strcasecmp(stemp->s_type,"uum")) {
412                         return(M_INTERNET);
413                         }
414                 }
415             }
416         syslog(LOG_ERR, "cannot find system '%s' in mail.sysinfo", name);
417         return(-1);
418         }
419
420
421 void fpgetfield(FILE *fp, char *string)
422 {
423         int a,b;
424
425         strcpy(string,"");
426         a=0;
427         do {
428                 b=getc(fp);
429                 if (b<1) {
430                         string[a]=0;
431                         return;
432                         }
433                 string[a]=b;
434                 ++a;
435                 } while (b!=0);
436         }
437
438
439
440 /*
441  * Load all of the fields of a message, except the actual text, into a
442  * table in memory (so we know how to process the message).
443  */
444 void msgfind(char *msgfile, struct minfo *buffer)
445 {
446         int b,e,mtype,aflag;
447         char bbb[1024];
448         char userid[1024];
449         FILE *fp;
450                 
451         strcpy(userid,"");
452         fp=fopen(msgfile,"rb");
453         if (fp==NULL) {
454                 syslog(LOG_ERR, "can't open message file: %s",strerror(errno));
455                 return;
456                 }
457         e=getc(fp);
458         if (e!=255) {
459                 syslog(LOG_ERR, "incorrect message format");
460                 goto END;
461                 }
462         mtype=getc(fp); aflag=getc(fp);
463         buffer->I=0L;
464         buffer->R[0]=0;
465         buffer->E[0]=0;
466         buffer->H[0]=0;
467         buffer->S[0]=0;
468         buffer->B[0]=0;
469         buffer->G[0]=0;
470
471 BONFGM: b=getc(fp); if (b<0) goto END;
472         if (b=='M') goto END;
473         fpgetfield(fp,bbb);
474         while ((bbb[0]==' ')&&(strlen(bbb)>1)) strcpy(bbb,&bbb[1]);
475         if (b=='A') {
476                 strcpy(buffer->A,bbb);
477                 if (strlen(userid)==0) {
478                         strcpy(userid,bbb);
479                         for (e=0; e<strlen(userid); ++e)
480                                 if (userid[e]==' ') userid[e]='_';
481                         }
482                 }
483         if (b=='O') strcpy(buffer->O,bbb);
484         if (b=='C') strcpy(buffer->C,bbb);
485         if (b=='N') strcpy(buffer->N,bbb);
486         if (b=='S') strcpy(buffer->S,bbb);
487         if (b=='P') {
488                 /* extract the user id from the path */
489                 for (e=0; e<strlen(bbb); ++e)
490                         if (bbb[e]=='!') strcpy(userid,&bbb[e+1]);
491
492                 /* now find the next hop */
493                 for (e=0; e<strlen(bbb); ++e) if (bbb[e]=='!') bbb[e]=0;
494                 strcpy(buffer->nexthop,bbb);
495                 }
496         if (b=='R') {
497                 for (e=0; e<strlen(bbb); ++e) if (bbb[e]=='_') bbb[e]=' ';
498                 strcpy(buffer->R,bbb);
499                 }
500         if (b=='D') strcpy(buffer->D,bbb);
501         if (b=='T') buffer->T=atol(bbb);
502         if (b=='I') buffer->I=atol(bbb);
503         if (b=='H') strcpy(buffer->H,bbb);
504         if (b=='B') strcpy(buffer->B,bbb);
505         if (b=='G') strcpy(buffer->G,bbb);
506         if (b=='E') strcpy(buffer->E,bbb);
507         goto BONFGM;
508
509 END:    if (buffer->I==0L) buffer->I=buffer->T;
510         fclose(fp);
511         }
512
513 void ship_to(char *filenm, char *sysnm) /* send spool file filenm to system sysnm */
514              
515              {
516         char sysflnm[100];
517         char commbuf1[100];
518         char commbuf2[100];
519         FILE *sysflfd;
520
521 #ifdef DEBUG
522         syslog(LOG_NOTICE, "shipping %s to %s", filenm, sysnm);
523 #endif
524         sprintf(sysflnm,"./network/systems/%s",sysnm);
525         sysflfd=fopen(sysflnm,"r");
526         if (sysflfd==NULL) syslog(LOG_ERR, "cannot open %s", sysflnm);
527         fgets(commbuf1,99,sysflfd);
528         commbuf1[strlen(commbuf1)-1] = 0;
529         fclose(sysflfd);
530         sprintf(commbuf2,commbuf1,filenm);
531         system(commbuf2);
532         }
533
534 /*
535  * proc_file_transfer()  -  handle a simple file transfer packet
536  *
537  */
538 void proc_file_transfer(char *tname)
539 {       /* name of temp file containing the whole message */
540         char buf[256];
541         char dest_room[ROOMNAMELEN];
542         char subdir_name[256];
543         FILE *tfp,*uud;
544         int a;
545
546         syslog(LOG_NOTICE, "processing network file transfer...");
547
548         tfp=fopen(tname,"rb");
549         if (tfp==NULL) syslog(LOG_ERR, "cannot open %s", tname);
550         getc(tfp); getc(tfp); getc(tfp);
551         do {
552                 a=getc(tfp);
553                 if (a!='M') {
554                         fpgetfield(tfp,buf);
555                         if (a=='O') {
556                                 strcpy(dest_room, buf);
557                                 }
558                         }
559                 } while ((a!='M')&&(a>=0));
560         if (a!='M') {
561                 fclose(tfp);
562                 syslog(LOG_ERR, "no message text for file transfer");
563                 return;
564                 }
565
566         strcpy(subdir_name, "---xxx---");
567         sprintf(buf, "GOTO %s", dest_room);
568         serv_puts(buf);
569         serv_gets(buf);
570         if (buf[0]=='2') {
571                 extract(subdir_name, &buf[4], 2);
572                 if (strlen(subdir_name) == 0) strcpy(subdir_name, "--xxx--");
573                 }
574
575         /* Change to the room's directory; if that fails, change to the
576          * bitbucket directory.  Then run uudecode.
577          */
578         sprintf(buf,"(cd %s/files/%s || cd %s/files/%s ) ; exec %s",
579                 bbs_home_directory, subdir_name,
580                 bbs_home_directory, config.c_bucket_dir,
581                 UUDECODE);
582
583         uud=(FILE *)popen(buf,"w");
584         if (uud==NULL) {
585                 syslog(LOG_ERR, "cannot open uudecode pipe");
586                 fclose(tfp);
587                 return;
588                 }
589
590         fgets(buf,128,tfp);
591         buf[strlen(buf)-1] = 0;
592         for (a=0; a<strlen(buf); ++a) if (buf[a]=='/') buf[a]='_';
593         fprintf(uud,"%s\n",buf);
594         printf("netproc: %s\n",buf);
595         while(a=getc(tfp), a>0) putc(a,uud);
596         fclose(tfp);
597         pclose(uud);
598         return;
599         }
600
601
602 /* send a bounce message */
603 void bounce(struct minfo *bminfo)
604 {
605
606         FILE *bounce;
607         char bfilename[64];
608         static int bseq = 1;
609         time_t now;
610
611         sprintf(bfilename,"./network/spoolin/bounce.%d.%d",getpid(),bseq++);
612         bounce = fopen(bfilename,"wb");
613         time(&now);
614                 
615         fprintf(bounce,"%c%c%c",0xFF,MES_NORMAL,0);
616         fprintf(bounce,"Ppostmaster%c",0);
617         fprintf(bounce,"T%ld%c",now,0);
618         fprintf(bounce,"APostmaster%c",0);
619         fprintf(bounce,"OMail%c",0);
620         fprintf(bounce,"N%s%c",config.c_nodename,0);
621         fprintf(bounce,"H%s%c",config.c_humannode,0);
622
623         if (strlen(bminfo->E) > 0) {
624                 fprintf(bounce,"R%s%c",bminfo->E,0);
625                 }
626         else {
627                 fprintf(bounce,"R%s%c",bminfo->A,0);
628                 }
629
630         fprintf(bounce,"D%s%c",bminfo->N,0);
631         fprintf(bounce,"M%s could not deliver your mail to:\n",
632                 config.c_humannode);
633         fprintf(bounce," \n %s\n \n",bminfo->R);
634         fprintf(bounce," because there is no such user on this system.\n");
635         fprintf(bounce," (Unsent message does *not* follow.  ");
636         fprintf(bounce,"Help to conserve bandwidth.)\n%c",0);
637         fclose(bounce);
638         }
639
640
641
642 /*
643  * process incoming files in ./network/spoolin
644  */
645 void inprocess(void) {
646         FILE *fp,*message,*testfp,*ls;
647         static struct minfo minfo;
648         struct recentmsg recentmsg;
649         char tname[128],aaa[1024],iname[256],sfilename[256],pfilename[256];
650         int a,b;
651         int FieldID;
652         struct syslist *stemp;
653         char *ptr = NULL;
654         char buf[256];
655         long msglen;
656         int bloklen;
657
658
659         sprintf(tname,"/tmp/net.t%d",getpid()); /* temp file name */
660         sprintf(iname,"/tmp/net.i%d",getpid()); /* temp file name */
661
662         load_filterlist();
663
664         chdir(bbs_home_directory);
665         
666         /* Let the shell do the dirty work. Get all data from spoolin */
667     do {
668         sprintf(aaa,"cd %s/network/spoolin; ls",bbs_home_directory);
669         ls=popen(aaa,"r");
670         if (ls==NULL) {
671                 syslog(LOG_ERR, "could not open dir cmd: %s", strerror(errno));
672                 }
673         if (ls!=NULL) {
674                 do {
675                         ptr=fgets(sfilename,256,ls);
676                         if (ptr!=NULL) sfilename[strlen(sfilename)-1] = 0;
677                         } while( (ptr!=NULL)&&((!strcmp(sfilename,"."))
678                                  ||(!strcmp(sfilename,".."))));
679                 if (ptr!=NULL) syslog(LOG_NOTICE, "processing %s", sfilename);
680                 pclose(ls);
681                 }
682
683       if (ptr!=NULL) {
684         sprintf(pfilename,"%s/network/spoolin/%s",bbs_home_directory,sfilename);
685         syslog(LOG_NOTICE, "processing <%s>", pfilename);
686         
687         fp = fopen(pfilename, "rb");
688         if (fp == NULL) {
689             syslog(LOG_ERR, "cannot open %s: %s", pfilename, strerror(errno));
690             fp = fopen("/dev/null" ,"rb");
691             }
692                 
693 NXMSG:  /* Seek to the beginning of the next message */
694         do {
695                 a=getc(fp);
696                 } while((a!=255)&&(a>=0));
697         if (a<0) goto ENDSTR;
698
699         message = fopen(tname,"wb");    /* This crates the temporary file. */
700         if (message == NULL) {
701                 syslog(LOG_ERR, "error creating %s: %s", tname,strerror(errno));
702                 goto ENDSTR;
703                 }
704         putc(255,message);              /* 0xFF (start-of-message) */
705         a = getc(fp); putc(a, message); /* type */
706         a = getc(fp); putc(a, message); /* mode */
707         do {
708                 FieldID = getc(fp);     /* Header field ID */
709                 putc(FieldID, message);
710                 do {
711                         a = getc(fp);
712                         putc(a, message);
713                         } while (a > 0);
714                 } while ((FieldID != 'M') && (a >= 0)); /* M is always last */
715
716         msglen = ftell(message);
717         fclose(message);
718
719         /* process the individual mesage */
720         minfo.D[0]=0;
721         minfo.C[0]=0;
722         minfo.B[0]=0;
723         minfo.G[0]=0;
724         minfo.R[0]=0;
725         msgfind(tname,&minfo);
726         strncpy(recentmsg.RMnodename,minfo.N,9);
727         recentmsg.RMnodename[9]=0;
728         recentmsg.RMnum=minfo.I;
729         syslog(LOG_NOTICE, "#%ld fm <%s> in <%s> @ <%s>",
730                 minfo.I,minfo.A,minfo.O,minfo.N);
731         if (strlen(minfo.R)>0) {
732                 syslog(LOG_NOTICE, "     to <%s>",minfo.R);
733                 if (strlen(minfo.D)>0) {
734                         syslog(LOG_NOTICE, "     @ <%s>",minfo.D);
735                         }
736                 }
737         if (!strcasecmp(minfo.D,FQDN)) strcpy(minfo.D,NODENAME);
738
739         /* this routine updates our info on the system that sent the message */
740         stemp = get_sys_ptr(minfo.N);
741         if ((stemp == NULL) && (get_sys_ptr(minfo.nexthop) != NULL)) {
742                 /* add non-neighbor system to map */
743                 syslog(LOG_NOTICE, "Adding non-neighbor system <%s> to map",
744                         slist->s_name);
745                 stemp = (struct syslist *)malloc((long)sizeof(struct syslist));
746                 stemp->next = slist;
747                 slist = stemp;
748                 strcpy(slist->s_name,minfo.N);
749                 strcpy(slist->s_type,"use");
750                 strcpy(slist->s_nexthop,minfo.nexthop);
751                 time(&slist->s_lastcontact);
752                 }
753         else if ((stemp == NULL) && (!strcasecmp(minfo.N,minfo.nexthop))) {
754                 /* add neighbor system to map */
755                 syslog(LOG_NOTICE, "Adding neighbor system <%s> to map",
756                         slist->s_name);
757                 sprintf(aaa,"%s/network/systems/%s",bbs_home_directory,minfo.N);
758                 testfp=fopen(aaa,"r");
759                 if (testfp!=NULL) {
760                         fclose(testfp);
761                         stemp = (struct syslist *)
762                                 malloc((long)sizeof(struct syslist));
763                         stemp->next = slist;
764                         slist = stemp;
765                         strcpy(slist->s_name,minfo.N);
766                         strcpy(slist->s_type,"bin");
767                         strcpy(slist->s_nexthop,"Mail");
768                         time(&slist->s_lastcontact);
769                         }
770                 }
771         /* now update last contact and long node name if we can */
772         if (stemp!=NULL) {
773                 time(&stemp->s_lastcontact);
774                 if (strlen(minfo.H) > 0) strcpy(stemp->s_humannode,minfo.H);
775                 if (strlen(minfo.B) > 0) strcpy(stemp->s_phonenum,minfo.B);
776                 if (strlen(minfo.G) > 0) strcpy(stemp->s_gdom,minfo.G);
777                 }
778
779         /* route the message if necessary */
780         if ((strcasecmp(minfo.D,NODENAME))&&(minfo.D[0]!=0)) { 
781                 a = get_sysinfo_type(minfo.D);
782                 syslog(LOG_NOTICE, "routing message to system <%s>", minfo.D);
783                 fflush(stdout);
784                 if (a==M_INTERNET) {
785                         if (fork()==0) {
786                                 syslog(LOG_NOTICE, "netmailer %s", tname);
787                                 fflush(stdout);
788                                 execlp("./netmailer","netmailer",
789                                         tname,NULL);
790                                 syslog(LOG_ERR, "error running netmailer: %s",
791                                         strerror(errno));
792                                 exit(errno);
793                                 }
794                         else while (wait(&b)!=(-1));
795                         }
796                 else if (a==M_BINARY) {
797                         ship_to(tname,minfo.D);
798                         }
799                 else {
800                         /* message falls into the bit bucket? */
801                         }
802                 }
803
804         /* check to see if it's a file transfer */
805         else if (!strncasecmp(minfo.S,"FILE",4)) {
806                 proc_file_transfer(tname);
807                 }
808
809         /* otherwise process it as a normal message */
810         else {
811
812                 if (!strcasecmp(minfo.R, "postmaster")) {
813                         strcpy(minfo.R, "");
814                         strcpy(minfo.C, "Aide");
815                         }
816
817                 if (strlen(minfo.R) > 0) {
818                         sprintf(buf,"GOTO _MAIL_");
819                         }
820                 if (is_banned(minfo.A,minfo.C,minfo.N)) {
821                         sprintf(buf,"GOTO %s", FILTERROOM);
822                         }
823                 else {
824                         if (strlen(minfo.C) > 0) {
825                                 sprintf(buf,"GOTO %s", minfo.C);
826                                 }
827                         else {
828                                 sprintf(buf,"GOTO %s", minfo.O);
829                                 }
830                         }
831                 serv_puts(buf);
832                 serv_gets(buf);
833                 if (buf[0] != '2') {
834                         syslog(LOG_ERR, "%s", buf);
835                         sprintf(buf,"GOTO _BITBUCKET_");
836                         serv_puts(buf);
837                         serv_gets(buf);
838                         }
839
840                 /* Open the temporary file containing the message */
841                 message = fopen(tname, "rb");
842                 if (message == NULL) {
843                         syslog(LOG_ERR, "cannot open %s: %s",
844                                 tname, strerror(errno));
845                         unlink(tname);
846                         goto NXMSG;
847                         }
848
849                 /* Transmit the message to the server */
850                 sprintf(buf, "ENT3 1|%s|%ld", minfo.R, msglen);
851                 serv_puts(buf);
852                 serv_gets(buf);
853                 if (!strncmp(buf, "570", 3)) {
854                         /* no such user, do a bounce */
855                         bounce(&minfo);
856                         }
857                 if (buf[0] == '7') {
858                         /* Always use the server's idea of the message length,
859                          * even though they should both be identical */
860                         msglen = atol(&buf[4]);
861                         while (msglen > 0L) {
862                                 bloklen = ((msglen >= 255L) ? 255 : ((int)msglen));
863                                 if (fread(buf, bloklen, 1, message) < 1) {
864                                         syslog(LOG_ERR,
865                                            "error trying to read %d bytes: %s",
866                                            bloklen, strerror(errno));
867                                         }
868                                 serv_write(buf, bloklen);
869                                 msglen = msglen - (long)bloklen;
870                                 }
871                         serv_puts("NOOP");
872                         serv_gets(buf);
873                         }
874                 else {
875                         syslog(LOG_ERR, "%s", buf);
876                         }
877
878                 fclose(message);
879                 }
880
881         unlink(tname);
882         goto NXMSG;
883
884 ENDSTR: fclose(fp);
885         unlink(pfilename);
886         }
887       } while(ptr!=NULL);
888     unlink(iname);
889     }
890
891
892 int checkpath(char *path, char *sys)    /* Checks to see whether its ok to send */
893                         /* Returns 1 for ok, send message       */
894             {           /* Returns 0 if message already there   */
895         int a;
896         char sys2[512];
897         strcpy(sys2,sys);
898         strcat(sys2,"!");
899
900 #ifdef DEBUG
901         syslog(LOG_NOTICE, "checkpath <%s> <%s> ... ", path, sys);
902 #endif
903         for (a=0; a<strlen(path); ++a) {
904                 if (!strncmp(&path[a],sys2,strlen(sys2))) return(0);
905                 }
906         return(1);
907         }
908
909 /*
910  * implement split horizon algorithm
911  */
912 int ismsgok(long int mpos, FILE *mmfp, char *sysname)
913 {
914         int a;
915         int ok = 0;             /* fail safe - no path, don't send it */
916         char fbuf[256];
917
918         fseek(mmfp,mpos,0);
919         if (getc(mmfp)!=255) return(0);
920         getc(mmfp); getc(mmfp);
921
922         while (a=getc(mmfp),((a!='M')&&(a!=0))) {
923                 fpgetfield(mmfp,fbuf);
924                 if (a=='P') {
925                         ok = checkpath(fbuf,sysname);
926                         }
927                 }
928 #ifdef DEBUG
929         syslog(LOG_NOTICE, "%s", ((ok)?"SEND":"(no)") );
930 #endif
931         return(ok);
932         }
933
934 int spool_out(struct msglist *cmlist, FILE *destfp, char *sysname)      /* spool list of messages to a file */
935                                         /* returns # of msgs spooled */
936               
937               
938 {
939         struct msglist *cmptr;
940         FILE *mmfp;
941         char mmtemp[128];
942         char fbuf[128];
943         int a;
944         int msgs_spooled = 0;
945         long msg_len;
946         int blok_len;
947
948         char buf[256];
949         char curr_rm[256];
950
951         strcpy(curr_rm, "");
952         sprintf(mmtemp, "/tmp/net.m%d", getpid());
953
954         /* for each message in the list... */
955         for (cmptr=cmlist; cmptr!=NULL; cmptr=cmptr->next) {
956
957                 /* make sure we're in the correct room... */
958                 if (strcasecmp(curr_rm, cmptr->m_rmname)) {
959                         sprintf(buf, "GOTO %s", cmptr->m_rmname);
960                         serv_puts(buf);
961                         serv_gets(buf);
962                         if (buf[0] == '2') {
963                                 strcpy(curr_rm, cmptr->m_rmname);
964                                 }
965                         else {
966                                 syslog(LOG_ERR, "%s", buf);
967                                 }
968                         }
969
970                 /* download the message from the server... */
971                 mmfp = fopen(mmtemp, "wb");
972                 sprintf(buf, "MSG3 %ld", cmptr->m_num);
973                 serv_puts(buf);
974                 serv_gets(buf);
975                 if (buf[0]=='6') {                      /* read the msg */
976                         msg_len = atol(&buf[4]);
977                         while (msg_len > 0L) {
978                                 blok_len = ((msg_len >= 256L) ? 256 : (int)msg_len);
979                                 serv_read(buf, blok_len);
980                                 fwrite(buf, blok_len, 1, mmfp);
981                                 msg_len = msg_len - (long)blok_len;
982                                 }
983                         }
984                 else {                                  /* or print the err */
985                         syslog(LOG_ERR, "%s", buf);
986                         }
987                 fclose(mmfp);
988         
989                 mmfp = fopen(mmtemp,"rb");
990
991                 if (ismsgok(0L,mmfp,sysname)) {
992                         ++msgs_spooled;
993                         fflush(stdout);
994                         fseek(mmfp,0L,0);
995                         fread(fbuf,3,1,mmfp);
996                         fwrite(fbuf,3,1,destfp);
997                         while (a=getc(mmfp),((a!=0)&&(a!='M'))) {
998                                 if (a!='C') putc(a,destfp);
999                                 fpgetfield(mmfp,fbuf);
1000                                 if (a=='P') fprintf(destfp,"%s!",NODENAME);
1001                                 if (a!='C')
1002                                         fwrite(fbuf,strlen(fbuf)+1,1,destfp);
1003                                 }
1004                         if (a=='M') {
1005                                 fprintf(destfp, "C%s%c",
1006                                         cmptr->m_rmname, 0);
1007                                 putc('M',destfp);
1008                                 do {
1009                                         a=getc(mmfp);
1010                                         putc(a,destfp);
1011                                         } while(a>0);
1012                                 }
1013                         }
1014                 fclose(mmfp);
1015                 }
1016
1017         unlink(mmtemp);
1018         return(msgs_spooled);
1019         }
1020
1021 void outprocess(char *sysname) /* send new room messages to sysname */
1022                {
1023         char sysflnm[64];
1024         char srmname[32];
1025         char shiptocmd[128];
1026         char lbuf[64];
1027         char tempflnm[64];
1028         char buf[256];
1029         struct msglist *cmlist = NULL;
1030         struct rmlist *crmlist = NULL;
1031         struct rmlist *rmptr,*rmptr2;
1032         struct msglist *cmptr,*cmptr2;
1033         FILE *sysflfp,*tempflfp;
1034         int outgoing_msgs;
1035         long thismsg;
1036
1037         sprintf(tempflnm,"/tmp/%s.%d",NODENAME,getpid());
1038         tempflfp=fopen(tempflnm,"w");
1039         if (tempflfp==NULL) return;
1040
1041
1042 /*
1043  * Read system file for node in question and put together room list
1044  */
1045         sprintf(sysflnm,"%s/network/systems/%s",bbs_home_directory,sysname);
1046         sysflfp=fopen(sysflnm,"r");
1047         if (sysflfp==NULL) return;
1048         fgets(shiptocmd,128,sysflfp); shiptocmd[strlen(shiptocmd)-1]=0;
1049         while(!feof(sysflfp)) {
1050                 if (fgets(srmname,32,sysflfp)==NULL) break;
1051                 srmname[strlen(srmname)-1]=0;
1052                 fgets(lbuf,32,sysflfp);
1053                 rmptr=(struct rmlist *)malloc(sizeof(struct rmlist));
1054                 rmptr->next = NULL;
1055                 strcpy(rmptr->rm_name,srmname);
1056                 strip_trailing_whitespace(rmptr->rm_name);
1057                 rmptr->rm_lastsent = atol(lbuf);
1058                 if (crmlist==NULL) crmlist=rmptr;
1059                 else if (!strcasecmp(rmptr->rm_name,"control")) {
1060                         /* control has to be first in room list */
1061                         rmptr->next = crmlist;
1062                         crmlist = rmptr;
1063                         }
1064                 else {
1065                         rmptr2=crmlist;
1066                         while (rmptr2->next != NULL) rmptr2=rmptr2->next;
1067                         rmptr2->next=rmptr;
1068                         }
1069                 }
1070         fclose(sysflfp);
1071
1072 /*
1073  * Assemble list of messages to be spooled
1074  */
1075         for (rmptr=crmlist; rmptr!=NULL; rmptr=rmptr->next) {
1076
1077                 sprintf(buf,"GOTO %s",rmptr->rm_name);
1078                 serv_puts(buf);
1079                 serv_gets(buf);
1080                 if (buf[0]!='2') {
1081                         syslog(LOG_ERR, "%s", buf);
1082                         }
1083                 else {
1084                         sprintf(buf, "MSGS GT|%ld", rmptr->rm_lastsent);
1085                         serv_puts(buf);
1086                         serv_gets(buf);
1087                         if (buf[0]=='1') while (serv_gets(buf), strcmp(buf,"000")) {
1088                                 thismsg = atol(buf);
1089                                 if ( thismsg > (rmptr->rm_lastsent) ) {
1090                                         rmptr->rm_lastsent = thismsg;
1091                                         
1092                                 cmptr=(struct msglist *)
1093                                       malloc(sizeof(struct msglist));
1094                                         cmptr->next = NULL;
1095                                         cmptr->m_num = thismsg;
1096                                         strcpy(cmptr->m_rmname, rmptr->rm_name);
1097         
1098                                         if (cmlist == NULL) cmlist = cmptr;
1099                                         else {
1100                                                 cmptr2 = cmlist;
1101                                                 while (cmptr2->next != NULL)
1102                                                 cmptr2 = cmptr2->next;
1103                                                 cmptr2->next = cmptr;
1104                                                 }
1105                                         }
1106                                 }
1107                         else {          /* print error from "msgs all" */
1108                                 syslog(LOG_ERR, "%s", buf);
1109                                 }
1110                         }
1111                 }
1112
1113         outgoing_msgs=0; cmptr2=cmlist; /* this loop counts the messages */
1114         while (cmptr2!=NULL) {
1115                 ++outgoing_msgs;
1116                 cmptr2 = cmptr2->next;
1117                 }
1118         syslog(LOG_NOTICE, "%d messages to be spooled to %s",
1119                 outgoing_msgs,sysname);
1120
1121 /*
1122  * Spool out the messages, but only if there are any.
1123  */
1124         if (outgoing_msgs!=0) outgoing_msgs=spool_out(cmlist,tempflfp,sysname);
1125         syslog(LOG_NOTICE, "%d messages actually spooled",
1126                 outgoing_msgs);
1127
1128 /*
1129  * Deallocate list of spooled messages.
1130  */
1131         while(cmlist!=NULL) {
1132                 cmptr=cmlist->next;
1133                 free(cmlist);
1134                 cmlist=cmptr;
1135                 }
1136
1137 /*
1138  * Rewrite system file and deallocate room list.
1139  */
1140         syslog(LOG_NOTICE, "Spooling...");
1141         sysflfp=fopen(sysflnm,"w");
1142         fprintf(sysflfp,"%s\n",shiptocmd);
1143         for (rmptr=crmlist; rmptr!=NULL; rmptr=rmptr->next)  
1144                 fprintf(sysflfp,"%s\n%ld\n",rmptr->rm_name,rmptr->rm_lastsent);
1145         fclose(sysflfp);
1146         while(crmlist!=NULL) {
1147                 rmptr=crmlist->next;
1148                 free(crmlist);
1149                 crmlist=rmptr;
1150                 }
1151
1152 /* 
1153  * Close temporary file, ship it out, and return
1154  */
1155         fclose(tempflfp);
1156         if (outgoing_msgs!=0) ship_to(tempflnm,sysname);
1157         unlink(tempflnm);
1158         }
1159
1160
1161 /*
1162  * Connect netproc to the Citadel server running on this computer.
1163  */
1164 void np_attach_to_server(void) {
1165         char buf[256];
1166         char portname[8];
1167         char *args[] = { "netproc", "localhost", NULL, NULL } ;
1168
1169         syslog(LOG_NOTICE, "Attaching to server...");
1170         sprintf(portname, "%d", config.c_port_number);
1171         args[2] = portname;
1172         attach_to_server(3, args);
1173         serv_gets(buf);
1174         syslog(LOG_NOTICE, "%s", &buf[4]);
1175         sprintf(buf,"IPGM %d", config.c_ipgm_secret);
1176         serv_puts(buf);
1177         serv_gets(buf);
1178         syslog(LOG_NOTICE, "%s", &buf[4]);
1179         if (buf[0]!='2') {
1180                 cleanup(2);
1181                 }
1182         }
1183
1184
1185
1186 /*
1187  * main
1188  */
1189 int main(int argc, char **argv)
1190 {
1191         char allst[32];
1192         FILE *allfp;
1193         int a;
1194         int import_only = 0;    /* if set to 1, don't export anything */
1195
1196         openlog("netproc", LOG_PID, LOG_USER);
1197         strcpy(bbs_home_directory, BBSDIR);
1198
1199         /*
1200          * Change directories if specified
1201          */
1202         for (a=1; a<argc; ++a) {
1203                 if (!strncmp(argv[a], "-h", 2)) {
1204                         strcpy(bbs_home_directory, argv[a]);
1205                         strcpy(bbs_home_directory, &bbs_home_directory[2]);
1206                         home_specified = 1;
1207                         }
1208                 else if (!strcmp(argv[a], "-i")) {
1209                         import_only = 1;
1210                         }
1211                 else {
1212                         fprintf(stderr, "netproc: usage: ");
1213                         fprintf(stderr, "netproc [-hHomeDir] [-i]\n");
1214                         exit(1);
1215                         }
1216                 }
1217
1218         get_config();
1219
1220         if (set_lockfile()!=0) {
1221                 syslog(LOG_NOTICE, "lock file exists: already running");
1222                 cleanup(1);
1223                 }
1224
1225         signal(SIGINT,cleanup);
1226         signal(SIGQUIT,cleanup);
1227         signal(SIGHUP,cleanup);
1228         signal(SIGTERM,cleanup);
1229
1230         syslog(LOG_NOTICE, "started.  pid=%d", getpid());
1231         fflush(stdout);
1232         np_attach_to_server();
1233         fflush(stdout);
1234
1235         if (load_syslist()!=0) syslog(LOG_ERR, "cannot load sysinfo");
1236         setup_special_nodes();
1237
1238         inprocess();    /* first collect incoming stuff */
1239
1240         if (import_only != 1) {
1241                 allfp=(FILE *)popen("cd ./network/systems; ls","r");
1242                 if (allfp!=NULL) {
1243                         while (fgets(allst,32,allfp)!=NULL) {
1244                                 allst[strlen(allst)-1] = 0;
1245                                 outprocess(allst);
1246                                 }
1247                         pclose(allfp);
1248                         }
1249                 /* import again in case anything new was generated */
1250                 inprocess();
1251                 }
1252
1253         rewrite_syslist();
1254         syslog(LOG_NOTICE, "processing ended.");
1255         cleanup(0);
1256         return 0;
1257         }