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