544bf1f04a60d1c0bd636f8cf3e04934e63ce4d5
[citadel.git] / citadel / modules / network / serv_netspool.c
1 /*
2  * This module handles shared rooms, inter-Citadel mail, and outbound
3  * mailing list processing.
4  *
5  * Copyright (c) 2000-2011 by the citadel.org team
6  *
7  *  This program is open source software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
22  * This is a fairly high-level type of critical section.  It ensures that no
23  * two threads work on the netconfigs files at the same time.  Since we do
24  * so many things inside these, here are the rules:
25  *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
26  *  2. Do *not* perform any I/O with the client during these sections.
27  *
28  */
29
30 /*
31  * Duration of time (in seconds) after which pending list subscribe/unsubscribe
32  * requests that have not been confirmed will be deleted.
33  */
34 #define EXP     259200  /* three days */
35
36 #include "sysdep.h"
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <stdio.h>
40 #include <fcntl.h>
41 #include <ctype.h>
42 #include <signal.h>
43 #include <pwd.h>
44 #include <errno.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <dirent.h>
48 #if TIME_WITH_SYS_TIME
49 # include <sys/time.h>
50 # include <time.h>
51 #else
52 # if HAVE_SYS_TIME_H
53 #  include <sys/time.h>
54 # else
55 #  include <time.h>
56 # endif
57 #endif
58 #ifdef HAVE_SYSCALL_H
59 # include <syscall.h>
60 #else 
61 # if HAVE_SYS_SYSCALL_H
62 #  include <sys/syscall.h>
63 # endif
64 #endif
65
66 #include <sys/wait.h>
67 #include <string.h>
68 #include <limits.h>
69 #include <libcitadel.h>
70 #include "citadel.h"
71 #include "server.h"
72 #include "citserver.h"
73 #include "support.h"
74 #include "config.h"
75 #include "user_ops.h"
76 #include "database.h"
77 #include "msgbase.h"
78 #include "internet_addressing.h"
79 #include "serv_network.h"
80 #include "clientsocket.h"
81 #include "file_ops.h"
82 #include "citadel_dirs.h"
83 #include "threads.h"
84
85 #ifndef HAVE_SNPRINTF
86 #include "snprintf.h"
87 #endif
88
89 #include "context.h"
90 #include "netconfig.h"
91 #include "netspool.h"
92 #include "netmail.h"
93 #include "ctdl_module.h"
94
95 /*
96  * Learn topology from path fields
97  */
98 void network_learn_topology(char *node, char *path) {
99         char nexthop[256];
100         NetMap *nmptr;
101
102         strcpy(nexthop, "");
103
104         if (num_tokens(path, '!') < 3) return;
105         for (nmptr = the_netmap; nmptr != NULL; nmptr = nmptr->next) {
106                 if (!strcasecmp(nmptr->nodename, node)) {
107                         extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop);
108                         nmptr->lastcontact = time(NULL);
109                         ++netmap_changed;
110                         return;
111                 }
112         }
113
114         /* If we got here then it's not in the map, so add it. */
115         nmptr = (NetMap *) malloc(sizeof (NetMap));
116         strcpy(nmptr->nodename, node);
117         nmptr->lastcontact = time(NULL);
118         extract_token(nmptr->nexthop, path, 0, '!', sizeof nmptr->nexthop);
119         nmptr->next = the_netmap;
120         the_netmap = nmptr;
121         ++netmap_changed;
122 }
123
124
125         
126
127 int read_spoolcontrol_file(SpoolControl **scc, char *filename)
128 {
129         FILE *fp;
130         char instr[SIZ];
131         char buf[SIZ];
132         char nodename[256];
133         char roomname[ROOMNAMELEN];
134         size_t miscsize = 0;
135         size_t linesize = 0;
136         int skipthisline = 0;
137         namelist *nptr = NULL;
138         maplist *mptr = NULL;
139         SpoolControl *sc;
140
141         fp = fopen(filename, "r");
142         if (fp == NULL) {
143                 return 0;
144         }
145         sc = malloc(sizeof(SpoolControl));
146         memset(sc, 0, sizeof(SpoolControl));
147         *scc = sc;
148
149         while (fgets(buf, sizeof buf, fp) != NULL) {
150                 buf[strlen(buf)-1] = 0;
151
152                 extract_token(instr, buf, 0, '|', sizeof instr);
153                 if (!strcasecmp(instr, strof(lastsent))) {
154                         sc->lastsent = extract_long(buf, 1);
155                 }
156                 else if (!strcasecmp(instr, strof(listrecp))) {
157                         nptr = (namelist *)
158                                 malloc(sizeof(namelist));
159                         nptr->next = sc->listrecps;
160                         extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
161                         sc->listrecps = nptr;
162                 }
163                 else if (!strcasecmp(instr, strof(participate))) {
164                         nptr = (namelist *)
165                                 malloc(sizeof(namelist));
166                         nptr->next = sc->participates;
167                         extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
168                         sc->participates = nptr;
169                 }
170                 else if (!strcasecmp(instr, strof(digestrecp))) {
171                         nptr = (namelist *)
172                                 malloc(sizeof(namelist));
173                         nptr->next = sc->digestrecps;
174                         extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
175                         sc->digestrecps = nptr;
176                 }
177                 else if (!strcasecmp(instr, strof(ignet_push_share))) {
178                         extract_token(nodename, buf, 1, '|', sizeof nodename);
179                         extract_token(roomname, buf, 2, '|', sizeof roomname);
180                         mptr = (maplist *) malloc(sizeof(maplist));
181                         mptr->next = sc->ignet_push_shares;
182                         strcpy(mptr->remote_nodename, nodename);
183                         strcpy(mptr->remote_roomname, roomname);
184                         sc->ignet_push_shares = mptr;
185                 }
186                 else {
187                         /* Preserve 'other' lines ... *unless* they happen to
188                          * be subscribe/unsubscribe pendings with expired
189                          * timestamps.
190                          */
191                         skipthisline = 0;
192                         if (!strncasecmp(buf, strof(subpending)"|", 11)) {
193                                 if (time(NULL) - extract_long(buf, 4) > EXP) {
194                                         skipthisline = 1;
195                                 }
196                         }
197                         if (!strncasecmp(buf, strof(unsubpending)"|", 13)) {
198                                 if (time(NULL) - extract_long(buf, 3) > EXP) {
199                                         skipthisline = 1;
200                                 }
201                         }
202
203                         if (skipthisline == 0) {
204                                 linesize = strlen(buf);
205                                 sc->misc = realloc(sc->misc,
206                                         (miscsize + linesize + 2) );
207                                 sprintf(&sc->misc[miscsize], "%s\n", buf);
208                                 miscsize = miscsize + linesize + 1;
209                         }
210                 }
211
212
213         }
214         fclose(fp);
215         return 1;
216 }
217
218 void free_spoolcontrol_struct(SpoolControl **scc)
219 {
220         SpoolControl *sc;
221         namelist *nptr = NULL;
222         maplist *mptr = NULL;
223
224         sc = *scc;
225         while (sc->listrecps != NULL) {
226                 nptr = sc->listrecps->next;
227                 free(sc->listrecps);
228                 sc->listrecps = nptr;
229         }
230         /* Do the same for digestrecps */
231         while (sc->digestrecps != NULL) {
232                 nptr = sc->digestrecps->next;
233                 free(sc->digestrecps);
234                 sc->digestrecps = nptr;
235         }
236         /* Do the same for participates */
237         while (sc->participates != NULL) {
238                 nptr = sc->participates->next;
239                 free(sc->participates);
240                 sc->participates = nptr;
241         }
242         while (sc->ignet_push_shares != NULL) {
243                 mptr = sc->ignet_push_shares->next;
244                 free(sc->ignet_push_shares);
245                 sc->ignet_push_shares = mptr;
246         }
247         free(sc->misc);
248         free(sc);
249         *scc=NULL;
250 }
251
252 int writenfree_spoolcontrol_file(SpoolControl **scc, char *filename)
253 {
254         char tempfilename[PATH_MAX];
255         int TmpFD;
256         SpoolControl *sc;
257         namelist *nptr = NULL;
258         maplist *mptr = NULL;
259         long len;
260         time_t unixtime;
261         struct timeval tv;
262         long reltid; /* if we don't have SYS_gettid, use "random" value */
263         StrBuf *Cfg;
264         int rc;
265
266         len = strlen(filename);
267         memcpy(tempfilename, filename, len + 1);
268
269
270 #if defined(HAVE_SYSCALL_H) && defined (SYS_gettid)
271         reltid = syscall(SYS_gettid);
272 #endif
273         gettimeofday(&tv, NULL);
274         /* Promote to time_t; types differ on some OSes (like darwin) */
275         unixtime = tv.tv_sec;
276
277         sprintf(tempfilename + len, ".%ld-%ld", reltid, unixtime);
278         sc = *scc;
279         errno = 0;
280         TmpFD = open(tempfilename, O_CREAT|O_EXCL|O_RDWR, S_IRUSR|S_IWUSR);
281         Cfg = NewStrBuf();
282         if ((TmpFD < 0) || (errno != 0)) {
283                 syslog(LOG_CRIT, "ERROR: cannot open %s: %s\n",
284                         filename, strerror(errno));
285                 free_spoolcontrol_struct(scc);
286                 unlink(tempfilename);
287         }
288         else {
289                 fchown(TmpFD, config.c_ctdluid, 0);
290                 StrBufAppendPrintf(Cfg, "lastsent|%ld\n", sc->lastsent);
291                 
292                 /* Write out the listrecps while freeing from memory at the
293                  * same time.  Am I clever or what?  :)
294                  */
295                 while (sc->listrecps != NULL) {
296                     StrBufAppendPrintf(Cfg, "listrecp|%s\n", sc->listrecps->name);
297                         nptr = sc->listrecps->next;
298                         free(sc->listrecps);
299                         sc->listrecps = nptr;
300                 }
301                 /* Do the same for digestrecps */
302                 while (sc->digestrecps != NULL) {
303                         StrBufAppendPrintf(Cfg, "digestrecp|%s\n", sc->digestrecps->name);
304                         nptr = sc->digestrecps->next;
305                         free(sc->digestrecps);
306                         sc->digestrecps = nptr;
307                 }
308                 /* Do the same for participates */
309                 while (sc->participates != NULL) {
310                         StrBufAppendPrintf(Cfg, "participate|%s\n", sc->participates->name);
311                         nptr = sc->participates->next;
312                         free(sc->participates);
313                         sc->participates = nptr;
314                 }
315                 while (sc->ignet_push_shares != NULL) {
316                         StrBufAppendPrintf(Cfg, "ignet_push_share|%s", sc->ignet_push_shares->remote_nodename);
317                         if (!IsEmptyStr(sc->ignet_push_shares->remote_roomname)) {
318                                 StrBufAppendPrintf(Cfg, "|%s", sc->ignet_push_shares->remote_roomname);
319                         }
320                         StrBufAppendPrintf(Cfg, "\n");
321                         mptr = sc->ignet_push_shares->next;
322                         free(sc->ignet_push_shares);
323                         sc->ignet_push_shares = mptr;
324                 }
325                 if (sc->misc != NULL) {
326                         StrBufAppendBufPlain(Cfg, sc->misc, -1, 0);
327                 }
328                 free(sc->misc);
329
330                 rc = write(TmpFD, ChrPtr(Cfg), StrLength(Cfg));
331                 if ((rc >=0 ) && (rc == StrLength(Cfg))) 
332                 {
333                         close(TmpFD);
334                         rename(tempfilename, filename);
335                 }
336                 else {
337                         syslog(LOG_EMERG, 
338                                       "unable to write %s; [%s]; not enough space on the disk?\n", 
339                                       tempfilename, 
340                                       strerror(errno));
341                         close(TmpFD);
342                         unlink(tempfilename);
343                 }
344                 FreeStrBuf(&Cfg);
345                 free(sc);
346                 *scc=NULL;
347         }
348         return 1;
349 }
350 int is_recipient(SpoolControl *sc, const char *Name)
351 {
352         namelist *nptr;
353         size_t len;
354
355         len = strlen(Name);
356         nptr = sc->listrecps;
357         while (nptr != NULL) {
358                 if (strncmp(Name, nptr->name, len)==0)
359                         return 1;
360                 nptr = nptr->next;
361         }
362         /* Do the same for digestrecps */
363         nptr = sc->digestrecps;
364         while (nptr != NULL) {
365                 if (strncmp(Name, nptr->name, len)==0)
366                         return 1;
367                 nptr = nptr->next;
368         }
369         /* Do the same for participates */
370         nptr = sc->participates;
371         while (nptr != NULL) {
372                 if (strncmp(Name, nptr->name, len)==0)
373                         return 1;
374                 nptr = nptr->next;
375         }
376         return 0;
377 }
378
379
380 /*
381  * Batch up and send all outbound traffic from the current room
382  */
383 void network_spoolout_room(char *room_to_spool) {
384         char buf[SIZ];
385         char filename[PATH_MAX];
386         SpoolControl *sc;
387         int i;
388
389         /*
390          * If the room doesn't exist, don't try to perform its networking tasks.
391          * Normally this should never happen, but once in a while maybe a room gets
392          * queued for networking and then deleted before it can happen.
393          */
394         if (CtdlGetRoom(&CC->room, room_to_spool) != 0) {
395                 syslog(LOG_CRIT, "ERROR: cannot load <%s>\n", room_to_spool);
396                 return;
397         }
398
399         assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
400         begin_critical_section(S_NETCONFIGS);
401
402         /* Only do net processing for rooms that have netconfigs */
403         if (!read_spoolcontrol_file(&sc, filename))
404         {
405                 end_critical_section(S_NETCONFIGS);
406                 return;
407         }
408         syslog(LOG_INFO, "Networking started for <%s>\n", CC->room.QRname);
409
410         /* If there are digest recipients, we have to build a digest */
411         if (sc->digestrecps != NULL) {
412                 sc->digestfp = tmpfile();
413                 fprintf(sc->digestfp, "Content-type: text/plain\n\n");
414         }
415
416         /* Do something useful */
417         CtdlForEachMessage(MSGS_GT, sc->lastsent, NULL, NULL, NULL,
418                 network_spool_msg, sc);
419
420         /* If we wrote a digest, deliver it and then close it */
421         snprintf(buf, sizeof buf, "room_%s@%s",
422                 CC->room.QRname, config.c_fqdn);
423         for (i=0; buf[i]; ++i) {
424                 buf[i] = tolower(buf[i]);
425                 if (isspace(buf[i])) buf[i] = '_';
426         }
427         if (sc->digestfp != NULL) {
428                 fprintf(sc->digestfp,   " -----------------------------------"
429                                         "------------------------------------"
430                                         "-------\n"
431                                         "You are subscribed to the '%s' "
432                                         "list.\n"
433                                         "To post to the list: %s\n",
434                                         CC->room.QRname, buf
435                 );
436                 network_deliver_digest(sc);     /* deliver and close */
437         }
438
439         /* Now rewrite the config file */
440         writenfree_spoolcontrol_file(&sc, filename);
441         end_critical_section(S_NETCONFIGS);
442 }
443
444 /*
445  * Process a buffer containing a single message from a single file
446  * from the inbound queue 
447  */
448 void network_process_buffer(char *buffer, long size) {
449         struct CtdlMessage *msg = NULL;
450         long pos;
451         int field;
452         struct recptypes *recp = NULL;
453         char target_room[ROOMNAMELEN];
454         struct ser_ret sermsg;
455         char *oldpath = NULL;
456         char filename[PATH_MAX];
457         FILE *fp;
458         char nexthop[SIZ];
459         unsigned char firstbyte;
460         unsigned char lastbyte;
461
462         syslog(LOG_DEBUG, "network_process_buffer() processing %ld bytes\n", size);
463
464         /* Validate just a little bit.  First byte should be FF and * last byte should be 00. */
465         firstbyte = buffer[0];
466         lastbyte = buffer[size-1];
467         if ( (firstbyte != 255) || (lastbyte != 0) ) {
468                 syslog(LOG_ERR, "Corrupt message ignored.  Length=%ld, firstbyte = %d, lastbyte = %d\n",
469                         size, firstbyte, lastbyte);
470                 return;
471         }
472
473         /* Set default target room to trash */
474         strcpy(target_room, TWITROOM);
475
476         /* Load the message into memory */
477         msg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage));
478         memset(msg, 0, sizeof(struct CtdlMessage));
479         msg->cm_magic = CTDLMESSAGE_MAGIC;
480         msg->cm_anon_type = buffer[1];
481         msg->cm_format_type = buffer[2];
482
483         for (pos = 3; pos < size; ++pos) {
484                 field = buffer[pos];
485                 msg->cm_fields[field] = strdup(&buffer[pos+1]);
486                 pos = pos + strlen(&buffer[(int)pos]);
487         }
488
489         /* Check for message routing */
490         if (msg->cm_fields['D'] != NULL) {
491                 if (strcasecmp(msg->cm_fields['D'], config.c_nodename)) {
492
493                         /* route the message */
494                         strcpy(nexthop, "");
495                         if (is_valid_node(nexthop, NULL, msg->cm_fields['D']) == 0) {
496                                 /* prepend our node to the path */
497                                 if (msg->cm_fields['P'] != NULL) {
498                                         oldpath = msg->cm_fields['P'];
499                                         msg->cm_fields['P'] = NULL;
500                                 }
501                                 else {
502                                         oldpath = strdup("unknown_user");
503                                 }
504                                 size = strlen(oldpath) + SIZ;
505                                 msg->cm_fields['P'] = malloc(size);
506                                 snprintf(msg->cm_fields['P'], size, "%s!%s",
507                                         config.c_nodename, oldpath);
508                                 free(oldpath);
509
510                                 /* serialize the message */
511                                 serialize_message(&sermsg, msg);
512
513                                 /* now send it */
514                                 if (IsEmptyStr(nexthop)) {
515                                         strcpy(nexthop, msg->cm_fields['D']);
516                                 }
517                                 snprintf(filename, 
518                                         sizeof filename,
519                                         "%s/%s@%lx%x",
520                                         ctdl_netout_dir,
521                                         nexthop,
522                                         time(NULL),
523                                         rand()
524                                 );
525                                 syslog(LOG_DEBUG, "Appending to %s\n", filename);
526                                 fp = fopen(filename, "ab");
527                                 if (fp != NULL) {
528                                         fwrite(sermsg.ser, sermsg.len, 1, fp);
529                                         fclose(fp);
530                                 }
531                                 else {
532                                         syslog(LOG_ERR, "%s: %s\n", filename, strerror(errno));
533                                 }
534                                 free(sermsg.ser);
535                                 CtdlFreeMessage(msg);
536                                 return;
537                         }
538                         
539                         else {  /* invalid destination node name */
540
541                                 network_bounce(msg,
542 "A message you sent could not be delivered due to an invalid destination node"
543 " name.  Please check the address and try sending the message again.\n");
544                                 msg = NULL;
545                                 return;
546
547                         }
548                 }
549         }
550
551         /*
552          * Check to see if we already have a copy of this message, and
553          * abort its processing if so.  (We used to post a warning to Aide>
554          * every time this happened, but the network is now so densely
555          * connected that it's inevitable.)
556          */
557         if (network_usetable(msg) != 0) {
558                 CtdlFreeMessage(msg);
559                 return;
560         }
561
562         /* Learn network topology from the path */
563         if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
564                 network_learn_topology(msg->cm_fields['N'], msg->cm_fields['P']);
565         }
566
567         /* Is the sending node giving us a very persuasive suggestion about
568          * which room this message should be saved in?  If so, go with that.
569          */
570         if (msg->cm_fields['C'] != NULL) {
571                 safestrncpy(target_room, msg->cm_fields['C'], sizeof target_room);
572         }
573
574         /* Otherwise, does it have a recipient?  If so, validate it... */
575         else if (msg->cm_fields['R'] != NULL) {
576                 recp = validate_recipients(msg->cm_fields['R'], NULL, 0);
577                 if (recp != NULL) if (recp->num_error != 0) {
578                         network_bounce(msg,
579                                 "A message you sent could not be delivered due to an invalid address.\n"
580                                 "Please check the address and try sending the message again.\n");
581                         msg = NULL;
582                         free_recipients(recp);
583                         syslog(LOG_DEBUG, "Bouncing message due to invalid recipient address.\n");
584                         return;
585                 }
586                 strcpy(target_room, "");        /* no target room if mail */
587         }
588
589         /* Our last shot at finding a home for this message is to see if
590          * it has the O field (Originating room) set.
591          */
592         else if (msg->cm_fields['O'] != NULL) {
593                 safestrncpy(target_room, msg->cm_fields['O'], sizeof target_room);
594         }
595
596         /* Strip out fields that are only relevant during transit */
597         if (msg->cm_fields['D'] != NULL) {
598                 free(msg->cm_fields['D']);
599                 msg->cm_fields['D'] = NULL;
600         }
601         if (msg->cm_fields['C'] != NULL) {
602                 free(msg->cm_fields['C']);
603                 msg->cm_fields['C'] = NULL;
604         }
605
606         /* save the message into a room */
607         if (PerformNetprocHooks(msg, target_room) == 0) {
608                 msg->cm_flags = CM_SKIP_HOOKS;
609                 CtdlSubmitMsg(msg, recp, target_room, 0);
610         }
611         CtdlFreeMessage(msg);
612         free_recipients(recp);
613 }
614
615
616 /*
617  * Process a single message from a single file from the inbound queue 
618  */
619 void network_process_message(FILE *fp, long msgstart, long msgend) {
620         long hold_pos;
621         long size;
622         char *buffer;
623
624         hold_pos = ftell(fp);
625         size = msgend - msgstart + 1;
626         buffer = malloc(size);
627         if (buffer != NULL) {
628                 fseek(fp, msgstart, SEEK_SET);
629                 if (fread(buffer, size, 1, fp) > 0) {
630                         network_process_buffer(buffer, size);
631                 }
632                 free(buffer);
633         }
634
635         fseek(fp, hold_pos, SEEK_SET);
636 }
637
638
639 /*
640  * Process a single file from the inbound queue 
641  */
642 void network_process_file(char *filename) {
643         FILE *fp;
644         long msgstart = (-1L);
645         long msgend = (-1L);
646         long msgcur = 0L;
647         int ch;
648
649
650         fp = fopen(filename, "rb");
651         if (fp == NULL) {
652                 syslog(LOG_CRIT, "Error opening %s: %s\n", filename, strerror(errno));
653                 return;
654         }
655
656         fseek(fp, 0L, SEEK_END);
657         syslog(LOG_INFO, "network: processing %ld bytes from %s\n", ftell(fp), filename);
658         rewind(fp);
659
660         /* Look for messages in the data stream and break them out */
661         while (ch = getc(fp), ch >= 0) {
662         
663                 if (ch == 255) {
664                         if (msgstart >= 0L) {
665                                 msgend = msgcur - 1;
666                                 network_process_message(fp, msgstart, msgend);
667                         }
668                         msgstart = msgcur;
669                 }
670
671                 ++msgcur;
672         }
673
674         msgend = msgcur - 1;
675         if (msgstart >= 0L) {
676                 network_process_message(fp, msgstart, msgend);
677         }
678
679         fclose(fp);
680         unlink(filename);
681 }
682
683
684 /*
685  * Process anything in the inbound queue
686  */
687 void network_do_spoolin(void) {
688         DIR *dp;
689         struct dirent *d;
690         struct stat statbuf;
691         char filename[PATH_MAX];
692         static time_t last_spoolin_mtime = 0L;
693
694         /*
695          * Check the spoolin directory's modification time.  If it hasn't
696          * been touched, we don't need to scan it.
697          */
698         if (stat(ctdl_netin_dir, &statbuf)) return;
699         if (statbuf.st_mtime == last_spoolin_mtime) {
700                 syslog(LOG_DEBUG, "network: nothing in inbound queue\n");
701                 return;
702         }
703         last_spoolin_mtime = statbuf.st_mtime;
704         syslog(LOG_DEBUG, "network: processing inbound queue\n");
705
706         /*
707          * Ok, there's something interesting in there, so scan it.
708          */
709         dp = opendir(ctdl_netin_dir);
710         if (dp == NULL) return;
711
712         while (d = readdir(dp), d != NULL) {
713                 if ((strcmp(d->d_name, ".")) && (strcmp(d->d_name, ".."))) {
714                         snprintf(filename, 
715                                 sizeof filename,
716                                 "%s/%s",
717                                 ctdl_netin_dir,
718                                 d->d_name
719                         );
720                         network_process_file(filename);
721                 }
722         }
723
724         closedir(dp);
725 }
726
727 /*
728  * Step 1: consolidate files in the outbound queue into one file per neighbor node
729  * Step 2: delete any files in the outbound queue that were for neighbors who no longer exist.
730  */
731 void network_consolidate_spoolout(void) {
732         DIR *dp;
733         struct dirent *d;
734         char filename[PATH_MAX];
735         char cmd[PATH_MAX];
736         char nexthop[256];
737         int i;
738         char *ptr;
739
740         /* Step 1: consolidate files in the outbound queue into one file per neighbor node */
741         dp = opendir(ctdl_netout_dir);
742         if (dp == NULL) return;
743         while (d = readdir(dp), d != NULL) {
744                 if (
745                         (strcmp(d->d_name, "."))
746                         && (strcmp(d->d_name, ".."))
747                         && (strchr(d->d_name, '@') != NULL)
748                 ) {
749                         safestrncpy(nexthop, d->d_name, sizeof nexthop);
750                         ptr = strchr(nexthop, '@');
751                         if (ptr) *ptr = 0;
752         
753                         snprintf(filename, 
754                                 sizeof filename,
755                                 "%s/%s",
756                                 ctdl_netout_dir,
757                                 d->d_name
758                         );
759         
760                         syslog(LOG_DEBUG, "Consolidate %s to %s\n", filename, nexthop);
761                         if (network_talking_to(nexthop, NTT_CHECK)) {
762                                 syslog(LOG_DEBUG,
763                                         "Currently online with %s - skipping for now\n",
764                                         nexthop
765                                 );
766                         }
767                         else {
768                                 network_talking_to(nexthop, NTT_ADD);
769                                 snprintf(cmd, sizeof cmd, "/bin/cat %s >>%s/%s && /bin/rm -f %s",
770                                         filename,
771                                         ctdl_netout_dir, nexthop,
772                                         filename
773                                 );
774                                 system(cmd);
775                                 network_talking_to(nexthop, NTT_REMOVE);
776                         }
777                 }
778         }
779         closedir(dp);
780
781         /* Step 2: delete any files in the outbound queue that were for neighbors who no longer exist */
782
783         dp = opendir(ctdl_netout_dir);
784         if (dp == NULL) return;
785
786         while (d = readdir(dp), d != NULL) {
787                 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
788                         continue;
789                 ptr = strchr(d->d_name, '@');
790                 if (d != NULL)
791                         continue;
792                 snprintf(filename, 
793                         sizeof filename,
794                         "%s/%s",
795                         ctdl_netout_dir,
796                         d->d_name
797                 );
798
799                 strcpy(nexthop, "");
800                 i = is_valid_node(nexthop, NULL, d->d_name);
801         
802                 if ( (i != 0) || !IsEmptyStr(nexthop) ) {
803                         unlink(filename);
804                 }
805         }
806
807
808         closedir(dp);
809 }
810
811
812
813
814 /*
815  * It's ok if these directories already exist.  Just fail silently.
816  */
817 void create_spool_dirs(void) {
818         if ((mkdir(ctdl_spool_dir, 0700) != 0) && (errno != EEXIST))
819                 syslog(LOG_EMERG, "unable to create directory [%s]: %s", ctdl_spool_dir, strerror(errno));
820         if (chown(ctdl_spool_dir, CTDLUID, (-1)) != 0)
821                 syslog(LOG_EMERG, "unable to set the access rights for [%s]: %s", ctdl_spool_dir, strerror(errno));
822         if ((mkdir(ctdl_netin_dir, 0700) != 0) && (errno != EEXIST))
823                 syslog(LOG_EMERG, "unable to create directory [%s]: %s", ctdl_netin_dir, strerror(errno));
824         if (chown(ctdl_netin_dir, CTDLUID, (-1)) != 0)
825                 syslog(LOG_EMERG, "unable to set the access rights for [%s]: %s", ctdl_netin_dir, strerror(errno));
826         if ((mkdir(ctdl_nettmp_dir, 0700) != 0) && (errno != EEXIST))
827                 syslog(LOG_EMERG, "unable to create directory [%s]: %s", ctdl_nettmp_dir, strerror(errno));
828         if (chown(ctdl_nettmp_dir, CTDLUID, (-1)) != 0)
829                 syslog(LOG_EMERG, "unable to set the access rights for [%s]: %s", ctdl_nettmp_dir, strerror(errno));
830         if ((mkdir(ctdl_netout_dir, 0700) != 0) && (errno != EEXIST))
831                 syslog(LOG_EMERG, "unable to create directory [%s]: %s", ctdl_netout_dir, strerror(errno));
832         if (chown(ctdl_netout_dir, CTDLUID, (-1)) != 0)
833                 syslog(LOG_EMERG, "unable to set the access rights for [%s]: %s", ctdl_netout_dir, strerror(errno));
834 }
835
836 /*
837  * Module entry point
838  */
839 CTDL_MODULE_INIT(network_spool)
840 {
841         if (!threading)
842         {
843                 create_spool_dirs();
844                 CtdlRegisterCleanupHook(destroy_network_queue_room);
845         }
846         return "network_spool";
847 }