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