* Reference count adjustments are now deferred by queuing
[citadel.git] / citadel / serv_network.c
index 055ef0f36f47172f589e426be9e5d33b84f0b8f3..2b4ca3e12896fd9f48d243b26b1a112583d46b56 100644 (file)
@@ -4,7 +4,7 @@
  * This module handles shared rooms, inter-Citadel mail, and outbound
  * mailing list processing.
  *
- * Copyright (C) 2000-2002 by Art Cancro and others.
+ * Copyright (C) 2000-2005 by Art Cancro and others.
  * This code is released under the terms of the GNU General Public License.
  *
  * ** NOTE **   A word on the S_NETCONFIGS semaphore:
 #include "serv_network.h"
 #include "clientsocket.h"
 #include "file_ops.h"
+#include "citadel_dirs.h"
 
 #ifndef HAVE_SNPRINTF
 #include "snprintf.h"
 #endif
 
-/* Nonzero while we are doing outbound network processing */
+/* Nonzero while we are doing network processing */
 static int doing_queue = 0;
 
 /*
  * When we do network processing, it's accomplished in two passes; one to
  * gather a list of rooms and one to actually do them.  It's ok that rplist
- * is global; this process *only* runs as part of the housekeeping loop and
- * therefore only one will run at a time.
+ * is global; we have a mutex that keeps it safe.
  */
 struct RoomProcList *rplist = NULL;
 
