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