when flushing a node, check whether we have a context before dereferencing the pointe...
[citadel.git] / citadel / modules / network / serv_networkclient.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 #include "sysdep.h"
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <stdio.h>
35 #include <fcntl.h>
36 #include <ctype.h>
37 #include <signal.h>
38 #include <pwd.h>
39 #include <errno.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <dirent.h>
43 #if TIME_WITH_SYS_TIME
44 # include <sys/time.h>
45 # include <time.h>
46 #else
47 # if HAVE_SYS_TIME_H
48 #  include <sys/time.h>
49 # else
50 #  include <time.h>
51 # endif
52 #endif
53 #ifdef HAVE_SYSCALL_H
54 # include <syscall.h>
55 #else 
56 # if HAVE_SYS_SYSCALL_H
57 #  include <sys/syscall.h>
58 # endif
59 #endif
60
61 #include <sys/wait.h>
62 #include <string.h>
63 #include <limits.h>
64 #include <libcitadel.h>
65 #include "citadel.h"
66 #include "server.h"
67 #include "citserver.h"
68 #include "support.h"
69 #include "config.h"
70 #include "user_ops.h"
71 #include "database.h"
72 #include "msgbase.h"
73 #include "internet_addressing.h"
74 #include "serv_network.h"
75 #include "clientsocket.h"
76 #include "file_ops.h"
77 #include "citadel_dirs.h"
78 #include "threads.h"
79
80 #ifndef HAVE_SNPRINTF
81 #include "snprintf.h"
82 #endif
83
84 #include "context.h"
85
86 #include "netconfig.h"
87 #include "ctdl_module.h"
88
89 struct CitContext networker_client_CC;
90
91 typedef enum _eNWCState {
92         eeGreating,
93         eAuth,
94         eNDOP,
95         eREAD,
96         eReadBLOB,
97         eCLOS,
98         eNUOP,
99         eWRIT,
100         eWriteBLOB,
101         eUCLS,
102         eQUIT
103 }eNWCState;
104
105
106 typedef struct _async_networker {
107         AsyncIO IO;
108         DNSQueryParts HostLookup;
109         eNWCState State;
110         long n;
111         StrBuf *SpoolFileName;
112         StrBuf *tempFileName;
113         StrBuf *node;
114         StrBuf *host;
115         StrBuf *port;
116         StrBuf *secret;
117         StrBuf          *Url;
118 } AsyncNetworker;
119
120 typedef eNextState(*NWClientHandler)(AsyncNetworker* NW);
121 eNextState nwc_get_one_host_ip(AsyncIO *IO);
122
123 eNextState nwc_connect_ip(AsyncIO *IO);
124
125 eNextState NWC_SendQUIT(AsyncNetworker *NW);
126 eNextState NWC_DispatchWriteDone(AsyncIO *IO);
127
128 void DeleteNetworker(void *vptr)
129 {
130         AsyncNetworker *NW = (AsyncNetworker *)vptr;
131         FreeStrBuf(&NW->SpoolFileName);
132         FreeStrBuf(&NW->tempFileName);
133         FreeStrBuf(&NW->node);
134         FreeStrBuf(&NW->host);
135         FreeStrBuf(&NW->port);
136         FreeStrBuf(&NW->secret);
137         FreeStrBuf(&NW->Url);
138         if (NW->IO.CitContext != NULL) {
139                 ((struct CitContext*)NW->IO.CitContext)->state = CON_IDLE;
140                 ((struct CitContext*)NW->IO.CitContext)->kill_me = 1;
141         }
142         FreeAsyncIOContents(&NW->IO);
143         free(NW);
144 }
145
146 #define NWC_DBG_SEND() syslog(LOG_DEBUG, "NW client[%ld]: > %s", NW->n, ChrPtr(NW->IO.SendBuf.Buf))
147 #define NWC_DBG_READ() syslog(LOG_DEBUG, "NW client[%ld]: < %s\n", NW->n, ChrPtr(NW->IO.IOBuf))
148 #define NWC_OK (strncasecmp(ChrPtr(NW->IO.IOBuf), "+OK", 3) == 0)
149
150 eNextState FinalizeNetworker(AsyncIO *IO)
151 {
152         AsyncNetworker *NW = (AsyncNetworker *)IO->Data;
153
154         network_talking_to(SKEY(NW->node), NTT_REMOVE);
155
156         DeleteNetworker(IO->Data);
157         return eAbort;
158 }
159
160 eNextState NWC_ReadGreeting(AsyncNetworker *NW)
161 {
162         char connected_to[SIZ];
163         NWC_DBG_READ();
164         /* Read the server greeting */
165         /* Check that the remote is who we think it is and warn the Aide if not */
166         extract_token (connected_to, ChrPtr(NW->IO.IOBuf), 1, ' ', sizeof connected_to);
167         if (strcmp(connected_to, ChrPtr(NW->node)) != 0)
168         {
169                 StrBufPrintf(NW->IO.ErrMsg,
170                              "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
171                              connected_to, ChrPtr(NW->node));
172                 syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
173                 CtdlAideMessage(ChrPtr(NW->IO.ErrMsg), "Network error");
174                 return eAbort;/// todo: aide message in anderer queue speichern
175         }
176         return eSendReply;
177 }
178
179 eNextState NWC_SendAuth(AsyncNetworker *NW)
180 {
181         /* We're talking to the correct node.  Now identify ourselves. */
182         StrBufPrintf(NW->IO.SendBuf.Buf, "NETP %s|%s\n", 
183                      config.c_nodename, 
184                      ChrPtr(NW->secret));
185         NWC_DBG_SEND();
186         return eSendReply;
187 }
188
189 eNextState NWC_ReadAuthReply(AsyncNetworker *NW)
190 {
191         NWC_DBG_READ();
192         if (ChrPtr(NW->IO.IOBuf)[0] == '2')
193         {
194                 return eSendReply;
195         }
196         else
197         {
198                 StrBufPrintf(NW->IO.ErrMsg,
199                              "Connected to node \"%s\" but my secret wasn't accurate.",
200                              ChrPtr(NW->node));
201                 syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
202                 CtdlAideMessage(ChrPtr(NW->IO.ErrMsg), "Network error");
203                 
204                 return eAbort;
205         }
206 }
207
208 eNextState NWC_SendNDOP(AsyncNetworker *NW)
209 {
210         NW->tempFileName = NewStrBuf();
211         NW->SpoolFileName = NewStrBuf();
212         StrBufPrintf(NW->tempFileName, 
213                      "%s/%s.%lx%x",
214                      ctdl_netin_dir,
215                      ChrPtr(NW->node),
216                      time(NULL),// TODO: get time from libev
217                      rand());
218         StrBufPrintf(NW->SpoolFileName, 
219                      "%s/%s.%lx%x",
220                      ctdl_nettmp_dir,
221                      ChrPtr(NW->node),
222                      time(NULL),// TODO: get time from libev
223                      rand());
224
225         /* We're talking to the correct node.  Now identify ourselves. */
226         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NDOP\n"));
227         NWC_DBG_SEND();
228         return eSendReply;
229 }
230
231 eNextState NWC_ReadNDOPReply(AsyncNetworker *NW)
232 {
233         int TotalSendSize;
234         NWC_DBG_READ();
235         if (ChrPtr(NW->IO.IOBuf)[0] == '2')
236         {
237
238                 NW->IO.IOB.TotalSentAlready = 0;
239                 TotalSendSize = atol (ChrPtr(NW->IO.IOBuf) + 4);
240                 syslog(LOG_DEBUG, "Expecting to transfer %ld bytes\n", NW->IO.IOB.TotalSendSize);
241                 if (TotalSendSize <= 0) {
242                         NW->State = eNUOP - 1;
243                 }
244                 else {
245                         int fd;
246                         fd = open(ChrPtr(NW->SpoolFileName), 
247                                   O_EXCL|O_CREAT|O_NONBLOCK|O_WRONLY, 
248                                   S_IRUSR|S_IWUSR);
249                         if (fd < 0)
250                         {
251                                 syslog(LOG_CRIT,
252                                        "cannot open %s: %s\n", 
253                                        ChrPtr(NW->SpoolFileName), 
254                                        strerror(errno));
255
256                                 NW->State = eQUIT - 1;
257                                 return eAbort;
258                         }
259                         FDIOBufferInit(&NW->IO.IOB, &NW->IO.RecvBuf, fd, TotalSendSize);
260                 }
261                 return eSendReply;
262         }
263         else
264         {
265                 return eAbort;
266         }
267 }
268
269 eNextState NWC_SendREAD(AsyncNetworker *NW)
270 {
271         eNextState rc;
272
273         if (NW->IO.IOB.TotalSentAlready < NW->IO.IOB.TotalSendSize)
274         {
275                 /*
276                  * If shutting down we can exit here and unlink the temp file.
277                  * this shouldn't loose us any messages.
278                  */
279                 if (server_shutting_down)
280                 {
281                         FDIOBufferDelete(&NW->IO.IOB);
282                         unlink(ChrPtr(NW->tempFileName));
283                         return eAbort;
284                 }
285                 StrBufPrintf(NW->IO.SendBuf.Buf, "READ %ld|%ld\n",
286                              NW->IO.IOB.TotalSentAlready,
287                              NW->IO.IOB.TotalSendSize);
288 /*
289                              ((NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready > IGNET_PACKET_SIZE)
290                               ? IGNET_PACKET_SIZE : 
291                               (NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready))
292                         );
293 */
294                 NWC_DBG_SEND();
295                 return eSendReply;
296         }
297         else 
298         {
299                 NW->State = eCLOS;
300                 rc = NWC_DispatchWriteDone(&NW->IO);
301                 NWC_DBG_SEND();
302
303                 return rc;
304         }
305 }
306
307 eNextState NWC_ReadREADState(AsyncNetworker *NW)
308 {
309         NWC_DBG_READ();
310         if (ChrPtr(NW->IO.IOBuf)[0] == '6')
311         {
312                 NW->IO.IOB.ChunkSendRemain = 
313                         NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
314                 return eReadFile;
315         }
316         return eAbort;
317 }
318 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW);
319 eNextState NWC_ReadREADBlob(AsyncNetworker *NW)
320 {
321         NWC_DBG_READ();
322         if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
323         {
324                 NW->State ++;
325
326                 FDIOBufferDelete(&NW->IO.IOB);
327                 
328                 if (link(ChrPtr(NW->SpoolFileName), ChrPtr(NW->tempFileName)) != 0) {
329                         syslog(LOG_ALERT, 
330                                "Could not link %s to %s: %s\n",
331                                ChrPtr(NW->tempFileName), 
332                                ChrPtr(NW->SpoolFileName), 
333                                strerror(errno));
334                 }
335         
336                 unlink(ChrPtr(NW->tempFileName));
337                 return NWC_DispatchWriteDone(&NW->IO);
338         }
339         else {
340                 NW->State --;
341                 NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
342                 return NWC_DispatchWriteDone(&NW->IO);
343         }
344 }
345
346 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW)
347 {
348         NWC_DBG_READ();
349         if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
350         {
351                 NW->State ++;
352
353                 FDIOBufferDelete(&NW->IO.IOB);
354                 
355                 if (link(ChrPtr(NW->SpoolFileName), ChrPtr(NW->tempFileName)) != 0) {
356                         syslog(LOG_ALERT, 
357                                "Could not link %s to %s: %s\n",
358                                ChrPtr(NW->tempFileName), 
359                                ChrPtr(NW->SpoolFileName), 
360                                strerror(errno));
361                 }
362         
363                 unlink(ChrPtr(NW->tempFileName));
364                 return NWC_DispatchWriteDone(&NW->IO);
365         }
366         else {
367                 NW->State --;
368                 NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
369                 return NWC_DispatchWriteDone(&NW->IO);
370         }
371 }
372 eNextState NWC_SendCLOS(AsyncNetworker *NW)
373 {
374         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("CLOS\n"));
375         NWC_DBG_SEND();
376         return eSendReply;
377 }
378
379 eNextState NWC_ReadCLOSReply(AsyncNetworker *NW)
380 {
381         NWC_DBG_READ();
382         if (ChrPtr(NW->IO.IOBuf)[0] != '2')
383                 return eTerminateConnection;
384         return eSendReply;
385 }
386
387
388 eNextState NWC_SendNUOP(AsyncNetworker *NW)
389 {
390         eNextState rc;
391         long TotalSendSize;
392         struct stat statbuf;
393         int fd;
394
395         StrBufPrintf(NW->tempFileName,
396                      "%s/%s",
397                      ctdl_netout_dir,
398                      ChrPtr(NW->node));
399         fd = open(ChrPtr(NW->tempFileName), O_RDONLY);
400         if (fd < 0) {
401                 if (errno != ENOENT) {
402                         syslog(LOG_CRIT,
403                                "cannot open %s: %s\n", 
404                                ChrPtr(NW->tempFileName), 
405                                strerror(errno));
406                 }
407                 NW->State = eQUIT;
408                 rc = NWC_SendQUIT(NW);
409                 NWC_DBG_SEND();
410         }
411
412         if (fstat(fd, &statbuf) == -1) {
413                 syslog(9, "FSTAT FAILED %s [%s]--\n", 
414                        ChrPtr(NW->tempFileName), 
415                        strerror(errno));
416                 if (fd > 0) close(fd);
417                 return eAbort;
418         }
419         TotalSendSize = statbuf.st_size;
420         if (TotalSendSize == 0) {
421                 syslog(LOG_DEBUG,
422                        "Nothing to send.\n");
423                 NW->State = eQUIT;
424                 rc = NWC_SendQUIT(NW);
425                 NWC_DBG_SEND();
426                 return rc;
427         }
428         FDIOBufferInit(&NW->IO.IOB, &NW->IO.SendBuf, fd, TotalSendSize);
429
430         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NUOP\n"));
431         NWC_DBG_SEND();
432         return eSendReply;
433
434 }
435 eNextState NWC_ReadNUOPReply(AsyncNetworker *NW)
436 {
437         NWC_DBG_READ();
438         if (ChrPtr(NW->IO.IOBuf)[0] != '2')
439                 return eAbort;
440         return eSendReply;
441 }
442
443 eNextState NWC_SendWRIT(AsyncNetworker *NW)
444 {
445         StrBufPrintf(NW->IO.SendBuf.Buf, "WRIT %ld\n", 
446                      NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready);
447         NWC_DBG_SEND();
448         return eSendReply;
449 }
450 eNextState NWC_ReadWRITReply(AsyncNetworker *NW)
451 {
452         NWC_DBG_READ();
453         if (ChrPtr(NW->IO.IOBuf)[0] != '7')
454         {
455                 return eAbort;
456         }
457
458         NW->IO.IOB.ChunkSendRemain = 
459                 NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
460         return eSendFile;
461 }
462
463 eNextState NWC_SendBlobDone(AsyncNetworker *NW)
464 {
465         eNextState rc;
466         if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
467         {
468                 NW->State ++;
469
470                 FDIOBufferDelete(&NW->IO.IOB);
471                 rc =  NWC_DispatchWriteDone(&NW->IO);
472                 NW->State --;
473                 return rc;
474         }
475         else {
476                 NW->State --;
477                 NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
478                 return NWC_DispatchWriteDone(&NW->IO);
479         }
480 }
481
482 eNextState NWC_SendUCLS(AsyncNetworker *NW)
483 {
484         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("UCLS 1\n"));
485         NWC_DBG_SEND();
486         return eSendReply;
487
488 }
489 eNextState NWC_ReadUCLS(AsyncNetworker *NW)
490 {
491         NWC_DBG_READ();
492
493         syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", NW->IO.IOB.ChunkSize, ChrPtr(NW->node));
494         if (ChrPtr(NW->IO.IOBuf)[0] == '2') {
495                 syslog(LOG_DEBUG, "Removing <%s>\n", ChrPtr(NW->tempFileName));
496                 unlink(ChrPtr(NW->tempFileName));
497         }
498         return eSendReply;
499 }
500
501 eNextState NWC_SendQUIT(AsyncNetworker *NW)
502 {
503         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("QUIT\n"));
504
505         NWC_DBG_SEND();
506         return eSendReply;
507 }
508
509 eNextState NWC_ReadQUIT(AsyncNetworker *NW)
510 {
511         NWC_DBG_READ();
512
513         return eAbort;
514 }
515
516
517 NWClientHandler NWC_ReadHandlers[] = {
518         NWC_ReadGreeting,
519         NWC_ReadAuthReply,
520         NWC_ReadNDOPReply,
521         NWC_ReadREADState,
522         NWC_ReadREADBlob,
523         NWC_ReadCLOSReply,
524         NWC_ReadNUOPReply,
525         NWC_ReadWRITReply,
526         NWC_SendBlobDone,
527         NWC_ReadUCLS,
528         NWC_ReadQUIT};
529
530 long NWC_ConnTimeout = 100;
531
532 const long NWC_SendTimeouts[] = {
533         100,
534         100,
535         100,
536         100,
537         100,
538         100,
539         100,
540         100
541 };
542 const ConstStr NWC[] = {
543         {HKEY("Connection broken during ")},
544         {HKEY("Connection broken during ")},
545         {HKEY("Connection broken during ")},
546         {HKEY("Connection broken during ")},
547         {HKEY("Connection broken during ")},
548         {HKEY("Connection broken during ")},
549         {HKEY("Connection broken during ")},
550         {HKEY("Connection broken during ")}
551 };
552
553 NWClientHandler NWC_SendHandlers[] = {
554         NULL,
555         NWC_SendAuth,
556         NWC_SendNDOP,
557         NWC_SendREAD,
558         NWC_ReadREADBlobDone,
559         NWC_SendCLOS,
560         NWC_SendNUOP,
561         NWC_SendWRIT,
562         NWC_SendBlobDone,
563         NWC_SendUCLS,
564         NWC_SendQUIT
565 };
566
567 const long NWC_ReadTimeouts[] = {
568         100,
569         100,
570         100,
571         100,
572         100,
573         100,
574         100,
575         100,
576         100,
577         100
578 };
579
580
581
582
583 eNextState nwc_get_one_host_ip_done(AsyncIO *IO)
584 {
585         AsyncNetworker *NW = IO->Data;
586         struct hostent *hostent;
587
588         QueryCbDone(IO);
589
590         hostent = NW->HostLookup.VParsedDNSReply;
591         if ((NW->HostLookup.DNSStatus == ARES_SUCCESS) && 
592             (hostent != NULL) ) {
593                 memset(&NW->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
594                 if (NW->IO.ConnectMe->IPv6) {
595                         memcpy(&NW->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
596                                &hostent->h_addr_list[0],
597                                sizeof(struct in6_addr));
598                         
599                         NW->IO.ConnectMe->Addr.sin6_family = hostent->h_addrtype;
600                         NW->IO.ConnectMe->Addr.sin6_port   = htons(atol(ChrPtr(NW->port)));//// TODO use the one from the URL.
601                 }
602                 else {
603                         struct sockaddr_in *addr = (struct sockaddr_in*) &NW->IO.ConnectMe->Addr;
604                         /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
605 //                      addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
606                         memcpy(&addr->sin_addr.s_addr, 
607                                hostent->h_addr_list[0], 
608                                sizeof(uint32_t));
609                         
610                         addr->sin_family = hostent->h_addrtype;
611                         addr->sin_port   = htons(504);/// default citadel port
612                         
613                 }
614                 return nwc_connect_ip(IO);
615         }
616         else
617                 return eAbort;
618 }
619
620
621 eNextState nwc_get_one_host_ip(AsyncIO *IO)
622 {
623         AsyncNetworker *NW = IO->Data;
624         /* 
625          * here we start with the lookup of one host.
626          */ 
627
628         InitC_ares_dns(IO);
629
630         syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
631
632         syslog(LOG_DEBUG, 
633                       "NWC client[%ld]: looking up %s-Record %s : %d ...\n", 
634                       NW->n, 
635                       (NW->IO.ConnectMe->IPv6)? "aaaa": "a",
636                       NW->IO.ConnectMe->Host, 
637                       NW->IO.ConnectMe->Port);
638
639         QueueQuery((NW->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a, 
640                    NW->IO.ConnectMe->Host, 
641                    &NW->IO, 
642                    &NW->HostLookup, 
643                    nwc_get_one_host_ip_done);
644         IO->NextState = eReadDNSReply;
645         return IO->NextState;
646 }
647 /**
648  * @brief lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
649  */
650 eReadState NWC_ReadServerStatus(AsyncIO *IO)
651 {
652 //      AsyncNetworker *NW = IO->Data;
653         eReadState Finished = eBufferNotEmpty; 
654
655         switch (IO->NextState) {
656         case eSendDNSQuery:
657         case eReadDNSReply:
658         case eDBQuery:
659         case eConnect:
660         case eTerminateConnection:
661         case eAbort:
662                 Finished = eReadFail;
663                 break;
664         case eSendReply: 
665         case eSendMore:
666         case eReadMore:
667         case eReadMessage: 
668                 Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
669                 break;
670         case eReadFile:
671         case eSendFile:
672         case eReadPayload:
673                 break;
674         }
675         return Finished;
676 }
677
678
679
680 eNextState NWC_FailNetworkConnection(AsyncIO *IO)
681 {
682         return eAbort;
683 }
684
685 void NWC_SetTimeout(eNextState NextTCPState, AsyncNetworker *NW)
686 {
687         double Timeout = 0.0;
688
689         syslog(LOG_DEBUG, "NWC3: %s\n", __FUNCTION__);
690
691         switch (NextTCPState) {
692         case eSendReply:
693         case eSendMore:
694                 break;
695         case eReadFile:
696         case eReadMessage:
697                 Timeout = NWC_ReadTimeouts[NW->State];
698                 break;
699         case eReadPayload:
700                 Timeout = 100000;
701                 /* TODO!!! */
702                 break;
703         case eSendDNSQuery:
704         case eReadDNSReply:
705         case eConnect:
706         case eSendFile:
707 //TODO
708         case eTerminateConnection:
709         case eDBQuery:
710         case eAbort:
711         case eReadMore://// TODO
712                 return;
713         }
714         SetNextTimeout(&NW->IO, Timeout);
715 }
716
717
718 eNextState NWC_DispatchReadDone(AsyncIO *IO)
719 {
720         syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
721         AsyncNetworker *NW = IO->Data;
722         eNextState rc;
723
724         rc = NWC_ReadHandlers[NW->State](NW);
725         if (rc != eReadMore)
726                 NW->State++;
727         NWC_SetTimeout(rc, NW);
728         return rc;
729 }
730 eNextState NWC_DispatchWriteDone(AsyncIO *IO)
731 {
732         syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
733         AsyncNetworker *NW = IO->Data;
734         eNextState rc;
735
736         rc = NWC_SendHandlers[NW->State](NW);
737         NWC_SetTimeout(rc, NW);
738         return rc;
739 }
740
741 /*****************************************************************************/
742 /*                     Networker CLIENT ERROR CATCHERS                       */
743 /*****************************************************************************/
744 eNextState NWC_Terminate(AsyncIO *IO)
745 {
746         syslog(LOG_DEBUG, "Nw: %s\n", __FUNCTION__);
747         FinalizeNetworker(IO);
748         return eAbort;
749 }
750
751 eNextState NWC_Timeout(AsyncIO *IO)
752 {
753         syslog(LOG_DEBUG, "NW: %s\n", __FUNCTION__);
754
755         return NWC_FailNetworkConnection(IO);
756 }
757 eNextState NWC_ConnFail(AsyncIO *IO)
758 {
759 ///     AsyncNetworker *NW = IO->Data;
760
761         syslog(LOG_DEBUG, "NW: %s\n", __FUNCTION__);
762 ////    StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State])); todo
763         return NWC_FailNetworkConnection(IO);
764 }
765 eNextState NWC_DNSFail(AsyncIO *IO)
766 {
767 ///     AsyncNetworker *NW = IO->Data;
768
769         syslog(LOG_DEBUG, "NW: %s\n", __FUNCTION__);
770 ////    StrBufPlain(IO->ErrMsg, CKEY(POP3C_ReadErrors[pMsg->State])); todo
771         return NWC_FailNetworkConnection(IO);
772 }
773 eNextState NWC_Shutdown(AsyncIO *IO)
774 {
775         syslog(LOG_DEBUG, "NW: %s\n", __FUNCTION__);
776 ////    pop3aggr *pMsg = IO->Data;
777
778         FinalizeNetworker(IO);
779         return eAbort;
780 }
781
782
783 eNextState nwc_connect_ip(AsyncIO *IO)
784 {
785         AsyncNetworker *NW = IO->Data;
786
787         syslog(LOG_DEBUG, "NW: %s\n", __FUNCTION__);
788         syslog(LOG_DEBUG, "network: polling <%s>\n", ChrPtr(NW->node));
789         syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", 
790                ChrPtr(NW->node), 
791                ChrPtr(NW->host),
792                ChrPtr(NW->port));
793         
794         return EvConnectSock(IO,
795                              NWC_ConnTimeout,
796                              NWC_ReadTimeouts[0],
797                              1);
798 }
799
800 void RunNetworker(AsyncNetworker *NW)
801 {
802         ParseURL(&NW->IO.ConnectMe, NW->Url, 504);
803
804         InitIOStruct(&NW->IO,
805                      NW,
806                      eReadMessage,
807                      NWC_ReadServerStatus,
808                      NWC_DNSFail,
809                      NWC_DispatchWriteDone,
810                      NWC_DispatchReadDone,
811                      NWC_Terminate,
812                      NWC_ConnFail,
813                      NWC_Timeout,
814                      NWC_Shutdown);
815
816         safestrncpy(((CitContext *)NW->IO.CitContext)->cs_host, 
817                     ChrPtr(NW->host),
818                     sizeof(((CitContext *)NW->IO.CitContext)->cs_host)); 
819
820         if (NW->IO.ConnectMe->IsIP) {
821                 QueueEventContext(&NW->IO,
822                                   nwc_connect_ip);
823         }
824         else { /* uneducated admin has chosen to add DNS to the equation... */
825                 QueueEventContext(&NW->IO,
826                                   nwc_get_one_host_ip);
827         }
828 }
829
830 /*
831  * Poll other Citadel nodes and transfer inbound/outbound network data.
832  * Set "full" to nonzero to force a poll of every node, or to zero to poll
833  * only nodes to which we have data to send.
834  */
835 void network_poll_other_citadel_nodes(int full_poll, char *working_ignetcfg)
836 {
837         AsyncNetworker *NW;
838         StrBuf *CfgData;
839         StrBuf *Line;
840         const char *lptr;
841         const char *CfgPtr;
842         int Done;
843         
844         int poll = 0;
845         
846         if ((working_ignetcfg == NULL) || (*working_ignetcfg == '\0')) {
847                 syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
848                 return;
849         }
850         become_session(&networker_client_CC);
851
852         CfgData = NewStrBufPlain(working_ignetcfg, -1);
853         Line = NewStrBufPlain(NULL, StrLength(CfgData));
854         Done = 0;
855         CfgPtr = NULL;
856         while (!Done)
857         {
858                 /* Use the string tokenizer to grab one line at a time */
859                 StrBufSipLine(Line, CfgData, &CfgPtr);
860                 Done = CfgPtr == StrBufNOTNULL;
861                 if (StrLength(Line) > 0)
862                 {
863                         if(server_shutting_down)
864                                 return;/* TODO free stuff*/
865                         lptr = NULL;
866                         poll = 0;
867                         NW = (AsyncNetworker*)malloc(sizeof(AsyncNetworker));
868                         memset(NW, 0, sizeof(AsyncNetworker));
869                         
870                         NW->node = NewStrBufPlain(NULL, StrLength(Line));
871                         NW->host = NewStrBufPlain(NULL, StrLength(Line));
872                         NW->port = NewStrBufPlain(NULL, StrLength(Line));
873                         NW->secret = NewStrBufPlain(NULL, StrLength(Line));
874                         
875                         StrBufExtract_NextToken(NW->node, Line, &lptr, '|');
876                         StrBufExtract_NextToken(NW->secret, Line, &lptr, '|');
877                         StrBufExtract_NextToken(NW->host, Line, &lptr, '|');
878                         StrBufExtract_NextToken(NW->port, Line, &lptr, '|');
879                         if ( (StrLength(NW->node) != 0) && 
880                              (StrLength(NW->secret) != 0) &&
881                              (StrLength(NW->host) != 0) &&
882                              (StrLength(NW->port) != 0))
883                         {
884                                 poll = full_poll;
885                                 if (poll == 0)
886                                 {
887                                         NW->SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
888                                         StrBufAppendBufPlain(NW->SpoolFileName, HKEY("/"), 0);
889                                         StrBufAppendBuf(NW->SpoolFileName, NW->node, 0);
890                                         if (access(ChrPtr(NW->SpoolFileName), R_OK) == 0) {
891                                                 poll = 1;
892                                         }
893                                 }
894                         }
895                         if (poll) {
896                                 NW->Url = NewStrBufPlain(NULL, StrLength(Line));
897                                 StrBufPrintf(NW->Url, "citadel://:%s@%s:%s", 
898                                              ChrPtr(NW->secret),
899                                              ChrPtr(NW->host),
900                                              ChrPtr(NW->port));
901                                 if (!network_talking_to(SKEY(NW->node), NTT_CHECK))
902                                 {
903                                         network_talking_to(SKEY(NW->node), NTT_ADD);
904                                         RunNetworker(NW);
905                                         continue;
906                                 }
907                         }
908                         DeleteNetworker(NW);
909                 }
910         }
911         FreeStrBuf(&CfgData);
912         FreeStrBuf(&Line);
913
914 }
915
916
917 void network_do_clientqueue(void)
918 {
919         char *working_ignetcfg;
920         int full_processing = 1;
921         static time_t last_run = 0L;
922
923         /*
924          * Run the full set of processing tasks no more frequently
925          * than once every n seconds
926          */
927         if ( (time(NULL) - last_run) < config.c_net_freq ) {
928                 full_processing = 0;
929                 syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
930                         config.c_net_freq - (time(NULL)- last_run)
931                 );
932         }
933
934         working_ignetcfg = load_working_ignetcfg();
935         /*
936          * Poll other Citadel nodes.  Maybe.  If "full_processing" is set
937          * then we poll everyone.  Otherwise we only poll nodes we have stuff
938          * to send to.
939          */
940         network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
941         if (working_ignetcfg)
942                 free(working_ignetcfg);
943 }
944
945
946
947 /*
948  * Module entry point
949  */
950 CTDL_MODULE_INIT(network_client)
951 {
952         if (!threading)
953         {
954                 CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
955                 
956                 CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER);
957         }
958         return "network_client";
959 }