@@ -363,7 +363,7 @@ void cmd_gnet(char *argbuf) {
        FILE *fp;
 
        if (CtdlAccessCheck(ac_room_aide)) return;
-       assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs");
+       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
        cprintf("%d Network settings for room #%ld <%s>\n",
                LISTING_FOLLOWS,
                CC->room.QRnumber, CC->room.QRname);
@@ -390,8 +390,8 @@ void cmd_snet(char *argbuf) {
        unbuffer_output();
 
        if (CtdlAccessCheck(ac_room_aide)) return;
-       safestrncpy(tempfilename, tmpnam(NULL), sizeof tempfilename);
-       assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs");
+       CtdlMakeTempFileName(tempfilename, sizeof tempfilename);
+       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
 
        fp = fopen(tempfilename, "w");
        if (fp == NULL) {
@@ -419,18 +419,185 @@ void cmd_snet(char *argbuf) {
 }
 
 
+/*
+ * Deliver digest messages
+ */
+void network_deliver_digest(struct SpoolControl *sc) {
+       char buf[SIZ];
+       int i;
+       struct CtdlMessage *msg;
+       long msglen;
+       long msgnum;
+       char *instr = NULL;
+       size_t instr_len = SIZ;
+       struct CtdlMessage *imsg;
+       struct namelist *nptr;
+
+       if (sc->num_msgs_spooled < 1) {
+               fclose(sc->digestfp);
+               sc->digestfp = NULL;
+               return;
+       }
+
+       msg = malloc(sizeof(struct CtdlMessage));
+       memset(msg, 0, sizeof(struct CtdlMessage));
+       msg->cm_magic = CTDLMESSAGE_MAGIC;
+       msg->cm_format_type = FMT_RFC822;
+       msg->cm_anon_type = MES_NORMAL;
+
+       sprintf(buf, "%ld", time(NULL));
+       msg->cm_fields['T'] = strdup(buf);
+       msg->cm_fields['A'] = strdup(CC->room.QRname);
+       snprintf(buf, sizeof buf, "[%s]", CC->room.QRname);
+       msg->cm_fields['U'] = strdup(buf);
+       sprintf(buf, "room_%s@%s", CC->room.QRname, config.c_fqdn);
+       for (i=0; i<strlen(buf); ++i) {
+               if (isspace(buf[i])) buf[i]='_';
+               buf[i] = tolower(buf[i]);
+       }
+       msg->cm_fields['F'] = strdup(buf);
+       msg->cm_fields['R'] = strdup(buf);
+
+       /*
+        * Go fetch the contents of the digest
+        */
+       fseek(sc->digestfp, 0L, SEEK_END);
+       msglen = ftell(sc->digestfp);
+
+       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;
+
+       fclose(sc->digestfp);
+       sc->digestfp = NULL;
+
+       msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
+       CtdlFreeMessage(msg);
+
+       /* Now generate the delivery instructions */
+
+       /* 
+        * Figure out how big a buffer we need to allocate
+        */
+       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
+               instr_len = instr_len + strlen(nptr->name) + 2;
+       }
+       
+       /*
+        * allocate...
+        */
+       lprintf(CTDL_DEBUG, "Generating delivery instructions\n");
+       instr = malloc(instr_len);
+       if (instr == NULL) {
+               lprintf(CTDL_EMERG, "Cannot allocate %ld bytes for instr...\n",
+                       (long)instr_len);
+               abort();
+       }
+       snprintf(instr, instr_len,
+               "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
+               "bounceto|postmaster@%s\n" ,
+               SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn );
+
+       /* Generate delivery instructions for each recipient */
+       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
+               size_t tmp = strlen(instr);
+               snprintf(&instr[tmp], instr_len - tmp,
+                        "remote|%s|0||\n", nptr->name);
+       }
+
+       /*
+        * Generate a message from the instructions
+        */
+       imsg = malloc(sizeof(struct CtdlMessage));
+       memset(imsg, 0, sizeof(struct CtdlMessage));
+       imsg->cm_magic = CTDLMESSAGE_MAGIC;
+       imsg->cm_anon_type = MES_NORMAL;
+       imsg->cm_format_type = FMT_RFC822;
+       imsg->cm_fields['A'] = strdup("Citadel");
+       imsg->cm_fields['M'] = instr;
+
+       /* Save delivery instructions in spoolout room */
+       CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
+       CtdlFreeMessage(imsg);
+}
+
+
+/*
+ * Deliver list messages to everyone on the list ... efficiently
+ */
+void network_deliver_list(struct CtdlMessage *msg, struct SpoolControl *sc) {
+       long msgnum;
+       char *instr = NULL;
+       size_t instr_len = SIZ;
+       struct CtdlMessage *imsg;
+       struct namelist *nptr;
+
+       /* Don't do this if there were no recipients! */
+       if (sc->listrecps == NULL) return;
+
+       /* Save the message to disk... */
+       msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
+
+       /* Now generate the delivery instructions */
+
+       /* 
+        * Figure out how big a buffer we need to allocate
+        */
+       for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
+               instr_len = instr_len + strlen(nptr->name) + 2;
+       }
+       
+       /*
+        * allocate...
+        */
+       lprintf(CTDL_DEBUG, "Generating delivery instructions\n");
+       instr = malloc(instr_len);
+       if (instr == NULL) {
+               lprintf(CTDL_EMERG, "Cannot allocate %ld bytes for instr...\n",
+                       (long)instr_len);
+               abort();
+       }
+       snprintf(instr, instr_len,
+               "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
+               "bounceto|postmaster@%s\n" ,
+               SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn );
+
+       /* Generate delivery instructions for each recipient */
+       for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
+               size_t tmp = strlen(instr);
+               snprintf(&instr[tmp], instr_len - tmp,
+                        "remote|%s|0||\n", nptr->name);
+       }
+
+       /*
+        * Generate a message from the instructions
+        */
+       imsg = malloc(sizeof(struct CtdlMessage));
+       memset(imsg, 0, sizeof(struct CtdlMessage));
+       imsg->cm_magic = CTDLMESSAGE_MAGIC;
+       imsg->cm_anon_type = MES_NORMAL;
+       imsg->cm_format_type = FMT_RFC822;
+       imsg->cm_fields['A'] = strdup("Citadel");
+       imsg->cm_fields['M'] = instr;
+
+       /* Save delivery instructions in spoolout room */
+       CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
+       CtdlFreeMessage(imsg);
+}
+
+
+
+
 /*
  * Spools out one message from the list.
  */
 void network_spool_msg(long msgnum, void *userdata) {
        struct SpoolControl *sc;
-       int err;
        int i;
        char *newpath = NULL;
-       char *instr = NULL;
        size_t instr_len = SIZ;
        struct CtdlMessage *msg = NULL;
-       struct CtdlMessage *imsg;
        struct namelist *nptr;
        struct maplist *mptr;
        struct ser_ret sermsg;
@@ -440,9 +607,8 @@ void network_spool_msg(long msgnum, void *userdata) {
        int bang = 0;
        int send = 1;
        int delete_after_send = 0;      /* Set to 1 to delete after spooling */
-       long list_msgnum = 0L;
        int ok_to_participate = 0;
-       char *end_of_headers = NULL;
+       struct recptypes *valid;
 
        sc = (struct SpoolControl *)userdata;
 
@@ -451,55 +617,41 @@ void network_spool_msg(long msgnum, void *userdata) {
         */
        instr_len = SIZ;
        if (sc->listrecps != NULL) {
-       
-               /* First, copy it to the spoolout room */
-               err = CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, msgnum, 0);
-               if (err != 0) return;
-
-               /* 
-                * Figure out how big a buffer we need to allocate
-                */
-               for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
-                       instr_len = instr_len + strlen(nptr->name);
-               }
-       
-               /*
-                * allocate...
-                */
-               lprintf(CTDL_DEBUG, "Generating delivery instructions\n");
-               instr = malloc(instr_len);
-               if (instr == NULL) {
-                       lprintf(CTDL_EMERG,
-                               "Cannot allocate %ld bytes for instr...\n",
-                               (long)instr_len);
-                       abort();
-               }
-               snprintf(instr, instr_len,
-                       "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-                       "bounceto|postmaster@%s\n" ,
-                       SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn );
-       
-               /* Generate delivery instructions for each recipient */
-               for (nptr = sc->listrecps; nptr != NULL; nptr = nptr->next) {
-                       size_t tmp = strlen(instr);
-                       snprintf(&instr[tmp], instr_len - tmp,
-                                "remote|%s|0||\n", nptr->name);
+               /* Fetch the message.  We're going to need to modify it
+                * in order to insert the [list name] in it, etc.
+                */
+               msg = CtdlFetchMessage(msgnum, 1);
+               if (msg != NULL) {
+
+                       /* Prepend "[List name]" to the subject */
+                       if (msg->cm_fields['U'] == NULL) {
+                               msg->cm_fields['U'] = strdup("(no subject)");
+                       }
+                       snprintf(buf, sizeof buf, "[%s] %s", CC->room.QRname, msg->cm_fields['U']);
+                       free(msg->cm_fields['U']);
+                       msg->cm_fields['U'] = strdup(buf);
+
+                       /* Set the recipient of the list message to the
+                        * email address of the room itself.
+                        * FIXME ... I want to be able to pick any address
+                        */
+                       if (msg->cm_fields['R'] != NULL) {
+                               free(msg->cm_fields['R']);
+                       }
+                       msg->cm_fields['R'] = malloc(256);
+                       snprintf(msg->cm_fields['R'], 256,
+                               "room_%s@%s", CC->room.QRname,
+                               config.c_fqdn);
+                       for (i=0; i<strlen(msg->cm_fields['R']); ++i) {
+                               if (isspace(msg->cm_fields['R'][i])) {
+                                       msg->cm_fields['R'][i] = '_';
+                               }
+                       }
+
+                       /* Handle delivery */
+                       network_deliver_list(msg, sc);
+                       CtdlFreeMessage(msg);
                }
-       
-               /*
-                * Generate a message from the instructions
-                */
-                       imsg = malloc(sizeof(struct CtdlMessage));
-               memset(imsg, 0, sizeof(struct CtdlMessage));
-               imsg->cm_magic = CTDLMESSAGE_MAGIC;
-               imsg->cm_anon_type = MES_NORMAL;
-               imsg->cm_format_type = FMT_RFC822;
-               imsg->cm_fields['A'] = strdup("Citadel");
-               imsg->cm_fields['M'] = instr;
-       
-               /* Save delivery instructions in spoolout room */
-               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
-               CtdlFreeMessage(imsg);
        }
 
        /*
@@ -531,17 +683,10 @@ void network_spool_msg(long msgnum, void *userdata) {
                        CC->redirect_alloc = SIZ;
 
                        safestrncpy(CC->preferred_formats, "text/plain", sizeof CC->preferred_formats);
-                       CtdlOutputPreLoadedMsg(msg, 0L, MT_MIME, HEADERS_NONE, 0, 0);
+                       CtdlOutputPreLoadedMsg(msg, MT_CITADEL, HEADERS_NONE, 0, 0);
 
-                       end_of_headers = bmstrstr(CC->redirect_buffer, "\n\r\n", strncmp);
-                       if (end_of_headers == NULL) {
-                               end_of_headers = bmstrstr(CC->redirect_buffer, "\n\n", strncmp);
-                       }
-                       if (end_of_headers == NULL) {
-                               end_of_headers = CC->redirect_buffer;
-                       }
-                       striplt(end_of_headers);
-                       fprintf(sc->digestfp, "\n%s\n", end_of_headers);
+                       striplt(CC->redirect_buffer);
+                       fprintf(sc->digestfp, "\n%s\n", CC->redirect_buffer);
 
                        free(CC->redirect_buffer);
                        CC->redirect_buffer = NULL;
@@ -593,52 +738,21 @@ void network_spool_msg(long msgnum, void *userdata) {
                                        }
                                }
 
-                               /* Now save it and generate delivery instructions */
-                               list_msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
-
                                /* 
                                 * Figure out how big a buffer we need to allocate
                                 */
                                for (nptr = sc->participates; nptr != NULL; nptr = nptr->next) {
-                                       instr_len = instr_len + strlen(nptr->name);
-                               }
-                       
-                               /*
-                                * allocate...
-                                */
-                               instr = malloc(instr_len);
-                               if (instr == NULL) {
-                                       lprintf(CTDL_EMERG,
-                                               "Cannot allocate %ld bytes for instr...\n",
-                                               (long)instr_len);
-                                       abort();
-                               }
-                               snprintf(instr, instr_len,
-                                       "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-                                       "bounceto|postmaster@%s\n" ,
-                                       SPOOLMIME, list_msgnum, (long)time(NULL), config.c_fqdn );
-                       
-                               /* Generate delivery instructions for each recipient */
-                               for (nptr = sc->participates; nptr != NULL; nptr = nptr->next) {
-                                       size_t tmp = strlen(instr);
-                                       snprintf(&instr[tmp], instr_len - tmp,
-                                                "remote|%s|0||\n", nptr->name);
+
+                                       if (msg->cm_fields['R'] == NULL) {
+                                               free(msg->cm_fields['R']);
+                                       }
+                                       msg->cm_fields['R'] = strdup(nptr->name);
+       
+                                       valid = validate_recipients(nptr->name);
+                                       CtdlSubmitMsg(msg, valid, "");
+                                       free(valid);
                                }
                        
-                               /*
-                                * Generate a message from the instructions
-                                */
-                               imsg = malloc(sizeof(struct CtdlMessage));
-                               memset(imsg, 0, sizeof(struct CtdlMessage));
-                               imsg->cm_magic = CTDLMESSAGE_MAGIC;
-                               imsg->cm_anon_type = MES_NORMAL;
-                               imsg->cm_format_type = FMT_RFC822;
-                               imsg->cm_fields['A'] = strdup("Citadel");
-                               imsg->cm_fields['M'] = instr;
-                       
-                               /* Save delivery instructions in spoolout room */
-                               CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
-                               CtdlFreeMessage(imsg);
                        }
                        CtdlFreeMessage(msg);
                }
