30dc7fe378a74a0a1d04d93ac80288d136c50585
[citadel.git] / citadel / serv_network.c
1 /*
2  * $Id$ 
3  *
4  * This module will eventually replace netproc and some of its utilities.  In
5  * the meantime, it serves as a mailing list manager.
6  *
7  * Copyright (C) 2000-2001 by Art Cancro and others.
8  * This code is released under the terms of the GNU General Public License.
9  *
10  */
11
12 #include "sysdep.h"
13 #include <stdlib.h>
14 #include <unistd.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <signal.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <sys/types.h>
21 #include <dirent.h>
22 #if TIME_WITH_SYS_TIME
23 # include <sys/time.h>
24 # include <time.h>
25 #else
26 # if HAVE_SYS_TIME_H
27 #  include <sys/time.h>
28 # else
29 #  include <time.h>
30 # endif
31 #endif
32
33 #include <sys/wait.h>
34 #include <string.h>
35 #include <limits.h>
36 #include "citadel.h"
37 #include "server.h"
38 #include "sysdep_decls.h"
39 #include "citserver.h"
40 #include "support.h"
41 #include "config.h"
42 #include "dynloader.h"
43 #include "room_ops.h"
44 #include "user_ops.h"
45 #include "policy.h"
46 #include "database.h"
47 #include "msgbase.h"
48 #include "tools.h"
49 #include "internet_addressing.h"
50 #include "serv_network.h"
51
52
53 /*
54  * When we do network processing, it's accomplished in two passes; one to
55  * gather a list of rooms and one to actually do them.  It's ok that rplist
56  * is global; this process *only* runs as part of the housekeeping loop and
57  * therefore only one will run at a time.
58  */
59 struct RoomProcList {
60         struct RoomProcList *next;
61         char name[ROOMNAMELEN];
62 };
63
64 struct RoomProcList *rplist = NULL;
65
66
67 /*
68  * We build a map of the Citadel network during network runs.
69  */
70 struct NetMap {
71         struct NetMap *next;
72         char nodename[SIZ];
73         time_t lastcontact;
74         char nexthop[SIZ];
75 };
76
77 struct NetMap *the_netmap = NULL;
78
79
80
81 /* 
82  * Read the network map from its configuration file into memory.
83  */
84 void read_network_map(void) {
85         char *serialized_map = NULL;
86         int i;
87         char buf[SIZ];
88         struct NetMap *nmptr;
89
90         serialized_map = CtdlGetSysConfig(IGNETMAP);
91         if (serialized_map == NULL) return;     /* if null, no entries */
92
93         /* Use the string tokenizer to grab one line at a time */
94         for (i=0; i<num_tokens(serialized_map, '\n'); ++i) {
95                 extract_token(buf, serialized_map, i, '\n');
96                 nmptr = (struct NetMap *) mallok(sizeof(struct NetMap));
97                 extract(nmptr->nodename, buf, 0);
98                 nmptr->lastcontact = extract_long(buf, 1);
99                 extract(nmptr->nexthop, buf, 2);
100                 nmptr->next = the_netmap;
101                 the_netmap = nmptr;
102         }
103
104         phree(serialized_map);
105 }
106
107
108 /*
109  * Write the network map from memory back to the configuration file.
110  */
111 void write_network_map(void) {
112         char *serialized_map = NULL;
113         struct NetMap *nmptr;
114
115         serialized_map = strdoop("");
116
117         if (the_netmap != NULL) {
118                 for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
119                         serialized_map = reallok(serialized_map,
120                                                 (strlen(serialized_map)+SIZ) );
121                         if (strlen(nmptr->nodename) > 0) {
122                                 sprintf(&serialized_map[strlen(serialized_map)],
123                                         "%s|%ld|%s\n",
124                                         nmptr->nodename,
125                                         nmptr->lastcontact,
126                                         nmptr->nexthop);
127                         }
128                 }
129         }
130
131         CtdlPutSysConfig(IGNETMAP, serialized_map);
132         phree(serialized_map);
133
134         /* Now free the list */
135         while (the_netmap != NULL) {
136                 nmptr = the_netmap->next;
137                 phree(the_netmap);
138                 the_netmap = nmptr;
139         }
140 }
141
142
143
144 /* 
145  * Check the network map and determine whether the supplied node name is
146  * valid.  If it is not a neighbor node, supply the name of a neighbor node
147  * which is the next hop.
148  */
149 int is_valid_node(char *nexthop, char *node) {
150         char *ignetcfg = NULL;
151         int i;
152         char linebuf[SIZ];
153         char buf[SIZ];
154         int retval;
155         struct NetMap *nmptr;
156
157         if (node == NULL) {
158                 return(-1);
159         }
160
161         /*
162          * First try the neighbor nodes
163          */
164         ignetcfg = CtdlGetSysConfig(IGNETCFG);
165         if (ignetcfg == NULL) {
166                 if (nexthop != NULL) {
167                         strcpy(nexthop, "");
168                 }
169                 return(-1);
170         }
171
172         retval = (-1);
173         if (nexthop != NULL) {
174                 strcpy(nexthop, "");
175         }
176
177         /* Use the string tokenizer to grab one line at a time */
178         for (i=0; i<num_tokens(ignetcfg, '\n'); ++i) {
179                 extract_token(linebuf, ignetcfg, i, '\n');
180                 extract(buf, linebuf, 0);
181                 if (!strcasecmp(buf, node)) {
182                         if (nexthop != NULL) {
183                                 strcpy(nexthop, "");
184                         }
185                         retval = 0;
186                 }
187         }
188
189         phree(ignetcfg);
190         if (retval == 0) {
191                 return(retval);         /* yup, it's a direct neighbor */
192         }
193
194         /*      
195          * If we get to this point we have to see if we know the next hop
196          */
197         if (the_netmap != NULL) {
198                 for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
199                         if (!strcasecmp(nmptr->nodename, node)) {
200                                 if (nexthop != NULL) {
201                                         strcpy(nexthop, nmptr->nexthop);
202                                 }
203                                 return(0);
204                         }
205                 }
206         }
207
208         /*
209          * If we get to this point, the supplied node name is bogus.
210          */
211         lprintf(5, "Invalid node name <%s>\n", node);
212         return(-1);
213 }
214
215
216
217
218
219 void cmd_gnet(char *argbuf) {
220         char filename[SIZ];
221         char buf[SIZ];
222         FILE *fp;
223
224         if (CtdlAccessCheck(ac_room_aide)) return;
225         assoc_file_name(filename, &CC->quickroom, "netconfigs");
226         cprintf("%d Network settings for room #%ld <%s>\n",
227                 LISTING_FOLLOWS,
228                 CC->quickroom.QRnumber, CC->quickroom.QRname);
229
230         fp = fopen(filename, "r");
231         if (fp != NULL) {
232                 while (fgets(buf, sizeof buf, fp) != NULL) {
233                         buf[strlen(buf)-1] = 0;
234                         cprintf("%s\n", buf);
235                 }
236                 fclose(fp);
237         }
238
239         cprintf("000\n");
240 }
241
242
243 void cmd_snet(char *argbuf) {
244         char tempfilename[SIZ];
245         char filename[SIZ];
246         char buf[SIZ];
247         FILE *fp;
248
249         if (CtdlAccessCheck(ac_room_aide)) return;
250         safestrncpy(tempfilename, tmpnam(NULL), sizeof tempfilename);
251         assoc_file_name(filename, &CC->quickroom, "netconfigs");
252
253         fp = fopen(tempfilename, "w");
254         if (fp == NULL) {
255                 cprintf("%d Cannot open %s: %s\n",
256                         ERROR+INTERNAL_ERROR,
257                         tempfilename,
258                         strerror(errno));
259         }
260
261         cprintf("%d %s\n", SEND_LISTING, tempfilename);
262         while (client_gets(buf), strcmp(buf, "000")) {
263                 fprintf(fp, "%s\n", buf);
264         }
265         fclose(fp);
266
267         /* Now copy the temp file to its permanent location
268          * (We use /bin/mv instead of link() because they may be on
269          * different filesystems)
270          */
271         unlink(filename);
272         snprintf(buf, sizeof buf, "/bin/mv %s %s", tempfilename, filename);
273         system(buf);
274 }
275
276
277
278 /*
279  * Spools out one message from the list.
280  */
281 void network_spool_msg(long msgnum, void *userdata) {
282         struct SpoolControl *sc;
283         struct namelist *nptr;
284         int err;
285         int i;
286         char *instr = NULL;
287         char *newpath = NULL;
288         size_t instr_len = SIZ;
289         struct CtdlMessage *msg;
290         struct CtdlMessage *imsg;
291         struct ser_ret sermsg;
292         FILE *fp;
293         char filename[SIZ];
294         char buf[SIZ];
295         int bang = 0;
296         int send = 1;
297         int delete_after_send = 0;      /* Set to 1 to delete after spooling */
298
299         sc = (struct SpoolControl *)userdata;
300
301         /*
302          * Process mailing list recipients
303          */
304         if (sc->listrecps != NULL) {
305         
306                 /* First, copy it to the spoolout room */
307                 err = CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, msgnum, 0);
308                 if (err != 0) return;
309
310                 /* 
311                  * Figure out how big a buffer we need to allocate
312                  */
313                 for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
314                         instr_len = instr_len + strlen(nptr->name);
315                 }
316         
317                 /*
318                  * allocate...
319                  */
320                 lprintf(9, "Generating delivery instructions\n");
321                 instr = mallok(instr_len);
322                 if (instr == NULL) {
323                         lprintf(1, "Cannot allocate %d bytes for instr...\n",
324                                 instr_len);
325                         abort();
326                 }
327                 sprintf(instr,
328                         "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
329                         "bounceto|postmaster@%s\n" ,
330                         SPOOLMIME, msgnum, time(NULL), config.c_fqdn );
331         
332                 /* Generate delivery instructions for each recipient */
333                 for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
334                         sprintf(&instr[strlen(instr)], "remote|%s|0||\n",
335                                 nptr->name);
336                 }
337         
338                 /*
339                  * Generate a message from the instructions
340                  */
341                 imsg = mallok(sizeof(struct CtdlMessage));
342                 memset(imsg, 0, sizeof(struct CtdlMessage));
343                 imsg->cm_magic = CTDLMESSAGE_MAGIC;
344                 imsg->cm_anon_type = MES_NORMAL;
345                 imsg->cm_format_type = FMT_RFC822;
346                 imsg->cm_fields['A'] = strdoop("Citadel");
347                 imsg->cm_fields['M'] = instr;
348         
349                 /* Save delivery instructions in spoolout room */
350                 CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL);
351                 CtdlFreeMessage(imsg);
352         }
353         
354         /*
355          * Process IGnet push shares
356          */
357         if (sc->ignet_push_shares != NULL) {
358         
359                 msg = CtdlFetchMessage(msgnum);
360                 if (msg != NULL) {
361
362                         /* Prepend our node name to the Path field whenever
363                          * sending a message to another IGnet node
364                          */
365                         if (msg->cm_fields['P'] == NULL) {
366                                 msg->cm_fields['P'] = strdoop("username");
367                         }
368                         newpath = mallok(strlen(msg->cm_fields['P']) + 
369                                         strlen(config.c_nodename) + 2);
370                         sprintf(newpath, "%s!%s", config.c_nodename,
371                                         msg->cm_fields['P']);
372                         phree(msg->cm_fields['P']);
373                         msg->cm_fields['P'] = newpath;
374
375                         /*
376                          * Force the message to appear in the correct room
377                          * on the far end by setting the C field correctly
378                          */
379                         if (msg->cm_fields['C'] != NULL) {
380                                 phree(msg->cm_fields['C']);
381                         }
382                         msg->cm_fields['C'] = strdoop(CC->quickroom.QRname);
383
384                         /*
385                          * Determine if this message is set to be deleted
386                          * after sending out on the network
387                          */
388                         if (msg->cm_fields['S'] != NULL) {
389                                 if (!strcasecmp(msg->cm_fields['S'],
390                                    "CANCEL")) {
391                                         delete_after_send = 1;
392                                 }
393                         }
394
395                         /* 
396                          * Now serialize it for transmission
397                          */
398                         serialize_message(&sermsg, msg);
399                         CtdlFreeMessage(msg);
400
401                         /* Now send it to every node */
402                         for (nptr = sc->ignet_push_shares; nptr != NULL;
403                             nptr = nptr->next) {
404
405                                 send = 1;
406
407                                 /* Check for valid node name */
408                                 if (is_valid_node(NULL, nptr->name) != 0) {
409                                         lprintf(3, "Invalid node <%s>\n",
410                                                 nptr->name);
411                                         send = 0;
412                                 }
413
414                                 /* Check for split horizon */
415                                 bang = num_tokens(msg->cm_fields['P'], '!');
416                                 if (bang > 1) for (i=0; i<(bang-1); ++i) {
417                                         extract_token(buf, msg->cm_fields['P'],
418                                                 i, '!');
419                                         if (!strcasecmp(buf, nptr->name)) {
420                                                 send = 0;
421                                         }
422                                 }
423
424                                 /* Send the message */
425                                 if (send == 1) {
426                                         sprintf(filename,
427                                                 "./network/spoolout/%s",
428                                                 nptr->name);
429                                         fp = fopen(filename, "ab");
430                                         if (fp != NULL) {
431                                                 fwrite(sermsg.ser,
432                                                         sermsg.len, 1, fp);
433                                                 fclose(fp);
434                                         }
435                                 }
436                         }
437                         phree(sermsg.ser);
438                 }
439         }
440
441         /* update lastsent */
442         sc->lastsent = msgnum;
443
444         /* Delete this message if delete-after-send is set */
445         if (delete_after_send) {
446                 CtdlDeleteMessages(CC->quickroom.QRname, msgnum, "");
447         }
448
449 }
450         
451
452
453
454 /*
455  * Batch up and send all outbound traffic from the current room
456  */
457 void network_spoolout_room(char *room_to_spool) {
458         char filename[SIZ];
459         char buf[SIZ];
460         char instr[SIZ];
461         FILE *fp;
462         struct SpoolControl sc;
463         /* struct namelist *digestrecps = NULL; */
464         struct namelist *nptr;
465
466         lprintf(7, "Spooling <%s>\n", room_to_spool);
467         if (getroom(&CC->quickroom, room_to_spool) != 0) {
468                 lprintf(1, "ERROR: cannot load <%s>\n", room_to_spool);
469                 return;
470         }
471
472         memset(&sc, 0, sizeof(struct SpoolControl));
473         assoc_file_name(filename, &CC->quickroom, "netconfigs");
474
475         fp = fopen(filename, "r");
476         if (fp == NULL) {
477                 lprintf(7, "Outbound batch processing skipped for <%s>\n",
478                         CC->quickroom.QRname);
479                 return;
480         }
481
482         lprintf(5, "Outbound batch processing started for <%s>\n",
483                 CC->quickroom.QRname);
484
485         while (fgets(buf, sizeof buf, fp) != NULL) {
486                 buf[strlen(buf)-1] = 0;
487
488                 extract(instr, buf, 0);
489                 if (!strcasecmp(instr, "lastsent")) {
490                         sc.lastsent = extract_long(buf, 1);
491                 }
492                 else if (!strcasecmp(instr, "listrecp")) {
493                         nptr = (struct namelist *)
494                                 mallok(sizeof(struct namelist));
495                         nptr->next = sc.listrecps;
496                         extract(nptr->name, buf, 1);
497                         sc.listrecps = nptr;
498                 }
499                 else if (!strcasecmp(instr, "ignet_push_share")) {
500                         nptr = (struct namelist *)
501                                 mallok(sizeof(struct namelist));
502                         nptr->next = sc.ignet_push_shares;
503                         extract(nptr->name, buf, 1);
504                         sc.ignet_push_shares = nptr;
505                 }
506
507
508         }
509         fclose(fp);
510
511
512         /* Do something useful */
513         CtdlForEachMessage(MSGS_GT, sc.lastsent, (-63), NULL, NULL,
514                 network_spool_msg, &sc);
515
516
517         /* Now rewrite the config file */
518         fp = fopen(filename, "w");
519         if (fp == NULL) {
520                 lprintf(1, "ERROR: cannot open %s: %s\n",
521                         filename, strerror(errno));
522         }
523         else {
524                 fprintf(fp, "lastsent|%ld\n", sc.lastsent);
525
526                 /* Write out the listrecps while freeing from memory at the
527                  * same time.  Am I clever or what?  :)
528                  */
529                 while (sc.listrecps != NULL) {
530                         fprintf(fp, "listrecp|%s\n", sc.listrecps->name);
531                         nptr = sc.listrecps->next;
532                         phree(sc.listrecps);
533                         sc.listrecps = nptr;
534                 }
535                 while (sc.ignet_push_shares != NULL) {
536                         fprintf(fp, "ignet_push_share|%s\n",
537                                 sc.ignet_push_shares->name);
538                         nptr = sc.ignet_push_shares->next;
539                         phree(sc.ignet_push_shares);
540                         sc.ignet_push_shares = nptr;
541                 }
542
543                 fclose(fp);
544         }
545
546         lprintf(5, "Outbound batch processing finished for <%s>\n",
547                 CC->quickroom.QRname);
548 }
549
550
551 /*
552  * Batch up and send all outbound traffic from the current room
553  */
554 void network_queue_room(struct quickroom *qrbuf, void *data) {
555         struct RoomProcList *ptr;
556
557         ptr = (struct RoomProcList *) mallok(sizeof (struct RoomProcList));
558         if (ptr == NULL) return;
559
560         safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
561         ptr->next = rplist;
562         rplist = ptr;
563 }
564
565
566 /*
567  * Learn topology from path fields
568  */
569 void network_learn_topology(char *node, char *path) {
570         char nexthop[SIZ];
571         struct NetMap *nmptr;
572
573         strcpy(nexthop, "");
574
575         if (num_tokens(path, '!') < 3) return;
576         for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
577                 if (!strcasecmp(nmptr->nodename, node)) {
578                         extract_token(nmptr->nexthop, path, 0, '!');
579                         nmptr->lastcontact = time(NULL);
580                         return;
581                 }
582         }
583
584         /* If we got here then it's not in the map, so add it. */
585         nmptr = (struct NetMap *) mallok(sizeof (struct NetMap));
586         strcpy(nmptr->nodename, node);
587         nmptr->lastcontact = time(NULL);
588         extract_token(nmptr->nexthop, path, 0, '!');
589         nmptr->next = the_netmap;
590         the_netmap = nmptr;
591 }
592
593
594
595 /*
596  * Process a buffer containing a single message from a single file
597  * from the inbound queue 
598  */
599 void network_process_buffer(char *buffer, long size) {
600         struct CtdlMessage *msg;
601         long pos;
602         int field;
603         int a;
604         int e = MES_LOCAL;
605         struct usersupp tempUS;
606         char recp[SIZ];
607         char target_room[ROOMNAMELEN];
608         struct ser_ret sermsg;
609         char *oldpath = NULL;
610         char filename[SIZ];
611         FILE *fp;
612
613         /* Set default target room to trash */
614         strcpy(target_room, TWITROOM);
615
616         /* Load the message into memory */
617         msg = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage));
618         memset(msg, 0, sizeof(struct CtdlMessage));
619         msg->cm_magic = CTDLMESSAGE_MAGIC;
620         msg->cm_anon_type = buffer[1];
621         msg->cm_format_type = buffer[2];
622
623         for (pos = 3; pos < size; ++pos) {
624                 field = buffer[pos];
625                 msg->cm_fields[field] = strdoop(&buffer[pos+1]);
626                 pos = pos + strlen(&buffer[(int)pos]);
627         }
628
629         /* Check for message routing */
630         if (msg->cm_fields['D'] != NULL) {
631                 if (strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
632
633                         /* route the message */
634                         if (is_valid_node(NULL, msg->cm_fields['D']) == 0) {
635
636                                 /* prepend our node to the path */
637                                 if (msg->cm_fields['P'] != NULL) {
638                                         oldpath = msg->cm_fields['P'];
639                                         msg->cm_fields['P'] = NULL;
640                                 }
641                                 else {
642                                         oldpath = strdoop("unknown_user");
643                                 }
644                                 msg->cm_fields['P'] =
645                                         mallok(strlen(oldpath) + SIZ);
646                                 sprintf(msg->cm_fields['P'], "%s!%s",
647                                         config.c_nodename, oldpath);
648                                 phree(oldpath);
649
650                                 /* serialize the message */
651                                 serialize_message(&sermsg, msg);
652
653                                 /* now send it */
654                                 sprintf(filename,
655                                         "./network/spoolout/%s",
656                                         msg->cm_fields['D']);
657                                 fp = fopen(filename, "ab");
658                                 if (fp != NULL) {
659                                         fwrite(sermsg.ser,
660                                                 sermsg.len, 1, fp);
661                                         fclose(fp);
662                                 }
663                                 phree(sermsg.ser);
664                                 CtdlFreeMessage(msg);
665                                 return;
666                         }
667                         
668                         else {  /* invalid destination node name */
669
670                                 /* FIXME bounce the msg */
671
672                         }
673                 }
674         }
675
676         /* FIXME check to see if we already have this message */
677
678         /* Learn network topology from the path */
679         if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
680                 network_learn_topology(msg->cm_fields['N'], 
681                                         msg->cm_fields['P']);
682         }
683
684         /* Does it have a recipient?  If so, validate it... */
685         if (msg->cm_fields['R'] != NULL) {
686
687                 safestrncpy(recp, msg->cm_fields['R'], sizeof(recp));
688
689                 e = alias(recp);        /* alias and mail type */
690                 if ((recp[0] == 0) || (e == MES_ERROR)) {
691
692                         /* FIXME bounce the msg */
693
694                 }
695                 else if (e == MES_LOCAL) {
696                         a = getuser(&tempUS, recp);
697                         if (a != 0) {
698                                 /* FIXME bounce the msg */
699                         }
700                         else {
701                                 MailboxName(target_room, &tempUS, MAILROOM);
702                         }
703                 }
704         }
705
706         else if (msg->cm_fields['C'] != NULL) {
707                 safestrncpy(target_room,
708                         msg->cm_fields['C'],
709                         sizeof target_room);
710         }
711
712         else if (msg->cm_fields['O'] != NULL) {
713                 safestrncpy(target_room,
714                         msg->cm_fields['O'],
715                         sizeof target_room);
716         }
717
718         /* save the message into a room */
719         msg->cm_flags = CM_SKIP_HOOKS;
720         CtdlSaveMsg(msg, "", target_room, 0);
721         CtdlFreeMessage(msg);
722 }
723
724
725 /*
726  * Process a single message from a single file from the inbound queue 
727  */
728 void network_process_message(FILE *fp, long msgstart, long msgend) {
729         long hold_pos;
730         long size;
731         char *buffer;
732
733         hold_pos = ftell(fp);
734         size = msgend - msgstart + 1;
735         buffer = mallok(size);
736         if (buffer != NULL) {
737                 fseek(fp, msgstart, SEEK_SET);
738                 fread(buffer, size, 1, fp);
739                 network_process_buffer(buffer, size);
740                 phree(buffer);
741         }
742
743         fseek(fp, hold_pos, SEEK_SET);
744 }
745
746
747 /*
748  * Process a single file from the inbound queue 
749  */
750 void network_process_file(char *filename) {
751         FILE *fp;
752         long msgstart = (-1L);
753         long msgend = (-1L);
754         long msgcur = 0L;
755         int ch;
756
757         lprintf(7, "network: processing <%s>\n", filename);
758
759         fp = fopen(filename, "rb");
760         if (fp == NULL) {
761                 lprintf(5, "Error opening %s: %s\n",
762                         filename, strerror(errno));
763                 return;
764         }
765
766         /* Look for messages in the data stream and break them out */
767         while (ch = getc(fp), ch >= 0) {
768         
769                 if (ch == 255) {
770                         if (msgstart >= 0L) {
771                                 msgend = msgcur - 1;
772                                 network_process_message(fp, msgstart, msgend);
773                         }
774                         msgstart = msgcur;
775                 }
776
777                 ++msgcur;
778         }
779
780         msgend = msgcur - 1;
781         if (msgstart >= 0L) {
782                 network_process_message(fp, msgstart, msgend);
783         }
784
785         fclose(fp);
786         unlink(filename);
787 }
788
789
790 /*
791  * Process anything in the inbound queue
792  */
793 void network_do_spoolin(void) {
794         DIR *dp;
795         struct dirent *d;
796         char filename[SIZ];
797
798         dp = opendir("./network/spoolin");
799         if (dp == NULL) return;
800
801         while (d = readdir(dp), d != NULL) {
802                 sprintf(filename, "./network/spoolin/%s", d->d_name);
803                 network_process_file(filename);
804         }
805
806
807         closedir(dp);
808 }
809
810
811 /*
812  * network_do_queue()
813  * 
814  * Run through the rooms doing various types of network stuff.
815  */
816 void network_do_queue(void) {
817         static int doing_queue = 0;
818         static time_t last_run = 0L;
819         struct RoomProcList *ptr;
820
821         /*
822          * Run no more frequently than once every n seconds
823          */
824         if ( (time(NULL) - last_run) < NETWORK_QUEUE_FREQUENCY ) return;
825
826         /*
827          * This is a simple concurrency check to make sure only one queue run
828          * is done at a time.  We could do this with a mutex, but since we
829          * don't really require extremely fine granularity here, we'll do it
830          * with a static variable instead.
831          */
832         if (doing_queue) return;
833         doing_queue = 1;
834         last_run = time(NULL);
835
836         read_network_map();
837
838         /* 
839          * Go ahead and run the queue
840          */
841         lprintf(7, "network: loading outbound queue\n");
842         ForEachRoom(network_queue_room, NULL);
843
844         lprintf(7, "network: running outbound queue\n");
845         while (rplist != NULL) {
846                 network_spoolout_room(rplist->name);
847                 ptr = rplist;
848                 rplist = rplist->next;
849                 phree(ptr);
850         }
851
852         lprintf(7, "network: processing inbound queue\n");
853         network_do_spoolin();
854
855         write_network_map();
856
857         lprintf(7, "network: queue run completed\n");
858         doing_queue = 0;
859 }
860
861
862 /*
863  * Module entry point
864  */
865 char *Dynamic_Module_Init(void)
866 {
867         CtdlRegisterProtoHook(cmd_gnet, "GNET", "Get network config");
868         CtdlRegisterProtoHook(cmd_snet, "SNET", "Get network config");
869         CtdlRegisterSessionHook(network_do_queue, EVT_TIMER);
870         return "$Id$";
871 }