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"
90 * receive network spool from the remote system
92 void receive_spool(int *sock, char *remote_nodename) {
93 int download_len = 0L;
94 int bytes_received = 0L;
96 char tempfilename[PATH_MAX];
97 char permfilename[PATH_MAX];
101 snprintf(tempfilename,
110 snprintf(permfilename,
119 if (sock_puts(sock, "NDOP") < 0) return;
120 if (sock_getln(sock, buf, sizeof buf) < 0) return;
121 syslog(LOG_DEBUG, "<%s\n", buf);
126 download_len = extract_long(&buf[4], 0);
127 if (download_len <= 0) {
132 fp = fopen(tempfilename, "w");
134 syslog(LOG_CRIT, "Cannot create %s: %s\n", tempfilename, strerror(errno));
138 syslog(LOG_DEBUG, "Expecting to transfer %d bytes\n", download_len);
139 while (bytes_received < download_len) {
141 * If shutting down we can exit here and unlink the temp file.
142 * this shouldn't loose us any messages.
144 if (server_shutting_down)
147 unlink(tempfilename);
150 snprintf(buf, sizeof buf, "READ %d|%d",
152 ((download_len - bytes_received > IGNET_PACKET_SIZE)
153 ? IGNET_PACKET_SIZE : (download_len - bytes_received))
156 if (sock_puts(sock, buf) < 0) {
158 unlink(tempfilename);
161 if (sock_getln(sock, buf, sizeof buf) < 0) {
163 unlink(tempfilename);
168 plen = extract_int(&buf[4], 0);
169 StrBuf *pbuf = NewStrBuf();
170 if (socket_read_blob(sock, pbuf, plen, CLIENT_TIMEOUT) != plen) {
171 syslog(LOG_INFO, "Short read from peer; aborting.\n");
173 unlink(tempfilename);
177 fwrite(ChrPtr(pbuf), plen, 1, fp);
178 bytes_received += plen;
185 /* Last chance for shutdown exit */
186 if (server_shutting_down)
188 unlink(tempfilename);
192 if (sock_puts(sock, "CLOS") < 0) {
193 unlink(tempfilename);
198 * From here on we must complete or messages will get lost
200 if (sock_getln(sock, buf, sizeof buf) < 0) {
201 unlink(tempfilename);
205 syslog(LOG_DEBUG, "%s\n", buf);
208 * Now move the temp file to its permanent location.
210 if (link(tempfilename, permfilename) != 0) {
211 syslog(LOG_ALERT, "Could not link %s to %s: %s\n",
212 tempfilename, permfilename, strerror(errno)
216 unlink(tempfilename);
222 * transmit network spool to the remote system
224 void transmit_spool(int *sock, char *remote_nodename)
229 long bytes_to_write, thisblock, bytes_written;
233 if (sock_puts(sock, "NUOP") < 0) return;
234 if (sock_getln(sock, buf, sizeof buf) < 0) return;
235 syslog(LOG_DEBUG, "<%s\n", buf);
240 snprintf(sfname, sizeof sfname,
245 fd = open(sfname, O_RDONLY);
247 if (errno != ENOENT) {
248 syslog(LOG_CRIT, "cannot open %s: %s\n", sfname, strerror(errno));
253 while (plen = (long) read(fd, pbuf, IGNET_PACKET_SIZE), plen > 0L) {
254 bytes_to_write = plen;
255 while (bytes_to_write > 0L) {
256 /* Exit if shutting down */
257 if (server_shutting_down)
263 snprintf(buf, sizeof buf, "WRIT %ld", bytes_to_write);
264 if (sock_puts(sock, buf) < 0) {
268 if (sock_getln(sock, buf, sizeof buf) < 0) {
272 thisblock = atol(&buf[4]);
274 if (sock_write(sock, pbuf, (int) thisblock) < 0) {
278 bytes_to_write -= thisblock;
279 bytes_written += thisblock;
289 /* Last chance for shutdown exit */
290 if(server_shutting_down)
293 if (sock_puts(sock, "UCLS 1") < 0) return;
296 * From here on we must complete or messages will get lost
298 if (sock_getln(sock, buf, sizeof buf) < 0) return;
299 syslog(LOG_NOTICE, "Sent %ld octets to <%s>\n", bytes_written, remote_nodename);
300 syslog(LOG_DEBUG, "<%s\n", buf);
302 syslog(LOG_DEBUG, "Removing <%s>\n", sfname);
309 * Poll one Citadel node (called by network_poll_other_citadel_nodes() below)
311 void network_poll_node(char *node, char *secret, char *host, char *port) {
315 char connected_to[SIZ];
318 if (network_talking_to(node, NTT_CHECK)) return;
319 network_talking_to(node, NTT_ADD);
320 syslog(LOG_DEBUG, "network: polling <%s>\n", node);
321 syslog(LOG_NOTICE, "Connecting to <%s> at %s:%s\n", node, host, port);
323 sock = sock_connect(host, port);
325 syslog(LOG_ERR, "Could not connect: %s\n", strerror(errno));
326 network_talking_to(node, NTT_REMOVE);
330 syslog(LOG_DEBUG, "Connected!\n");
331 CCC->sReadBuf = NewStrBuf();
332 CCC->sMigrateBuf = NewStrBuf();
335 /* Read the server greeting */
336 if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
337 syslog(LOG_DEBUG, ">%s\n", buf);
339 /* Check that the remote is who we think it is and warn the Aide if not */
340 extract_token (connected_to, buf, 1, ' ', sizeof connected_to);
341 if (strcmp(connected_to, node))
343 snprintf(err_buf, sizeof(err_buf),
344 "Connected to node \"%s\" but I was expecting to connect to node \"%s\".",
347 syslog(LOG_ERR, "%s\n", err_buf);
348 CtdlAideMessage(err_buf, "Network error");
351 /* We're talking to the correct node. Now identify ourselves. */
352 snprintf(buf, sizeof buf, "NETP %s|%s", config.c_nodename, secret);
353 syslog(LOG_DEBUG, "<%s\n", buf);
354 if (sock_puts(&sock, buf) <0) goto bail;
355 if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
356 syslog(LOG_DEBUG, ">%s\n", buf);
361 /* At this point we are authenticated. */
362 if (!server_shutting_down)
363 receive_spool(&sock, node);
364 if (!server_shutting_down)
365 transmit_spool(&sock, node);
368 sock_puts(&sock, "QUIT");
370 FreeStrBuf(&CCC->sReadBuf);
371 FreeStrBuf(&CCC->sMigrateBuf);
374 network_talking_to(node, NTT_REMOVE);
380 * Poll other Citadel nodes and transfer inbound/outbound network data.
381 * Set "full" to nonzero to force a poll of every node, or to zero to poll
382 * only nodes to which we have data to send.
384 void network_poll_other_citadel_nodes(int full_poll) {
394 if (working_ignetcfg == NULL) {
395 syslog(LOG_DEBUG, "network: no neighbor nodes are configured - not polling.\n");
399 /* Use the string tokenizer to grab one line at a time */
400 for (i=0; i<num_tokens(working_ignetcfg, '\n'); ++i) {
401 if(server_shutting_down)
403 extract_token(linebuf, working_ignetcfg, i, '\n', sizeof linebuf);
404 extract_token(node, linebuf, 0, '|', sizeof node);
405 extract_token(secret, linebuf, 1, '|', sizeof secret);
406 extract_token(host, linebuf, 2, '|', sizeof host);
407 extract_token(port, linebuf, 3, '|', sizeof port);
408 if ( !IsEmptyStr(node) && !IsEmptyStr(secret)
409 && !IsEmptyStr(host) && !IsEmptyStr(port)) {
418 if (access(spoolfile, R_OK) == 0) {
423 network_poll_node(node, secret, host, port);
431 void network_do_clientqueue(void)
433 int full_processing = 1;
434 static time_t last_run = 0L;
437 * Run the full set of processing tasks no more frequently
438 * than once every n seconds
440 if ( (time(NULL) - last_run) < config.c_net_freq ) {
442 syslog(LOG_DEBUG, "Network full processing in %ld seconds.\n",
443 config.c_net_freq - (time(NULL)- last_run)
449 * Poll other Citadel nodes. Maybe. If "full_processing" is set
450 * then we poll everyone. Otherwise we only poll nodes we have stuff
453 network_poll_other_citadel_nodes(full_processing);
461 CTDL_MODULE_INIT(network_client)
465 CtdlRegisterSessionHook(network_do_clientqueue, EVT_TIMER);
467 return "network_client";