@@ -647,90 +761,84 @@ void network_spool_msg(long msgnum, void *userdata) {
        /*
         * Process IGnet push shares
         */
-       if (sc->ignet_push_shares != NULL) {
-       
-               msg = CtdlFetchMessage(msgnum, 1);
-               if (msg != NULL) {
-                       size_t newpath_len;
+       msg = CtdlFetchMessage(msgnum, 1);
+       if (msg != NULL) {
+               size_t newpath_len;
 
-                       /* Prepend our node name to the Path field whenever
-                        * sending a message to another IGnet node
-                        */
-                       if (msg->cm_fields['P'] == NULL) {
-                               msg->cm_fields['P'] = strdup("username");
-                       }
-                       newpath_len = strlen(msg->cm_fields['P']) +
-                                strlen(config.c_nodename) + 2;
-                       newpath = malloc(newpath_len);
-                       snprintf(newpath, newpath_len, "%s!%s",
-                                config.c_nodename, msg->cm_fields['P']);
-                       free(msg->cm_fields['P']);
-                       msg->cm_fields['P'] = newpath;
-
-                       /*
-                        * Determine if this message is set to be deleted
-                        * after sending out on the network
-                        */
-                       if (msg->cm_fields['S'] != NULL) {
-                               if (!strcasecmp(msg->cm_fields['S'],
-                                  "CANCEL")) {
-                                       delete_after_send = 1;
-                               }
+               /* Prepend our node name to the Path field whenever
+                * sending a message to another IGnet node
+                */
+               if (msg->cm_fields['P'] == NULL) {
+                       msg->cm_fields['P'] = strdup("username");
+               }
+               newpath_len = strlen(msg->cm_fields['P']) +
+                        strlen(config.c_nodename) + 2;
+               newpath = malloc(newpath_len);
+               snprintf(newpath, newpath_len, "%s!%s",
+                        config.c_nodename, msg->cm_fields['P']);
+               free(msg->cm_fields['P']);
+               msg->cm_fields['P'] = newpath;
+
+               /*
+                * Determine if this message is set to be deleted
+                * after sending out on the network
+                */
+               if (msg->cm_fields['S'] != NULL) {
+                       if (!strcasecmp(msg->cm_fields['S'], "CANCEL")) {
+                               delete_after_send = 1;
                        }
+               }
 
-                       /* Now send it to every node */
-                       for (mptr = sc->ignet_push_shares; mptr != NULL;
-                           mptr = mptr->next) {
+               /* Now send it to every node */
+               if (sc->ignet_push_shares != NULL)
+                 for (mptr = sc->ignet_push_shares; mptr != NULL;
+                   mptr = mptr->next) {
 
-                               send = 1;
+                       send = 1;
 
-                               /* Check for valid node name */
-                               if (is_valid_node(NULL, NULL, mptr->remote_nodename) != 0) {
-                                       lprintf(CTDL_ERR, "Invalid node <%s>\n",
-                                               mptr->remote_nodename);
-                                       send = 0;
-                               }
+                       /* Check for valid node name */
+                       if (is_valid_node(NULL, NULL, mptr->remote_nodename) != 0) {
+                               lprintf(CTDL_ERR, "Invalid node <%s>\n",
+                                       mptr->remote_nodename);
+                               send = 0;
+                       }
 
-                               /* Check for split horizon */
-                               lprintf(CTDL_DEBUG, "Path is %s\n", msg->cm_fields['P']);
-                               bang = num_tokens(msg->cm_fields['P'], '!');
-                               if (bang > 1) for (i=0; i<(bang-1); ++i) {
-                                       extract_token(buf, msg->cm_fields['P'],
-                                               i, '!', sizeof buf);
-                                       if (!strcasecmp(buf, mptr->remote_nodename)) {
-                                               send = 0;
-                                       }
+                       /* Check for split horizon */
+                       lprintf(CTDL_DEBUG, "Path is %s\n", msg->cm_fields['P']);
+                       bang = num_tokens(msg->cm_fields['P'], '!');
+                       if (bang > 1) for (i=0; i<(bang-1); ++i) {
+                               extract_token(buf, msg->cm_fields['P'],
+                                       i, '!', sizeof buf);
+                               if (!strcasecmp(buf, mptr->remote_nodename)) {
+                                       send = 0;
                                }
+                       }
 
-                               /* Send the message */
-                               if (send == 1) {
+                       /* Send the message */
+                       if (send == 1) {
 
-                                       /*
-                                        * Force the message to appear in the correct room
-                                        * on the far end by setting the C field correctly
-                                        */
-                                       if (msg->cm_fields['C'] != NULL) {
-                                               free(msg->cm_fields['C']);
-                                       }
-                                       if (strlen(mptr->remote_roomname) > 0) {
-                                               msg->cm_fields['C'] = strdup(mptr->remote_roomname);
-                                       }
-                                       else {
-                                               msg->cm_fields['C'] = strdup(CC->room.QRname);
-                                       }
+                               /*
+                                * Force the message to appear in the correct room
+                                * on the far end by setting the C field correctly
+                                */
+                               if (msg->cm_fields['C'] != NULL) {
+                                       free(msg->cm_fields['C']);
+                               }
+                               if (strlen(mptr->remote_roomname) > 0) {
+                                       msg->cm_fields['C'] = strdup(mptr->remote_roomname);
+                               }
+                               else {
+                                       msg->cm_fields['C'] = strdup(CC->room.QRname);
+                               }
 
-                                       /* serialize it for transmission */
-                                       serialize_message(&sermsg, msg);
+                               /* serialize it for transmission */
+                               serialize_message(&sermsg, msg);
+                               if (sermsg.len > 0) {
 
                                        /* write it to the spool file */
-                                       snprintf(filename, sizeof filename,
-#ifndef HAVE_SPOOL_DIR
-                                                        "."
-#else
-                                                        SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                                        "/network/spoolout/%s",
-                                                        mptr->remote_nodename);
+                                       snprintf(filename, sizeof filename,"%s/%s",
+                                                       ctdl_netout_dir,
+                                                       mptr->remote_nodename);
                                        lprintf(CTDL_DEBUG, "Appending to %s\n", filename);
                                        fp = fopen(filename, "ab");
                                        if (fp != NULL) {
@@ -741,13 +849,14 @@ void network_spool_msg(long msgnum, void *userdata) {
                                        else {
                                                lprintf(CTDL_ERR, "%s: %s\n", filename, strerror(errno));
                                        }
-
+       
                                        /* free the serialized version */
                                        free(sermsg.ser);
                                }
+
                        }
-                       CtdlFreeMessage(msg);
                }
+               CtdlFreeMessage(msg);
        }
 
        /* update lastsent */
@@ -755,112 +864,12 @@ void network_spool_msg(long msgnum, void *userdata) {
 
        /* Delete this message if delete-after-send is set */
        if (delete_after_send) {
-               CtdlDeleteMessages(CC->room.QRname, msgnum, "");
+               CtdlDeleteMessages(CC->room.QRname, &msgnum, 1, "");
        }
 
 }
        
 
-/*
- * Deliver digest messages
- */
-void network_deliver_digest(struct SpoolControl *sc) {
-       char buf[SIZ];
-       int i;
-       struct CtdlMessage *msg;
-       long msglen;
-       long msgnum;
-       char *instr = NULL;
-       size_t instr_len = SIZ;
-       struct CtdlMessage *imsg;
-       struct namelist *nptr;
-
-       if (sc->num_msgs_spooled < 1) {
-               fclose(sc->digestfp);
-               sc->digestfp = NULL;
-               return;
-       }
-
-       msg = malloc(sizeof(struct CtdlMessage));
-       memset(msg, 0, sizeof(struct CtdlMessage));
-       msg->cm_magic = CTDLMESSAGE_MAGIC;
-       msg->cm_format_type = FMT_RFC822;
-       msg->cm_anon_type = MES_NORMAL;
-
-       sprintf(buf, "%ld", time(NULL));
-       msg->cm_fields['T'] = strdup(buf);
-       msg->cm_fields['A'] = strdup(CC->room.QRname);
-       snprintf(buf, sizeof buf, "[%s]", CC->room.QRname);
-       msg->cm_fields['U'] = strdup(buf);
-       sprintf(buf, "room_%s@%s", CC->room.QRname, config.c_fqdn);
-       for (i=0; i<strlen(buf); ++i) {
-               if (isspace(buf[i])) buf[i]='_';
-               buf[i] = tolower(buf[i]);
-       }
-       msg->cm_fields['F'] = strdup(buf);
-
-       fseek(sc->digestfp, 0L, SEEK_END);
-       msglen = ftell(sc->digestfp);
-
-       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;
-
-       fclose(sc->digestfp);
-       sc->digestfp = NULL;
-
-       msgnum = CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM);
-       CtdlFreeMessage(msg);
-
-       /* Now generate the delivery instructions */
-
-       /* 
-        * Figure out how big a buffer we need to allocate
-        */
-       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
-               instr_len = instr_len + strlen(nptr->name);
-       }
-       
-       /*
-        * allocate...
-        */
-       lprintf(CTDL_DEBUG, "Generating delivery instructions\n");
-       instr = malloc(instr_len);
-       if (instr == NULL) {
-               lprintf(CTDL_EMERG, "Cannot allocate %ld bytes for instr...\n",
-                       (long)instr_len);
-               abort();
-       }
-       snprintf(instr, instr_len,
-               "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-               "bounceto|postmaster@%s\n" ,
-               SPOOLMIME, msgnum, (long)time(NULL), config.c_fqdn );
-
-       /* Generate delivery instructions for each recipient */
-       for (nptr = sc->digestrecps; nptr != NULL; nptr = nptr->next) {
-               size_t tmp = strlen(instr);
-               snprintf(&instr[tmp], instr_len - tmp,
-                        "remote|%s|0||\n", nptr->name);
-       }
-
-       /*
-        * Generate a message from the instructions
-        */
-       imsg = malloc(sizeof(struct CtdlMessage));
-       memset(imsg, 0, sizeof(struct CtdlMessage));
-       imsg->cm_magic = CTDLMESSAGE_MAGIC;
-       imsg->cm_anon_type = MES_NORMAL;
-       imsg->cm_format_type = FMT_RFC822;
-       imsg->cm_fields['A'] = strdup("Citadel");
-       imsg->cm_fields['M'] = instr;
-
-       /* Save delivery instructions in spoolout room */
-       CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM);
-       CtdlFreeMessage(imsg);
-}
-
-
 /*
  * Batch up and send all outbound traffic from the current room
  */
@@ -880,16 +889,22 @@ void network_spoolout_room(char *room_to_spool) {
        int skipthisline = 0;
        int i;
 
+       /*
+        * If the room doesn't exist, don't try to perform its networking tasks.
+        * 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) {
                lprintf(CTDL_CRIT, "ERROR: cannot load <%s>\n", room_to_spool);
                return;
        }
 
        memset(&sc, 0, sizeof(struct SpoolControl));
-       assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs");
+       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
 
        begin_critical_section(S_NETCONFIGS);
 
+       /* Only do net processing for rooms that have netconfigs */
        fp = fopen(filename, "r");
        if (fp == NULL) {
                end_critical_section(S_NETCONFIGS);
@@ -982,7 +997,7 @@ void network_spoolout_room(char *room_to_spool) {
        }
 
        /* Do something useful */
-       CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL,
+       CtdlForEachMessage(MSGS_GT, sc.lastsent, NULL, NULL, NULL,
                network_spool_msg, &sc);
 
        /* If we wrote a digest, deliver it and then close it */
@@ -1082,7 +1097,7 @@ int network_sync_to(char *target_node) {
        FILE *fp;
 
        /* Grab the configuration line we're looking for */
-       assoc_file_name(filename, sizeof filename, &CC->room, "netconfigs");
+       assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir);
        begin_critical_section(S_NETCONFIGS);
        fp = fopen(filename, "r");
        if (fp == NULL) {
@@ -1117,7 +1132,7 @@ int network_sync_to(char *target_node) {
        if (!found_node) return(-1);
 
        /* Send ALL messages */
-       num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL,
+       num_spooled = CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL,
                network_spool_msg, &sc);
 
        /* Concise cleanup because we know there's only one node in the sc */
@@ -1161,8 +1176,10 @@ void network_queue_room(struct ctdlroom *qrbuf, void *data) {
        if (ptr == NULL) return;
 
        safestrncpy(ptr->name, qrbuf->QRname, sizeof ptr->name);
+       begin_critical_section(S_RPLIST);
        ptr->next = rplist;
        rplist = ptr;
+       end_critical_section(S_RPLIST);
 }
 
 
@@ -1282,7 +1299,7 @@ void network_bounce(struct CtdlMessage *msg, char *reason) {
 
        /* Now submit the message */
        valid = validate_recipients(recipient);
-       if (valid != NULL) if (valid->num_error > 0) {
+       if (valid != NULL) if (valid->num_error != 0) {
                free(valid);
                valid = NULL;
        }
@@ -1380,13 +1397,11 @@ void network_process_buffer(char *buffer, long size) {
                                if (strlen(nexthop) == 0) {
                                        strcpy(nexthop, msg->cm_fields['D']);
                                }
-                               snprintf(filename, sizeof filename,
-#ifndef HAVE_SPOOL_DIR
-                                                "."
-#else
-                                                SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                                "/network/spoolout/%s", nexthop);
+                               snprintf(filename, 
+                                                sizeof filename,
+                                                "%s/%s",
+                                                ctdl_netout_dir,
+                                                nexthop);
                                lprintf(CTDL_DEBUG, "Appending to %s\n", filename);
                                fp = fopen(filename, "ab");
                                if (fp != NULL) {
@@ -1442,7 +1457,7 @@ void network_process_buffer(char *buffer, long size) {
        /* Otherwise, does it have a recipient?  If so, validate it... */
        else if (msg->cm_fields['R'] != NULL) {
                recp = validate_recipients(msg->cm_fields['R']);
-               if (recp != NULL) if (recp->num_error > 0) {
+               if (recp != NULL) if (recp->num_error != 0) {
                        network_bounce(msg,
 "A message you sent could not be delivered due to an invalid address.\n"
 "Please check the address and try sending the message again.\n");
@@ -1554,26 +1569,35 @@ void network_process_file(char *filename) {
 void network_do_spoolin(void) {
        DIR *dp;
        struct dirent *d;
+       struct stat statbuf;
        char filename[256];
+       static time_t last_spoolin_mtime = 0L;
 
-       dp = opendir(
-#ifndef HAVE_SPOOL_DIR
-                                "."
-#else
-                                SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                "/network/spoolin");
+       /*
+        * Check the spoolin directory's modification time.  If it hasn't
+        * been touched, we don't need to scan it.
+        */
+       if (stat(ctdl_netin_dir, &statbuf)) return;
+       if (statbuf.st_mtime == last_spoolin_mtime) {
+               lprintf(CTDL_DEBUG, "network: nothing in inbound queue\n");
+               return;
+       }
+       last_spoolin_mtime = statbuf.st_mtime;
+       lprintf(CTDL_DEBUG, "network: processing inbound queue\n");
+
+       /*
+        * Ok, there's something interesting in there, so scan it.
+        */
+       dp = opendir(ctdl_netin_dir);
        if (dp == NULL) return;
 
        while (d = readdir(dp), d != NULL) {
                if ((strcmp(d->d_name, ".")) && (strcmp(d->d_name, ".."))) {
-                       snprintf(filename, sizeof filename,
-#ifndef HAVE_SPOOL_DIR
-                                        "."
-#else
-                                        SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                        "/network/spoolin/%s", d->d_name);
+                       snprintf(filename, 
+                                        sizeof filename,
+                                        "%s/%s",
+                                        ctdl_netin_dir,
+                                        d->d_name);
                        network_process_file(filename);
                }
        }
@@ -1592,25 +1616,17 @@ void network_purge_spoolout(void) {
        char nexthop[256];
        int i;
 
-       dp = opendir(
-#ifndef HAVE_SPOOL_DIR
-                                "."
-#else
-                                SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                "/network/spoolout");
+       dp = opendir(ctdl_netout_dir);
        if (dp == NULL) return;
 
        while (d = readdir(dp), d != NULL) {
                if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
                        continue;
-               snprintf(filename, sizeof filename,
-#ifndef HAVE_SPOOL_DIR
-                                "."
-#else
-                                SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                                "/network/spoolout/%s", d->d_name);
+               snprintf(filename, 
+                                sizeof filename,
+                                "%s/%s",
+                                ctdl_netout_dir,
+                                d->d_name);
 
                strcpy(nexthop, "");
                i = is_valid_node(nexthop, NULL, d->d_name);
@@ -1637,7 +1653,7 @@ void receive_spool(int sock, char *remote_nodename) {
        long plen;
        FILE *fp;
 
-       strcpy(tempfilename, tmpnam(NULL));
+       CtdlMakeTempFileName(tempfilename, sizeof tempfilename);
        if (sock_puts(sock, "NDOP") < 0) return;
        if (sock_gets(sock, buf) < 0) return;
        lprintf(CTDL_DEBUG, "<%s\n", buf);
@@ -1695,14 +1711,13 @@ void receive_spool(int sock, char *remote_nodename) {
                                download_len, remote_nodename);
        lprintf(CTDL_DEBUG, "%s", buf);
        /* TODO: make move inline. forking is verry expensive. */
-       snprintf(buf, sizeof buf, "mv %s "
-#ifndef HAVE_SPOOL_DIR
-                        "."
-#else
-                        SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                        "/network/spoolin/%s.%ld",
-                        tempfilename, remote_nodename, (long) getpid());
+       snprintf(buf, 
+                        sizeof buf, 
+                        "mv %s %s/%s.%ld",
+                        tempfilename, 
+                        ctdl_netin_dir,
+                        remote_nodename, 
+                        (long) getpid());
        system(buf);
 }
 
@@ -1728,12 +1743,9 @@ void transmit_spool(int sock, char *remote_nodename)
        }
 
        snprintf(sfname, sizeof sfname, 
-#ifndef HAVE_SPOOL_DIR
-                        "."
-#else
-                        SPOOL_DIR
-#endif /* HAVE_SPOOL_DIR */
-                        "/network/spoolout/%s", remote_nodename);
+                        "%s/%s",
+                        ctdl_netout_dir,
+                        remote_nodename);
        fd = open(sfname, O_RDONLY);
        if (fd < 0) {
                if (errno != ENOENT) {
@@ -1859,13 +1871,11 @@ void network_poll_other_citadel_nodes(int full_poll) {
                   && (strlen(host) > 0) && strlen(port) > 0) {
                        poll = full_poll;
                        if (poll == 0) {
-                               snprintf(spoolfile, sizeof spoolfile,
-#ifndef HAVE_SPOOL_DIR
-                                                "."
-#else
-                                                SPOOL_DIR
-#endif
-                                                "/network/spoolout/%s", node);
+                               snprintf(spoolfile, 
+                                                sizeof spoolfile,
+                                                "%s/%s",
+                                                ctdl_netout_dir, 
+                                                node);
                                if (access(spoolfile, R_OK) == 0) {
                                        poll = 1;
                                }
@@ -1885,17 +1895,12 @@ void network_poll_other_citadel_nodes(int full_poll) {
  * It's ok if these directories already exist.  Just fail silently.
  */
 void create_spool_dirs(void) {
-#ifndef HAVE_SPOOL_DIR
-       mkdir("./network", 0700);
-       mkdir("./network/systems", 0700);
-       mkdir("./network/spoolin", 0700);
-       mkdir("./network/spoolout", 0700);
-#else
-       mkdir(SPOOL_DIR "/network", 0700);
-       mkdir(SPOOL_DIR "/network/systems", 0700);
-       mkdir(SPOOL_DIR "/network/spoolin", 0700);
-       mkdir(SPOOL_DIR "/network/spoolout", 0700);
-#endif /* HAVE_SPOOL_DIR */
+       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));
 }
 
 
@@ -1920,8 +1925,6 @@ void network_do_queue(void) {
                full_processing = 0;
        }
 
-       create_spool_dirs();
-
        /*
         * This is a simple concurrency check to make sure only one queue run
         * is done at a time.  We could do this with a mutex, but since we
@@ -1953,17 +1956,35 @@ void network_do_queue(void) {
        if (full_processing) {
                lprintf(CTDL_DEBUG, "network: loading outbound queue\n");
                ForEachRoom(network_queue_room, NULL);
+       }
 
+       if (rplist != NULL) {
                lprintf(CTDL_DEBUG, "network: running outbound queue\n");
                while (rplist != NULL) {
-                       network_spoolout_room(rplist->name);
+                       char spoolroomname[ROOMNAMELEN];
+                       safestrncpy(spoolroomname, rplist->name, sizeof spoolroomname);
+                       begin_critical_section(S_RPLIST);
+
+                       /* pop this record off the list */
                        ptr = rplist;
                        rplist = rplist->next;
                        free(ptr);
+
+                       /* invalidate any duplicate entries to prevent double processing */
+                       for (ptr=rplist; ptr!=NULL; ptr=ptr->next) {
+                               if (!strcasecmp(ptr->name, spoolroomname)) {
+                                       ptr->name[0] = 0;
+                               }
+                       }
+
+                       end_critical_section(S_RPLIST);
+                       if (spoolroomname[0] != 0) {
+                               network_spoolout_room(spoolroomname);
+                       }
                }
        }
 
-       lprintf(CTDL_DEBUG, "network: processing inbound queue\n");
+       /* If there is anything in the inbound queue, process it */
        network_do_spoolin();
 
        /* Save the network map back to disk */