195b3918fcefed3bf91326a4db65d477bd6f27b3
[citadel.git] / citadel / serv_network.c
1 /*
2  * $Id$ 
3  *
4  * This module handles shared rooms, inter-Citadel mail, and outbound
5  * mailing list processing.
6  *
7  * Copyright (C) 2000-2001 by Art Cancro and others.
8  * This code is released under the terms of the GNU General Public License.
9  *
10  */
11
12 /*
13  * FIXME
14  * Don't allow polls during network processing
15  */
16
17 #include "sysdep.h"
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <stdio.h>
21 #include <fcntl.h>
22 #include <signal.h>
23 #include <pwd.h>
24 #include <errno.h>
25 #include <sys/types.h>
26 #include <dirent.h>
27 #if TIME_WITH_SYS_TIME
28 # include <sys/time.h>
29 # include <time.h>
30 #else
31 # if HAVE_SYS_TIME_H
32 #  include <sys/time.h>
33 # else
34 #  include <time.h>
35 # endif
36 #endif
37
38 #include <sys/wait.h>
39 #include <string.h>
40 #include <limits.h>
41 #include "citadel.h"
42 #include "server.h"
43 #include "sysdep_decls.h"
44 #include "citserver.h"
45 #include "support.h"
46 #include "config.h"
47 #include "dynloader.h"
48 #include "room_ops.h"
49 #include "user_ops.h"
50 #include "policy.h"
51 #include "database.h"
52 #include "msgbase.h"
53 #include "tools.h"
54 #include "internet_addressing.h"
55 #include "serv_network.h"
56 #include "clientsocket.h"
57 #include "file_ops.h"
58
59
60 /*
61  * When we do network processing, it's accomplished in two passes; one to
62  * gather a list of rooms and one to actually do them.  It's ok that rplist
63  * is global; this process *only* runs as part of the housekeeping loop and
64  * therefore only one will run at a time.
65  */
66 struct RoomProcList *rplist = NULL;
67
68 /*
69  * We build a map of network nodes during processing.
70  */
71 struct NetMap *the_netmap = NULL;
72
73
74
75
76
77
78 /*
79  * Manage the use table.  This is a list of messages which have recently
80  * arrived on the system.  It is maintained and queried to prevent the same
81  * message from being entered into the  FIXME not finished
82  */
83 int network_usetable(int operation, struct CtdlMessage *msg) {
84
85         static struct UseTable *ut = NULL;
86         struct UseTable *uptr = NULL;
87
88         switch(operation) {
89         
90                 case UT_LOAD:
91                         return(1);
92                         break;
93
94                 case UT_SAVE:
95                         return(1);
96                         break;
97
98                 case UT_INSERT:
99                         return(1);
100                         break;
101
102         }
103
104         /* should never get here unless illegal operation specified */
105         return(2);
106 }
107
108
109 /* 
110  * Read the network map from its configuration file into memory.
111  */
112 void read_network_map(void) {
113         char *serialized_map = NULL;
114         int i;
115         char buf[SIZ];
116         struct NetMap *nmptr;
117
118         serialized_map = CtdlGetSysConfig(IGNETMAP);
119         if (serialized_map == NULL) return;     /* if null, no entries */
120
121         /* Use the string tokenizer to grab one line at a time */
122         for (i=0; i<num_tokens(serialized_map, '\n'); ++i) {
123                 extract_token(buf, serialized_map, i, '\n');
124                 nmptr = (struct NetMap *) mallok(sizeof(struct NetMap));
125                 extract(nmptr->nodename, buf, 0);
126                 nmptr->lastcontact = extract_long(buf, 1);
127                 extract(nmptr->nexthop, buf, 2);
128                 nmptr->next = the_netmap;
129                 the_netmap = nmptr;
130         }
131
132         phree(serialized_map);
133 }
134
135
136 /*
137  * Write the network map from memory back to the configuration file.
138  */
139 void write_network_map(void) {
140         char *serialized_map = NULL;
141         struct NetMap *nmptr;
142
143         serialized_map = strdoop("");
144
145         if (the_netmap != NULL) {
146                 for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
147                         serialized_map = reallok(serialized_map,
148                                                 (strlen(serialized_map)+SIZ) );
149                         if (strlen(nmptr->nodename) > 0) {
150                                 sprintf(&serialized_map[strlen(serialized_map)],
151                                         "%s|%ld|%s\n",
152                                         nmptr->nodename,
153                                         nmptr->lastcontact,
154                                         nmptr->nexthop);
155                         }
156                 }
157         }
158
159         CtdlPutSysConfig(IGNETMAP, serialized_map);
160         phree(serialized_map);
161
162         /* Now free the list */
163         while (the_netmap != NULL) {
164                 nmptr = the_netmap->next;
165                 phree(the_netmap);
166                 the_netmap = nmptr;
167         }
168 }
169
170
171
172 /* 
173  * Check the network map and determine whether the supplied node name is
174  * valid.  If it is not a neighbor node, supply the name of a neighbor node
175  * which is the next hop.  If it *is* a neighbor node, we also fill in the
176  * shared secret.
177  */
178 int is_valid_node(char *nexthop, char *secret, char *node) {
179         char *ignetcfg = NULL;
180         int i;
181         char linebuf[SIZ];
182         char buf[SIZ];
183         int retval;
184         struct NetMap *nmptr;
185
186         if (node == NULL) {
187                 return(-1);
188         }
189
190         /*
191          * First try the neighbor nodes
192          */
193         ignetcfg = CtdlGetSysConfig(IGNETCFG);
194         if (ignetcfg == NULL) {
195                 if (nexthop != NULL) {
196                         strcpy(nexthop, "");
197                 }
198                 return(-1);
199         }
200
201         retval = (-1);
202         if (nexthop != NULL) {
203                 strcpy(nexthop, "");
204         }
205
206         /* Use the string tokenizer to grab one line at a time */
207         for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
208                 extract_token(linebuf, ignetcfg, i, '\n');
209                 extract(buf, linebuf, 0);
210                 if (!strcasecmp(buf, node)) {
211                         if (nexthop != NULL) {
212                                 strcpy(nexthop, "");
213                         }
214                         if (secret != NULL) {
215                                 extract(secret, linebuf, 1);
216                         }
217                         retval = 0;
218                 }
219         }
220
221         phree(ignetcfg);
222         if (retval == 0) {
223                 return(retval);         /* yup, it's a direct neighbor */
224         }
225
226         /*      
227          * If we get to this point we have to see if we know the next hop
228          */
229         if (the_netmap != NULL) {
230                 for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
231                         if (!strcasecmp(nmptr->nodename, node)) {
232                                 if (nexthop != NULL) {
233                                         strcpy(nexthop, nmptr->nexthop);
234                                 }
235                                 return(0);
236                         }
237                 }
238         }
239
240         /*
241          * If we get to this point, the supplied node name is bogus.
242          */
243         lprintf(5, "Invalid node name <%s>\n", node);
244         return(-1);
245 }
246
247
248
249
250
251 void cmd_gnet(char *argbuf) {
252         char filename[SIZ];
253         char buf[SIZ];
254         FILE *fp;
255
256         if (CtdlAccessCheck(ac_room_aide)) return;
257         assoc_file_name(filename, &CC->quickroom, "netconfigs");
258         cprintf("%d Network settings for room #%ld <%s>\n",
259                 LISTING_FOLLOWS,
260                 CC->quickroom.QRnumber, CC->quickroom.QRname);
261
262         fp = fopen(filename, "r");
263         if (fp != NULL) {
264                 while (fgets(buf, sizeof buf, fp) != NULL) {
265                         buf[strlen(buf)-1] = 0;
266                         cprintf("%s\n", buf);
267                 }
268                 fclose(fp);
269         }
270
271         cprintf("000\n");
272 }
273
274
275 void cmd_snet(char *argbuf) {
276         char tempfilename[SIZ];
277         char filename[SIZ];
278         char buf[SIZ];
279         FILE *fp;
280
281         if (CtdlAccessCheck(ac_room_aide)) return;
282         safestrncpy(tempfilename, tmpnam(NULL), sizeof tempfilename);
283         assoc_file_name(filename, &CC->quickroom, "netconfigs");
284
285         fp = fopen(tempfilename, "w");
286         if (fp == NULL) {
287                 cprintf("%d Cannot open %s: %s\n",
288                         ERROR+INTERNAL_ERROR,
289                         tempfilename,
290                         strerror(errno));
291         }
292
293         cprintf("%d %s\n", SEND_LISTING, tempfilename);
294         while (client_gets(buf), strcmp(buf, "000")) {
295                 fprintf(fp, "%s\n", buf);
296         }
297         fclose(fp);
298
299         /* Now copy the temp file to its permanent location
300          * (We use /bin/mv instead of link() because they may be on
301          * different filesystems)
302          */
303         unlink(filename);
304         snprintf(buf, sizeof buf, "/bin/mv %s %s", tempfilename, filename);
305         system(buf);
306 }
307
308
309
310 /*
311  * Spools out one message from the list.
312  */
313 void network_spool_msg(long msgnum, void *userdata) {
314         struct SpoolControl *sc;
315         struct namelist *nptr;
316         int err;
317         int i;
318         char *instr = NULL;
319         char *newpath = NULL;
320         size_t instr_len = SIZ;
321         struct CtdlMessage *msg = NULL;
322         struct CtdlMessage *imsg;
323         struct ser_ret sermsg;
324         FILE *fp;
325         char filename[SIZ];
326         char buf[SIZ];
327         int bang = 0;
328         int send = 1;
329         int delete_after_send = 0;      /* Set to 1 to delete after spooling */
330
331         sc = (struct SpoolControl *)userdata;
332
333         /*
334          * Process mailing list recipients
335          */
336         if (sc->listrecps != NULL) {
337         
338                 /* First, copy it to the spoolout room */
339                 err = CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, msgnum, 0);
340                 if (err != 0) return;
341
342                 /* 
343                  * Figure out how big a buffer we need to allocate
344                  */
345                 for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
346                         instr_len = instr_len + strlen(nptr->name);
347                 }
348         
349                 /*
350                  * allocate...
351                  */
352                 lprintf(9, "Generating delivery instructions\n");
353                 instr = mallok(instr_len);
354                 if (instr == NULL) {
355                         lprintf(1, "Cannot allocate %d bytes for instr...\n",
356                                 instr_len);
357                         abort();
358                 }
359                 sprintf(instr,
360                         "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
361                         "bounceto|postmaster@%s\n" ,
362                         SPOOLMIME, msgnum, time(NULL), config.c_fqdn );
363         
364                 /* Generate delivery instructions for each recipient */
365                 for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
366                         sprintf(&instr[strlen(instr)], "remote|%s|0||\n",
367                                 nptr->name);
368                 }
369         
370                 /*
371                  * Generate a message from the instructions
372                  */
373                 imsg = mallok(sizeof(struct CtdlMessage));
374                 memset(imsg, 0, sizeof(struct CtdlMessage));
375                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
376                 imsg->cm_anon_type = MES_NORMAL;
377                 imsg->cm_format_type = FMT_RFC822;
378                 imsg->cm_fields['A'] = strdoop("Citadel");
379                 imsg->cm_fields['M'] = instr;
380         
381                 /* Save delivery instructions in spoolout room */
382                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
383                 CtdlFreeMessage(imsg);
384         }
385         
386         /*
387          * Process IGnet push shares
388          */
389         if (sc->ignet_push_shares != NULL) {
390         
391                 msg = CtdlFetchMessage(msgnum);
392                 if (msg != NULL) {
393
394                         /* Prepend our node name to the Path field whenever
395                          * sending a message to another IGnet node
396                          */
397                         if (msg->cm_fields['P'] == NULL) {
398                                 msg->cm_fields['P'] = strdoop("username");
399                         }
400                         newpath = mallok(strlen(msg->cm_fields['P']) + 
401                                         strlen(config.c_nodename) + 2);
402                         sprintf(newpath, "%s!%s", config.c_nodename,
403                                         msg->cm_fields['P']);
404                         phree(msg->cm_fields['P']);
405                         msg->cm_fields['P'] = newpath;
406
407                         /*
408                          * Force the message to appear in the correct room
409                          * on the far end by setting the C field correctly
410                          */
411                         if (msg->cm_fields['C'] != NULL) {
412                                 phree(msg->cm_fields['C']);
413                         }
414                         msg->cm_fields['C'] = strdoop(CC->quickroom.QRname);
415
416                         /*
417                          * Determine if this message is set to be deleted
418                          * after sending out on the network
419                          */
420                         if (msg->cm_fields['S'] != NULL) {
421                                 if (!strcasecmp(msg->cm_fields['S'],
422                                    "CANCEL")) {
423                                         delete_after_send = 1;
424                                 }
425                         }
426
427                         /* 
428                          * Now serialize it for transmission
429                          */
430                         serialize_message(&sermsg, msg);
431
432                         /* Now send it to every node */
433                         for (nptr = sc->ignet_push_shares; nptr != NULL;
434                             nptr = nptr->next) {
435
436                                 send = 1;
437
438                                 /* Check for valid node name */
439                                 if (is_valid_node(NULL,NULL,nptr->name) != 0) {
440                                         lprintf(3, "Invalid node <%s>\n",
441                                                 nptr->name);
442                                         send = 0;
443                                 }
444
445                                 /* Check for split horizon */
446                                 lprintf(9, "Path is %s\n", msg->cm_fields['P']);
447                                 bang = num_tokens(msg->cm_fields['P'], '!');
448                                 if (bang > 1) for (i=0; i<(bang-1); ++i) {
449                                         extract_token(buf, msg->cm_fields['P'],
450                                                 i, '!');
451                                         if (!strcasecmp(buf, nptr->name)) {
452                                                 send = 0;
453                                         }
454                                 }
455
456                                 /* Send the message */
457                                 if (send == 1) {
458                                         sprintf(filename,
459                                                 "./network/spoolout/%s",
460                                                 nptr->name);
461                                         fp = fopen(filename, "ab");
462                                         if (fp != NULL) {
463                                                 fwrite(sermsg.ser,
464                                                         sermsg.len, 1, fp);
465                                                 fclose(fp);
466                                         }
467                                 }
468                         }
469                         phree(sermsg.ser);
470                         CtdlFreeMessage(msg);
471                 }
472         }
473
474         /* update lastsent */
475         sc->lastsent = msgnum;
476
477         /* Delete this message if delete-after-send is set */
478         if (delete_after_send) {
479                 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
480         }
481
482 }
483         
484
485
486
487 /*
488  * Batch up and send all outbound traffic from the current room
489  */
490 void network_spoolout_room(char *room_to_spool) {
491         char filename[SIZ];
492         char buf[SIZ];
493         char instr[SIZ];
494         FILE *fp;
495         struct SpoolControl sc;
496         /* struct namelist *digestrecps = NULL; */
497         struct namelist *nptr;
498
499         lprintf(7, "Spooling <%s>\n", room_to_spool);
500         if (getroom(&CC->quickroom, room_to_spool) != 0) {
501                 lprintf(1, "ERROR: cannot load <%s>\n", room_to_spool);
502                 return;
503         }
504
505         memset(&sc, 0, sizeof(struct SpoolControl));
506         assoc_file_name(filename, &CC->quickroom, "netconfigs");
507
508         fp = fopen(filename, "r");
509         if (fp == NULL) {
510                 lprintf(7, "Outbound batch processing skipped for <%s>\n",
511                         CC->quickroom.QRname);
512                 return;
513         }
514
515         lprintf(5, "Outbound batch processing started for <%s>\n",
516                 CC->quickroom.QRname);
517
518         while (fgets(buf, sizeof buf, fp) != NULL) {
519                 buf[strlen(buf)-1] = 0;
520
521                 extract(instr, buf, 0);
522                 if (!strcasecmp(instr, "lastsent")) {
523                         sc.lastsent = extract_long(buf, 1);
524                 }
525                 else if (!strcasecmp(instr, "listrecp")) {
526                         nptr = (struct namelist *)
527                                 mallok(sizeof(struct namelist));
528                         nptr->next = sc.listrecps;
529                         extract(nptr->name, buf, 1);
530                         sc.listrecps = nptr;
531                 }
532                 else if (!strcasecmp(instr, "ignet_push_share")) {
533                         nptr = (struct namelist *)
534                                 mallok(sizeof(struct namelist));
535                         nptr->next = sc.ignet_push_shares;
536                         extract(nptr->name, buf, 1);
537                         sc.ignet_push_shares = nptr;
538                 }
539
540
541         }
542         fclose(fp);
543
544
545         /* Do something useful */
546         CtdlForEachMessage(MSGS_GT, sc.lastsent, (-63), NULL, NULL,
547                 network_spool_msg, &sc);
548
549
550         /* Now rewrite the config file */
551         fp = fopen(filename, "w");
552         if (fp == NULL) {
553                 lprintf(1, "ERROR: cannot open %s: %s\n",
554                         filename, strerror(errno));
555         }
556         else {
557                 fprintf(fp, "lastsent|%ld\n", sc.lastsent);
558
559                 /* Write out the listrecps while freeing from memory at the
560                  * same time.  Am I clever or what?  :)
561                  */
562                 while (sc.listrecps != NULL) {
563                         fprintf(fp, "listrecp|%s\n", sc.listrecps->name);
564                         nptr = sc.listrecps->next;
565                         phree(sc.listrecps);
566                         sc.listrecps = nptr;
567                 }
568                 while (sc.ignet_push_shares != NULL) {
569                         fprintf(fp, "ignet_push_share|%s\n",
570                                 sc.ignet_push_shares->name);
571                         nptr = sc.ignet_push_shares->next;
572                         phree(sc.ignet_push_shares);
573                         sc.ignet_push_shares = nptr;
574                 }
575
576                 fclose(fp);
577         }
578
579         lprintf(5, "Outbound batch processing finished for <%s>\n",
580                 CC->quickroom.QRname);
581 }
582
583
584 /*
585  * Batch up and send all outbound traffic from the current room
586  */
587 void network_queue_room(struct quickroom *qrbuf, void *data) {
588         struct RoomProcList *ptr;
589
590         ptr = (struct RoomProcList *) mallok(sizeof (struct RoomProcList));
591         if (ptr == NULL) return;
592
593         safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
594         ptr->next = rplist;
595         rplist = ptr;
596 }
597
598
599 /*
600  * Learn topology from path fields
601  */
602 void network_learn_topology(char *node, char *path) {
603         char nexthop[SIZ];
604         struct NetMap *nmptr;
605
606         strcpy(nexthop, "");
607
608         if (num_tokens(path, '!') < 3) return;
609         for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
610                 if (!strcasecmp(nmptr->nodename, node)) {
611                         extract_token(nmptr->nexthop, path, 0, '!');
612                         nmptr->lastcontact = time(NULL);
613                         return;
614                 }
615         }
616
617         /* If we got here then it's not in the map, so add it. */
618         nmptr = (struct NetMap *) mallok(sizeof (struct NetMap));
619         strcpy(nmptr->nodename, node);
620         nmptr->lastcontact = time(NULL);
621         extract_token(nmptr->nexthop, path, 0, '!');
622         nmptr->next = the_netmap;
623         the_netmap = nmptr;
624 }
625
626
627
628
629 /*
630  * Bounce a message back to the sender
631  */
632 void network_bounce(struct CtdlMessage *msg, char *reason) {
633         static int serialnum = 0;
634         FILE *fp;
635         char filename[SIZ];
636         struct ser_ret sermsg;
637         char *oldpath = NULL;
638         char buf[SIZ];
639
640         lprintf(9, "entering network_bounce()\n");
641
642         if (msg == NULL) return;
643
644         /* 
645          * Give it a fresh message ID
646          */
647         if (msg->cm_fields['I'] != NULL) {
648                 phree(msg->cm_fields['I']);
649         }
650         sprintf(buf, "%ld.%04x.%04x@%s",
651                 time(NULL), getpid(), ++serialnum, config.c_fqdn);
652         msg->cm_fields['I'] = strdoop(buf);
653
654         /*
655          * FIXME ... right now we're just sending a bounce; we really want to
656          * include the text of the bounced message.
657          */
658         if (msg->cm_fields['M'] != NULL) {
659                 phree(msg->cm_fields['M']);
660         }
661         msg->cm_fields['M'] = strdoop(reason);
662         msg->cm_format_type = 0;
663
664         /*
665          * Turn the message around
666          */
667         if (msg->cm_fields['R'] == NULL) {
668                 phree(msg->cm_fields['R']);
669         }
670
671         if (msg->cm_fields['D'] == NULL) {
672                 phree(msg->cm_fields['D']);
673         }
674
675         msg->cm_fields['R'] = msg->cm_fields['A'];
676         msg->cm_fields['D'] = msg->cm_fields['N'];
677         msg->cm_fields['A'] = strdoop(BOUNCESOURCE);
678         msg->cm_fields['N'] = strdoop(config.c_nodename);
679         
680         if (!strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
681                 phree(msg->cm_fields['D']);
682         }
683
684         /*
685          * If this is a bounce of a bounce, send it to the Aide> room
686          * instead of looping around forever
687          */
688         if (msg->cm_fields['D'] == NULL) if (msg->cm_fields['R'] != NULL)
689            if (!strcasecmp(msg->cm_fields['R'], BOUNCESOURCE)) {
690                 phree(msg->cm_fields['R']);
691                 if (msg->cm_fields['C'] != NULL) {
692                         phree(msg->cm_fields['C']);
693                 }
694                 msg->cm_fields['C'] = strdoop(AIDEROOM);
695         }
696
697         /* prepend our node to the path */
698         if (msg->cm_fields['P'] != NULL) {
699                 oldpath = msg->cm_fields['P'];
700                 msg->cm_fields['P'] = NULL;
701         }
702         else {
703                 oldpath = strdoop("unknown_user");
704         }
705         msg->cm_fields['P'] = mallok(strlen(oldpath) + SIZ);
706         sprintf(msg->cm_fields['P'], "%s!%s", config.c_nodename, oldpath);
707         phree(oldpath);
708
709         /* serialize the message */
710         serialize_message(&sermsg, msg);
711
712         /* now send it */
713         sprintf(filename, "./network/spoolin/bounce.%04x.%04x",
714                 getpid(), serialnum);
715
716         fp = fopen(filename, "ab");
717         if (fp != NULL) {
718                 fwrite(sermsg.ser,
719                         sermsg.len, 1, fp);
720                 fclose(fp);
721         }
722         phree(sermsg.ser);
723         CtdlFreeMessage(msg);
724         lprintf(9, "leaving network_bounce()\n");
725 }
726
727
728
729
730 /*
731  * Process a buffer containing a single message from a single file
732  * from the inbound queue 
733  */
734 void network_process_buffer(char *buffer, long size) {
735         struct CtdlMessage *msg;
736         long pos;
737         int field;
738         int a;
739         int e = MES_LOCAL;
740         struct usersupp tempUS;
741         char recp[SIZ];
742         char target_room[ROOMNAMELEN];
743         struct ser_ret sermsg;
744         char *oldpath = NULL;
745         char filename[SIZ];
746         FILE *fp;
747
748         /* Set default target room to trash */
749         strcpy(target_room, TWITROOM);
750
751         /* Load the message into memory */
752         msg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
753         memset(msg, 0, sizeof(struct CtdlMessage));
754         msg->cm_magic = CTDLMESSAGE_MAGIC;
755         msg->cm_anon_type = buffer[1];
756         msg->cm_format_type = buffer[2];
757
758         for (pos = 3; pos < size; ++pos) {
759                 field = buffer[pos];
760                 msg->cm_fields[field] = strdoop(&buffer[pos+1]);
761                 pos = pos + strlen(&buffer[(int)pos]);
762         }
763
764         /* Check for message routing */
765         if (msg->cm_fields['D'] != NULL) {
766                 if (strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
767
768                         /* route the message */
769                         if (is_valid_node(NULL, NULL,
770                            msg->cm_fields['D']) == 0) {
771
772                                 /* prepend our node to the path */
773                                 if (msg->cm_fields['P'] != NULL) {
774                                         oldpath = msg->cm_fields['P'];
775                                         msg->cm_fields['P'] = NULL;
776                                 }
777                                 else {
778                                         oldpath = strdoop("unknown_user");
779                                 }
780                                 msg->cm_fields['P'] =
781                                         mallok(strlen(oldpath) + SIZ);
782                                 sprintf(msg->cm_fields['P'], "%s!%s",
783                                         config.c_nodename, oldpath);
784                                 phree(oldpath);
785
786                                 /* serialize the message */
787                                 serialize_message(&sermsg, msg);
788
789                                 /* now send it */
790                                 sprintf(filename,
791                                         "./network/spoolout/%s",
792                                         msg->cm_fields['D']);
793                                 fp = fopen(filename, "ab");
794                                 if (fp != NULL) {
795                                         fwrite(sermsg.ser,
796                                                 sermsg.len, 1, fp);
797                                         fclose(fp);
798                                 }
799                                 phree(sermsg.ser);
800                                 CtdlFreeMessage(msg);
801                                 return;
802                         }
803                         
804                         else {  /* invalid destination node name */
805
806                                 network_bounce(msg,
807 "A message you sent could not be delivered due to an invalid destination node"
808 " name.  Please check the address and try sending the message again.\n");
809                                 msg = NULL;
810                                 return;
811
812                         }
813                 }
814         }
815
816         /* FIXME check to see if we already have this message */
817
818         /* Learn network topology from the path */
819         if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
820                 network_learn_topology(msg->cm_fields['N'], 
821                                         msg->cm_fields['P']);
822         }
823
824         /* Does it have a recipient?  If so, validate it... */
825         if (msg->cm_fields['R'] != NULL) {
826
827                 safestrncpy(recp, msg->cm_fields['R'], sizeof(recp));
828
829                 e = alias(recp);        /* alias and mail type */
830                 if ((recp[0] == 0) || (e == MES_ERROR)) {
831
832                         network_bounce(msg,
833 "A message you sent could not be delivered due to an invalid address.\n"
834 "Please check the address and try sending the message again.\n");
835                         msg = NULL;
836                         return;
837
838                 }
839                 else if (e == MES_LOCAL) {
840                         a = getuser(&tempUS, recp);
841                         if (a != 0) {
842
843                                 network_bounce(msg,
844 "A message you sent could not be delivered because the user does not exist\n"
845 "on this system.  Please check the address and try again.\n");
846                                 msg = NULL;
847                                 return;
848
849                         }
850                         else {
851                                 MailboxName(target_room, &tempUS, MAILROOM);
852                         }
853                 }
854         }
855
856         else if (msg->cm_fields['C'] != NULL) {
857                 safestrncpy(target_room,
858                         msg->cm_fields['C'],
859                         sizeof target_room);
860         }
861
862         else if (msg->cm_fields['O'] != NULL) {
863                 safestrncpy(target_room,
864                         msg->cm_fields['O'],
865                         sizeof target_room);
866         }
867
868         /* save the message into a room */
869         msg->cm_flags = CM_SKIP_HOOKS;
870         CtdlSaveMsg(msg, "", target_room, 0);
871         CtdlFreeMessage(msg);
872 }
873
874
875 /*
876  * Process a single message from a single file from the inbound queue 
877  */
878 void network_process_message(FILE *fp, long msgstart, long msgend) {
879         long hold_pos;
880         long size;
881         char *buffer;
882
883         hold_pos = ftell(fp);
884         size = msgend - msgstart + 1;
885         buffer = mallok(size);
886         if (buffer != NULL) {
887                 fseek(fp, msgstart, SEEK_SET);
888                 fread(buffer, size, 1, fp);
889                 network_process_buffer(buffer, size);
890                 phree(buffer);
891         }
892
893         fseek(fp, hold_pos, SEEK_SET);
894 }
895
896
897 /*
898  * Process a single file from the inbound queue 
899  */
900 void network_process_file(char *filename) {
901         FILE *fp;
902         long msgstart = (-1L);
903         long msgend = (-1L);
904         long msgcur = 0L;
905         int ch;
906
907         lprintf(7, "network: processing <%s>\n", filename);
908
909         fp = fopen(filename, "rb");
910         if (fp == NULL) {
911                 lprintf(5, "Error opening %s: %s\n",
912                         filename, strerror(errno));
913                 return;
914         }
915
916         /* Look for messages in the data stream and break them out */
917         while (ch = getc(fp), ch >= 0) {
918         
919                 if (ch == 255) {
920                         if (msgstart >= 0L) {
921                                 msgend = msgcur - 1;
922                                 network_process_message(fp, msgstart, msgend);
923                         }
924                         msgstart = msgcur;
925                 }
926
927                 ++msgcur;
928         }
929
930         msgend = msgcur - 1;
931         if (msgstart >= 0L) {
932                 network_process_message(fp, msgstart, msgend);
933         }
934
935         fclose(fp);
936         unlink(filename);
937 }
938
939
940 /*
941  * Process anything in the inbound queue
942  */
943 void network_do_spoolin(void) {
944         DIR *dp;
945         struct dirent *d;
946         char filename[SIZ];
947
948         dp = opendir("./network/spoolin");
949         if (dp == NULL) return;
950
951         while (d = readdir(dp), d != NULL) {
952                 sprintf(filename, "./network/spoolin/%s", d->d_name);
953                 network_process_file(filename);
954         }
955
956
957         closedir(dp);
958 }
959
960
961
962
963
964 /*
965  * receive network spool from the remote system
966  */
967 void receive_spool(int sock, char *remote_nodename) {
968         long download_len;
969         long bytes_received;
970         char buf[SIZ];
971         static char pbuf[IGNET_PACKET_SIZE];
972         char tempfilename[PATH_MAX];
973         long plen;
974         FILE *fp;
975
976         strcpy(tempfilename, tmpnam(NULL));
977         if (sock_puts(sock, "NDOP") < 0) return;
978         if (sock_gets(sock, buf) < 0) return;
979         lprintf(9, "<%s\n", buf);
980         if (buf[0] != '2') {
981                 return;
982         }
983         download_len = extract_long(&buf[4], 0);
984
985         bytes_received = 0L;
986         fp = fopen(tempfilename, "w");
987         if (fp == NULL) {
988                 lprintf(9, "cannot open download file locally: %s\n",
989                         strerror(errno));
990                 return;
991         }
992
993         while (bytes_received < download_len) {
994                 sprintf(buf, "READ %ld|%ld",
995                         bytes_received,
996                      ((download_len - bytes_received > IGNET_PACKET_SIZE)
997                  ? IGNET_PACKET_SIZE : (download_len - bytes_received)));
998                 if (sock_puts(sock, buf) < 0) {
999                         fclose(fp);
1000                         unlink(tempfilename);
1001                         return;
1002                 }
1003                 if (sock_gets(sock, buf) < 0) {
1004                         fclose(fp);
1005                         unlink(tempfilename);
1006                         return;
1007                 }
1008                 if (buf[0] == '6') {
1009                         plen = extract_long(&buf[4], 0);
1010                         if (sock_read(sock, pbuf, plen) < 0) {
1011                                 fclose(fp);
1012                                 unlink(tempfilename);
1013                                 return;
1014                         }
1015                         fwrite((char *) pbuf, plen, 1, fp);
1016                         bytes_received = bytes_received + plen;
1017                 }
1018         }
1019
1020         fclose(fp);
1021         if (sock_puts(sock, "CLOS") < 0) {
1022                 unlink(tempfilename);
1023                 return;
1024         }
1025         if (sock_gets(sock, buf) < 0) {
1026                 unlink(tempfilename);
1027                 return;
1028         }
1029         lprintf(9, "%s\n", buf);
1030         sprintf(buf, "mv %s ./network/spoolin/%s.%ld",
1031                 tempfilename, remote_nodename, (long) getpid());
1032         system(buf);
1033 }
1034
1035
1036
1037 /*
1038  * transmit network spool to the remote system
1039  */
1040 void transmit_spool(int sock, char *remote_nodename)
1041 {
1042         char buf[SIZ];
1043         char pbuf[4096];
1044         long plen;
1045         long bytes_to_write, thisblock;
1046         int fd;
1047         char sfname[128];
1048
1049         if (sock_puts(sock, "NUOP") < 0) return;
1050         if (sock_gets(sock, buf) < 0) return;
1051         lprintf(9, "<%s\n", buf);
1052         if (buf[0] != '2') {
1053                 return;
1054         }
1055
1056         sprintf(sfname, "./network/spoolout/%s", remote_nodename);
1057         fd = open(sfname, O_RDONLY);
1058         if (fd < 0) {
1059                 if (errno == ENOENT) {
1060                         lprintf(9, "Nothing to send.\n");
1061                 } else {
1062                         lprintf(5, "cannot open upload file locally: %s\n",
1063                                 strerror(errno));
1064                 }
1065                 return;
1066         }
1067         while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) {
1068                 bytes_to_write = plen;
1069                 while (bytes_to_write > 0L) {
1070                         sprintf(buf, "WRIT %ld", bytes_to_write);
1071                         if (sock_puts(sock, buf) < 0) {
1072                                 close(fd);
1073                                 return;
1074                         }
1075                         if (sock_gets(sock, buf) < 0) {
1076                                 close(fd);
1077                                 return;
1078                         }
1079                         thisblock = atol(&buf[4]);
1080                         if (buf[0] == '7') {
1081                                 if (sock_write(sock, pbuf,
1082                                    (int) thisblock) < 0) {
1083                                         close(fd);
1084                                         return;
1085                                 }
1086                                 bytes_to_write = bytes_to_write - thisblock;
1087                         } else {
1088                                 goto ABORTUPL;
1089                         }
1090                 }
1091         }
1092
1093 ABORTUPL:
1094         close(fd);
1095         if (sock_puts(sock, "UCLS 1") < 0) return;
1096         if (sock_gets(sock, buf) < 0) return;
1097         lprintf(9, "<%s\n", buf);
1098         if (buf[0] == '2') {
1099                 unlink(sfname);
1100         }
1101 }
1102
1103
1104
1105 /*
1106  * Poll one Citadel node (called by network_poll_other_citadel_nodes() below)
1107  */
1108 void network_poll_node(char *node, char *secret, char *host, char *port) {
1109         int sock;
1110         char buf[SIZ];
1111
1112         if (network_talking_to(node, NTT_CHECK)) return;
1113         network_talking_to(node, NTT_ADD);
1114         lprintf(5, "Polling node <%s> at %s:%s\n", node, host, port);
1115
1116         sock = sock_connect(host, port, "tcp");
1117         if (sock < 0) {
1118                 lprintf(7, "Could not connect: %s\n", strerror(errno));
1119                 network_talking_to(node, NTT_REMOVE);
1120                 return;
1121         }
1122         
1123         lprintf(9, "Connected!\n");
1124
1125         /* Read the server greeting */
1126         if (sock_gets(sock, buf) < 0) goto bail;
1127         lprintf(9, ">%s\n", buf);
1128
1129         /* Identify ourselves */
1130         sprintf(buf, "NETP %s|%s", config.c_nodename, secret);
1131         lprintf(9, "<%s\n", buf);
1132         if (sock_puts(sock, buf) <0) goto bail;
1133         if (sock_gets(sock, buf) < 0) goto bail;
1134         lprintf(9, ">%s\n", buf);
1135         if (buf[0] != '2') goto bail;
1136
1137         /* At this point we are authenticated. */
1138         receive_spool(sock, node);
1139         transmit_spool(sock, node);
1140
1141         sock_puts(sock, "QUIT");
1142 bail:   sock_close(sock);
1143         network_talking_to(node, NTT_REMOVE);
1144 }
1145
1146
1147
1148 /*
1149  * Poll other Citadel nodes and transfer inbound/outbound network data.
1150  */
1151 void network_poll_other_citadel_nodes(void) {
1152         char *ignetcfg = NULL;
1153         int i;
1154         char linebuf[SIZ];
1155         char node[SIZ];
1156         char host[SIZ];
1157         char port[SIZ];
1158         char secret[SIZ];
1159
1160         ignetcfg = CtdlGetSysConfig(IGNETCFG);
1161         if (ignetcfg == NULL) return;   /* no nodes defined */
1162
1163         /* Use the string tokenizer to grab one line at a time */
1164         for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
1165                 extract_token(linebuf, ignetcfg, i, '\n');
1166                 extract(node, linebuf, 0);
1167                 extract(secret, linebuf, 1);
1168                 extract(host, linebuf, 2);
1169                 extract(port, linebuf, 3);
1170                 if ( (strlen(node) > 0) && (strlen(secret) > 0) 
1171                    && (strlen(host) > 0) && strlen(port) > 0) {
1172                         network_poll_node(node, secret, host, port);
1173                 }
1174         }
1175
1176         phree(ignetcfg);
1177 }
1178
1179
1180
1181
1182
1183
1184
1185 /*
1186  * network_do_queue()
1187  * 
1188  * Run through the rooms doing various types of network stuff.
1189  */
1190 void network_do_queue(void) {
1191         static int doing_queue = 0;
1192         static time_t last_run = 0L;
1193         struct RoomProcList *ptr;
1194
1195         /*
1196          * Run no more frequently than once every n seconds
1197          */
1198         if ( (time(NULL) - last_run) < NETWORK_QUEUE_FREQUENCY ) return;
1199
1200         /*
1201          * This is a simple concurrency check to make sure only one queue run
1202          * is done at a time.  We could do this with a mutex, but since we
1203          * don't really require extremely fine granularity here, we'll do it
1204          * with a static variable instead.
1205          */
1206         if (doing_queue) return;
1207         doing_queue = 1;
1208         last_run = time(NULL);
1209
1210         /*
1211          * Poll other Citadel nodes.
1212          */
1213         network_poll_other_citadel_nodes();
1214
1215         /*
1216          * Load the network map into memory.
1217          */
1218         read_network_map();
1219
1220         /* 
1221          * Go ahead and run the queue
1222          */
1223         lprintf(7, "network: loading outbound queue\n");
1224         ForEachRoom(network_queue_room, NULL);
1225
1226         lprintf(7, "network: running outbound queue\n");
1227         while (rplist != NULL) {
1228                 network_spoolout_room(rplist->name);
1229                 ptr = rplist;
1230                 rplist = rplist->next;
1231                 phree(ptr);
1232         }
1233
1234         lprintf(7, "network: processing inbound queue\n");
1235         network_do_spoolin();
1236
1237         write_network_map();
1238
1239         lprintf(7, "network: queue run completed\n");
1240         doing_queue = 0;
1241 }
1242
1243
1244
1245 /*
1246  * cmd_netp() - authenticate to the server as another Citadel node polling
1247  *              for network traffic
1248  */
1249 void cmd_netp(char *cmdbuf)
1250 {
1251         char node[SIZ];
1252         char pass[SIZ];
1253
1254         char secret[SIZ];
1255         char nexthop[SIZ];
1256
1257         extract(node, cmdbuf, 0);
1258         extract(pass, cmdbuf, 1);
1259
1260         if (is_valid_node(nexthop, secret, node) != 0) {
1261                 cprintf("%d authentication failed\n", ERROR);
1262                 return;
1263         }
1264
1265         if (strcasecmp(pass, secret)) {
1266                 cprintf("%d authentication failed\n", ERROR);
1267                 return;
1268         }
1269
1270         if (network_talking_to(node, NTT_CHECK)) {
1271                 cprintf("%d Already talking to %s right now\n", ERROR);
1272                 return;
1273         }
1274
1275         safestrncpy(CC->net_node, node, sizeof CC->net_node);
1276         network_talking_to(node, NTT_ADD);
1277         cprintf("%d authenticated as network node '%s'\n", OK,
1278                 CC->net_node);
1279 }
1280
1281
1282
1283 /*
1284  * Module entry point
1285  */
1286 char *Dynamic_Module_Init(void)
1287 {
1288         CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
1289         CtdlRegisterProtoHook(cmd_snet, "SNET", "Get network config");
1290         CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
1291         CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
1292         return "$Id$";
1293 }