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