]> code.citadel.org Git - citadel.git/blobdiff - citadel/msgbase.c
* Applied a patch sent in by Wilfried Goesgens which allows the various
[citadel.git] / citadel / msgbase.c
index 1661146c349aa20f39751e6947f62672a5d7be9f..51a61cdc3f95f46c003cb1eca5ff2281244aee31 100644 (file)
 #include "room_ops.h"
 #include "user_ops.h"
 #include "file_ops.h"
+#include "config.h"
 #include "control.h"
 #include "tools.h"
 #include "mime_parser.h"
 #include "html.h"
 #include "genstamp.h"
 #include "internet_addressing.h"
+#include "serv_fulltext.h"
 
-extern struct config config;
 long config_msgnum;
 
 
@@ -141,7 +142,13 @@ int alias(char *name)
        striplt(name);
        remove_any_whitespace_to_the_left_or_right_of_at_symbol(name);
 
-       fp = fopen("network/mail.aliases", "r");
+       fp = fopen(
+#ifndef HAVE_ETG_DIR
+                          "network/"
+#else
+                          ETC_DIR
+#endif
+                          "mail.aliases", "r");
        if (fp == NULL) {
                fp = fopen("/dev/null", "r");
        }
@@ -236,7 +243,13 @@ void get_mm(void)
 {
        FILE *fp;
 
-       fp = fopen("citadel.control", "r");
+       fp = fopen(
+#ifndef HAVE_RUN_DIR
+                          "."
+#else
+                          RUN_DIR
+#endif
+                          "/citadel.control", "r");
        if (fp == NULL) {
                lprintf(CTDL_CRIT, "Cannot open citadel.control: %s\n",
                        strerror(errno));
@@ -305,24 +318,34 @@ void CtdlGetSeen(char *buf, int which_set) {
 /*
  * Manipulate the "seen msgs" string (or other message set strings)
  */
-void CtdlSetSeen(long target_msgnum, int target_setting, int which_set) {
-       char newseen[SIZ];
+void CtdlSetSeen(long target_msgnum, int target_setting, int which_set,
+               struct ctdluser *which_user, struct ctdlroom *which_room) {
        struct cdbdata *cdbfr;
-       int i;
+       int i, j;
        int is_seen = 0;
-       int was_seen = 1;
+       int was_seen = 0;
        long lo = (-1L);
        long hi = (-1L);
+       long t = (-1L);
+       int trimming = 0;
        struct visit vbuf;
        long *msglist;
        int num_msgs = 0;
        char vset[SIZ];
+       char *is_set;   /* actually an array of booleans */
+       int num_sets;
+       int s;
+       char setstr[SIZ], lostr[SIZ], histr[SIZ];
+       size_t tmp;
 
        lprintf(CTDL_DEBUG, "CtdlSetSeen(%ld, %d, %d)\n",
                target_msgnum, target_setting, which_set);
 
        /* Learn about the user and room in question */
-       CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
+       CtdlGetRelationship(&vbuf,
+               ((which_user != NULL) ? which_user : &CC->user),
+               ((which_room != NULL) ? which_room : &CC->room)
+       );
 
        /* Load the message list */
        cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
@@ -335,12 +358,50 @@ void CtdlSetSeen(long target_msgnum, int target_setting, int which_set) {
                return; /* No messages at all?  No further action. */
        }
 
+       is_set = malloc(num_msgs * sizeof(char));
+       memset(is_set, 0, (num_msgs * sizeof(char)) );
+
        /* Decide which message set we're manipulating */
-       if (which_set == ctdlsetseen_seen) strcpy(vset, vbuf.v_seen);
-       if (which_set == ctdlsetseen_answered) strcpy(vset, vbuf.v_answered);
+       switch(which_set) {
+               case ctdlsetseen_seen:
+                       safestrncpy(vset, vbuf.v_seen, sizeof vset);
+                       break;
+               case ctdlsetseen_answered:
+                       safestrncpy(vset, vbuf.v_answered, sizeof vset);
+                       break;
+       }
+
+       /* lprintf(CTDL_DEBUG, "before optimize: %s\n", vset); */
+
+       /* Translate the existing sequence set into an array of booleans */
+       num_sets = num_tokens(vset, ',');
+       for (s=0; s<num_sets; ++s) {
+               extract_token(setstr, vset, s, ',', sizeof setstr);
+
+               extract_token(lostr, setstr, 0, ':', sizeof lostr);
+               if (num_tokens(setstr, ':') >= 2) {
+                       extract_token(histr, setstr, 1, ':', sizeof histr);
+                       if (!strcmp(histr, "*")) {
+                               snprintf(histr, sizeof histr, "%ld", LONG_MAX);
+                       }
+               }
+               else {
+                       strcpy(histr, lostr);
+               }
+               lo = atol(lostr);
+               hi = atol(histr);
+
+               for (i = 0; i < num_msgs; ++i) {
+                       if ((msglist[i] >= lo) && (msglist[i] <= hi)) {
+                               is_set[i] = 1;
+                       }
+               }
+       }
 
-       lprintf(CTDL_DEBUG, "before optimize: %s\n", vset);
-       strcpy(newseen, "");
+       /* Now translate the array of booleans back into a sequence set */
+       strcpy(vset, "");
+       lo = (-1L);
+       hi = (-1L);
 
        for (i=0; i<num_msgs; ++i) {
                is_seen = 0;
@@ -349,34 +410,46 @@ void CtdlSetSeen(long target_msgnum, int target_setting, int which_set) {
                        is_seen = target_setting;
                }
                else {
-                       if (is_msg_in_mset(vset, msglist[i])) {
-                               is_seen = 1;
-                       }
+                       is_seen = is_set[i];
                }
 
-               if (is_seen == 1) {
+               if (is_seen) {
                        if (lo < 0L) lo = msglist[i];
                        hi = msglist[i];
                }
+
                if (  ((is_seen == 0) && (was_seen == 1))
                   || ((is_seen == 1) && (i == num_msgs-1)) ) {
-                       size_t tmp;
 
-                       if ( (strlen(newseen) + 20) > SIZ) {
-                               strcpy(newseen, &newseen[20]);
-                               newseen[0] = '*';
+                       /* begin trim-o-matic code */
+                       j=9;
+                       trimming = 0;
+                       while ( (strlen(vset) + 20) > sizeof vset) {
+                               remove_token(vset, 0, ',');
+                               trimming = 1;
+                               if (j--) break; /* loop no more than 9 times */
                        }
-                       tmp = strlen(newseen);
+                       if ( (trimming) && (which_set == ctdlsetseen_seen) ) {
+                               t = atol(vset);
+                               if (t<2) t=2;
+                               --t;
+                               snprintf(lostr, sizeof lostr,
+                                       "1:%ld,%s", t, vset);
+                               safestrncpy(vset, lostr, sizeof vset);
+                       }
+                       /* end trim-o-matic code */
+
+                       tmp = strlen(vset);
                        if (tmp > 0) {
-                               strcat(newseen, ",");
-                               tmp++;
+                               strcat(vset, ",");
+                               ++tmp;
                        }
                        if (lo == hi) {
-                               snprintf(&newseen[tmp], sizeof newseen - tmp,
+                               snprintf(&vset[tmp], (sizeof vset) - tmp,
                                         "%ld", lo);
                        }
                        else {
-                               snprintf(&newseen[tmp], sizeof newseen - tmp,
+                               snprintf(&vset[tmp], (sizeof vset) - tmp,
                                         "%ld:%ld", lo, hi);
                        }
                        lo = (-1L);
@@ -386,12 +459,23 @@ void CtdlSetSeen(long target_msgnum, int target_setting, int which_set) {
        }
 
        /* Decide which message set we're manipulating */
-       if (which_set == ctdlsetseen_seen) strcpy(vbuf.v_seen, newseen);
-       if (which_set == ctdlsetseen_answered) strcpy(vbuf.v_answered, newseen);
+       switch (which_set) {
+               case ctdlsetseen_seen:
+                       safestrncpy(vbuf.v_seen, vset, sizeof vbuf.v_seen);
+                       break;
+               case ctdlsetseen_answered:
+                       safestrncpy(vbuf.v_answered, vset,
+                                               sizeof vbuf.v_answered);
+                       break;
+       }
+       free(is_set);
 
-       lprintf(CTDL_DEBUG, " after optimize: %s\n", newseen);
+       /* lprintf(CTDL_DEBUG, " after optimize: %s\n", vset); */
        free(msglist);
-       CtdlSetRelationship(&vbuf, &CC->user, &CC->room);
+       CtdlSetRelationship(&vbuf,
+               ((which_user != NULL) ? which_user : &CC->user),
+               ((which_room != NULL) ? which_room : &CC->room)
+       );
 }
 
 
@@ -490,7 +574,7 @@ int CtdlForEachMessage(int mode, long ref,
        if (num_msgs > 0)
                for (a = 0; a < num_msgs; ++a) {
                        thismsg = msglist[a];
-                       is_seen = is_msg_in_mset(vbuf.v_seen, thismsg);
+                       is_seen = is_msg_in_sequence_set(vbuf.v_seen, thismsg);
                        if (is_seen) lastold = thismsg;
                        if ((thismsg > 0L)
                            && (
@@ -522,7 +606,7 @@ int CtdlForEachMessage(int mode, long ref,
 
 /*
  * cmd_msgs()  -  get list of message #'s in this room
- *                implements the MSGS server command using CtdlForEachMessage()
+ *             implements the MSGS server command using CtdlForEachMessage()
  */
 void cmd_msgs(char *cmdbuf)
 {
@@ -620,17 +704,17 @@ void do_help_subst(char *buffer)
        help_subst(buffer, "^variantname", CITADEL);
        snprintf(buf2, sizeof buf2, "%d", config.c_maxsessions);
        help_subst(buffer, "^maxsessions", buf2);
-       help_subst(buffer, "^bbsdir", BBSDIR);
+       help_subst(buffer, "^bbsdir", CTDLDIR);
 }
 
 
 
 /*
  * memfmout()  -  Citadel text formatter and paginator.
- *             Although the original purpose of this routine was to format
- *             text to the reader's screen width, all we're really using it
- *             for here is to format text out to 80 columns before sending it
- *             to the client.  The client software may reformat it again.
+ *          Although the original purpose of this routine was to format
+ *          text to the reader's screen width, all we're really using it
+ *          for here is to format text out to 80 columns before sending it
+ *          to the client.  The client software may reformat it again.
  */
 void memfmout(
        int width,              /* screen width to use */
@@ -717,7 +801,7 @@ void memfmout(
  * Callback function for mime parser that simply lists the part
  */
 void list_this_part(char *name, char *filename, char *partnum, char *disp,
-                   void *content, char *cbtype, size_t length, char *encoding,
+                   void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                    void *cbuserdata)
 {
 
@@ -729,7 +813,7 @@ void list_this_part(char *name, char *filename, char *partnum, char *disp,
  * Callback function for multipart prefix
  */
 void list_this_pref(char *name, char *filename, char *partnum, char *disp,
-                   void *content, char *cbtype, size_t length, char *encoding,
+                   void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                    void *cbuserdata)
 {
        cprintf("pref=%s|%s\n", partnum, cbtype);
@@ -739,7 +823,7 @@ void list_this_pref(char *name, char *filename, char *partnum, char *disp,
  * Callback function for multipart sufffix
  */
 void list_this_suff(char *name, char *filename, char *partnum, char *disp,
-                   void *content, char *cbtype, size_t length, char *encoding,
+                   void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                    void *cbuserdata)
 {
        cprintf("suff=%s|%s\n", partnum, cbtype);
@@ -750,8 +834,8 @@ void list_this_suff(char *name, char *filename, char *partnum, char *disp,
  * Callback function for mime parser that opens a section for downloading
  */
 void mime_download(char *name, char *filename, char *partnum, char *disp,
-                  void *content, char *cbtype, size_t length, char *encoding,
-                  void *cbuserdata)
+                  void *content, char *cbtype, char *cbcharset, size_t length,
+                  char *encoding, void *cbuserdata)
 {
 
        /* Silently go away if there's already a download open... */
@@ -909,7 +993,7 @@ void CtdlFreeMessage(struct CtdlMessage *msg)
  *
  */
 void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, size_t length, char *encoding,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                void *cbuserdata)
 {
        struct ma_info *ma;
@@ -927,7 +1011,7 @@ void fixed_output_pre(char *name, char *filename, char *partnum, char *disp,
  * Post callback function for multipart/alternative
  */
 void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, size_t length, char *encoding,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                void *cbuserdata)
 {
        struct ma_info *ma;
@@ -945,7 +1029,7 @@ void fixed_output_post(char *name, char *filename, char *partnum, char *disp,
  * Inline callback function for mime parser that wants to display text
  */
 void fixed_output(char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, size_t length, char *encoding,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                void *cbuserdata)
        {
                char *ptr;
@@ -998,7 +1082,7 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp,
  * we're going to send.
  */
 void choose_preferred(char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, size_t length, char *encoding,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                void *cbuserdata)
 {
        char buf[1024];
@@ -1021,7 +1105,7 @@ void choose_preferred(char *name, char *filename, char *partnum, char *disp,
  * Now that we've chosen our preferred part, output it.
  */
 void output_preferred(char *name, char *filename, char *partnum, char *disp,
-               void *content, char *cbtype, size_t length, char *encoding,
+               void *content, char *cbtype, char *cbcharset, size_t length, char *encoding,
                void *cbuserdata)
 {
        int i;
@@ -1048,8 +1132,11 @@ void output_preferred(char *name, char *filename, char *partnum, char *disp,
                                ++add_newline;
                        }
 
-                       cprintf("Content-type: %s\n", cbtype);
-                       cprintf("Content-length: %d\n",
+                       cprintf("Content-type: %s", cbtype);
+                       if (strlen(cbcharset) > 0) {
+                               cprintf("; charset=%s", cbcharset);
+                       }
+                       cprintf("\nContent-length: %d\n",
                                (int)(length + add_newline) );
                        if (strlen(encoding) > 0) {
                                cprintf("Content-transfer-encoding: %s\n", encoding);
@@ -1066,7 +1153,7 @@ void output_preferred(char *name, char *filename, char *partnum, char *disp,
 
        /* No translations required or possible: output as text/plain */
        cprintf("Content-type: text/plain\n\n");
-       fixed_output(name, filename, partnum, disp, content, cbtype,
+       fixed_output(name, filename, partnum, disp, content, cbtype, cbcharset,
                        length, encoding, cbuserdata);
 }
 
@@ -1096,11 +1183,11 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
        /* FIXME: check message id against msglist for this room */
 
        /*
-        * Fetch the message from disk.  If we're in sooper-fast headers
+        * Fetch the message from disk.  If we're in any sort of headers
         * only mode, request that we don't even bother loading the body
         * into memory.
         */
-       if (headers_only == HEADERS_FAST) {
+       if ( (headers_only == HEADERS_FAST) || (headers_only == HEADERS_ONLY) ) {
                TheMessage = CtdlFetchMessage(msg_num, 0);
        }
        else {
@@ -1215,7 +1302,7 @@ int CtdlOutputPreLoadedMsg(
 
        /* nhdr=yes means that we're only displaying headers, no body */
        if ( (TheMessage->cm_anon_type == MES_ANONONLY)
-           && (mode == MT_CITADEL)
+          && (mode == MT_CITADEL)
           && (do_proto)
           ) {
                cprintf("nhdr=yes\n");
@@ -1311,9 +1398,9 @@ int CtdlOutputPreLoadedMsg(
                                        safestrncpy(lnode, mptr, sizeof lnode);
                                else if (i == 'F')
                                        safestrncpy(fuser, mptr, sizeof fuser);
-                               else if (i == 'O')
+                               /* else if (i == 'O')
                                        cprintf("X-Citadel-Room: %s%s",
-                                               mptr, nl);
+                                               mptr, nl); */
                                else if (i == 'N')
                                        safestrncpy(snode, mptr, sizeof snode);
                                else if (i == 'R')
@@ -1348,16 +1435,20 @@ int CtdlOutputPreLoadedMsg(
                cprintf(">%s", nl);
 
                if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONONLY)) {
-                       cprintf("From: x@x.org (----)%s", nl);
+                       // cprintf("From: x@x.org (----)%s", nl);
+                       cprintf("From: \"----\" <x@x.org>%s", nl);
                }
                else if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONOPT)) {
-                       cprintf("From: x@x.org (anonymous)%s", nl);
+                       // cprintf("From: x@x.org (anonymous)%s", nl);
+                       cprintf("From: \"anonymous\" <x@x.org>%s", nl);
                }
                else if (strlen(fuser) > 0) {
-                       cprintf("From: %s (%s)%s", fuser, luser, nl);
+                       // cprintf("From: %s (%s)%s", fuser, luser, nl);
+                       cprintf("From: \"%s\" <%s>%s", luser, fuser, nl);
                }
                else {
-                       cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
+                       // cprintf("From: %s@%s (%s)%s", suser, snode, luser, nl);
+                       cprintf("From: \"%s\" <%s@%s>%s", luser, suser, snode, nl);
                }
 
                cprintf("Organization: %s%s", lnode, nl);
@@ -1377,9 +1468,9 @@ START_TEXT:
        if (TheMessage->cm_format_type == FMT_RFC822) {
                if ( (mode == MT_CITADEL) || (mode == MT_MIME) ) {
                        mime_parser(mptr, NULL,
-                               *list_this_part,
-                               *list_this_pref,
-                               *list_this_suff,
+                               (do_proto ? *list_this_part : NULL),
+                               (do_proto ? *list_this_pref : NULL),
+                               (do_proto ? *list_this_suff : NULL),
                                NULL, 0);
                }
                else if (mode == MT_RFC822) {   /* unparsed RFC822 dump */
@@ -1626,10 +1717,10 @@ void cmd_opna(char *cmdbuf)
 int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
        int i;
        char hold_rm[ROOMNAMELEN];
-        struct cdbdata *cdbfr;
-        int num_msgs;
-        long *msglist;
-        long highest_msg = 0L;
+       struct cdbdata *cdbfr;
+       int num_msgs;
+       long *msglist;
+       long highest_msg = 0L;
        struct CtdlMessage *msg = NULL;
 
        lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n",
@@ -1674,25 +1765,25 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
                return(ERROR + ROOM_NOT_FOUND);
        }
 
-        cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
-        if (cdbfr == NULL) {
-                msglist = NULL;
-                num_msgs = 0;
-        } else {
-                msglist = malloc(cdbfr->len);
-                if (msglist == NULL)
-                        lprintf(CTDL_ALERT, "ERROR malloc msglist!\n");
-                num_msgs = cdbfr->len / sizeof(long);
-                memcpy(msglist, cdbfr->ptr, cdbfr->len);
-                cdb_free(cdbfr);
-        }
+       cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
+       if (cdbfr == NULL) {
+               msglist = NULL;
+               num_msgs = 0;
+       } else {
+               msglist = malloc(cdbfr->len);
+               if (msglist == NULL)
+                       lprintf(CTDL_ALERT, "ERROR malloc msglist!\n");
+               num_msgs = cdbfr->len / sizeof(long);
+               memcpy(msglist, cdbfr->ptr, cdbfr->len);
+               cdb_free(cdbfr);
+       }
 
 
        /* Make sure the message doesn't already exist in this room.  It
         * is absolutely taboo to have more than one reference to the same
         * message in a room.
         */
-        if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
+       if (num_msgs > 0) for (i=0; i<num_msgs; ++i) {
                if (msglist[i] == msgid) {
                        lputroom(&CC->room);    /* unlock the room */
                        getroom(&CC->room, hold_rm);
@@ -1702,27 +1793,27 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
                }
        }
 
-        /* Now add the new message */
-        ++num_msgs;
-        msglist = realloc(msglist, (num_msgs * sizeof(long)));
+       /* Now add the new message */
+       ++num_msgs;
+       msglist = realloc(msglist, (num_msgs * sizeof(long)));
 
-        if (msglist == NULL) {
-                lprintf(CTDL_ALERT, "ERROR: can't realloc message list!\n");
-        }
-        msglist[num_msgs - 1] = msgid;
+       if (msglist == NULL) {
+               lprintf(CTDL_ALERT, "ERROR: can't realloc message list!\n");
+       }
+       msglist[num_msgs - 1] = msgid;
 
-        /* Sort the message list, so all the msgid's are in order */
-        num_msgs = sort_msglist(msglist, num_msgs);
+       /* Sort the message list, so all the msgid's are in order */
+       num_msgs = sort_msglist(msglist, num_msgs);
 
-        /* Determine the highest message number */
-        highest_msg = msglist[num_msgs - 1];
+       /* Determine the highest message number */
+       highest_msg = msglist[num_msgs - 1];
 
-        /* Write it back to disk. */
-        cdb_store(CDB_MSGLISTS, &CC->room.QRnumber, (int)sizeof(long),
-                  msglist, (int)(num_msgs * sizeof(long)));
+       /* Write it back to disk. */
+       cdb_store(CDB_MSGLISTS, &CC->room.QRnumber, (int)sizeof(long),
+                 msglist, (int)(num_msgs * sizeof(long)));
 
-        /* Free up the memory we used. */
-        free(msglist);
+       /* Free up the memory we used. */
+       free(msglist);
 
        /* Update the highest-message pointer and unlock the room. */
        CC->room.QRhighest = highest_msg;
@@ -1736,7 +1827,7 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) {
 
        /* Return success. */
        if (msg != NULL) CtdlFreeMessage(msg);
-        return (0);
+       return (0);
 }
 
 
@@ -1753,7 +1844,7 @@ long send_message(struct CtdlMessage *msg) {
        long newmsgid;
        long retval;
        char msgidbuf[256];
-        struct ser_ret smr;
+       struct ser_ret smr;
        int is_bigmsg = 0;
        char *holdM = NULL;
 
@@ -1776,17 +1867,17 @@ long send_message(struct CtdlMessage *msg) {
        }
 
        /* Serialize our data structure for storage in the database */  
-        serialize_message(&smr, msg);
+       serialize_message(&smr, msg);
 
        if (is_bigmsg) {
                msg->cm_fields['M'] = holdM;
        }
 
-        if (smr.len == 0) {
-                cprintf("%d Unable to serialize message\n",
-                        ERROR + INTERNAL_ERROR);
-                return (-1L);
-        }
+       if (smr.len == 0) {
+               cprintf("%d Unable to serialize message\n",
+                       ERROR + INTERNAL_ERROR);
+               return (-1L);
+       }
 
        /* Write our little bundle of joy into the message base */
        if (cdb_store(CDB_MSGMAIN, &newmsgid, (int)sizeof(long),
@@ -1806,7 +1897,7 @@ long send_message(struct CtdlMessage *msg) {
        }
 
        /* Free the memory we used for the serialized message */
-        free(smr.ser);
+       free(smr.ser);
 
        /* Return the *local* message ID to the caller
         * (even if we're storing an incoming network message)
@@ -2019,7 +2110,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
 
        lprintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm);
        if (strcasecmp(actual_rm, CC->room.QRname)) {
-               getroom(&CC->room, actual_rm);
+               /* getroom(&CC->room, actual_rm); */
+               usergoto(actual_rm, 0, 1, NULL, NULL);
        }
 
        /*
@@ -2050,7 +2142,30 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,      /* message to save */
        memset(&smi, 0, sizeof(struct MetaData));
        smi.meta_msgnum = newmsgid;
        smi.meta_refcount = 0;
-       safestrncpy(smi.meta_content_type, content_type, sizeof smi.meta_content_type);
+       safestrncpy(smi.meta_content_type, content_type,
+                       sizeof smi.meta_content_type);
+
+       /* As part of the new metadata record, measure how
+        * big this message will be when displayed as RFC822.
+        * Both POP and IMAP use this, and it's best to just take the hit now
+        * instead of having to potentially measure thousands of messages when
+        * a mailbox is opened later.
+        */
+
+       if (CC->redirect_buffer != NULL) {
+               lprintf(CTDL_ALERT, "CC->redirect_buffer is not NULL during message submission!\n");
+               abort();
+       }
+       CC->redirect_buffer = malloc(SIZ);
+       CC->redirect_len = 0;
+       CC->redirect_alloc = SIZ;
+       CtdlOutputPreLoadedMsg(msg, 0L, MT_RFC822, HEADERS_ALL, 0, 1);
+       smi.meta_rfc822_length = CC->redirect_len;
+       free(CC->redirect_buffer);
+       CC->redirect_buffer = NULL;
+       CC->redirect_len = 0;
+       CC->redirect_alloc = 0;
+
        PutMetaData(&smi);
 
        /* Now figure out where to store the pointers */
@@ -2063,7 +2178,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        if ((!CC->internal_pgm) || (recps == NULL)) {
                if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) {
                        lprintf(CTDL_ERR, "ERROR saving message pointer!\n");
-                       CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0);
+                       CtdlSaveMsgPointerInRoom(config.c_aideroom,
+                                                       newmsgid, 0);
                }
        }
 
@@ -2077,8 +2193,9 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        if (recps != NULL)
         if (recps->num_room > 0)
          for (i=0; i<num_tokens(recps->recp_room, '|'); ++i) {
-               extract_token(recipient, recps->recp_room, i, '|', sizeof recipient);
-               lprintf(CTDL_DEBUG, "Delivering to local room <%s>\n", recipient);
+               extract_token(recipient, recps->recp_room, i,
+                                       '|', sizeof recipient);
+               lprintf(CTDL_DEBUG, "Delivering to room <%s>\n", recipient);
                CtdlSaveMsgPointerInRoom(recipient, newmsgid, 0);
        }
 
@@ -2094,17 +2211,20 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
        if (recps != NULL)
         if (recps->num_local > 0)
          for (i=0; i<num_tokens(recps->recp_local, '|'); ++i) {
-               extract_token(recipient, recps->recp_local, i, '|', sizeof recipient);
+               extract_token(recipient, recps->recp_local, i,
+                                       '|', sizeof recipient);
                lprintf(CTDL_DEBUG, "Delivering private local mail to <%s>\n",
                        recipient);
                if (getuser(&userbuf, recipient) == 0) {
-                       MailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM);
+                       MailboxName(actual_rm, sizeof actual_rm,
+                                       &userbuf, MAILROOM);
                        CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0);
                        BumpNewMailCounter(userbuf.usernum);
                }
                else {
                        lprintf(CTDL_DEBUG, "No user <%s>\n", recipient);
-                       CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0);
+                       CtdlSaveMsgPointerInRoom(config.c_aideroom,
+                                                       newmsgid, 0);
                }
        }
 
@@ -2123,7 +2243,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        if (recps != NULL)
         if (recps->num_ignet > 0)
          for (i=0; i<num_tokens(recps->recp_ignet, '|'); ++i) {
-               extract_token(recipient, recps->recp_ignet, i, '|', sizeof recipient);
+               extract_token(recipient, recps->recp_ignet, i,
+                               '|', sizeof recipient);
 
                hold_R = msg->cm_fields['R'];
                hold_D = msg->cm_fields['D'];
@@ -2135,8 +2256,13 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,      /* message to save */
                serialize_message(&smr, msg);
                if (smr.len > 0) {
                        snprintf(submit_filename, sizeof submit_filename,
-                               "./network/spoolin/netmail.%04lx.%04x.%04x",
-                               (long) getpid(), CC->cs_pid, ++seqnum);
+#ifndef HAVE_SPOOL_DIR
+                                        CTDLDIR
+#else
+                                        SPOOL_DIR
+#endif
+                                        "/network/spoolin/netmail.%04lx.%04x.%04x",
+                                        (long) getpid(), CC->cs_pid, ++seqnum);
                        network_fp = fopen(submit_filename, "wb+");
                        if (network_fp != NULL) {
                                fwrite(smr.ser, smr.len, 1, network_fp);
@@ -2154,7 +2280,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        /* Go back to the room we started from */
        lprintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm);
        if (strcasecmp(hold_rm, CC->room.QRname))
-               getroom(&CC->room, hold_rm);
+               /* getroom(&CC->room, hold_rm); */
+               usergoto(hold_rm, 0, 1, NULL, NULL);
 
        /* For internet mail, generate delivery instructions.
         * Yes, this is recursive.  Deal with it.  Infinite recursion does
@@ -2179,7 +2306,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                                 "remote|%s|0||\n", recipient);
                }
 
-               imsg = malloc(sizeof(struct CtdlMessage));
+               imsg = malloc(sizeof(struct CtdlMessage));
                memset(imsg, 0, sizeof(struct CtdlMessage));
                imsg->cm_magic = CTDLMESSAGE_MAGIC;
                imsg->cm_anon_type = MES_NORMAL;
@@ -3085,7 +3212,7 @@ void PutMetaData(struct MetaData *smibuf)
 
 /*
  * AdjRefCount  -  change the reference count for a message;
- *                 delete the message if it reaches zero
+ *              delete the message if it reaches zero
  */
 void AdjRefCount(long msgnum, int incr)
 {
@@ -3099,24 +3226,30 @@ void AdjRefCount(long msgnum, int incr)
         */
        begin_critical_section(S_SUPPMSGMAIN);
        GetMetaData(&smi, msgnum);
-       lprintf(CTDL_DEBUG, "Ref count for message <%ld> before write is <%d>\n",
-               msgnum, smi.meta_refcount);
        smi.meta_refcount += incr;
        PutMetaData(&smi);
        end_critical_section(S_SUPPMSGMAIN);
-       lprintf(CTDL_DEBUG, "Ref count for message <%ld> after write is <%d>\n",
-               msgnum, smi.meta_refcount);
+       lprintf(CTDL_DEBUG, "msg %ld ref count incr %d, is now %d\n",
+               msgnum, incr, smi.meta_refcount);
 
        /* If the reference count is now zero, delete the message
         * (and its supplementary record as well).
+        * FIXME ... defer this so it doesn't keep the user waiting.
         */
        if (smi.meta_refcount == 0) {
                lprintf(CTDL_DEBUG, "Deleting message <%ld>\n", msgnum);
+
+               /* Remove from fulltext index */
+               if (config.c_enable_fulltext) {
+                       ft_index_message(msgnum, 0);
+               }
+
+               /* Remove from message base */
                delnum = msgnum;
                cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long));
                cdb_delete(CDB_BIGMSGS, &delnum, (int)sizeof(long));
 
-               /* We have to delete the metadata record too! */
+               /* Remove metadata record */
                delnum = (0L - msgnum);
                cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long));
        }
@@ -3276,10 +3409,10 @@ char *CtdlGetSysConfig(char *sysconfname) {
                conf = NULL;
        }
        else {
-               msg = CtdlFetchMessage(msgnum, 1);
-               if (msg != NULL) {
-                       conf = strdup(msg->cm_fields['M']);
-                       CtdlFreeMessage(msg);
+               msg = CtdlFetchMessage(msgnum, 1);
+               if (msg != NULL) {
+                       conf = strdup(msg->cm_fields['M']);
+                       CtdlFreeMessage(msg);
                }
                else {
                        conf = NULL;