* This module handles shared rooms, inter-Citadel mail, and outbound
* mailing list processing.
*
- * Copyright (C) 2000-2005 by Art Cancro and others.
- * This code is released under the terms of the GNU General Public License.
+ * Copyright (c) 2000-2010 by the citadel.org team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* ** NOTE ** A word on the S_NETCONFIGS semaphore:
* This is a fairly high-level type of critical section. It ensures that no
#include "citserver.h"
#include "support.h"
#include "config.h"
-#include "room_ops.h"
#include "user_ops.h"
-#include "policy.h"
#include "database.h"
#include "msgbase.h"
#include "internet_addressing.h"
#include "snprintf.h"
#endif
+#include "context.h"
#include "ctdl_module.h"
cdbut = cdb_fetch(CDB_USETABLE, msgid, strlen(msgid));
if (cdbut != NULL) {
cdb_free(cdbut);
+ CtdlLogPrintf(CTDL_DEBUG, "network_usetable() : we already have %s\n", msgid);
return(1);
}
/* If we got to this point, it's unique: add it. */
strcpy(ut.ut_msgid, msgid);
ut.ut_timestamp = time(NULL);
- cdb_store(CDB_USETABLE, msgid, strlen(msgid),
- &ut, sizeof(struct UseTable) );
+ cdb_store(CDB_USETABLE, msgid, strlen(msgid), &ut, sizeof(struct UseTable) );
return(0);
}
void cmd_gnet(char *argbuf) {
- char filename[SIZ];
+ char filename[PATH_MAX];
char buf[SIZ];
FILE *fp;
void cmd_snet(char *argbuf) {
- char tempfilename[SIZ];
- char filename[SIZ];
+ char tempfilename[PATH_MAX];
+ char filename[PATH_MAX];
char buf[SIZ];
FILE *fp, *newfp;
long msglen;
char *recps = NULL;
size_t recps_len = SIZ;
+ size_t siz;
struct recptypes *valid;
namelist *nptr;
+ char bounce_to[256];
if (sc->num_msgs_spooled < 1) {
fclose(sc->digestfp);
msg->cm_fields['F'] = strdup(buf);
msg->cm_fields['R'] = strdup(buf);
+ /* Set the 'List-ID' header */
+ msg->cm_fields['L'] = malloc(1024);
+ snprintf(msg->cm_fields['L'], 1024,
+ "%s <%ld.list-id.%s>",
+ CC->room.QRname,
+ CC->room.QRnumber,
+ config.c_fqdn
+ );
+
/*
* Go fetch the contents of the digest
*/
msg->cm_fields['M'] = malloc(msglen + 1);
fseek(sc->digestfp, 0L, SEEK_SET);
- fread(msg->cm_fields['M'], (size_t)msglen, 1, sc->digestfp);
- msg->cm_fields['M'][msglen] = 0;
+ siz = fread(msg->cm_fields['M'], (size_t)msglen, 1, sc->digestfp);
+ msg->cm_fields['M'][msglen] = '\0';
fclose(sc->digestfp);
sc->digestfp = NULL;
strcat(recps, nptr->name);
}
+ /* Where do we want bounces and other noise to be heard? Surely not the list members! */
+ snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn);
+
/* Now submit the message */
valid = validate_recipients(recps, NULL, 0);
free(recps);
- CtdlSubmitMsg(msg, valid, NULL);
+ if (valid != NULL) {
+ valid->bounce_to = strdup(bounce_to);
+ valid->envelope_from = strdup(bounce_to);
+ CtdlSubmitMsg(msg, valid, NULL, 0);
+ }
CtdlFreeMessage(msg);
free_recipients(valid);
}
size_t recps_len = SIZ;
struct recptypes *valid;
namelist *nptr;
+ char bounce_to[256];
/* Don't do this if there were no recipients! */
if (sc->listrecps == NULL) return;
strcat(recps, nptr->name);
}
+ /* Where do we want bounces and other noise to be heard? Surely not the list members! */
+ snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", config.c_fqdn);
+
/* Now submit the message */
valid = validate_recipients(recps, NULL, 0);
free(recps);
- CtdlSubmitMsg(msg, valid, NULL);
- free_recipients(valid);
+ if (valid != NULL) {
+ valid->bounce_to = strdup(bounce_to);
+ valid->envelope_from = strdup(bounce_to);
+ CtdlSubmitMsg(msg, valid, NULL, 0);
+ free_recipients(valid);
+ }
/* Do not call CtdlFreeMessage(msg) here; the caller will free it. */
}
maplist *mptr;
struct ser_ret sermsg;
FILE *fp;
- char filename[SIZ];
+ char filename[PATH_MAX];
char buf[SIZ];
int bang = 0;
int send = 1;
msg = CtdlFetchMessage(msgnum, 1);
if (msg != NULL) {
+ /* Set the 'List-ID' header */
+ if (msg->cm_fields['L'] != NULL) {
+ free(msg->cm_fields['L']);
+ }
+ msg->cm_fields['L'] = malloc(1024);
+ snprintf(msg->cm_fields['L'], 1024,
+ "%s <%ld.list-id.%s>",
+ CC->room.QRname,
+ CC->room.QRnumber,
+ config.c_fqdn
+ );
+
/* Prepend "[List name]" to the subject */
if (msg->cm_fields['U'] == NULL) {
msg->cm_fields['U'] = strdup("(no subject)");
fprintf(sc->digestfp, "Subject: %s\n", msg->cm_fields['U']);
}
- CC->redirect_buffer = malloc(SIZ);
- CC->redirect_len = 0;
- CC->redirect_alloc = SIZ;
-
+ CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
+
safestrncpy(CC->preferred_formats, "text/plain", sizeof CC->preferred_formats);
- CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0);
+ CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0, 0);
- striplt(CC->redirect_buffer);
- fprintf(sc->digestfp, "\n%s\n", CC->redirect_buffer);
+ StrBufTrim(CC->redirect_buffer);
+ fwrite(HKEY("\n"), 1, sc->digestfp);
+ fwrite(SKEY(CC->redirect_buffer), 1, sc->digestfp);
+ fwrite(HKEY("\n"), 1, sc->digestfp);
- free(CC->redirect_buffer);
- CC->redirect_buffer = NULL;
- CC->redirect_len = 0;
- CC->redirect_alloc = 0;
+ FreeStrBuf(&CC->redirect_buffer);
sc->num_msgs_spooled += 1;
free(msg);
msg->cm_fields['R'] = strdup(nptr->name);
valid = validate_recipients(nptr->name, NULL, 0);
- CtdlSubmitMsg(msg, valid, "");
+ CtdlSubmitMsg(msg, valid, "", 0);
free_recipients(valid);
}
buf[strlen(buf)-1] = 0;
extract_token(instr, buf, 0, '|', sizeof instr);
- if (!strcasecmp(instr, "lastsent")) {
+ if (!strcasecmp(instr, strof(lastsent))) {
sc->lastsent = extract_long(buf, 1);
}
- else if (!strcasecmp(instr, "listrecp")) {
+ else if (!strcasecmp(instr, strof(listrecp))) {
nptr = (namelist *)
malloc(sizeof(namelist));
nptr->next = sc->listrecps;
extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
sc->listrecps = nptr;
}
- else if (!strcasecmp(instr, "participate")) {
+ else if (!strcasecmp(instr, strof(participate))) {
nptr = (namelist *)
malloc(sizeof(namelist));
nptr->next = sc->participates;
extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
sc->participates = nptr;
}
- else if (!strcasecmp(instr, "digestrecp")) {
+ else if (!strcasecmp(instr, strof(digestrecp))) {
nptr = (namelist *)
malloc(sizeof(namelist));
nptr->next = sc->digestrecps;
extract_token(nptr->name, buf, 1, '|', sizeof nptr->name);
sc->digestrecps = nptr;
}
- else if (!strcasecmp(instr, "ignet_push_share")) {
+ else if (!strcasecmp(instr, strof(ignet_push_share))) {
extract_token(nodename, buf, 1, '|', sizeof nodename);
extract_token(roomname, buf, 2, '|', sizeof roomname);
mptr = (maplist *) malloc(sizeof(maplist));
* timestamps.
*/
skipthisline = 0;
- if (!strncasecmp(buf, "subpending|", 11)) {
+ if (!strncasecmp(buf, strof(subpending)"|", 11)) {
if (time(NULL) - extract_long(buf, 4) > EXP) {
skipthisline = 1;
}
}
- if (!strncasecmp(buf, "unsubpending|", 13)) {
+ if (!strncasecmp(buf, strof(unsubpending)"|", 13)) {
if (time(NULL) - extract_long(buf, 3) > EXP) {
skipthisline = 1;
}
*/
void network_spoolout_room(char *room_to_spool) {
char buf[SIZ];
- char filename[SIZ];
+ char filename[PATH_MAX];
SpoolControl *sc;
int i;
* Normally this should never happen, but once in a while maybe a room gets
* queued for networking and then deleted before it can happen.
*/
- if (getroom(&CC->room, room_to_spool) != 0) {
+ if (CtdlGetRoom(&CC->room, room_to_spool) != 0) {
CtdlLogPrintf(CTDL_CRIT, "ERROR: cannot load <%s>\n", room_to_spool);
return;
}
assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
-
- CtdlLogPrintf(CTDL_INFO, "Networking started for <%s>\n", CC->room.QRname);
begin_critical_section(S_NETCONFIGS);
/* Only do net processing for rooms that have netconfigs */
-
if (!read_spoolcontrol_file(&sc, filename))
{
end_critical_section(S_NETCONFIGS);
return;
}
+ CtdlLogPrintf(CTDL_INFO, "Networking started for <%s>\n", CC->room.QRname);
/* If there are digest recipients, we have to build a digest */
if (sc->digestrecps != NULL) {
char sc_type[256];
char sc_node[256];
char sc_room[256];
- char filename[256];
+ char filename[PATH_MAX];
FILE *fp;
/* Grab the configuration line we're looking for */
if ( (valid == NULL) && IsEmptyStr(force_room) ) {
strcpy(force_room, config.c_aideroom);
}
- CtdlSubmitMsg(msg, valid, force_room);
+ CtdlSubmitMsg(msg, valid, force_room, 0);
/* Clean up */
if (valid != NULL) free_recipients(valid);
char target_room[ROOMNAMELEN];
struct ser_ret sermsg;
char *oldpath = NULL;
- char filename[SIZ];
+ char filename[PATH_MAX];
FILE *fp;
char nexthop[SIZ];
unsigned char firstbyte;
unsigned char lastbyte;
+ CtdlLogPrintf(CTDL_DEBUG, "network_process_buffer() processing %ld bytes\n", size);
+
/* Validate just a little bit. First byte should be FF and * last byte should be 00. */
firstbyte = buffer[0];
lastbyte = buffer[size-1];
/* Learn network topology from the path */
if ((msg->cm_fields['N'] != NULL) && (msg->cm_fields['P'] != NULL)) {
- network_learn_topology(msg->cm_fields['N'],
- msg->cm_fields['P']);
+ network_learn_topology(msg->cm_fields['N'], msg->cm_fields['P']);
}
/* Is the sending node giving us a very persuasive suggestion about
* which room this message should be saved in? If so, go with that.
*/
if (msg->cm_fields['C'] != NULL) {
- safestrncpy(target_room,
- msg->cm_fields['C'],
- sizeof target_room);
+ safestrncpy(target_room, msg->cm_fields['C'], sizeof target_room);
}
/* Otherwise, does it have a recipient? If so, validate it... */
"Please check the address and try sending the message again.\n");
msg = NULL;
free_recipients(recp);
+ CtdlLogPrintf(CTDL_DEBUG, "Bouncing message due to invalid recipient address.\n");
return;
}
strcpy(target_room, ""); /* no target room if mail */
* it has the O field (Originating room) set.
*/
else if (msg->cm_fields['O'] != NULL) {
- safestrncpy(target_room,
- msg->cm_fields['O'],
- sizeof target_room);
+ safestrncpy(target_room, msg->cm_fields['O'], sizeof target_room);
}
/* Strip out fields that are only relevant during transit */
/* save the message into a room */
if (PerformNetprocHooks(msg, target_room) == 0) {
msg->cm_flags = CM_SKIP_HOOKS;
- CtdlSubmitMsg(msg, recp, target_room);
+ CtdlSubmitMsg(msg, recp, target_room, 0);
}
CtdlFreeMessage(msg);
free_recipients(recp);
buffer = malloc(size);
if (buffer != NULL) {
fseek(fp, msgstart, SEEK_SET);
- fread(buffer, size, 1, fp);
- network_process_buffer(buffer, size);
+ if (fread(buffer, size, 1, fp) > 0) {
+ network_process_buffer(buffer, size);
+ }
free(buffer);
}
DIR *dp;
struct dirent *d;
struct stat statbuf;
- char filename[256];
+ char filename[PATH_MAX];
static time_t last_spoolin_mtime = 0L;
/*
void network_purge_spoolout(void) {
DIR *dp;
struct dirent *d;
- char filename[256];
+ char filename[PATH_MAX];
char nexthop[256];
int i;
/*
* receive network spool from the remote system
*/
-void receive_spool(int sock, char *remote_nodename) {
+void receive_spool(int *sock, char *remote_nodename) {
+ size_t siz;
long download_len = 0L;
long bytes_received = 0L;
long bytes_copied = 0L;
if (plen > sizeof buf) {
plen = sizeof buf;
}
- fread(buf, plen, 1, fp);
+ siz = fread(buf, plen, 1, fp);
fwrite(buf, plen, 1, newfp);
bytes_copied += plen;
}
/*
* transmit network spool to the remote system
*/
-void transmit_spool(int sock, char *remote_nodename)
+void transmit_spool(int *sock, char *remote_nodename)
{
char buf[SIZ];
char pbuf[4096];
void network_poll_node(char *node, char *secret, char *host, char *port) {
int sock;
char buf[SIZ];
+ char err_buf[SIZ];
+ char connected_to[SIZ];
+ CitContext *CCC=CC;
if (network_talking_to(node, NTT_CHECK)) return;
network_talking_to(node, NTT_ADD);
}
CtdlLogPrintf(CTDL_DEBUG, "Connected!\n");
-
+ CCC->sReadBuf = NewStrBuf();
+ CCC->sMigrateBuf = NewStrBuf();
+ CCC->sPos = NULL;
/* Read the server greeting */
- if (sock_getln(sock, buf, sizeof buf) < 0) goto bail;
- CtdlLogPrintf(CTDL_DEBUG, ">%s\n", buf);
-
- /* Identify ourselves */
- snprintf(buf, sizeof buf, "NETP %s|%s", config.c_nodename, secret);
- CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
- if (sock_puts(sock, buf) <0) goto bail;
- if (sock_getln(sock, buf, sizeof buf) < 0) goto bail;
+ if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
CtdlLogPrintf(CTDL_DEBUG, ">%s\n", buf);
- if (buf[0] != '2') goto bail;
- /* At this point we are authenticated. */
- if (!CtdlThreadCheckStop())
- receive_spool(sock, node);
- if (!CtdlThreadCheckStop())
- transmit_spool(sock, node);
-
- sock_puts(sock, "QUIT");
-bail: sock_close(sock);
+ /* Check that the remote is who we think it is and warn the Aide if not */
+ extract_token (connected_to, buf, 1, ' ', sizeof connected_to);
+ if (strcmp(connected_to, node))
+ {
+ snprintf (err_buf, sizeof(err_buf), "Connected to node \"%s\" but I was expecting to connect to node \"%s\".", connected_to, node);
+ CtdlLogPrintf(CTDL_ERR, "%s\n", err_buf);
+ CtdlAideMessage(err_buf, "Network error");
+ }
+ else {
+ /* We're talking to the correct node. Now identify ourselves. */
+ snprintf(buf, sizeof buf, "NETP %s|%s", config.c_nodename, secret);
+ CtdlLogPrintf(CTDL_DEBUG, "<%s\n", buf);
+ if (sock_puts(&sock, buf) <0) goto bail;
+ if (sock_getln(&sock, buf, sizeof buf) < 0) goto bail;
+ CtdlLogPrintf(CTDL_DEBUG, ">%s\n", buf);
+ if (buf[0] != '2') goto bail;
+
+ /* At this point we are authenticated. */
+ if (!CtdlThreadCheckStop())
+ receive_spool(&sock, node);
+ if (!CtdlThreadCheckStop())
+ transmit_spool(&sock, node);
+ }
+
+ sock_puts(&sock, "QUIT");
+bail:
+ FreeStrBuf(&CCC->sReadBuf);
+ FreeStrBuf(&CCC->sMigrateBuf);
+ if (sock != -1)
+ sock_close(sock);
network_talking_to(node, NTT_REMOVE);
}
* It's ok if these directories already exist. Just fail silently.
*/
void create_spool_dirs(void) {
- mkdir(ctdl_spool_dir, 0700);
- chown(ctdl_spool_dir, CTDLUID, (-1));
- mkdir(ctdl_netin_dir, 0700);
- chown(ctdl_netin_dir, CTDLUID, (-1));
- mkdir(ctdl_netout_dir, 0700);
- chown(ctdl_netout_dir, CTDLUID, (-1));
+ if ((mkdir(ctdl_spool_dir, 0700) != 0) && (errno != EEXIST))
+ CtdlLogPrintf(CTDL_EMERG, "unable to create directory [%s]: %s", ctdl_spool_dir, strerror(errno));
+ if (chown(ctdl_spool_dir, CTDLUID, (-1)) != 0)
+ CtdlLogPrintf(CTDL_EMERG, "unable to set the access rights for [%s]: %s", ctdl_spool_dir, strerror(errno));
+ if ((mkdir(ctdl_netin_dir, 0700) != 0) && (errno != EEXIST))
+ CtdlLogPrintf(CTDL_EMERG, "unable to create directory [%s]: %s", ctdl_netin_dir, strerror(errno));
+ if (chown(ctdl_netin_dir, CTDLUID, (-1)) != 0)
+ CtdlLogPrintf(CTDL_EMERG, "unable to set the access rights for [%s]: %s", ctdl_netin_dir, strerror(errno));
+ if ((mkdir(ctdl_netout_dir, 0700) != 0) && (errno != EEXIST))
+ CtdlLogPrintf(CTDL_EMERG, "unable to create directory [%s]: %s", ctdl_netout_dir, strerror(errno));
+ if (chown(ctdl_netout_dir, CTDLUID, (-1)) != 0)
+ CtdlLogPrintf(CTDL_EMERG, "unable to set the access rights for [%s]: %s", ctdl_netout_dir, strerror(errno));
}
struct CitContext networkerCC;
/* Give the networker its own private CitContext */
- memset(&networkerCC, 0, sizeof(struct CitContext));
- networkerCC.internal_pgm = 1;
- networkerCC.cs_pid = 0;
- pthread_setspecific(MyConKey, (void *)&networkerCC );
+ CtdlFillSystemContext(&networkerCC, "network");
+ citthread_setspecific(MyConKey, (void *)&networkerCC );
/*
* Run the full set of processing tasks no more frequently
*/
if (full_processing && !CtdlThreadCheckStop()) {
CtdlLogPrintf(CTDL_DEBUG, "network: loading outbound queue\n");
- ForEachRoom(network_queue_room, NULL);
+ CtdlForEachRoom(network_queue_room, NULL);
}
if (rplist != NULL) {
char secret[256];
char nexthop[256];
+ char err_buf[SIZ];
/* Authenticate */
extract_token(node, cmdbuf, 0, '|', sizeof node);
extract_token(pass, cmdbuf, 1, '|', sizeof pass);
if (doing_queue) {
- CtdlLogPrintf(CTDL_WARNING, "Network node <%s> refused - spooling", node);
- cprintf("%d spooling - try again in a few minutes\n",
- ERROR + RESOURCE_BUSY);
+ CtdlLogPrintf(CTDL_WARNING, "Network node <%s> refused - spooling\n", node);
+ cprintf("%d spooling - try again in a few minutes\n", ERROR + RESOURCE_BUSY);
return;
}
v = is_valid_node(nexthop, secret, node);
if (v != 0) {
- CtdlLogPrintf(CTDL_WARNING, "Unknown node <%s>\n", node);
- cprintf("%d authentication failed\n",
- ERROR + PASSWORD_REQUIRED);
+ snprintf(err_buf, sizeof err_buf,
+ "An unknown Citadel server called \"%s\" attempted to connect from %s [%s].\n",
+ node, CC->cs_host, CC->cs_addr
+ );
+ CtdlLogPrintf(CTDL_WARNING, err_buf);
+ cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
+ CtdlAideMessage(err_buf, "IGNet Networking.");
return;
}
if (strcasecmp(pass, secret)) {
- CtdlLogPrintf(CTDL_WARNING, "Bad password for network node <%s>", node);
+ snprintf(err_buf, sizeof err_buf,
+ "A Citadel server at %s [%s] failed to authenticate as network node \"%s\".\n",
+ CC->cs_host, CC->cs_addr, node
+ );
+ CtdlLogPrintf(CTDL_WARNING, err_buf);
cprintf("%d authentication failed\n", ERROR + PASSWORD_REQUIRED);
+ CtdlAideMessage(err_buf, "IGNet Networking.");
return;
}
safestrncpy(CC->net_node, node, sizeof CC->net_node);
network_talking_to(node, NTT_ADD);
- CtdlLogPrintf(CTDL_NOTICE, "Network node <%s> logged in\n", CC->net_node);
- cprintf("%d authenticated as network node '%s'\n", CIT_OK,
- CC->net_node);
+ CtdlLogPrintf(CTDL_NOTICE, "Network node <%s> logged in from %s [%s]\n",
+ CC->net_node, CC->cs_host, CC->cs_addr
+ );
+ cprintf("%d authenticated as network node '%s'\n", CIT_OK, CC->net_node);
}
+
int network_room_handler (struct ctdlroom *room)
{
network_queue_room(room, NULL);
return 0;
}
+
/*
* Module entry point
*/