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