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