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