2 * This module handles shared rooms, inter-Citadel mail, and outbound
3 * mailing list processing.
5 * Copyright (c) 2000-2011 by the citadel.org team
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.
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.
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
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.
41 #include <sys/types.h>
43 #if TIME_WITH_SYS_TIME
44 # include <sys/time.h>
48 # include <sys/time.h>
56 # if HAVE_SYS_SYSCALL_H
57 # include <sys/syscall.h>
64 #include <libcitadel.h>
67 #include "citserver.h"
73 #include "internet_addressing.h"
74 #include "serv_network.h"
75 #include "clientsocket.h"
77 #include "citadel_dirs.h"
86 #include "netconfig.h"
87 #include "ctdl_module.h"
91 * receive network spool from the remote system
93 void receive_spool(int *sock, char *remote_nodename) {
94 int download_len = 0L;
95 int bytes_received = 0L;
97 char tempfilename[PATH_MAX];
98 char permfilename[PATH_MAX];
102 snprintf(tempfilename,
111 snprintf(permfilename,
120 if (sock_puts(sock, "NDOP") < 0) return;
121 if (sock_getln(sock, buf, sizeof buf) < 0) return;
122 syslog(LOG_DEBUG, "<%s\n", buf);
127 download_len = extract_long(&buf[4], 0);
128 if (download_len <= 0) {
133 fp = fopen(tempfilename, "w");
135 syslog(LOG_CRIT, "Cannot create %s: %s\n", tempfilename, strerror(errno));
139 syslog(LOG_DEBUG, "Expecting to transfer %d bytes\n", download_len);
140 while (bytes_received < download_len) {
142 * If shutting down we can exit here and unlink the temp file.
143 * this shouldn't loose us any messages.
145 if (server_shutting_down)
148 unlink(tempfilename);
151 snprintf(buf, sizeof buf, "READ %d|%d",
153 ((download_len - bytes_received > IGNET_PACKET_SIZE)
154 ? IGNET_PACKET_SIZE : (download_len - bytes_received))
157 if (sock_puts(sock, buf) < 0) {
159 unlink(tempfilename);
162 if (sock_getln(sock, buf, sizeof buf) < 0) {
164 unlink(tempfilename);
169 plen = extract_int(&buf[4], 0);
170 StrBuf *pbuf = NewStrBuf();
171 if (socket_read_blob(sock, pbuf, plen, CLIENT_TIMEOUT) != plen) {
172 syslog(LOG_INFO, "Short read from peer; aborting.\n");
174 unlink(tempfilename);
178 fwrite(ChrPtr(pbuf), plen, 1, fp);
179 bytes_received += plen;
186 /* Last chance for shutdown exit */
187 if (server_shutting_down)
189 unlink(tempfilename);
193 if (sock_puts(sock, "CLOS") < 0) {
194 unlink(tempfilename);
199 * From here on we must complete or messages will get lost
201 if (sock_getln(sock, buf, sizeof buf) < 0) {
202 unlink(tempfilename);
206 syslog(LOG_DEBUG, "%s\n", buf);
209 * Now move the temp file to its permanent location.
211 if (link(tempfilename, permfilename) != 0) {
212 syslog(LOG_ALERT, "Could not link %s to %s: %s\n",
213 tempfilename, permfilename, strerror(errno)
217 unlink(tempfilename);
223 * transmit network spool to the remote system
225 void transmit_spool(int *sock, char *remote_nodename)
230 long bytes_to_write, thisblock, bytes_written;
234 if (sock_puts(sock, "NUOP") < 0) return;
235 if (sock_getln(sock, buf, sizeof buf) < 0) return;
236 syslog(LOG_DEBUG, "<%s\n", buf);
241 snprintf(sfname, sizeof sfname,
246 fd = open(sfname, O_RDONLY);
248 if (errno != ENOENT) {
249 syslog(LOG_CRIT, "cannot open %s: %s\n", sfname, strerror(errno));
254 while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) {
255 bytes_to_write = plen;
256 while (bytes_to_write > 0L) {
257 /* Exit if shutting down */
258 if (server_shutting_down)
264 snprintf(buf, sizeof buf, "WRIT %ld", bytes_to_write);
265 if (sock_puts(sock, buf) < 0) {
269 if (sock_getln(sock, buf, sizeof buf) < 0) {
273 thisblock = atol(&buf[4]);
275 if (sock_write(sock, pbuf, (int) thisblock) < 0) {
279 bytes_to_write -= thisblock;
280 bytes_written += thisblock;
290 /* Last chance for shutdown exit */
291 if(server_shutting_down)
294 if (sock_puts(sock, "UCLS 1") < 0) return;
297 * From here on we must complete or messages will get lost
299 if (sock_getln(sock, buf, sizeof buf) < 0) return;
300 syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", bytes_written, remote_nodename);
301 syslog(LOG_DEBUG, "<%s\n", buf);
303 syslog(LOG_DEBUG, "Removing <%s>\n", sfname);
310 * Poll one Citadel node (called by network_poll_other_citadel_nodes() below)
312 void network_poll_node(char *node, char *secret, char *host, char *port) {
316 char connected_to[SIZ];
319 if (network_talking_to(node, NTT_CHECK)) return;
320 network_talking_to(node, NTT_ADD);
321 syslog(LOG_DEBUG, "network: polling <%s>\n", node);
322 syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", node, host, port);
324 sock = sock_connect(host, port);
326 syslog(LOG_ERR, "Could not connect: %s\n", strerror(errno));
327 network_talking_to(node, NTT_REMOVE);
331 syslog(LOG_DEBUG, "Connected!\n");
332 CCC->SBuf.Buf = NewStrBuf();
333 CCC->sMigrateBuf = NewStrBuf();
334 CCC->SBuf.ReadWritePointer = NULL;
336 /* Read the server greeting */
337 if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
338 syslog(LOG_DEBUG, ">%s\n", buf);
340 /* Check that the remote is who we think it is and warn the Aide if not */
341 extract_token (connected_to, buf, 1, ' ', sizeof connected_to);
342 if (strcmp(connected_to, node))
344 snprintf(err_buf, sizeof(err_buf),
345 "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
348 syslog(LOG_ERR, "%s\n", err_buf);
349 CtdlAideMessage(err_buf, "Network error");
352 /* We're talking to the correct node. Now identify ourselves. */
353 snprintf(buf, sizeof buf, "NETP %s|%s", config.c_nodename, secret);
354 syslog(LOG_DEBUG, "<%s\n", buf);
355 if (sock_puts(&sock, buf) <0) goto bail;
356 if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
357 syslog(LOG_DEBUG, ">%s\n", buf);
362 /* At this point we are authenticated. */
363 if (!server_shutting_down)
364 receive_spool(&sock, node);
365 if (!server_shutting_down)
366 transmit_spool(&sock, node);
369 sock_puts(&sock, "QUIT");
371 FreeStrBuf(&CCC->SBuf.Buf);
372 FreeStrBuf(&CCC->sMigrateBuf);
375 network_talking_to(node, NTT_REMOVE);
381 * Poll other Citadel nodes and transfer inbound/outbound network data.
382 * Set "full" to nonzero to force a poll of every node, or to zero to poll
383 * only nodes to which we have data to send.
385 void network_poll_other_citadel_nodes(int full_poll, char *working_ignetcfg)
396 if (working_ignetcfg == NULL) {
397 syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
401 /* Use the string tokenizer to grab one line at a time */
402 for (i=0; i<num_tokens(working_ignetcfg, '\n'); ++i) {
403 if(server_shutting_down)
405 extract_token(linebuf, working_ignetcfg, i, '\n', sizeof linebuf);
406 extract_token(node, linebuf, 0, '|', sizeof node);
407 extract_token(secret, linebuf, 1, '|', sizeof secret);
408 extract_token(host, linebuf, 2, '|', sizeof host);
409 extract_token(port, linebuf, 3, '|', sizeof port);
410 if ( !IsEmptyStr(node) && !IsEmptyStr(secret)
411 && !IsEmptyStr(host) && !IsEmptyStr(port)) {
420 if (access(spoolfile, R_OK) == 0) {
425 network_poll_node(node, secret, host, port);
433 void network_do_clientqueue(void)
435 char *working_ignetcfg;
436 int full_processing = 1;
437 static time_t last_run = 0L;
440 * Run the full set of processing tasks no more frequently
441 * than once every n seconds
443 if ( (time(NULL) - last_run) < config.c_net_freq ) {
445 syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
446 config.c_net_freq - (time(NULL)- last_run)
450 working_ignetcfg = load_working_ignetcfg();
452 * Poll other Citadel nodes. Maybe. If "full_processing" is set
453 * then we poll everyone. Otherwise we only poll nodes we have stuff
456 network_poll_other_citadel_nodes(full_processing, working_ignetcfg);
457 if (working_ignetcfg)
458 free(working_ignetcfg);
466 CTDL_MODULE_INIT(network_client)
470 CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER);
472 return "network_client";