]> code.citadel.org Git - citadel.git/blob - citadel/serv_network.c
* Completion of (most of) digest mode. Still needs some polish.
[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                                         "-------\r\n");
463                 CtdlRedirectOutput(sc->digestfp, -1);
464                 CtdlOutputMsg(msgnum, MT_RFC822, HEADERS_ALL, 0, 1);
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
599         sprintf(buf, "room_%s@%s", CC->quickroom.QRname, config.c_fqdn);
600         for (i=0; i<strlen(buf); ++i) {
601                 if (isspace(buf[i])) buf[i]='_';
602                 buf[i] = tolower(buf[i]);
603         }
604         msg->cm_fields['F'] = strdoop(buf);
605
606         msg->cm_fields['A'] = strdoop(CC->quickroom.QRname);
607
608         fseek(sc->digestfp, 0L, SEEK_END);
609         msglen = ftell(sc->digestfp);
610
611         msg->cm_fields['M'] = mallok(msglen + 1);
612         fseek(sc->digestfp, 0L, SEEK_SET);
613         fread(msg->cm_fields['M'], (size_t)msglen, 1, sc->digestfp);
614         msg->cm_fields['M'][msglen] = 0;
615
616         fclose(sc->digestfp);
617         sc->digestfp = NULL;
618
619         msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
620         CtdlFreeMessage(msg);
621
622         /* Now generate the delivery instructions */
623
624         /* 
625          * Figure out how big a buffer we need to allocate
626          */
627         for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
628                 instr_len = instr_len + strlen(nptr->name);
629         }
630         
631         /*
632          * allocate...
633          */
634         lprintf(9, "Generating delivery instructions\n");
635         instr = mallok(instr_len);
636         if (instr == NULL) {
637                 lprintf(1, "Cannot allocate %ld bytes for instr...\n",
638                         (long)instr_len);
639                 abort();
640         }
641         snprintf(instr, instr_len,
642                 "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
643                 "bounceto|postmaster@%s\n" ,
644                 SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn );
645
646         /* Generate delivery instructions for each recipient */
647         for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
648                 size_t tmp = strlen(instr);
649                 snprintf(&instr[tmp], instr_len - tmp,
650                          "remote|%s|0||\n", nptr->name);
651         }
652
653         /*
654          * Generate a message from the instructions
655          */
656         imsg = mallok(sizeof(struct CtdlMessage));
657         memset(imsg, 0, sizeof(struct CtdlMessage));
658         imsg->cm_magic = CTDLMESSAGE_MAGIC;
659         imsg->cm_anon_type = MES_NORMAL;
660         imsg->cm_format_type = FMT_RFC822;
661         imsg->cm_fields['A'] = strdoop("Citadel");
662         imsg->cm_fields['M'] = instr;
663
664         /* Save delivery instructions in spoolout room */
665         CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
666         CtdlFreeMessage(imsg);
667 }
668
669
670 /*
671  * Batch up and send all outbound traffic from the current room
672  */
673 void network_spoolout_room(char *room_to_spool) {
674         char filename[SIZ];
675         char buf[SIZ];
676         char instr[SIZ];
677         FILE *fp;
678         struct SpoolControl sc;
679         struct namelist *nptr;
680
681         lprintf(7, "Spooling <%s>\n", room_to_spool);
682         if (getroom(&CC->quickroom, room_to_spool) != 0) {
683                 lprintf(1, "ERROR: cannot load <%s>\n", room_to_spool);
684                 return;
685         }
686
687         memset(&sc, 0, sizeof(struct SpoolControl));
688         assoc_file_name(filename, sizeof filename, &CC->quickroom, "netconfigs");
689
690         fp = fopen(filename, "r");
691         if (fp == NULL) {
692                 lprintf(7, "Outbound batch processing skipped for <%s>\n",
693                         CC->quickroom.QRname);
694                 return;
695         }
696
697         lprintf(5, "Outbound batch processing started for <%s>\n",
698                 CC->quickroom.QRname);
699
700         while (fgets(buf, sizeof buf, fp) != NULL) {
701                 buf[strlen(buf)-1] = 0;
702
703                 extract(instr, buf, 0);
704                 if (!strcasecmp(instr, "lastsent")) {
705                         sc.lastsent = extract_long(buf, 1);
706                 }
707                 else if (!strcasecmp(instr, "listrecp")) {
708                         nptr = (struct namelist *)
709                                 mallok(sizeof(struct namelist));
710                         nptr->next = sc.listrecps;
711                         extract(nptr->name, buf, 1);
712                         sc.listrecps = nptr;
713                 }
714                 else if (!strcasecmp(instr, "digestrecp")) {
715                         nptr = (struct namelist *)
716                                 mallok(sizeof(struct namelist));
717                         nptr->next = sc.digestrecps;
718                         extract(nptr->name, buf, 1);
719                         sc.digestrecps = nptr;
720                 }
721                 else if (!strcasecmp(instr, "ignet_push_share")) {
722                         nptr = (struct namelist *)
723                                 mallok(sizeof(struct namelist));
724                         nptr->next = sc.ignet_push_shares;
725                         extract(nptr->name, buf, 1);
726                         sc.ignet_push_shares = nptr;
727                 }
728
729
730         }
731         fclose(fp);
732
733         /* If there are digest recipients, we have to build a digest */
734         if (sc.digestrecps != NULL) {
735                 sc.digestfp = tmpfile();
736                 fprintf(sc.digestfp, "Content-type: text/plain\r\n\r\n");
737         }
738
739         /* Do something useful */
740         CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL,
741                 network_spool_msg, &sc);
742
743         /* If we wrote a digest, deliver it and then close it */
744         if (sc.digestfp != NULL) {
745                 fprintf(sc.digestfp,    " -----------------------------------"
746                                         "------------------------------------"
747                                         "-------\r\n"
748                                         "You are subscribed to the '%s' "
749                                         "list.\r\nTo unsubscribe, blah blah "
750                                         "blah FIXME.\r\n",
751                                         CC->quickroom.QRname
752                 );
753                 network_deliver_digest(&sc);    /* deliver and close */
754         }
755
756         /* Now rewrite the config file */
757         fp = fopen(filename, "w");
758         if (fp == NULL) {
759                 lprintf(1, "ERROR: cannot open %s: %s\n",
760                         filename, strerror(errno));
761         }
762         else {
763                 fprintf(fp, "lastsent|%ld\n", sc.lastsent);
764
765                 /* Write out the listrecps while freeing from memory at the
766                  * same time.  Am I clever or what?  :)
767                  */
768                 while (sc.listrecps != NULL) {
769                         fprintf(fp, "listrecp|%s\n", sc.listrecps->name);
770                         nptr = sc.listrecps->next;
771                         phree(sc.listrecps);
772                         sc.listrecps = nptr;
773                 }
774                 /* Do the same for digestrecps */
775                 while (sc.digestrecps != NULL) {
776                         fprintf(fp, "digestrecp|%s\n", sc.digestrecps->name);
777                         nptr = sc.digestrecps->next;
778                         phree(sc.digestrecps);
779                         sc.digestrecps = nptr;
780                 }
781                 while (sc.ignet_push_shares != NULL) {
782                         fprintf(fp, "ignet_push_share|%s\n",
783                                 sc.ignet_push_shares->name);
784                         nptr = sc.ignet_push_shares->next;
785                         phree(sc.ignet_push_shares);
786                         sc.ignet_push_shares = nptr;
787                 }
788
789                 fclose(fp);
790         }
791
792         lprintf(5, "Outbound batch processing finished for <%s>\n",
793                 CC->quickroom.QRname);
794 }
795
796
797 /*
798  * Batch up and send all outbound traffic from the current room
799  */
800 void network_queue_room(struct quickroom *qrbuf, void *data) {
801         struct RoomProcList *ptr;
802
803         ptr = (struct RoomProcList *) mallok(sizeof (struct RoomProcList));
804         if (ptr == NULL) return;
805
806         safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
807         ptr->next = rplist;
808         rplist = ptr;
809 }
810
811
812 /*
813  * Learn topology from path fields
814  */
815 void network_learn_topology(char *node, char *path) {
816         char nexthop[SIZ];
817         struct NetMap *nmptr;
818
819         strcpy(nexthop, "");
820
821         if (num_tokens(path, '!') < 3) return;
822         for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
823                 if (!strcasecmp(nmptr->nodename, node)) {
824                         extract_token(nmptr->nexthop, path, 0, '!');
825                         nmptr->lastcontact = time(NULL);
826                         return;
827                 }
828         }
829
830         /* If we got here then it's not in the map, so add it. */
831         nmptr = (struct NetMap *) mallok(sizeof (struct NetMap));
832         strcpy(nmptr->nodename, node);
833         nmptr->lastcontact = time(NULL);
834         extract_token(nmptr->nexthop, path, 0, '!');
835         nmptr->next = the_netmap;
836         the_netmap = nmptr;
837 }
838
839
840
841
842 /*
843  * Bounce a message back to the sender
844  */
845 void network_bounce(struct CtdlMessage *msg, char *reason) {
846         char *oldpath = NULL;
847         char buf[SIZ];
848         char bouncesource[SIZ];
849         char recipient[SIZ];
850         struct recptypes *valid = NULL;
851         char force_room[ROOMNAMELEN];
852         static int serialnum = 0;
853         size_t size;
854
855         lprintf(9, "entering network_bounce()\n");
856
857         if (msg == NULL) return;
858
859         snprintf(bouncesource, sizeof bouncesource, "%s@%s", BOUNCESOURCE, config.c_nodename);
860
861         /* 
862          * Give it a fresh message ID
863          */
864         if (msg->cm_fields['I'] != NULL) {
865                 phree(msg->cm_fields['I']);
866         }
867         snprintf(buf, sizeof buf, "%ld.%04lx.%04x@%s",
868                 (long)time(NULL), (long)getpid(), ++serialnum, config.c_fqdn);
869         msg->cm_fields['I'] = strdoop(buf);
870
871         /*
872          * FIXME ... right now we're just sending a bounce; we really want to
873          * include the text of the bounced message.
874          */
875         if (msg->cm_fields['M'] != NULL) {
876                 phree(msg->cm_fields['M']);
877         }
878         msg->cm_fields['M'] = strdoop(reason);
879         msg->cm_format_type = 0;
880
881         /*
882          * Turn the message around
883          */
884         if (msg->cm_fields['R'] == NULL) {
885                 phree(msg->cm_fields['R']);
886         }
887
888         if (msg->cm_fields['D'] == NULL) {
889                 phree(msg->cm_fields['D']);
890         }
891
892         snprintf(recipient, sizeof recipient, "%s@%s",
893                 msg->cm_fields['A'], msg->cm_fields['N']);
894
895         if (msg->cm_fields['A'] == NULL) {
896                 phree(msg->cm_fields['A']);
897         }
898
899         if (msg->cm_fields['N'] == NULL) {
900                 phree(msg->cm_fields['N']);
901         }
902
903         msg->cm_fields['A'] = strdoop(BOUNCESOURCE);
904         msg->cm_fields['N'] = strdoop(config.c_nodename);
905         
906
907         /* prepend our node to the path */
908         if (msg->cm_fields['P'] != NULL) {
909                 oldpath = msg->cm_fields['P'];
910                 msg->cm_fields['P'] = NULL;
911         }
912         else {
913                 oldpath = strdoop("unknown_user");
914         }
915         size = strlen(oldpath) + SIZ;
916         msg->cm_fields['P'] = mallok(size);
917         snprintf(msg->cm_fields['P'], size, "%s!%s", config.c_nodename, oldpath);
918         phree(oldpath);
919
920         /* Now submit the message */
921         valid = validate_recipients(recipient);
922         if (valid != NULL) if (valid->num_error > 0) {
923                 phree(valid);
924                 valid = NULL;
925         }
926         if ( (valid == NULL) || (!strcasecmp(recipient, bouncesource)) ) {
927                 strcpy(force_room, config.c_aideroom);
928         }
929         else {
930                 strcpy(force_room, "");
931         }
932         if ( (valid == NULL) && (strlen(force_room) == 0) ) {
933                 strcpy(force_room, config.c_aideroom);
934         }
935         CtdlSubmitMsg(msg, valid, force_room);
936
937         /* Clean up */
938         if (valid != NULL) phree(valid);
939         CtdlFreeMessage(msg);
940         lprintf(9, "leaving network_bounce()\n");
941 }
942
943
944
945
946 /*
947  * Process a buffer containing a single message from a single file
948  * from the inbound queue 
949  */
950 void network_process_buffer(char *buffer, long size) {
951         struct CtdlMessage *msg;
952         long pos;
953         int field;
954         struct recptypes *recp = NULL;
955         char target_room[ROOMNAMELEN];
956         struct ser_ret sermsg;
957         char *oldpath = NULL;
958         char filename[SIZ];
959         FILE *fp;
960         char buf[SIZ];
961
962         /* Set default target room to trash */
963         strcpy(target_room, TWITROOM);
964
965         /* Load the message into memory */
966         msg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
967         memset(msg, 0, sizeof(struct CtdlMessage));
968         msg->cm_magic = CTDLMESSAGE_MAGIC;
969         msg->cm_anon_type = buffer[1];
970         msg->cm_format_type = buffer[2];
971
972         for (pos = 3; pos < size; ++pos) {
973                 field = buffer[pos];
974                 msg->cm_fields[field] = strdoop(&buffer[pos+1]);
975                 pos = pos + strlen(&buffer[(int)pos]);
976         }
977
978         /* Check for message routing */
979         if (msg->cm_fields['D'] != NULL) {
980                 if (strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
981
982                         /* route the message */
983                         if (is_valid_node(NULL, NULL,
984                            msg->cm_fields['D']) == 0) {
985
986                                 /* prepend our node to the path */
987                                 if (msg->cm_fields['P'] != NULL) {
988                                         oldpath = msg->cm_fields['P'];
989                                         msg->cm_fields['P'] = NULL;
990                                 }
991                                 else {
992                                         oldpath = strdoop("unknown_user");
993                                 }
994                                 size = strlen(oldpath) + SIZ;
995                                 msg->cm_fields['P'] = mallok(size);
996                                 snprintf(msg->cm_fields['P'], size, "%s!%s",
997                                         config.c_nodename, oldpath);
998                                 phree(oldpath);
999
1000                                 /* serialize the message */
1001                                 serialize_message(&sermsg, msg);
1002
1003                                 /* now send it */
1004                                 snprintf(filename, sizeof filename,
1005                                         "./network/spoolout/%s",
1006                                         msg->cm_fields['D']);
1007                                 fp = fopen(filename, "ab");
1008                                 if (fp != NULL) {
1009                                         fwrite(sermsg.ser,
1010                                                 sermsg.len, 1, fp);
1011                                         fclose(fp);
1012                                 }
1013                                 phree(sermsg.ser);
1014                                 CtdlFreeMessage(msg);
1015                                 return;
1016                         }
1017                         
1018                         else {  /* invalid destination node name */
1019
1020                                 network_bounce(msg,
1021 "A message you sent could not be delivered due to an invalid destination node"
1022 " name.  Please check the address and try sending the message again.\n");
1023                                 msg = NULL;
1024                                 return;
1025
1026                         }
1027                 }
1028         }
1029
1030         /*
1031          * Check to see if we already have a copy of this message
1032          */
1033         if (network_usetable(msg) != 0) {
1034                 snprintf(buf, sizeof buf,
1035                         "Loopzapper rejected message <%s> "
1036                         "from <%s> in <%s> @ <%s>\n",
1037                         ((msg->cm_fields['I']!=NULL)?(msg->cm_fields['I']):""),
1038                         ((msg->cm_fields['A']!=NULL)?(msg->cm_fields['A']):""),
1039                         ((msg->cm_fields['O']!=NULL)?(msg->cm_fields['O']):""),
1040                         ((msg->cm_fields['N']!=NULL)?(msg->cm_fields['N']):"")
1041                 );
1042                 aide_message(buf);
1043                 CtdlFreeMessage(msg);
1044                 msg = NULL;
1045                 return;
1046         }
1047
1048         /* Learn network topology from the path */
1049         if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
1050                 network_learn_topology(msg->cm_fields['N'], 
1051                                         msg->cm_fields['P']);
1052         }
1053
1054         /* Does it have a recipient?  If so, validate it... */
1055         if (msg->cm_fields['R'] != NULL) {
1056                 recp = validate_recipients(msg->cm_fields['R']);
1057                 if (recp != NULL) if (recp->num_error > 0) {
1058                         network_bounce(msg,
1059 "A message you sent could not be delivered due to an invalid address.\n"
1060 "Please check the address and try sending the message again.\n");
1061                         msg = NULL;
1062                         phree(recp);
1063                         return;
1064                 }
1065                 strcpy(target_room, "");        /* no target room if mail */
1066         }
1067
1068         else if (msg->cm_fields['C'] != NULL) {
1069                 safestrncpy(target_room,
1070                         msg->cm_fields['C'],
1071                         sizeof target_room);
1072         }
1073
1074         else if (msg->cm_fields['O'] != NULL) {
1075                 safestrncpy(target_room,
1076                         msg->cm_fields['O'],
1077                         sizeof target_room);
1078         }
1079
1080         /* Strip out fields that are only relevant during transit */
1081         if (msg->cm_fields['D'] != NULL) {
1082                 phree(msg->cm_fields['D']);
1083                 msg->cm_fields['D'] = NULL;
1084         }
1085         if (msg->cm_fields['C'] != NULL) {
1086                 phree(msg->cm_fields['C']);
1087                 msg->cm_fields['C'] = NULL;
1088         }
1089
1090         /* save the message into a room */
1091         if (PerformNetprocHooks(msg, target_room) == 0) {
1092                 msg->cm_flags = CM_SKIP_HOOKS;
1093                 CtdlSubmitMsg(msg, recp, target_room);
1094         }
1095         CtdlFreeMessage(msg);
1096         phree(recp);
1097 }
1098
1099
1100 /*
1101  * Process a single message from a single file from the inbound queue 
1102  */
1103 void network_process_message(FILE *fp, long msgstart, long msgend) {
1104         long hold_pos;
1105         long size;
1106         char *buffer;
1107
1108         hold_pos = ftell(fp);
1109         size = msgend - msgstart + 1;
1110         buffer = mallok(size);
1111         if (buffer != NULL) {
1112                 fseek(fp, msgstart, SEEK_SET);
1113                 fread(buffer, size, 1, fp);
1114                 network_process_buffer(buffer, size);
1115                 phree(buffer);
1116         }
1117
1118         fseek(fp, hold_pos, SEEK_SET);
1119 }
1120
1121
1122 /*
1123  * Process a single file from the inbound queue 
1124  */
1125 void network_process_file(char *filename) {
1126         FILE *fp;
1127         long msgstart = (-1L);
1128         long msgend = (-1L);
1129         long msgcur = 0L;
1130         int ch;
1131
1132         lprintf(7, "network: processing <%s>\n", filename);
1133
1134         fp = fopen(filename, "rb");
1135         if (fp == NULL) {
1136                 lprintf(5, "Error opening %s: %s\n",
1137                         filename, strerror(errno));
1138                 return;
1139         }
1140
1141         /* Look for messages in the data stream and break them out */
1142         while (ch = getc(fp), ch >= 0) {
1143         
1144                 if (ch == 255) {
1145                         if (msgstart >= 0L) {
1146                                 msgend = msgcur - 1;
1147                                 network_process_message(fp, msgstart, msgend);
1148                         }
1149                         msgstart = msgcur;
1150                 }
1151
1152                 ++msgcur;
1153         }
1154
1155         msgend = msgcur - 1;
1156         if (msgstart >= 0L) {
1157                 network_process_message(fp, msgstart, msgend);
1158         }
1159
1160         fclose(fp);
1161         unlink(filename);
1162 }
1163
1164
1165 /*
1166  * Process anything in the inbound queue
1167  */
1168 void network_do_spoolin(void) {
1169         DIR *dp;
1170         struct dirent *d;
1171         char filename[SIZ];
1172
1173         dp = opendir("./network/spoolin");
1174         if (dp == NULL) return;
1175
1176         while (d = readdir(dp), d != NULL) {
1177                 snprintf(filename, sizeof filename, "./network/spoolin/%s", d->d_name);
1178                 network_process_file(filename);
1179         }
1180
1181
1182         closedir(dp);
1183 }
1184
1185
1186
1187
1188
1189 /*
1190  * receive network spool from the remote system
1191  */
1192 void receive_spool(int sock, char *remote_nodename) {
1193         long download_len;
1194         long bytes_received;
1195         char buf[SIZ];
1196         static char pbuf[IGNET_PACKET_SIZE];
1197         char tempfilename[PATH_MAX];
1198         long plen;
1199         FILE *fp;
1200
1201         strcpy(tempfilename, tmpnam(NULL));
1202         if (sock_puts(sock, "NDOP") < 0) return;
1203         if (sock_gets(sock, buf) < 0) return;
1204         lprintf(9, "<%s\n", buf);
1205         if (buf[0] != '2') {
1206                 return;
1207         }
1208         download_len = extract_long(&buf[4], 0);
1209
1210         bytes_received = 0L;
1211         fp = fopen(tempfilename, "w");
1212         if (fp == NULL) {
1213                 lprintf(9, "cannot open download file locally: %s\n",
1214                         strerror(errno));
1215                 return;
1216         }
1217
1218         while (bytes_received < download_len) {
1219                 snprintf(buf, sizeof buf, "READ %ld|%ld",
1220                         bytes_received,
1221                      ((download_len - bytes_received > IGNET_PACKET_SIZE)
1222                  ? IGNET_PACKET_SIZE : (download_len - bytes_received)));
1223                 if (sock_puts(sock, buf) < 0) {
1224                         fclose(fp);
1225                         unlink(tempfilename);
1226                         return;
1227                 }
1228                 if (sock_gets(sock, buf) < 0) {
1229                         fclose(fp);
1230                         unlink(tempfilename);
1231                         return;
1232                 }
1233                 if (buf[0] == '6') {
1234                         plen = extract_long(&buf[4], 0);
1235                         if (sock_read(sock, pbuf, plen) < 0) {
1236                                 fclose(fp);
1237                                 unlink(tempfilename);
1238                                 return;
1239                         }
1240                         fwrite((char *) pbuf, plen, 1, fp);
1241                         bytes_received = bytes_received + plen;
1242                 }
1243         }
1244
1245         fclose(fp);
1246         if (sock_puts(sock, "CLOS") < 0) {
1247                 unlink(tempfilename);
1248                 return;
1249         }
1250         if (sock_gets(sock, buf) < 0) {
1251                 unlink(tempfilename);
1252                 return;
1253         }
1254         lprintf(9, "%s\n", buf);
1255         snprintf(buf, sizeof buf, "mv %s ./network/spoolin/%s.%ld",
1256                 tempfilename, remote_nodename, (long) getpid());
1257         system(buf);
1258 }
1259
1260
1261
1262 /*
1263  * transmit network spool to the remote system
1264  */
1265 void transmit_spool(int sock, char *remote_nodename)
1266 {
1267         char buf[SIZ];
1268         char pbuf[4096];
1269         long plen;
1270         long bytes_to_write, thisblock;
1271         int fd;
1272         char sfname[128];
1273
1274         if (sock_puts(sock, "NUOP") < 0) return;
1275         if (sock_gets(sock, buf) < 0) return;
1276         lprintf(9, "<%s\n", buf);
1277         if (buf[0] != '2') {
1278                 return;
1279         }
1280
1281         snprintf(sfname, sizeof sfname, "./network/spoolout/%s", remote_nodename);
1282         fd = open(sfname, O_RDONLY);
1283         if (fd < 0) {
1284                 if (errno == ENOENT) {
1285                         lprintf(9, "Nothing to send.\n");
1286                 } else {
1287                         lprintf(5, "cannot open upload file locally: %s\n",
1288                                 strerror(errno));
1289                 }
1290                 return;
1291         }
1292         while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) {
1293                 bytes_to_write = plen;
1294                 while (bytes_to_write > 0L) {
1295                         snprintf(buf, sizeof buf, "WRIT %ld", bytes_to_write);
1296                         if (sock_puts(sock, buf) < 0) {
1297                                 close(fd);
1298                                 return;
1299                         }
1300                         if (sock_gets(sock, buf) < 0) {
1301                                 close(fd);
1302                                 return;
1303                         }
1304                         thisblock = atol(&buf[4]);
1305                         if (buf[0] == '7') {
1306                                 if (sock_write(sock, pbuf,
1307                                    (int) thisblock) < 0) {
1308                                         close(fd);
1309                                         return;
1310                                 }
1311                                 bytes_to_write = bytes_to_write - thisblock;
1312                         } else {
1313                                 goto ABORTUPL;
1314                         }
1315                 }
1316         }
1317
1318 ABORTUPL:
1319         close(fd);
1320         if (sock_puts(sock, "UCLS 1") < 0) return;
1321         if (sock_gets(sock, buf) < 0) return;
1322         lprintf(9, "<%s\n", buf);
1323         if (buf[0] == '2') {
1324                 unlink(sfname);
1325         }
1326 }
1327
1328
1329
1330 /*
1331  * Poll one Citadel node (called by network_poll_other_citadel_nodes() below)
1332  */
1333 void network_poll_node(char *node, char *secret, char *host, char *port) {
1334         int sock;
1335         char buf[SIZ];
1336
1337         if (network_talking_to(node, NTT_CHECK)) return;
1338         network_talking_to(node, NTT_ADD);
1339         lprintf(5, "Polling node <%s> at %s:%s\n", node, host, port);
1340
1341         sock = sock_connect(host, port, "tcp");
1342         if (sock < 0) {
1343                 lprintf(7, "Could not connect: %s\n", strerror(errno));
1344                 network_talking_to(node, NTT_REMOVE);
1345                 return;
1346         }
1347         
1348         lprintf(9, "Connected!\n");
1349
1350         /* Read the server greeting */
1351         if (sock_gets(sock, buf) < 0) goto bail;
1352         lprintf(9, ">%s\n", buf);
1353
1354         /* Identify ourselves */
1355         snprintf(buf, sizeof buf, "NETP %s|%s", config.c_nodename, secret);
1356         lprintf(9, "<%s\n", buf);
1357         if (sock_puts(sock, buf) <0) goto bail;
1358         if (sock_gets(sock, buf) < 0) goto bail;
1359         lprintf(9, ">%s\n", buf);
1360         if (buf[0] != '2') goto bail;
1361
1362         /* At this point we are authenticated. */
1363         receive_spool(sock, node);
1364         transmit_spool(sock, node);
1365
1366         sock_puts(sock, "QUIT");
1367 bail:   sock_close(sock);
1368         network_talking_to(node, NTT_REMOVE);
1369 }
1370
1371
1372
1373 /*
1374  * Poll other Citadel nodes and transfer inbound/outbound network data.
1375  */
1376 void network_poll_other_citadel_nodes(void) {
1377         char *ignetcfg = NULL;
1378         int i;
1379         char linebuf[SIZ];
1380         char node[SIZ];
1381         char host[SIZ];
1382         char port[SIZ];
1383         char secret[SIZ];
1384
1385         ignetcfg = CtdlGetSysConfig(IGNETCFG);
1386         if (ignetcfg == NULL) return;   /* no nodes defined */
1387
1388         /* Use the string tokenizer to grab one line at a time */
1389         for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
1390                 extract_token(linebuf, ignetcfg, i, '\n');
1391                 extract(node, linebuf, 0);
1392                 extract(secret, linebuf, 1);
1393                 extract(host, linebuf, 2);
1394                 extract(port, linebuf, 3);
1395                 if ( (strlen(node) > 0) && (strlen(secret) > 0) 
1396                    && (strlen(host) > 0) && strlen(port) > 0) {
1397                         network_poll_node(node, secret, host, port);
1398                 }
1399         }
1400
1401         phree(ignetcfg);
1402 }
1403
1404
1405
1406
1407
1408
1409
1410 /*
1411  * network_do_queue()
1412  * 
1413  * Run through the rooms doing various types of network stuff.
1414  */
1415 void network_do_queue(void) {
1416         static int doing_queue = 0;
1417         static time_t last_run = 0L;
1418         struct RoomProcList *ptr;
1419
1420         /*
1421          * Run no more frequently than once every n seconds
1422          */
1423         if ( (time(NULL) - last_run) < config.c_net_freq ) return;
1424
1425         /*
1426          * This is a simple concurrency check to make sure only one queue run
1427          * is done at a time.  We could do this with a mutex, but since we
1428          * don't really require extremely fine granularity here, we'll do it
1429          * with a static variable instead.
1430          */
1431         if (doing_queue) return;
1432         doing_queue = 1;
1433         last_run = time(NULL);
1434
1435         /*
1436          * Poll other Citadel nodes.
1437          */
1438         network_poll_other_citadel_nodes();
1439
1440         /*
1441          * Load the network map and filter list into memory.
1442          */
1443         read_network_map();
1444         filterlist = load_filter_list();
1445
1446         /* 
1447          * Go ahead and run the queue
1448          */
1449         lprintf(7, "network: loading outbound queue\n");
1450         ForEachRoom(network_queue_room, NULL);
1451
1452         lprintf(7, "network: running outbound queue\n");
1453         while (rplist != NULL) {
1454                 network_spoolout_room(rplist->name);
1455                 ptr = rplist;
1456                 rplist = rplist->next;
1457                 phree(ptr);
1458         }
1459
1460         lprintf(7, "network: processing inbound queue\n");
1461         network_do_spoolin();
1462
1463         /* Save the network map back to disk */
1464         write_network_map();
1465
1466         /* Free the filter list in memory */
1467         free_filter_list(filterlist);
1468         filterlist = NULL;
1469
1470         lprintf(7, "network: queue run completed\n");
1471         doing_queue = 0;
1472 }
1473
1474
1475 /*
1476  * cmd_netp() - authenticate to the server as another Citadel node polling
1477  *              for network traffic
1478  */
1479 void cmd_netp(char *cmdbuf)
1480 {
1481         char node[SIZ];
1482         char pass[SIZ];
1483
1484         char secret[SIZ];
1485         char nexthop[SIZ];
1486
1487         extract(node, cmdbuf, 0);
1488         extract(pass, cmdbuf, 1);
1489
1490         if (is_valid_node(nexthop, secret, node) != 0) {
1491                 cprintf("%d authentication failed\n", ERROR);
1492                 return;
1493         }
1494
1495         if (strcasecmp(pass, secret)) {
1496                 cprintf("%d authentication failed\n", ERROR);
1497                 return;
1498         }
1499
1500         if (network_talking_to(node, NTT_CHECK)) {
1501                 cprintf("%d Already talking to %s right now\n", ERROR, node);
1502                 return;
1503         }
1504
1505         safestrncpy(CC->net_node, node, sizeof CC->net_node);
1506         network_talking_to(node, NTT_ADD);
1507         cprintf("%d authenticated as network node '%s'\n", CIT_OK,
1508                 CC->net_node);
1509 }
1510
1511
1512
1513
1514
1515 /*
1516  * Module entry point
1517  */
1518 char *Dynamic_Module_Init(void)
1519 {
1520         CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
1521         CtdlRegisterProtoHook(cmd_snet, "SNET", "Set network config");
1522         CtdlRegisterProtoHook(cmd_netp, "NETP", "Identify as network poller");
1523         CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
1524         return "$Id$";
1525 }