More places to use the right api function.
[citadel.git] / citadel / modules / networkclient / serv_networkclient.c
1 /*
2  * This module handles shared rooms, inter-Citadel mail, and outbound
3  * mailing list processing.
4  *
5  * Copyright (c) 2000-2012 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, version 3.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
16  * This is a fairly high-level type of critical section.  It ensures that no
17  * two threads work on the netconfigs files at the same time.  Since we do
18  * so many things inside these, here are the rules:
19  *  1. begin_critical_section(S_NETCONFIGS) *before* begin_ any others.
20  *  2. Do *not* perform any I/O with the client during these sections.
21  *
22  */
23
24
25 #include "sysdep.h"
26 #include <stdlib.h>
27 #include <unistd.h>
28 #include <stdio.h>
29 #include <fcntl.h>
30 #include <ctype.h>
31 #include <signal.h>
32 #include <pwd.h>
33 #include <errno.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <dirent.h>
37 #if TIME_WITH_SYS_TIME
38 # include <sys/time.h>
39 # include <time.h>
40 #else
41 # if HAVE_SYS_TIME_H
42 #  include <sys/time.h>
43 # else
44 #  include <time.h>
45 # endif
46 #endif
47 #ifdef HAVE_SYSCALL_H
48 # include <syscall.h>
49 #else 
50 # if HAVE_SYS_SYSCALL_H
51 #  include <sys/syscall.h>
52 # endif
53 #endif
54
55 #include <sys/wait.h>
56 #include <string.h>
57 #include <limits.h>
58 #include <libcitadel.h>
59 #include "citadel.h"
60 #include "server.h"
61 #include "citserver.h"
62 #include "support.h"
63 #include "config.h"
64 #include "user_ops.h"
65 #include "database.h"
66 #include "msgbase.h"
67 #include "internet_addressing.h"
68 #include "clientsocket.h"
69 #include "citadel_dirs.h"
70 #include "threads.h"
71 #include "context.h"
72 #include "ctdl_module.h"
73
74 struct CitContext networker_client_CC;
75
76 #define NODE ChrPtr(((AsyncNetworker*)IO->Data)->node)
77 #define N ((AsyncNetworker*)IO->Data)->n
78
79 int NetworkClientDebugEnabled = 0;
80
81 #define NCDBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (NetworkClientDebugEnabled != 0))
82
83 #define EVN_syslog(LEVEL, FORMAT, ...)                                  \
84         NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
85                                "%s[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
86                                IOSTR, IO->ID, CCID, NODE, N, __VA_ARGS__)
87
88 #define EVNM_syslog(LEVEL, FORMAT)                                      \
89         NCDBGLOG(LEVEL) syslog(LEVEL,                                   \
90                                "%s[%ld]CC[%d]NW[%s][%ld]" FORMAT,       \
91                                IOSTR, IO->ID, CCID, NODE, N)
92
93 #define EVNCS_syslog(LEVEL, FORMAT, ...) \
94         NCDBGLOG(LEVEL) syslog(LEVEL, "%s[%ld]NW[%s][%ld]" FORMAT,      \
95                                IOSTR, IO->ID, NODE, N, __VA_ARGS__)
96
97 #define EVNCSM_syslog(LEVEL, FORMAT) \
98         NCDBGLOG(LEVEL) syslog(LEVEL, "%s[%ld]NW[%s][%ld]" FORMAT,      \
99                                IOSTR, IO->ID, NODE, N)
100
101
102 typedef enum _eNWCState {
103         eGreating,
104         eAuth,
105         eNDOP,
106         eREAD,
107         eReadBLOB,
108         eCLOS,
109         eNUOP,
110         eWRIT,
111         eWriteBLOB,
112         eUCLS,
113         eQUIT
114 }eNWCState;
115
116 typedef enum _eNWCVState {
117         eNWCVSLookup,
118         eNWCVSConnecting,
119         eNWCVSConnFail,
120         eNWCVSGreating,
121         eNWCVSAuth,
122         eNWCVSAuthFailNTT,
123         eNWCVSAuthFail,
124         eNWCVSNDOP,
125         eNWCVSNDOPDone,
126         eNWCVSNUOP,
127         eNWCVSNUOPDone,
128         eNWCVSFail
129 }eNWCVState;
130
131 ConstStr NWCStateStr[] = {
132         {HKEY("Looking up Host")},
133         {HKEY("Connecting host")},
134         {HKEY("Failed to connect")},
135         {HKEY("Rread Greeting")},
136         {HKEY("Authenticating")},
137         {HKEY("Auth failed by NTT")},
138         {HKEY("Auth failed")},
139         {HKEY("Downloading")},
140         {HKEY("Downloading Success")},
141         {HKEY("Uploading Spoolfile")},
142         {HKEY("Uploading done")},
143         {HKEY("failed")}
144 };
145
146 void SetNWCState(AsyncIO *IO, eNWCVState State)
147 {
148         CitContext* CCC = IO->CitContext;
149         memcpy(CCC->cs_clientname, NWCStateStr[State].Key, NWCStateStr[State].len + 1);
150 }
151
152 typedef struct _async_networker {
153         AsyncIO IO;
154         DNSQueryParts HostLookup;
155         eNWCState State;
156         long n;
157         StrBuf *SpoolFileName;
158         StrBuf *tempFileName;
159         StrBuf *node;
160         StrBuf *host;
161         StrBuf *port;
162         StrBuf *secret;
163         StrBuf          *Url;
164 } AsyncNetworker;
165
166 typedef eNextState(*NWClientHandler)(AsyncNetworker* NW);
167 eNextState nwc_get_one_host_ip(AsyncIO *IO);
168
169 eNextState nwc_connect_ip(AsyncIO *IO);
170
171 eNextState NWC_SendQUIT(AsyncNetworker *NW);
172 eNextState NWC_DispatchWriteDone(AsyncIO *IO);
173
174 void DeleteNetworker(void *vptr)
175 {
176         AsyncNetworker *NW = (AsyncNetworker *)vptr;
177         FreeStrBuf(&NW->SpoolFileName);
178         FreeStrBuf(&NW->tempFileName);
179         FreeStrBuf(&NW->node);
180         FreeStrBuf(&NW->host);
181         FreeStrBuf(&NW->port);
182         FreeStrBuf(&NW->secret);
183         FreeStrBuf(&NW->Url);
184         FreeStrBuf(&NW->IO.ErrMsg);
185         FreeAsyncIOContents(&NW->IO);
186         if (NW->HostLookup.VParsedDNSReply != NULL) {
187                 NW->HostLookup.DNSReplyFree(NW->HostLookup.VParsedDNSReply);
188                 NW->HostLookup.VParsedDNSReply = NULL;
189         }
190         free(NW);
191 }
192
193 #define NWC_DBG_SEND() EVN_syslog(LOG_DEBUG, ": > %s", ChrPtr(NW->IO.SendBuf.Buf))
194 #define NWC_DBG_READ() EVN_syslog(LOG_DEBUG, ": < %s\n", ChrPtr(NW->IO.IOBuf))
195 #define NWC_OK (strncasecmp(ChrPtr(NW->IO.IOBuf), "+OK", 3) == 0)
196
197 eNextState NWC_SendFailureMessage(AsyncIO *IO)
198 {
199         AsyncNetworker *NW = IO->Data;
200         long lens[2];
201         const char *strs[2];
202
203         strs[0] = ChrPtr(NW->node);
204         lens[0] = StrLength(NW->node);
205         
206         strs[1] = ChrPtr(NW->IO.ErrMsg);
207         lens[1] = StrLength(NW->IO.ErrMsg);
208         CtdlAideFPMessage(
209                 ChrPtr(NW->IO.ErrMsg),
210                 "Networker error",
211                 2, strs, (long*) &lens,
212                 IO->Now,
213                 IO->ID, CCID);
214         
215         return eAbort;
216 }
217
218 eNextState FinalizeNetworker(AsyncIO *IO)
219 {
220         AsyncNetworker *NW = (AsyncNetworker *)IO->Data;
221
222         CtdlNetworkTalkingTo(SKEY(NW->node), NTT_REMOVE);
223
224         DeleteNetworker(IO->Data);
225         return eAbort;
226 }
227
228 eNextState NWC_ReadGreeting(AsyncNetworker *NW)
229 {
230         char connected_to[SIZ];
231         AsyncIO *IO = &NW->IO;
232         SetNWCState(IO, eNWCVSGreating);
233         NWC_DBG_READ();
234         /* Read the server greeting */
235         /* Check that the remote is who we think it is and warn the Aide if not */
236         extract_token (connected_to, ChrPtr(NW->IO.IOBuf), 1, ' ', sizeof connected_to);
237         if (strcmp(connected_to, ChrPtr(NW->node)) != 0)
238         {
239                 if (NW->IO.ErrMsg == NULL)
240                         NW->IO.ErrMsg = NewStrBuf();
241                 StrBufPrintf(NW->IO.ErrMsg,
242                              "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
243                              connected_to, ChrPtr(NW->node));
244                 EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
245
246                 return EventQueueDBOperation(IO, NWC_SendFailureMessage, 1);
247         }
248         return eSendReply;
249 }
250
251 eNextState NWC_SendAuth(AsyncNetworker *NW)
252 {
253         AsyncIO *IO = &NW->IO;
254         SetNWCState(IO, eNWCVSAuth);
255         /* We're talking to the correct node.  Now identify ourselves. */
256         StrBufPrintf(NW->IO.SendBuf.Buf, "NETP %s|%s\n", 
257                      config.c_nodename, 
258                      ChrPtr(NW->secret));
259         NWC_DBG_SEND();
260         return eSendReply;
261 }
262
263 eNextState NWC_ReadAuthReply(AsyncNetworker *NW)
264 {
265         AsyncIO *IO = &NW->IO;
266         NWC_DBG_READ();
267         if (ChrPtr(NW->IO.IOBuf)[0] == '2')
268         {
269                 return eSendReply;
270         }
271         else
272         {
273                 int Error = atol(ChrPtr(NW->IO.IOBuf));
274                 if (NW->IO.ErrMsg == NULL)
275                         NW->IO.ErrMsg = NewStrBuf();
276                 StrBufPrintf(NW->IO.ErrMsg,
277                              "Connected to node \"%s\" but my secret wasn't accurate.\nReason was:%s\n",
278                              ChrPtr(NW->node), ChrPtr(NW->IO.IOBuf) + 4);
279                 if (Error == 552) {
280                         SetNWCState(IO, eNWCVSAuthFailNTT);
281                         EVN_syslog(LOG_INFO,
282                                    "Already talking to %s; skipping this time.\n",
283                                    ChrPtr(NW->node));
284                         
285                 }
286                 else {
287                         SetNWCState(IO, eNWCVSAuthFailNTT);
288                         EVN_syslog(LOG_ERR, "%s\n", ChrPtr(NW->IO.ErrMsg));
289                         return EventQueueDBOperation(IO, NWC_SendFailureMessage, 1);
290                 }
291                 return eAbort;
292         }
293 }
294
295 eNextState NWC_SendNDOP(AsyncNetworker *NW)
296 {
297         AsyncIO *IO = &NW->IO;
298         SetNWCState(IO, eNWCVSNDOP);
299         NW->tempFileName = NewStrBuf();
300         NW->SpoolFileName = NewStrBuf();
301         StrBufPrintf(NW->SpoolFileName,
302                      "%s/%s.%lx%x",
303                      ctdl_netin_dir,
304                      ChrPtr(NW->node),
305                      time(NULL),// TODO: get time from libev
306                      rand());
307         StrBufStripSlashes(NW->SpoolFileName, 1);
308         StrBufPrintf(NW->tempFileName, 
309                      "%s/%s.%lx%x",
310                      ctdl_nettmp_dir,
311                      ChrPtr(NW->node),
312                      time(NULL),// TODO: get time from libev
313                      rand());
314         StrBufStripSlashes(NW->tempFileName, 1);
315         /* We're talking to the correct node.  Now identify ourselves. */
316         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NDOP\n"));
317         NWC_DBG_SEND();
318         return eSendReply;
319 }
320
321 eNextState NWC_ReadNDOPReply(AsyncNetworker *NW)
322 {
323         AsyncIO *IO = &NW->IO;
324         int TotalSendSize;
325         NWC_DBG_READ();
326         if (ChrPtr(NW->IO.IOBuf)[0] == '2')
327         {
328                 int LogLevel = LOG_DEBUG;
329
330                 NW->IO.IOB.TotalSentAlready = 0;
331
332                 TotalSendSize = atol (ChrPtr(NW->IO.IOBuf) + 4);
333
334                 if (TotalSendSize > 0)
335                         LogLevel = LOG_INFO;
336
337                 EVN_syslog(LogLevel,
338                            "Expecting to transfer %d bytes to %s\n",
339                            TotalSendSize,
340                            ChrPtr(NW->tempFileName));
341
342                 if (TotalSendSize <= 0) {
343                         NW->State = eNUOP - 1;
344                 }
345                 else {
346                         int fd;
347                         fd = open(ChrPtr(NW->tempFileName), 
348                                   O_EXCL|O_CREAT|O_NONBLOCK|O_WRONLY, 
349                                   S_IRUSR|S_IWUSR);
350                         if (fd < 0)
351                         {
352                                 SetNWCState(IO, eNWCVSFail);
353                                 EVN_syslog(LOG_CRIT,
354                                        "cannot open %s: %s\n", 
355                                        ChrPtr(NW->tempFileName), 
356                                        strerror(errno));
357
358                                 NW->State = eQUIT - 1;
359                                 return eAbort;
360                         }
361                         FDIOBufferInit(&NW->IO.IOB, &NW->IO.RecvBuf, fd, TotalSendSize);
362                 }
363                 return eSendReply;
364         }
365         else
366         {
367                 SetNWCState(IO, eNWCVSFail);
368                 return eAbort;
369         }
370 }
371
372 eNextState NWC_SendREAD(AsyncNetworker *NW)
373 {
374         AsyncIO *IO = &NW->IO;
375         eNextState rc;
376
377         if (NW->IO.IOB.TotalSentAlready < NW->IO.IOB.TotalSendSize)
378         {
379                 /*
380                  * If shutting down we can exit here and unlink the temp file.
381                  * this shouldn't loose us any messages.
382                  */
383                 if (server_shutting_down)
384                 {
385                         FDIOBufferDelete(&NW->IO.IOB);
386                         unlink(ChrPtr(NW->tempFileName));
387                         FDIOBufferDelete(&IO->IOB);
388                         SetNWCState(IO, eNWCVSFail);
389                         return eAbort;
390                 }
391                 StrBufPrintf(NW->IO.SendBuf.Buf, "READ "LOFF_T_FMT"|%ld\n",
392                              NW->IO.IOB.TotalSentAlready,
393                              NW->IO.IOB.TotalSendSize);
394 /*
395                              ((NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready > IGNET_PACKET_SIZE)
396                               ? IGNET_PACKET_SIZE : 
397                               (NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready))
398                         );
399 */
400                 NWC_DBG_SEND();
401                 return eSendReply;
402         }
403         else 
404         {
405                 NW->State = eCLOS;
406                 rc = NWC_DispatchWriteDone(&NW->IO);
407                 NWC_DBG_SEND();
408
409                 return rc;
410         }
411 }
412
413 eNextState NWC_ReadREADState(AsyncNetworker *NW)
414 {
415         AsyncIO *IO = &NW->IO;
416         NWC_DBG_READ();
417         if (ChrPtr(NW->IO.IOBuf)[0] == '6')
418         {
419                 NW->IO.IOB.ChunkSendRemain = 
420                         NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
421                 return eReadFile;
422         }
423         FDIOBufferDelete(&IO->IOB);
424         return eAbort;
425 }
426 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW);
427 eNextState NWC_ReadREADBlob(AsyncNetworker *NW)
428 {
429         eNextState rc;
430         AsyncIO *IO = &NW->IO;
431         NWC_DBG_READ();
432         if (NW->IO.IOB.TotalSendSize == NW->IO.IOB.TotalSentAlready)
433         {
434                 NW->State ++;
435
436                 FDIOBufferDelete(&NW->IO.IOB);
437
438                 if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
439                         EVN_syslog(LOG_ALERT, 
440                                "Could not link %s to %s: %s\n",
441                                ChrPtr(NW->tempFileName), 
442                                ChrPtr(NW->SpoolFileName), 
443                                strerror(errno));
444                 }
445                 else {
446                         EVN_syslog(LOG_INFO, 
447                                "moved %s to %s\n",
448                                ChrPtr(NW->tempFileName), 
449                                ChrPtr(NW->SpoolFileName));
450                 }
451
452                 unlink(ChrPtr(NW->tempFileName));
453                 rc = NWC_DispatchWriteDone(&NW->IO);
454                 NW->State --;
455                 return rc;
456         }
457         else {
458                 NW->State --;
459                 NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
460                 return eSendReply; //NWC_DispatchWriteDone(&NW->IO);
461         }
462 }
463
464 eNextState NWC_ReadREADBlobDone(AsyncNetworker *NW)
465 {
466         eNextState rc;
467         AsyncIO *IO = &NW->IO;
468 /* we don't have any data to debug print here. */
469         if (NW->IO.IOB.TotalSentAlready >= NW->IO.IOB.TotalSendSize)
470         {
471                 NW->State ++;
472
473                 FDIOBufferDelete(&NW->IO.IOB);
474                 if (link(ChrPtr(NW->tempFileName), ChrPtr(NW->SpoolFileName)) != 0) {
475                         EVN_syslog(LOG_ALERT, 
476                                "Could not link %s to %s: %s\n",
477                                ChrPtr(NW->tempFileName), 
478                                ChrPtr(NW->SpoolFileName), 
479                                strerror(errno));
480                 }
481                 else {
482                         EVN_syslog(LOG_INFO, 
483                                "moved %s to %s\n",
484                                ChrPtr(NW->tempFileName), 
485                                ChrPtr(NW->SpoolFileName));
486                 }
487         
488                 unlink(ChrPtr(NW->tempFileName));
489                 rc = NWC_DispatchWriteDone(&NW->IO);
490                 NW->State --;
491                 return rc;
492         }
493         else {
494                 NW->State --;
495                 NW->IO.IOB.ChunkSendRemain = NW->IO.IOB.ChunkSize;
496                 return NWC_DispatchWriteDone(&NW->IO);
497         }
498 }
499 eNextState NWC_SendCLOS(AsyncNetworker *NW)
500 {
501         AsyncIO *IO = &NW->IO;
502         SetNWCState(IO, eNWCVSNDOPDone);
503         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("CLOS\n"));
504         NWC_DBG_SEND();
505         return eSendReply;
506 }
507
508 eNextState NWC_ReadCLOSReply(AsyncNetworker *NW)
509 {
510         AsyncIO *IO = &NW->IO;
511         NWC_DBG_READ();
512         FDIOBufferDelete(&IO->IOB);
513         if (ChrPtr(NW->IO.IOBuf)[0] != '2')
514                 return eTerminateConnection;
515         return eSendReply;
516 }
517
518
519 eNextState NWC_SendNUOP(AsyncNetworker *NW)
520 {
521         AsyncIO *IO = &NW->IO;
522         eNextState rc;
523         long TotalSendSize;
524         struct stat statbuf;
525         int fd;
526
527         SetNWCState(IO, eNWCVSNUOP);
528         StrBufPrintf(NW->SpoolFileName,
529                      "%s/%s",
530                      ctdl_netout_dir,
531                      ChrPtr(NW->node));
532         StrBufStripSlashes(NW->SpoolFileName, 1);
533
534         fd = open(ChrPtr(NW->SpoolFileName), O_EXCL|O_NONBLOCK|O_RDONLY);
535         if (fd < 0) {
536                 if (errno != ENOENT) {
537                         EVN_syslog(LOG_CRIT,
538                                "cannot open %s: %s\n", 
539                                ChrPtr(NW->SpoolFileName), 
540                                strerror(errno));
541                 }
542                 NW->State = eQUIT;
543                 rc = NWC_SendQUIT(NW);
544                 NWC_DBG_SEND();
545                 return rc;
546         }
547
548         if (fstat(fd, &statbuf) == -1) {
549                 EVN_syslog(LOG_CRIT, "FSTAT FAILED %s [%s]--\n", 
550                            ChrPtr(NW->SpoolFileName), 
551                            strerror(errno));
552                 if (fd > 0) close(fd);
553                 return eAbort;
554         }
555         TotalSendSize = statbuf.st_size;
556         if (TotalSendSize == 0) {
557                 EVNM_syslog(LOG_DEBUG,
558                        "Nothing to send.\n");
559                 NW->State = eQUIT;
560                 rc = NWC_SendQUIT(NW);
561                 NWC_DBG_SEND();
562                 if (fd > 0) close(fd);
563                 return rc;
564         }
565         else
566         {
567                 EVN_syslog(LOG_INFO,
568                            "sending %s to %s\n", 
569                            ChrPtr(NW->SpoolFileName),
570                            ChrPtr(NW->node));
571         }
572
573         FDIOBufferInit(&NW->IO.IOB, &NW->IO.SendBuf, fd, TotalSendSize);
574
575         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("NUOP\n"));
576         NWC_DBG_SEND();
577         return eSendReply;
578
579 }
580 eNextState NWC_ReadNUOPReply(AsyncNetworker *NW)
581 {
582         AsyncIO *IO = &NW->IO;
583         NWC_DBG_READ();
584         if (ChrPtr(NW->IO.IOBuf)[0] != '2') {
585                 FDIOBufferDelete(&IO->IOB);
586                 return eAbort;
587         }
588         return eSendReply;
589 }
590
591 eNextState NWC_SendWRIT(AsyncNetworker *NW)
592 {
593         AsyncIO *IO = &NW->IO;
594         StrBufPrintf(NW->IO.SendBuf.Buf, "WRIT "LOFF_T_FMT"\n", 
595                      NW->IO.IOB.TotalSendSize - NW->IO.IOB.TotalSentAlready);
596         NWC_DBG_SEND();
597         return eSendReply;
598 }
599 eNextState NWC_ReadWRITReply(AsyncNetworker *NW)
600 {
601         AsyncIO *IO = &NW->IO;
602         NWC_DBG_READ();
603         if (ChrPtr(NW->IO.IOBuf)[0] != '7')
604         {
605                 FDIOBufferDelete(&IO->IOB);
606                 return eAbort;
607         }
608
609         NW->IO.IOB.ChunkSendRemain = 
610                 NW->IO.IOB.ChunkSize = atol(ChrPtr(NW->IO.IOBuf)+4);
611         return eSendFile;
612 }
613
614 eNextState NWC_SendBlobDone(AsyncNetworker *NW)
615 {
616         AsyncIO *IO = &NW->IO;
617         eNextState rc;
618         if (NW->IO.IOB.TotalSentAlready >= IO->IOB.TotalSendSize)
619         {
620                 NW->State ++;
621
622                 FDIOBufferDelete(&IO->IOB);
623                 rc =  NWC_DispatchWriteDone(IO);
624                 NW->State --;
625                 return rc;
626         }
627         else {
628                 NW->State --;
629                 IO->IOB.ChunkSendRemain = IO->IOB.ChunkSize;
630                 rc = NWC_DispatchWriteDone(IO);
631                 NW->State --;
632                 return rc;
633         }
634 }
635
636 eNextState NWC_SendUCLS(AsyncNetworker *NW)
637 {
638         AsyncIO *IO = &NW->IO;
639         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("UCLS 1\n"));
640         NWC_DBG_SEND();
641         return eSendReply;
642
643 }
644 eNextState NWC_ReadUCLS(AsyncNetworker *NW)
645 {
646         AsyncIO *IO = &NW->IO;
647         NWC_DBG_READ();
648
649         EVN_syslog(LOG_NOTICE,
650                    "Sent %s [%ld] octets to <%s>\n",
651                    ChrPtr(NW->SpoolFileName),
652                    NW->IO.IOB.ChunkSize,
653                    ChrPtr(NW->node));
654
655         if (ChrPtr(NW->IO.IOBuf)[0] == '2') {
656                 EVN_syslog(LOG_DEBUG, "Removing <%s>\n", ChrPtr(NW->SpoolFileName));
657                 unlink(ChrPtr(NW->SpoolFileName));
658         }
659         FDIOBufferDelete(&IO->IOB);
660         SetNWCState(IO, eNWCVSNUOPDone);
661         return eSendReply;
662 }
663
664 eNextState NWC_SendQUIT(AsyncNetworker *NW)
665 {
666         AsyncIO *IO = &NW->IO;
667         StrBufPlain(NW->IO.SendBuf.Buf, HKEY("QUIT\n"));
668
669         NWC_DBG_SEND();
670         return eSendReply;
671 }
672
673 eNextState NWC_ReadQUIT(AsyncNetworker *NW)
674 {
675         AsyncIO *IO = &NW->IO;
676         NWC_DBG_READ();
677
678         return eAbort;
679 }
680
681
682 NWClientHandler NWC_ReadHandlers[] = {
683         NWC_ReadGreeting,
684         NWC_ReadAuthReply,
685         NWC_ReadNDOPReply,
686         NWC_ReadREADState,
687         NWC_ReadREADBlob,
688         NWC_ReadCLOSReply,
689         NWC_ReadNUOPReply,
690         NWC_ReadWRITReply,
691         NWC_SendBlobDone,
692         NWC_ReadUCLS,
693         NWC_ReadQUIT};
694
695 long NWC_ConnTimeout = 100;
696
697 const long NWC_SendTimeouts[] = {
698         100,
699         100,
700         100,
701         100,
702         100,
703         100,
704         100,
705         100
706 };
707 const ConstStr NWC[] = {
708         {HKEY("Connection broken during ")},
709         {HKEY("Connection broken during ")},
710         {HKEY("Connection broken during ")},
711         {HKEY("Connection broken during ")},
712         {HKEY("Connection broken during ")},
713         {HKEY("Connection broken during ")},
714         {HKEY("Connection broken during ")},
715         {HKEY("Connection broken during ")}
716 };
717
718 NWClientHandler NWC_SendHandlers[] = {
719         NULL,
720         NWC_SendAuth,
721         NWC_SendNDOP,
722         NWC_SendREAD,
723         NWC_ReadREADBlobDone,
724         NWC_SendCLOS,
725         NWC_SendNUOP,
726         NWC_SendWRIT,
727         NWC_SendBlobDone,
728         NWC_SendUCLS,
729         NWC_SendQUIT
730 };
731
732 const long NWC_ReadTimeouts[] = {
733         100,
734         100,
735         100,
736         100,
737         100,
738         100,
739         100,
740         100,
741         100,
742         100
743 };
744
745
746
747
748 eNextState nwc_get_one_host_ip_done(AsyncIO *IO)
749 {
750         AsyncNetworker *NW = IO->Data;
751         struct hostent *hostent;
752
753         QueryCbDone(IO);
754
755         hostent = NW->HostLookup.VParsedDNSReply;
756         if ((NW->HostLookup.DNSStatus == ARES_SUCCESS) && 
757             (hostent != NULL) ) {
758                 memset(&NW->IO.ConnectMe->Addr, 0, sizeof(struct in6_addr));
759                 if (NW->IO.ConnectMe->IPv6) {
760                         memcpy(&NW->IO.ConnectMe->Addr.sin6_addr.s6_addr, 
761                                &hostent->h_addr_list[0],
762                                sizeof(struct in6_addr));
763                         
764                         NW->IO.ConnectMe->Addr.sin6_family = hostent->h_addrtype;
765                         NW->IO.ConnectMe->Addr.sin6_port   = htons(atol(ChrPtr(NW->port)));//// TODO use the one from the URL.
766                 }
767                 else {
768                         struct sockaddr_in *addr = (struct sockaddr_in*) &NW->IO.ConnectMe->Addr;
769                         /* Bypass the ns lookup result like this: IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); */
770 //                      addr->sin_addr.s_addr = htonl((uint32_t)&hostent->h_addr_list[0]);
771                         memcpy(&addr->sin_addr.s_addr, 
772                                hostent->h_addr_list[0], 
773                                sizeof(uint32_t));
774                         
775                         addr->sin_family = hostent->h_addrtype;
776                         addr->sin_port   = htons(504);/// default citadel port
777                         
778                 }
779                 return nwc_connect_ip(IO);
780         }
781         else
782                 return eAbort;
783 }
784
785
786 eNextState nwc_get_one_host_ip(AsyncIO *IO)
787 {
788         AsyncNetworker *NW = IO->Data;
789         /* 
790          * here we start with the lookup of one host.
791          */ 
792
793         EVN_syslog(LOG_DEBUG, "NWC: %s\n", __FUNCTION__);
794
795         EVN_syslog(LOG_DEBUG, 
796                    "NWC client[%ld]: looking up %s-Record %s : %d ...\n", 
797                    NW->n, 
798                    (NW->IO.ConnectMe->IPv6)? "aaaa": "a",
799                    NW->IO.ConnectMe->Host, 
800                    NW->IO.ConnectMe->Port);
801
802         QueueQuery((NW->IO.ConnectMe->IPv6)? ns_t_aaaa : ns_t_a, 
803                    NW->IO.ConnectMe->Host, 
804                    &NW->IO, 
805                    &NW->HostLookup, 
806                    nwc_get_one_host_ip_done);
807         IO->NextState = eReadDNSReply;
808         return IO->NextState;
809 }
810 /**
811  * @brief lineread Handler; understands when to read more POP3 lines, and when this is a one-lined reply.
812  */
813 eReadState NWC_ReadServerStatus(AsyncIO *IO)
814 {
815 //      AsyncNetworker *NW = IO->Data;
816         eReadState Finished = eBufferNotEmpty; 
817
818         switch (IO->NextState) {
819         case eSendDNSQuery:
820         case eReadDNSReply:
821         case eDBQuery:
822         case eConnect:
823         case eTerminateConnection:
824         case eAbort:
825                 Finished = eReadFail;
826                 break;
827         case eSendReply: 
828         case eSendMore:
829         case eReadMore:
830         case eReadMessage: 
831                 Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
832                 break;
833         case eReadFile:
834         case eSendFile:
835         case eReadPayload:
836                 break;
837         }
838         return Finished;
839 }
840
841
842
843 eNextState NWC_FailNetworkConnection(AsyncIO *IO)
844 {
845         SetNWCState(IO, eNWCVSConnFail);
846         return EventQueueDBOperation(IO, NWC_SendFailureMessage, 1);
847 }
848
849 void NWC_SetTimeout(eNextState NextTCPState, AsyncNetworker *NW)
850 {
851         AsyncIO *IO = &NW->IO;
852         double Timeout = 0.0;
853
854         EVN_syslog(LOG_DEBUG, "%s - %d\n", __FUNCTION__, NextTCPState);
855
856         switch (NextTCPState) {
857         case eSendMore:
858         case eSendReply:
859         case eReadMessage:
860                 Timeout = NWC_ReadTimeouts[NW->State];
861                 break;
862         case eReadFile:
863         case eSendFile:
864         case eReadPayload:
865                 Timeout = 100000;
866                 break;
867         case eSendDNSQuery:
868         case eReadDNSReply:
869         case eDBQuery:
870         case eReadMore:
871         case eConnect:
872         case eTerminateConnection:
873         case eAbort:
874                 return;
875         }
876         if (Timeout > 0) {
877                 EVN_syslog(LOG_DEBUG, 
878                            "%s - %d %f\n",
879                            __FUNCTION__,
880                            NextTCPState,
881                            Timeout);
882                 SetNextTimeout(&NW->IO, Timeout*100);
883         }
884 }
885
886
887 eNextState NWC_DispatchReadDone(AsyncIO *IO)
888 {
889         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
890         AsyncNetworker *NW = IO->Data;
891         eNextState rc;
892
893         rc = NWC_ReadHandlers[NW->State](NW);
894
895         if (rc != eReadMore)
896                 NW->State++;
897
898         NWC_SetTimeout(rc, NW);
899
900         return rc;
901 }
902 eNextState NWC_DispatchWriteDone(AsyncIO *IO)
903 {
904         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
905         AsyncNetworker *NW = IO->Data;
906         eNextState rc;
907
908         rc = NWC_SendHandlers[NW->State](NW);
909         NWC_SetTimeout(rc, NW);
910         return rc;
911 }
912
913 /*****************************************************************************/
914 /*                     Networker CLIENT ERROR CATCHERS                       */
915 /*****************************************************************************/
916 eNextState NWC_Terminate(AsyncIO *IO)
917 {
918         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
919         FinalizeNetworker(IO);
920         return eAbort;
921 }
922
923 eNextState NWC_TerminateDB(AsyncIO *IO)
924 {
925         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
926         FinalizeNetworker(IO);
927         return eAbort;
928 }
929
930 eNextState NWC_Timeout(AsyncIO *IO)
931 {
932         AsyncNetworker *NW = IO->Data;
933         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
934
935         if (NW->IO.ErrMsg == NULL)
936                 NW->IO.ErrMsg = NewStrBuf();
937         StrBufPrintf(NW->IO.ErrMsg, "Timeout while talking to %s \r\n", ChrPtr(NW->host));
938         return NWC_FailNetworkConnection(IO);
939 }
940 eNextState NWC_ConnFail(AsyncIO *IO)
941 {
942         AsyncNetworker *NW = IO->Data;
943
944         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
945         if (NW->IO.ErrMsg == NULL)
946                 NW->IO.ErrMsg = NewStrBuf();
947         StrBufPrintf(NW->IO.ErrMsg, "failed to connect %s \r\n", ChrPtr(NW->host));
948
949         return NWC_FailNetworkConnection(IO);
950 }
951 eNextState NWC_DNSFail(AsyncIO *IO)
952 {
953         AsyncNetworker *NW = IO->Data;
954
955         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
956         if (NW->IO.ErrMsg == NULL)
957                 NW->IO.ErrMsg = NewStrBuf();
958         StrBufPrintf(NW->IO.ErrMsg, "failed to look up %s \r\n", ChrPtr(NW->host));
959
960         return NWC_FailNetworkConnection(IO);
961 }
962 eNextState NWC_Shutdown(AsyncIO *IO)
963 {
964         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
965
966         FinalizeNetworker(IO);
967         return eAbort;
968 }
969
970
971 eNextState nwc_connect_ip(AsyncIO *IO)
972 {
973         AsyncNetworker *NW = IO->Data;
974
975         SetNWCState(&NW->IO, eNWCVSConnecting);
976         EVN_syslog(LOG_DEBUG, "%s\n", __FUNCTION__);
977         EVN_syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", 
978                    ChrPtr(NW->node), 
979                    ChrPtr(NW->host),
980                    ChrPtr(NW->port));
981         
982         return EvConnectSock(IO,
983                              NWC_ConnTimeout,
984                              NWC_ReadTimeouts[0],
985                              1);
986 }
987
988 static int NetworkerCount = 0;
989 void RunNetworker(AsyncNetworker *NW)
990 {
991         NW->n = NetworkerCount++;
992         CtdlNetworkTalkingTo(SKEY(NW->node), NTT_ADD);
993         syslog(LOG_DEBUG, "NW[%s][%ld]: polling\n", ChrPtr(NW->node), NW->n);
994         ParseURL(&NW->IO.ConnectMe, NW->Url, 504);
995
996         InitIOStruct(&NW->IO,
997                      NW,
998                      eReadMessage,
999                      NWC_ReadServerStatus,
1000                      NWC_DNSFail,
1001                      NWC_DispatchWriteDone,
1002                      NWC_DispatchReadDone,
1003                      NWC_Terminate,
1004                      NWC_TerminateDB,
1005                      NWC_ConnFail,
1006                      NWC_Timeout,
1007                      NWC_Shutdown);
1008
1009         safestrncpy(((CitContext *)NW->IO.CitContext)->cs_host, 
1010                     ChrPtr(NW->host),
1011                     sizeof(((CitContext *)NW->IO.CitContext)->cs_host)); 
1012
1013         if (NW->IO.ConnectMe->IsIP) {
1014                 SetNWCState(&NW->IO, eNWCVSLookup);
1015                 QueueEventContext(&NW->IO,
1016                                   nwc_connect_ip);
1017         }
1018         else { /* uneducated admin has chosen to add DNS to the equation... */
1019                 SetNWCState(&NW->IO, eNWCVSConnecting);
1020                 QueueEventContext(&NW->IO,
1021                                   nwc_get_one_host_ip);
1022         }
1023 }
1024
1025 /*
1026  * Poll other Citadel nodes and transfer inbound/outbound network data.
1027  * Set "full" to nonzero to force a poll of every node, or to zero to poll
1028  * only nodes to which we have data to send.
1029  */
1030 void network_poll_other_citadel_nodes(int full_poll, HashList *ignetcfg)
1031 {
1032         const char *key;
1033         long len;
1034         HashPos *Pos;
1035         void *vCfg;
1036         AsyncNetworker *NW;
1037         StrBuf *SpoolFileName;
1038         
1039         int poll = 0;
1040         
1041         if (GetCount(ignetcfg) ==0) {
1042                 syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
1043                 return;
1044         }
1045         become_session(&networker_client_CC);
1046
1047         SpoolFileName = NewStrBufPlain(ctdl_netout_dir, -1);
1048
1049         Pos = GetNewHashPos(ignetcfg, 0);
1050
1051         while (GetNextHashPos(ignetcfg, Pos, &len, &key, &vCfg))
1052         {
1053                 /* Use the string tokenizer to grab one line at a time */
1054                 if(server_shutting_down)
1055                         return;/* TODO free stuff*/
1056                 CtdlNodeConf *pNode = (CtdlNodeConf*) vCfg;
1057                 poll = 0;
1058                 NW = (AsyncNetworker*)malloc(sizeof(AsyncNetworker));
1059                 memset(NW, 0, sizeof(AsyncNetworker));
1060                 
1061                 NW->node = NewStrBufDup(pNode->NodeName);
1062                 NW->host = NewStrBufDup(pNode->Host);
1063                 NW->port = NewStrBufDup(pNode->Port);
1064                 NW->secret = NewStrBufDup(pNode->Secret);
1065                 
1066                 if ( (StrLength(NW->node) != 0) && 
1067                      (StrLength(NW->secret) != 0) &&
1068                      (StrLength(NW->host) != 0) &&
1069                      (StrLength(NW->port) != 0))
1070                 {
1071                         poll = full_poll;
1072                         if (poll == 0)
1073                         {
1074                                 StrBufAppendBufPlain(SpoolFileName, HKEY("/"), 0);
1075                                 StrBufAppendBuf(SpoolFileName, NW->node, 0);
1076                                 StrBufStripSlashes(SpoolFileName, 1);
1077                                 
1078                                 if (access(ChrPtr(SpoolFileName), R_OK) == 0) {
1079                                         poll = 1;
1080                                 }
1081                         }
1082                 }
1083                 if (poll && 
1084                     (StrLength(NW->host) > 0) && 
1085                     strcmp("0.0.0.0", ChrPtr(NW->host)))
1086                 {
1087                         NW->Url = NewStrBuf();
1088                         StrBufPrintf(NW->Url, "citadel://%s@%s:%s", 
1089                                      ChrPtr(NW->secret),
1090                                      ChrPtr(NW->host),
1091                                      ChrPtr(NW->port));
1092                         if (!CtdlNetworkTalkingTo(SKEY(NW->node), NTT_CHECK))
1093                         {
1094                                 RunNetworker(NW);
1095                                 continue;
1096                         }
1097                 }
1098                 DeleteNetworker(NW);
1099         }
1100         FreeStrBuf(&SpoolFileName);
1101         DeleteHashPos(&Pos);
1102 }
1103
1104
1105 void network_do_clientqueue(void)
1106 {
1107         HashList *working_ignetcfg;
1108         int full_processing = 1;
1109         static time_t last_run = 0L;
1110
1111         /*
1112          * Run the full set of processing tasks no more frequently
1113          * than once every n seconds
1114          */
1115         if ( (time(NULL) - last_run) < config.c_net_freq ) {
1116                 full_processing = 0;
1117                 syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
1118                         config.c_net_freq - (time(NULL)- last_run)
1119                 );
1120         }
1121
1122         working_ignetcfg = CtdlLoadIgNetCfg();
1123         /*
1124          * Poll other Citadel nodes.  Maybe.  If "full_processing" is set
1125          * then we poll everyone.  Otherwise we only poll nodes we have stuff
1126          * to send to.
1127          */
1128         network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
1129         DeleteHash(&working_ignetcfg);
1130 }
1131
1132 void LogDebugEnableNetworkClient(const int n)
1133 {
1134         NetworkClientDebugEnabled = n;
1135 }
1136 /*
1137  * Module entry point
1138  */
1139 CTDL_MODULE_INIT(network_client)
1140 {
1141         if (!threading)
1142         {
1143                 CtdlFillSystemContext(&networker_client_CC, "CitNetworker");
1144                 
1145                 CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER, PRIO_SEND + 10);
1146                 CtdlRegisterDebugFlagHook(HKEY("networkclient"), LogDebugEnableNetworkClient, &NetworkClientDebugEnabled);
1147
1148         }
1149         return "networkclient";
1150 }