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