Route all access to cm_fields[] through api functions
[citadel.git] / citadel / msgbase.c
index 830a07501b5e33a47f3e191243eda9701acb5b5c..cc52aec73a6ab67abb45b58e5931f8357b8ad486 100644 (file)
@@ -38,6 +38,9 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <regex.h>
+
+#include "md5.h"
+
 #include <libcitadel.h>
 #include "citadel.h"
 #include "server.h"
@@ -85,32 +88,179 @@ char *msgkeys[] = {
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
        NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, 
        NULL, 
-       "from",
-       NULL, NULL, NULL,
-       "exti",
-       "rfca",
-       NULL, 
-       "hnod",
-       "msgn",
-       "jrnl",
-       NULL,
-       "list",
-       "text",
-       "node",
-       "room",
-       "path",
-       NULL,
-       "rcpt",
-       "spec",
-       "time",
-       "subj",
-       NULL,
-       "wefw",
-       NULL,
-       "cccc",
-       NULL
+       "from", /* A */
+       NULL,   /* B */
+       NULL,   /* C */
+       NULL,   /* D */
+       "exti", /* E */
+       "rfca", /* F */
+       NULL,   /* G */
+       "hnod", /* H */
+       "msgn", /* I */
+       "jrnl", /* J */
+       "rep2", /* K */
+       "list", /* L */
+       "text", /* M */
+       "node", /* N */
+       "room", /* O */
+       "path", /* P */
+       NULL,   /* Q */
+       "rcpt", /* R */
+       "spec", /* S */
+       "time", /* T */
+       "subj", /* U */
+       "nvto", /* V */
+       "wefw", /* W */
+       NULL,   /* X */
+       "cccc", /* Y */
+       NULL    /* Z */
+};
+
+eMsgField FieldOrder[]  = {
+/* Important fields */
+       emessageId   ,
+       eMessagePath ,
+       eTimestamp   ,
+       eAuthor      ,
+       erFc822Addr  ,
+       eOriginalRoom,
+       eNodeName    ,
+       eHumanNode   ,
+       eRecipient   ,
+       eDestination ,
+/* Semi-important fields */
+       eBig_message ,
+       eRemoteRoom  ,
+       eExclusiveID ,
+       eWeferences  ,
+       eJournal     ,
+/* G is not used yet, may become virus signature*/
+       eReplyTo     ,
+       eListID      ,
+/* Q is not used yet */
+       eSpecialField,
+       eenVelopeTo  ,
+/* X is not used yet */
+/* Z is not used yet */
+       eCarbonCopY  ,
+       eMsgSubject  ,
+/* internal only */
+       eErrorMsg    ,
+       eSuppressIdx ,
+       eExtnotify   ,
+/* Message text (MUST be last) */
+       eMesageText 
+/* Not saved to disk: 
+       eVltMsgNum
+*/
 };
 
+static const long NDiskFields = sizeof(FieldOrder) / sizeof(eMsgField);
+void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
+{
+       if (Msg->cm_fields[which] != NULL)
+               free (Msg->cm_fields[which]);
+       Msg->cm_fields[which] = malloc(length + 1);
+       memcpy(Msg->cm_fields[which], buf, length);
+       Msg->cm_fields[which][length] = '\0';
+}
+
+void CM_SetFieldLONG(struct CtdlMessage *Msg, eMsgField which, long lvalue)
+{
+       char buf[128];
+       long len;
+       len = snprintf(buf, sizeof(buf), "%ld", lvalue);
+       CM_SetField(Msg, which, buf, len);
+}
+void CM_CutFieldAt(struct CtdlMessage *Msg, eMsgField WhichToCut, long maxlen)
+{
+       if (Msg->cm_fields[WhichToCut] == NULL)
+               return;
+
+       if (strlen(Msg->cm_fields[WhichToCut]) > maxlen)
+               Msg->cm_fields[WhichToCut][maxlen] = '\0';
+}
+
+void CM_FlushField(struct CtdlMessage *Msg, eMsgField which)
+{
+       if (Msg->cm_fields[which] != NULL)
+               free (Msg->cm_fields[which]);
+       Msg->cm_fields[which] = NULL;
+}
+
+void CM_CopyField(struct CtdlMessage *Msg, eMsgField WhichToPutTo, eMsgField WhichtToCopy)
+{
+       long len;
+       if (Msg->cm_fields[WhichToPutTo] != NULL)
+               free (Msg->cm_fields[WhichToPutTo]);
+
+       if (Msg->cm_fields[WhichtToCopy] != NULL)
+       {
+               len = strlen(Msg->cm_fields[WhichtToCopy]);
+               Msg->cm_fields[WhichToPutTo] = malloc(len + 1);
+               memcpy(Msg->cm_fields[WhichToPutTo], Msg->cm_fields[WhichToPutTo], len);
+               Msg->cm_fields[WhichToPutTo][len] = '\0';
+       }
+       else
+               Msg->cm_fields[WhichToPutTo] = NULL;
+}
+
+
+void CM_PrependToField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
+{
+       if (Msg->cm_fields[which] != NULL) {
+               long oldmsgsize;
+               long newmsgsize;
+               char *new;
+
+               oldmsgsize = strlen(Msg->cm_fields[which]) + 1;
+               newmsgsize = length + oldmsgsize;
+
+               new = malloc(newmsgsize);
+               memcpy(new, buf, length);
+               memcpy(new + length, Msg->cm_fields[which], oldmsgsize);
+               free(Msg->cm_fields[which]);
+               Msg->cm_fields[which] = new;
+       }
+       else {
+               Msg->cm_fields[which] = malloc(length + 1);
+               memcpy(Msg->cm_fields[which], buf, length);
+               Msg->cm_fields[which][length] = '\0';
+       }
+}
+
+void CM_SetAsField(struct CtdlMessage *Msg, eMsgField which, char **buf, long length)
+{
+       if (Msg->cm_fields[which] != NULL)
+               free (Msg->cm_fields[which]);
+
+       Msg->cm_fields[which] = *buf;
+       *buf = NULL;
+}
+
+void CM_SetAsFieldSB(struct CtdlMessage *Msg, eMsgField which, StrBuf **buf)
+{
+       if (Msg->cm_fields[which] != NULL)
+               free (Msg->cm_fields[which]);
+
+       Msg->cm_fields[which] = SmashStrBuf(buf);
+}
+
+void CM_GetAsField(struct CtdlMessage *Msg, eMsgField which, char **ret, long *retlen)
+{
+       if (Msg->cm_fields[which] != NULL)
+       {
+               *retlen = strlen(Msg->cm_fields[which]);
+               *ret = Msg->cm_fields[which];
+               Msg->cm_fields[which] = NULL;
+       }
+       else
+       {
+               *ret = NULL;
+               *retlen = 0;
+       }
+}
+
 /*
  * This function is self explanatory.
  * (What can I say, I'm in a weird mood today...)
@@ -275,11 +425,11 @@ void headers_listing(long msgnum, void *userdata)
 
        cprintf("%ld|%s|%s|%s|%s|%s|\n",
                msgnum,
-               (msg->cm_fields['T'] ? msg->cm_fields['T'] : "0"),
-               (msg->cm_fields['A'] ? msg->cm_fields['A'] : ""),
-               (msg->cm_fields['N'] ? msg->cm_fields['N'] : ""),
-               (msg->cm_fields['F'] ? msg->cm_fields['F'] : ""),
-               (msg->cm_fields['U'] ? msg->cm_fields['U'] : "")
+               (msg->cm_fields[eTimestamp] ? msg->cm_fields[eTimestamp] : "0"),
+               (msg->cm_fields[eAuthor] ? msg->cm_fields[eAuthor] : ""),
+               (msg->cm_fields[eNodeName] ? msg->cm_fields[eNodeName] : ""),
+               (msg->cm_fields[erFc822Addr] ? msg->cm_fields[erFc822Addr] : ""),
+               (msg->cm_fields[eMsgSubject] ? msg->cm_fields[eMsgSubject] : "")
        );
        CtdlFreeMessage(msg);
 }
@@ -299,8 +449,8 @@ void headers_euid(long msgnum, void *userdata)
 
        cprintf("%ld|%s|%s\n", 
                msgnum, 
-               (msg->cm_fields['E'] ? msg->cm_fields['E'] : ""),
-               (msg->cm_fields['T'] ? msg->cm_fields['T'] : "0"));
+               (msg->cm_fields[eExclusiveID] ? msg->cm_fields[eExclusiveID] : ""),
+               (msg->cm_fields[eTimestamp] ? msg->cm_fields[eTimestamp] : "0"));
        CtdlFreeMessage(msg);
 }
 
@@ -370,7 +520,7 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
        int is_seen = 0;
        int was_seen = 0;
        long lo = (-1L);
-       long hi = (-1L);
+       long hi = (-1L); /// TODO: we just write here. y?
        visit vbuf;
        long *msglist;
        int num_msgs = 0;
@@ -889,12 +1039,12 @@ void cmd_msgs(char *cmdbuf)
                template->cm_anon_type = MES_NORMAL;
 
                while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) {
+                       long tValueLen;
                        extract_token(tfield, buf, 0, '|', sizeof tfield);
-                       extract_token(tvalue, buf, 1, '|', sizeof tvalue);
+                       tValueLen = extract_token(tvalue, buf, 1, '|', sizeof tvalue);
                        for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) {
                                if (!strcasecmp(tfield, msgkeys[i])) {
-                                       template->cm_fields[i] =
-                                               strdup(tvalue);
+                                       CM_SetField(template, i, tvalue, tValueLen);
                                }
                        }
                }
@@ -1133,7 +1283,7 @@ void mime_download(char *name, char *filename, char *partnum, char *disp,
                        return;
                }
        
-               rv = fwrite(content, length, 1, CC->download_fp);
+               rv = fwrite(content, length, 1, CCC->download_fp);
                if (rv <= 0) {
                        MSG_syslog(LOG_EMERG, "mime_download(): Couldn't write: %s\n",
                                   strerror(errno));
@@ -1199,6 +1349,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body)
        MSG_syslog(LOG_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body);
        dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long));
        if (dmsgtext == NULL) {
+               MSG_syslog(LOG_ERR, "CtdlFetchMessage(%ld, %d) Failed!\n", msgnum, with_body);
                return NULL;
        }
        mptr = dmsgtext->ptr;
@@ -1228,13 +1379,15 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body)
         * have just processed the 'M' (message text) field.
         */
        do {
+               long len;
                if (mptr >= upper_bound) {
                        break;
                }
                field_header = *mptr++;
-               ret->cm_fields[field_header] = strdup(mptr);
+               len = strlen(mptr);
+               CM_SetField(ret, field_header, mptr, len);
 
-               while (*mptr++ != 0);   /* advance to next field */
+               mptr += len + 1;        /* advance to next field */
 
        } while ((mptr < upper_bound) && (field_header != 'M'));
 
@@ -1245,16 +1398,15 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body)
         * so go ahead and fetch that.  Failing that, just set a dummy
         * body so other code doesn't barf.
         */
-       if ( (ret->cm_fields['M'] == NULL) && (with_body) ) {
+       if ( (ret->cm_fields[eMesageText] == NULL) && (with_body) ) {
                dmsgtext = cdb_fetch(CDB_BIGMSGS, &msgnum, sizeof(long));
                if (dmsgtext != NULL) {
-                       ret->cm_fields['M'] = dmsgtext->ptr;
-                       dmsgtext->ptr = NULL;
+                       CM_SetAsField(ret, eMesageText, &dmsgtext->ptr, dmsgtext->len);
                        cdb_free(dmsgtext);
                }
        }
-       if (ret->cm_fields['M'] == NULL) {
-               ret->cm_fields['M'] = strdup("\r\n\r\n (no text)\r\n");
+       if (ret->cm_fields[eMesageText] == NULL) {
+               CM_SetField(ret, eMesageText, HKEY("\r\n\r\n (no text)\r\n"));
        }
 
        /* Perform "before read" hooks (aborting if any return nonzero) */
@@ -1307,6 +1459,49 @@ void CtdlFreeMessage(struct CtdlMessage *msg)
        free(msg);
 }
 
+int DupCMField(eMsgField i, struct CtdlMessage *OrgMsg, struct CtdlMessage *NewMsg)
+{
+       long len;
+       len = strlen(OrgMsg->cm_fields[i]);
+       NewMsg->cm_fields[i] = malloc(len + 1);
+       if (NewMsg->cm_fields[i] == NULL)
+               return 0;
+       memcpy(NewMsg->cm_fields[i], OrgMsg->cm_fields[i], len);
+       NewMsg->cm_fields[i][len] = '\0';
+       return 1;
+}
+
+struct CtdlMessage * CtdlDuplicateMessage(struct CtdlMessage *OrgMsg)
+{
+       int i;
+       struct CtdlMessage *NewMsg;
+
+       if (is_valid_message(OrgMsg) == 0) 
+               return NULL;
+       NewMsg = (struct CtdlMessage *)malloc(sizeof(struct CtdlMessage));
+       if (NewMsg == NULL)
+               return NULL;
+
+       memcpy(NewMsg, OrgMsg, sizeof(struct CtdlMessage));
+
+       memset(&NewMsg->cm_fields, 0, sizeof(char*) * 256);
+       
+       for (i = 0; i < 256; ++i)
+       {
+               if (OrgMsg->cm_fields[i] != NULL)
+               {
+                       if (!DupCMField(i, OrgMsg, NewMsg))
+                       {
+                               CtdlFreeMessage(NewMsg);
+                               return NULL;
+                       }
+               }
+       }
+
+       return NewMsg;
+}
+
+
 
 /*
  * Pre callback function for multipart/alternative
@@ -1654,11 +1849,13 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
                  int do_proto,         /* do Citadel protocol responses? */
                  int crlf,             /* Use CRLF newlines instead of LF? */
                  char *section,        /* NULL or a message/rfc822 section */
-                 int flags             /* various flags; see msgbase.h */
+                 int flags,            /* various flags; see msgbase.h */
+                 char **Author,
+                 char **Address
 ) {
        struct CitContext *CCC = CC;
        struct CtdlMessage *TheMessage = NULL;
-       int retcode = om_no_such_msg;
+       int retcode = CIT_OK;
        struct encapmsg encap;
        int r;
 
@@ -1727,11 +1924,22 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
        if (section) if (!IsEmptyStr(section)) if (strcmp(section, "0")) {
                memset(&encap, 0, sizeof encap);
                safestrncpy(encap.desired_section, section, sizeof encap.desired_section);
-               mime_parser(TheMessage->cm_fields['M'],
+               mime_parser(TheMessage->cm_fields[eMesageText],
                        NULL,
                        *extract_encapsulated_message,
                        NULL, NULL, (void *)&encap, 0
                );
+
+               if ((Author != NULL) && (*Author == NULL))
+               {
+                       *Author = TheMessage->cm_fields[eAuthor];
+                       TheMessage->cm_fields[eAuthor] = NULL;
+               }
+               if ((Address != NULL) && (*Address == NULL))
+               {       
+                       *Address = TheMessage->cm_fields[erFc822Addr];
+                       TheMessage->cm_fields[erFc822Addr] = NULL;
+               }
                CtdlFreeMessage(TheMessage);
                TheMessage = NULL;
 
@@ -1745,18 +1953,33 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
                         * encapsulated message instead of the top-level
                         * message.  Isn't that neat?
                         */
-
                }
                else {
-                       if (do_proto) cprintf("%d msg %ld has no part %s\n",
-                               ERROR + MESSAGE_NOT_FOUND, msg_num, section);
+                       if (do_proto) {
+                               cprintf("%d msg %ld has no part %s\n",
+                                       ERROR + MESSAGE_NOT_FOUND,
+                                       msg_num,
+                                       section);
+                       }
                        retcode = om_no_such_msg;
                }
 
        }
 
        /* Ok, output the message now */
-       retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf, flags);
+       if (retcode == CIT_OK)
+               retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf, flags);
+       if ((Author != NULL) && (*Author == NULL))
+       {
+               *Author = TheMessage->cm_fields[eAuthor];
+               TheMessage->cm_fields[eAuthor] = NULL;
+       }
+       if ((Address != NULL) && (*Address == NULL))
+       {       
+               *Address = TheMessage->cm_fields[erFc822Addr];
+               TheMessage->cm_fields[erFc822Addr] = NULL;
+       }
+
        CtdlFreeMessage(TheMessage);
 
        return(retcode);
@@ -1784,7 +2007,7 @@ char *qp_encode_email_addrs(char *source)
 
        if (source == NULL) return source;
        if (IsEmptyStr(source)) return source;
-       cit_backtrace();
+       if (MessageDebugEnabled != 0) cit_backtrace();
        MSG_syslog(LOG_DEBUG, "qp_encode_email_addrs: [%s]\n", source);
 
        AddrPtr = malloc (sizeof (long) * nAddrPtrMax);
@@ -1918,16 +2141,15 @@ void OutputCtdlMsgHeaders(
        struct CtdlMessage *TheMessage,
        int do_proto)           /* do Citadel protocol responses? */
 {
-       char allkeys[30];
-       int i, k, n;
+       int i;
        int suppress_f = 0;
        char buf[SIZ];
        char display_name[256];
 
        /* begin header processing loop for Citadel message format */
        safestrncpy(display_name, "<unknown>", sizeof display_name);
-       if (TheMessage->cm_fields['A']) {
-               strcpy(buf, TheMessage->cm_fields['A']);
+       if (TheMessage->cm_fields[eAuthor]) {
+               strcpy(buf, TheMessage->cm_fields[eAuthor]);
                if (TheMessage->cm_anon_type == MES_ANONONLY) {
                        safestrncpy(display_name, "****", sizeof display_name);
                }
@@ -1951,35 +2173,37 @@ void OutputCtdlMsgHeaders(
         * local Citadel network.
         */
        suppress_f = 0;
-       if (TheMessage->cm_fields['N'] != NULL)
-               if (!IsEmptyStr(TheMessage->cm_fields['N']))
-                       if (haschar(TheMessage->cm_fields['N'], '.') == 0) {
+       if (TheMessage->cm_fields[eNodeName] != NULL)
+               if (!IsEmptyStr(TheMessage->cm_fields[eNodeName]))
+                       if (haschar(TheMessage->cm_fields[eNodeName], '.') == 0) {
                                suppress_f = 1;
                        }
 
        /* Now spew the header fields in the order we like them. */
-       n = safestrncpy(allkeys, FORDER, sizeof allkeys);
-       for (i=0; i<n; ++i) {
-               k = (int) allkeys[i];
-               if (k != 'M') {
-                       if ( (TheMessage->cm_fields[k] != NULL)
-                            && (msgkeys[k] != NULL) ) {
-                               if ((k == 'V') || (k == 'R') || (k == 'Y')) {
-                                       sanitize_truncated_recipient(TheMessage->cm_fields[k]);
+       for (i=0; i< NDiskFields; ++i) {
+               eMsgField Field;
+               Field = FieldOrder[i];
+               if (Field != eMesageText) {
+                       if ( (TheMessage->cm_fields[Field] != NULL)
+                            && (msgkeys[Field] != NULL) ) {
+                               if ((Field == eenVelopeTo) ||
+                                   (Field == eRecipient) ||
+                                   (Field == eCarbonCopY)) {
+                                       sanitize_truncated_recipient(TheMessage->cm_fields[Field]);
                                }
-                               if (k == 'A') {
+                               if (Field == eAuthor) {
                                        if (do_proto) cprintf("%s=%s\n",
-                                                             msgkeys[k],
+                                                             msgkeys[Field],
                                                              display_name);
                                }
-                               else if ((k == 'F') && (suppress_f)) {
+                               else if ((Field == erFc822Addr) && (suppress_f)) {
                                        /* do nothing */
                                }
                                /* Masquerade display name if needed */
                                else {
                                        if (do_proto) cprintf("%s=%s\n",
-                                                             msgkeys[k],
-                                                             TheMessage->cm_fields[k]
+                                                             msgkeys[Field],
+                                                             TheMessage->cm_fields[Field]
                                                );
                                }
                        }
@@ -2010,7 +2234,7 @@ void OutputRFC822MsgHeaders(
                if (TheMessage->cm_fields[i]) {
                        mptr = mpptr = TheMessage->cm_fields[i];
                                
-                       if (i == 'A') {
+                       if (i == eAuthor) {
                                safestrncpy(luser, mptr, sizeof_luser);
                                safestrncpy(suser, mptr, sizeof_suser);
                        }
@@ -2024,7 +2248,7 @@ void OutputRFC822MsgHeaders(
                        else if (i == 'P') {
                                cprintf("Return-Path: %s%s", mptr, nl);
                        }
-                       else if (i == 'L') {
+                       else if (i == eListID) {
                                cprintf("List-ID: %s%s", mptr, nl);
                        }
                        else if (i == 'V') {
@@ -2042,7 +2266,7 @@ void OutputRFC822MsgHeaders(
                        }
                        else if (i == 'I')
                                safestrncpy(mid, mptr, sizeof_mid); /// TODO: detect @ here and copy @nodename in if not found.
-                       else if (i == 'F')
+                       else if (i == erFc822Addr)
                                safestrncpy(fuser, mptr, sizeof_fuser);
                        /* else if (i == 'O')
                           cprintf("X-Citadel-Room: %s%s",
@@ -2086,7 +2310,7 @@ void OutputRFC822MsgHeaders(
                                        }
                                }
                        }
-                       else if (i == 'K') {
+                       else if (i == eReplyTo) {
                                hptr = mptr;
                                while ((*hptr != '\0') && isspace(*hptr))
                                        hptr ++;
@@ -2118,7 +2342,7 @@ void Dump_RFC822HeadersBody(
        int nllen = strlen(nl);
        char *mptr;
 
-       mptr = TheMessage->cm_fields['M'];
+       mptr = TheMessage->cm_fields[eMesageText];
 
 
        prev_ch = '\0';
@@ -2178,7 +2402,6 @@ void Dump_RFC822HeadersBody(
        }
        if (outlen > 0) {
                client_write(outbuf, outlen);
-               outlen = 0;
        }
 }
 
@@ -2200,7 +2423,7 @@ void DumpFormatFixed(
        int nllen = strlen (nl);
        char *mptr;
 
-       mptr = TheMessage->cm_fields['M'];
+       mptr = TheMessage->cm_fields[eMesageText];
        
        if (mode == MT_MIME) {
                cprintf("Content-type: text/plain\n\n");
@@ -2302,8 +2525,8 @@ int CtdlOutputPreLoadedMsg(
        /* Suppress envelope recipients if required to avoid disclosing BCC addresses.
         * Pad it with spaces in order to avoid changing the RFC822 length of the message.
         */
-       if ( (flags & SUPPRESS_ENV_TO) && (TheMessage->cm_fields['V'] != NULL) ) {
-               memset(TheMessage->cm_fields['V'], ' ', strlen(TheMessage->cm_fields['V']));
+       if ( (flags & SUPPRESS_ENV_TO) && (TheMessage->cm_fields[eenVelopeTo] != NULL) ) {
+               memset(TheMessage->cm_fields[eenVelopeTo], ' ', strlen(TheMessage->cm_fields[eenVelopeTo]));
        }
                
        /* Are we downloading a MIME component? */
@@ -2318,7 +2541,7 @@ int CtdlOutputPreLoadedMsg(
                                ERROR + RESOURCE_BUSY);
                } else {
                        /* Parse the message text component */
-                       mptr = TheMessage->cm_fields['M'];
+                       mptr = TheMessage->cm_fields[eMesageText];
                        mime_parser(mptr, NULL, *mime_download, NULL, NULL, NULL, 0);
                        /* If there's no file open by this time, the requested
                         * section wasn't found, so print an error
@@ -2345,7 +2568,7 @@ int CtdlOutputPreLoadedMsg(
                        /* Parse the message text component */
                        int found_it = 0;
 
-                       mptr = TheMessage->cm_fields['M'];
+                       mptr = TheMessage->cm_fields[eMesageText];
                        mime_parser(mptr, NULL, *mime_spew_section, NULL, NULL, (void *)&found_it, 0);
                        /* If section wasn't found, print an error
                         */
@@ -2443,7 +2666,7 @@ START_TEXT:
        /* Tell the client about the MIME parts in this message */
        if (TheMessage->cm_format_type == FMT_RFC822) {
                if ( (mode == MT_CITADEL) || (mode == MT_MIME) ) {
-                       mptr = TheMessage->cm_fields['M'];
+                       mptr = TheMessage->cm_fields[eMesageText];
                        memset(&ma, 0, sizeof(struct ma_info));
                        mime_parser(mptr, NULL,
                                (do_proto ? *list_this_part : NULL),
@@ -2484,7 +2707,7 @@ START_TEXT:
         * message to the reader's screen width.
         */
        if (TheMessage->cm_format_type == FMT_CITADEL) {
-               mptr = TheMessage->cm_fields['M'];
+               mptr = TheMessage->cm_fields[eMesageText];
 
                if (mode == MT_MIME) {
                        cprintf("Content-type: text/x-citadel-variformat\n\n");
@@ -2537,7 +2760,7 @@ void cmd_msg0(char *cmdbuf)
        msgid = extract_long(cmdbuf, 0);
        headers_only = extract_int(cmdbuf, 1);
 
-       CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL, 0);
+       CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL, 0, NULL, NULL);
        return;
 }
 
@@ -2553,7 +2776,7 @@ void cmd_msg2(char *cmdbuf)
        msgid = extract_long(cmdbuf, 0);
        headers_only = extract_int(cmdbuf, 1);
 
-       CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL, 0);
+       CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL, 0, NULL, NULL);
 }
 
 
@@ -2607,7 +2830,7 @@ void cmd_msg4(char *cmdbuf)
 
        msgid = extract_long(cmdbuf, 0);
        extract_token(section, cmdbuf, 1, '|', sizeof section);
-       CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) , 0);
+       CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) , 0, NULL, NULL);
 }
 
 
@@ -2640,7 +2863,7 @@ void cmd_opna(char *cmdbuf)
        extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
        safestrncpy(CC->download_desired_section, desired_section,
                sizeof CC->download_desired_section);
-       CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL, 0);
+       CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL, 0, NULL, NULL);
 }                      
 
 
@@ -2656,8 +2879,8 @@ void cmd_dlat(char *cmdbuf)
        extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section);
        safestrncpy(CC->download_desired_section, desired_section,
                sizeof CC->download_desired_section);
-       CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL, 0);
-}                      
+       CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL, 0, NULL, NULL);
+}
 
 
 /*
@@ -2749,6 +2972,8 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
        msglist = realloc(msglist, (sizeof(long) * (num_msgs + num_msgs_to_be_merged)) );
        if (msglist == NULL) {
                MSGM_syslog(LOG_ALERT, "ERROR: can't realloc message list!\n");
+               free(msgs_to_be_merged);
+               return (ERROR + INTERNAL_ERROR);
        }
        memcpy(&msglist[num_msgs], msgs_to_be_merged, (sizeof(long) * num_msgs_to_be_merged) );
        num_msgs += num_msgs_to_be_merged;
@@ -2788,8 +3013,8 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
                                ReplicationChecks(msg);
                
                                /* If the message has an Exclusive ID, index that... */
-                               if (msg->cm_fields['E'] != NULL) {
-                                       index_message_by_euid(msg->cm_fields['E'], &CCC->room, msgid);
+                               if (msg->cm_fields[eExclusiveID] != NULL) {
+                                       index_message_by_euid(msg->cm_fields[eExclusiveID], &CCC->room, msgid);
                                }
 
                                /* Free up the memory we may have allocated */
@@ -2852,29 +3077,30 @@ long send_message(struct CtdlMessage *msg) {
        long newmsgid;
        long retval;
        char msgidbuf[256];
+       long msgidbuflen;
        struct ser_ret smr;
        int is_bigmsg = 0;
        char *holdM = NULL;
 
        /* Get a new message number */
        newmsgid = get_new_message_number();
-       snprintf(msgidbuf, sizeof msgidbuf, "%08lX-%08lX@%s",
-                (long unsigned int) time(NULL),
-                (long unsigned int) newmsgid,
-                config.c_fqdn
+       msgidbuflen = snprintf(msgidbuf, sizeof msgidbuf, "%08lX-%08lX@%s",
+                              (long unsigned int) time(NULL),
+                              (long unsigned int) newmsgid,
+                              config.c_fqdn
                );
 
        /* Generate an ID if we don't have one already */
-       if (msg->cm_fields['I']==NULL) {
-               msg->cm_fields['I'] = strdup(msgidbuf);
+       if (msg->cm_fields[emessageId]==NULL) {
+               CM_SetField(msg, emessageId, msgidbuf, msgidbuflen);
        }
 
        /* If the message is big, set its body aside for storage elsewhere */
-       if (msg->cm_fields['M'] != NULL) {
-               if (strlen(msg->cm_fields['M']) > BIGMSG) {
+       if (msg->cm_fields[eMesageText] != NULL) {
+               if (strlen(msg->cm_fields[eMesageText]) > BIGMSG) {
                        is_bigmsg = 1;
-                       holdM = msg->cm_fields['M'];
-                       msg->cm_fields['M'] = NULL;
+                       holdM = msg->cm_fields[eMesageText];
+                       msg->cm_fields[eMesageText] = NULL;
                }
        }
 
@@ -2882,7 +3108,7 @@ long send_message(struct CtdlMessage *msg) {
        serialize_message(&smr, msg);
 
        if (is_bigmsg) {
-               msg->cm_fields['M'] = holdM;
+               msg->cm_fields[eMesageText] = holdM;
        }
 
        if (smr.len == 0) {
@@ -2932,7 +3158,9 @@ void serialize_message(struct ser_ret *ret,               /* return values */
        struct CitContext *CCC = CC;
        size_t wlen, fieldlen;
        int i;
-       static char *forder = FORDER;
+       long lengths[NDiskFields];
+       
+       memset(lengths, 0, sizeof(lengths));
 
        /*
         * Check for valid message format
@@ -2945,9 +3173,12 @@ void serialize_message(struct ser_ret *ret,              /* return values */
        }
 
        ret->len = 3;
-       for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL)
-                                    ret->len = ret->len +
-                                            strlen(msg->cm_fields[(int)forder[i]]) + 2;
+       for (i=0; i < NDiskFields; ++i)
+               if (msg->cm_fields[FieldOrder[i]] != NULL)
+               {
+                       lengths[i] = strlen(msg->cm_fields[FieldOrder[i]]);
+                       ret->len += lengths[i] + 2;
+               }
 
        ret->ser = malloc(ret->len);
        if (ret->ser == NULL) {
@@ -2963,12 +3194,19 @@ void serialize_message(struct ser_ret *ret,             /* return values */
        ret->ser[2] = msg->cm_format_type;
        wlen = 3;
 
-       for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
-                       fieldlen = strlen(msg->cm_fields[(int)forder[i]]);
-                       ret->ser[wlen++] = (char)forder[i];
-                       safestrncpy((char *)&ret->ser[wlen], msg->cm_fields[(int)forder[i]], fieldlen+1);
+       for (i=0; i < NDiskFields; ++i)
+               if (msg->cm_fields[FieldOrder[i]] != NULL)
+               {
+                       fieldlen = lengths[i];
+                       ret->ser[wlen++] = (char)FieldOrder[i];
+
+                       memcpy(&ret->ser[wlen],
+                              msg->cm_fields[FieldOrder[i]],
+                              fieldlen+1);
+
                        wlen = wlen + fieldlen + 1;
                }
+
        if (ret->len != wlen) {
                MSG_syslog(LOG_ERR, "ERROR: len=%ld wlen=%ld\n",
                           (long)ret->len, (long)wlen);
@@ -2978,47 +3216,6 @@ void serialize_message(struct ser_ret *ret,              /* return values */
 }
 
 
-/*
- * Serialize a struct CtdlMessage into the format used on disk and network.
- * 
- * This function loads up a "struct ser_ret" (defined in server.h) which
- * contains the length of the serialized message and a pointer to the
- * serialized message in memory.  THE LATTER MUST BE FREED BY THE CALLER.
- */
-void dump_message(struct CtdlMessage *msg,     /* unserialized msg */
-                 long Siz)                     /* how many chars ? */
-{
-       int i;
-       static char *forder = FORDER;
-       char *buf;
-
-       /*
-        * Check for valid message format
-        */
-       if (is_valid_message(msg) == 0) {
-               struct CitContext *CCC = CC;
-               MSGM_syslog(LOG_ERR, "dump_message() aborting due to invalid message\n");
-               return;
-       }
-
-       buf = (char*) malloc (Siz + 1);
-
-       for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) {
-                       snprintf (buf, Siz, " msg[%c] = %s ...\n", (char) forder[i], 
-                                  msg->cm_fields[(int)forder[i]]);
-                       if (client_write (buf, strlen(buf)) == -1)
-                       {
-                               struct CitContext *CCC = CC;
-                               MSGM_syslog(LOG_ERR, "dump_message(): aborting due to write failure.\n");
-                               return;
-                       }
-               }
-
-       return;
-}
-
-
-
 /*
  * Check to see if any messages already exist in the current room which
  * carry the same Exclusive ID as this one.  If any are found, delete them.
@@ -3034,12 +3231,12 @@ void ReplicationChecks(struct CtdlMessage *msg) {
 
        /* No exclusive id?  Don't do anything. */
        if (msg == NULL) return;
-       if (msg->cm_fields['E'] == NULL) return;
-       if (IsEmptyStr(msg->cm_fields['E'])) return;
+       if (msg->cm_fields[eExclusiveID] == NULL) return;
+       if (IsEmptyStr(msg->cm_fields[eExclusiveID])) return;
        /*MSG_syslog(LOG_DEBUG, "Exclusive ID: <%s> for room <%s>\n",
-         msg->cm_fields['E'], CCC->room.QRname);*/
+         msg->cm_fields[eExclusiveID], CCC->room.QRname);*/
 
-       old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields['E'], &CCC->room);
+       old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CCC->room);
        if (old_msgnum > 0L) {
                MSG_syslog(LOG_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum);
                CtdlDeleteMessages(CCC->room.QRname, &old_msgnum, 1, "");
@@ -3058,7 +3255,6 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        )
 {
        char submit_filename[128];
-       char generated_timestamp[32];
        char hold_rm[ROOMNAMELEN];
        char actual_rm[ROOMNAMELEN];
        char force_room[ROOMNAMELEN];
@@ -3091,36 +3287,35 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
        /* If this message has no timestamp, we take the liberty of
         * giving it one, right now.
         */
-       if (msg->cm_fields['T'] == NULL) {
-               snprintf(generated_timestamp, sizeof generated_timestamp, "%ld", (long)time(NULL));
-               msg->cm_fields['T'] = strdup(generated_timestamp);
+       if (msg->cm_fields[eTimestamp] == NULL) {
+               CM_SetFieldLONG(msg, eTimestamp, time(NULL));
        }
 
        /* If this message has no path, we generate one.
         */
-       if (msg->cm_fields['P'] == NULL) {
-               if (msg->cm_fields['A'] != NULL) {
-                       msg->cm_fields['P'] = strdup(msg->cm_fields['A']);
-                       for (a=0; !IsEmptyStr(&msg->cm_fields['P'][a]); ++a) {
-                               if (isspace(msg->cm_fields['P'][a])) {
-                                       msg->cm_fields['P'][a] = ' ';
+       if (msg->cm_fields[eMessagePath] == NULL) {
+               if (msg->cm_fields[eAuthor] != NULL) {
+                       CM_CopyField(msg, eMessagePath, eAuthor);
+                       for (a=0; !IsEmptyStr(&msg->cm_fields[eMessagePath][a]); ++a) {
+                               if (isspace(msg->cm_fields[eMessagePath][a])) {
+                                       msg->cm_fields[eMessagePath][a] = ' ';
                                }
                        }
                }
                else {
-                       msg->cm_fields['P'] = strdup("unknown");
+                       CM_SetField(msg,eMessagePath, HKEY("unknown"));
                }
        }
 
        if (force == NULL) {
-               strcpy(force_room, "");
+               force_room[0] = '\0';
        }
        else {
                strcpy(force_room, force);
        }
 
        /* Learn about what's inside, because it's what's inside that counts */
-       if (msg->cm_fields['M'] == NULL) {
+       if (msg->cm_fields[eMesageText] == NULL) {
                MSGM_syslog(LOG_ERR, "ERROR: attempt to save message with NULL body\n");
                return(-2);
        }
@@ -3134,7 +3329,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                break;
        case 4:
                strcpy(content_type, "text/plain");
-               mptr = bmstrcasestr(msg->cm_fields['M'], "Content-type:");
+               mptr = bmstrcasestr(msg->cm_fields[eMesageText], "Content-type:");
                if (mptr != NULL) {
                        char *aptr;
                        safestrncpy(content_type, &mptr[13], sizeof content_type);
@@ -3184,8 +3379,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        /*
         * If this message has no O (room) field, generate one.
         */
-       if (msg->cm_fields['O'] == NULL) {
-               msg->cm_fields['O'] = strdup(CCC->room.QRname);
+       if (msg->cm_fields[eOriginalRoom] == NULL) {
+               CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname));
        }
 
        /* Perform "before save" hooks (aborting if any return nonzero) */
@@ -3280,7 +3475,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                snprintf(bounce_to, sizeof bounce_to, "%s@%s", CCC->user.fullname, config.c_nodename);
        }
        else {
-               snprintf(bounce_to, sizeof bounce_to, "%s@%s", msg->cm_fields['A'], msg->cm_fields['N']);
+               snprintf(bounce_to, sizeof bounce_to, "%s@%s", msg->cm_fields[eAuthor], msg->cm_fields[eNodeName]);
        }
 
        /* If this is private, local mail, make a copy in the
@@ -3288,8 +3483,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,      /* message to save */
         */
        if ((recps != NULL) && (recps->num_local > 0))
                for (i=0; i<num_tokens(recps->recp_local, '|'); ++i) {
-                       extract_token(recipient, recps->recp_local, i,
-                                     '|', sizeof recipient);
+                       long recipientlen;
+                       recipientlen = extract_token(recipient,
+                                                    recps->recp_local, i,
+                                                    '|', sizeof recipient);
                        MSG_syslog(LOG_DEBUG, "Delivering private local mail to <%s>\n",
                               recipient);
                        if (CtdlGetUser(&userbuf, recipient) == 0) {
@@ -3300,13 +3497,17 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                                        /* Generate a instruction message for the Funambol notification
                                         * server, in the same style as the SMTP queue
                                         */
+                                       long instrlen;
                                        instr_alloc = 1024;
                                        instr = malloc(instr_alloc);
-                                       snprintf(instr, instr_alloc,
-                                                "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-                                                "bounceto|%s\n",
-                                                SPOOLMIME, newmsgid, (long)time(NULL),
-                                                bounce_to
+                                       instrlen = snprintf(
+                                               instr, instr_alloc,
+                                               "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
+                                               "bounceto|%s\n",
+                                               SPOOLMIME,
+                                               newmsgid,
+                                               (long)time(NULL), //todo: time() is expensive!
+                                               bounce_to
                                                );
                                
                                        imsg = malloc(sizeof(struct CtdlMessage));
@@ -3314,11 +3515,11 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                                        imsg->cm_magic = CTDLMESSAGE_MAGIC;
                                        imsg->cm_anon_type = MES_NORMAL;
                                        imsg->cm_format_type = FMT_RFC822;
-                                       imsg->cm_fields['U'] = strdup("QMSG");
-                                       imsg->cm_fields['A'] = strdup("Citadel");
-                                       imsg->cm_fields['J'] = strdup("do not journal");
-                                       imsg->cm_fields['M'] = instr;   /* imsg owns this memory now */
-                                       imsg->cm_fields['2'] = strdup(recipient);
+                                       CM_SetField(imsg, eMsgSubject, HKEY("QMSG"));
+                                       CM_SetField(imsg, eAuthor, HKEY("Citadel"));
+                                       CM_SetField(imsg, eJournal, HKEY("do not journal"));
+                                       CM_SetAsField(imsg, eMesageText, &instr, instrlen);
+                                       CM_SetField(imsg, eExtnotify, recipient, recipientlen);
                                        CtdlSubmitMsg(imsg, NULL, FNBL_QUEUE_ROOM, 0);
                                        CtdlFreeMessage(imsg);
                                }
@@ -3331,12 +3532,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
 
        /* Perform "after save" hooks */
        MSGM_syslog(LOG_DEBUG, "Performing after-save hooks\n");
-       if (msg->cm_fields['3'] != NULL) free(msg->cm_fields['3']);
-       msg->cm_fields['3'] = malloc(20);
-       snprintf(msg->cm_fields['3'], 20, "%ld", newmsgid);
+       if (msg->cm_fields[eVltMsgNum] != NULL) free(msg->cm_fields[eVltMsgNum]);
+       msg->cm_fields[eVltMsgNum] = malloc(20);
+       snprintf(msg->cm_fields[eVltMsgNum], 20, "%ld", newmsgid);
        PerformMessageHooks(msg, EVT_AFTERSAVE);
-       free(msg->cm_fields['3']);
-       msg->cm_fields['3'] = NULL;
+       free(msg->cm_fields[eVltMsgNum]);
+       msg->cm_fields[eVltMsgNum] = NULL;
 
        /* For IGnet mail, we have to save a new copy into the spooler for
         * each recipient, with the R and D fields set to the recipient and
@@ -3351,12 +3552,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                        extract_token(recipient, recps->recp_ignet, i,
                                      '|', sizeof recipient);
 
-                       hold_R = msg->cm_fields['R'];
-                       hold_D = msg->cm_fields['D'];
-                       msg->cm_fields['R'] = malloc(SIZ);
-                       msg->cm_fields['D'] = malloc(128);
-                       extract_token(msg->cm_fields['R'], recipient, 0, '@', SIZ);
-                       extract_token(msg->cm_fields['D'], recipient, 1, '@', 128);
+                       hold_R = msg->cm_fields[eRecipient];
+                       hold_D = msg->cm_fields[eDestination];
+                       msg->cm_fields[eRecipient] = malloc(SIZ);
+                       msg->cm_fields[eDestination] = malloc(128);
+                       extract_token(msg->cm_fields[eRecipient], recipient, 0, '@', SIZ);
+                       extract_token(msg->cm_fields[eDestination], recipient, 1, '@', 128);
                
                        serialize_message(&smr, msg);
                        if (smr.len > 0) {
@@ -3376,10 +3577,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                                free(smr.ser);
                        }
 
-                       free(msg->cm_fields['R']);
-                       free(msg->cm_fields['D']);
-                       msg->cm_fields['R'] = hold_R;
-                       msg->cm_fields['D'] = hold_D;
+                       free(msg->cm_fields[eRecipient]);
+                       free(msg->cm_fields[eDestination]);
+                       msg->cm_fields[eRecipient] = hold_R;
+                       msg->cm_fields[eDestination] = hold_D;
                }
 
        /* Go back to the room we started from */
@@ -3435,10 +3636,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                imsg->cm_magic = CTDLMESSAGE_MAGIC;
                imsg->cm_anon_type = MES_NORMAL;
                imsg->cm_format_type = FMT_RFC822;
-               imsg->cm_fields['U'] = strdup("QMSG");
-               imsg->cm_fields['A'] = strdup("Citadel");
-               imsg->cm_fields['J'] = strdup("do not journal");
-               imsg->cm_fields['M'] = SmashStrBuf(&SpoolMsg);  /* imsg owns this memory now */
+               imsg->cm_fields[eMsgSubject] = strdup("QMSG");
+               imsg->cm_fields[eAuthor] = strdup("Citadel");
+               imsg->cm_fields[eJournal] = strdup("do not journal");
+               imsg->cm_fields[eMesageText] = SmashStrBuf(&SpoolMsg);  /* imsg owns this memory now */
                CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
                CtdlFreeMessage(imsg);
        }
@@ -3466,7 +3667,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        /*
         * Determine whether this message qualifies for journaling.
         */
-       if (msg->cm_fields['J'] != NULL) {
+       if (msg->cm_fields[eJournal] != NULL) {
                qualified_for_journaling = 0;
        }
        else {
@@ -3500,18 +3701,16 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
 }
 
 
-
-void aide_message (char *text, char *subject)
-{
-       quickie_message("Citadel",NULL,NULL,AIDEROOM,text,FMT_CITADEL,subject);
-}
-
-
 /*
  * Convenience function for generating small administrative messages.
  */
-void quickie_message(const char *from, const char *fromaddr, char *to, char *room, const char *text, 
-                    int format_type, const char *subject)
+void quickie_message(const char *from,
+                    const char *fromaddr,
+                    const char *to,
+                    char *room,
+                    const char *text, 
+                    int format_type,
+                    const char *subject)
 {
        struct CtdlMessage *msg;
        struct recptypes *recp = NULL;
@@ -3523,35 +3722,98 @@ void quickie_message(const char *from, const char *fromaddr, char *to, char *roo
        msg->cm_format_type = format_type;
 
        if (from != NULL) {
-               msg->cm_fields['A'] = strdup(from);
+               msg->cm_fields[eAuthor] = strdup(from);
        }
        else if (fromaddr != NULL) {
-               msg->cm_fields['A'] = strdup(fromaddr);
-               if (strchr(msg->cm_fields['A'], '@')) {
-                       *strchr(msg->cm_fields['A'], '@') = 0;
+               msg->cm_fields[eAuthor] = strdup(fromaddr);
+               if (strchr(msg->cm_fields[eAuthor], '@')) {
+                       *strchr(msg->cm_fields[eAuthor], '@') = 0;
                }
        }
        else {
-               msg->cm_fields['A'] = strdup("Citadel");
+               msg->cm_fields[eAuthor] = strdup("Citadel");
        }
 
-       if (fromaddr != NULL) msg->cm_fields['F'] = strdup(fromaddr);
-       if (room != NULL) msg->cm_fields['O'] = strdup(room);
-       msg->cm_fields['N'] = strdup(NODENAME);
+       if (fromaddr != NULL) msg->cm_fields[erFc822Addr] = strdup(fromaddr);
+       if (room != NULL) msg->cm_fields[eOriginalRoom] = strdup(room);
+       msg->cm_fields[eNodeName] = strdup(NODENAME);
        if (to != NULL) {
-               msg->cm_fields['R'] = strdup(to);
+               msg->cm_fields[eRecipient] = strdup(to);
                recp = validate_recipients(to, NULL, 0);
        }
        if (subject != NULL) {
-               msg->cm_fields['U'] = strdup(subject);
+               msg->cm_fields[eMsgSubject] = strdup(subject);
        }
-       msg->cm_fields['M'] = strdup(text);
+       msg->cm_fields[eMesageText] = strdup(text);
 
        CtdlSubmitMsg(msg, recp, room, 0);
        CtdlFreeMessage(msg);
        if (recp != NULL) free_recipients(recp);
 }
 
+void flood_protect_quickie_message(const char *from,
+                                  const char *fromaddr,
+                                  const char *to,
+                                  char *room,
+                                  const char *text, 
+                                  int format_type,
+                                  const char *subject,
+                                  int nCriterions,
+                                  const char **CritStr,
+                                  long *CritStrLen,
+                                  long ccid,
+                                  long ioid,
+                                  time_t NOW)
+{
+       int i;
+       u_char rawdigest[MD5_DIGEST_LEN];
+       struct MD5Context md5context;
+       StrBuf *guid;
+       char timestamp[64];
+       long tslen;
+       time_t tsday = NOW / (8*60*60); /* just care for a day... */
+
+       tslen = snprintf(timestamp, sizeof(timestamp), "%ld", tsday);
+       MD5Init(&md5context);
+
+       for (i = 0; i < nCriterions; i++)
+               MD5Update(&md5context,
+                         (const unsigned char*)CritStr[i], CritStrLen[i]);
+       MD5Update(&md5context,
+                 (const unsigned char*)timestamp, tslen);
+       MD5Final(rawdigest, &md5context);
+
+       guid = NewStrBufPlain(NULL,
+                             MD5_DIGEST_LEN * 2 + 12);
+       StrBufHexEscAppend(guid, NULL, rawdigest, MD5_DIGEST_LEN);
+       StrBufAppendBufPlain(guid, HKEY("_fldpt"), 0);
+       if (StrLength(guid) > 40)
+               StrBufCutAt(guid, 40, NULL);
+
+       if (CheckIfAlreadySeen("FPAideMessage",
+                              guid,
+                              NOW,
+                              tsday,
+                              eUpdate,
+                              ccid,
+                              ioid)!= 0)
+       {
+               FreeStrBuf(&guid);
+               /* yes, we did. flood protection kicks in. */
+               syslog(LOG_DEBUG,
+                      "not sending message again\n");
+               return;
+       }
+       FreeStrBuf(&guid);
+       /* no, this message isn't sent recently; go ahead. */
+       quickie_message(from,
+                       fromaddr,
+                       to,
+                       room,
+                       text, 
+                       format_type,
+                       subject);
+}
 
 
 /*
@@ -3560,7 +3822,7 @@ void quickie_message(const char *from, const char *fromaddr, char *to, char *roo
 StrBuf *CtdlReadMessageBodyBuf(char *terminator,       /* token signalling EOT */
                               long tlen,
                               size_t maxlen,           /* maximum message length */
-                              char *exist,             /* if non-null, append to it;
+                              StrBuf *exist,           /* if non-null, append to it;
                                                           exist is ALWAYS freed  */
                               int crlf,                /* CRLF newlines instead of LF */
                               int *sock                /* socket handle or 0 for this session's client socket */
@@ -3577,8 +3839,7 @@ StrBuf *CtdlReadMessageBodyBuf(char *terminator,  /* token signalling EOT */
                Message = NewStrBufPlain(NULL, 4 * SIZ);
        }
        else {
-               Message = NewStrBufPlain(exist, -1);
-               free(exist);
+               Message = NewStrBufDup(exist);
        }
 
        /* Do we need to change leading ".." to "." for SMTP escaping? */
@@ -3641,7 +3902,7 @@ ReadAsyncMsg *NewAsyncMsg(const char *terminator, /* token signalling EOT */
                          long tlen,
                          size_t maxlen,                /* maximum message length */
                          size_t expectlen,             /* if we expect a message, how long should it be? */
-                         char *exist,                  /* if non-null, append to it;
+                         StrBuf *exist,                /* if non-null, append to it;
                                                           exist is ALWAYS freed  */
                          long eLen,                    /* length of exist */
                          int crlf                      /* CRLF newlines instead of LF */
@@ -3664,8 +3925,7 @@ ReadAsyncMsg *NewAsyncMsg(const char *terminator, /* token signalling EOT */
                NewMsg->MsgBuf = NewStrBufPlain(NULL, len);
        }
        else {
-               NewMsg->MsgBuf = NewStrBufPlain(exist, eLen);
-               free(exist);
+               NewMsg->MsgBuf = NewStrBufDup(exist);
        }
        /* Do we need to change leading ".." to "." for SMTP escaping? */
        if ((tlen == 1) && (*terminator == '.')) {
@@ -3798,7 +4058,7 @@ eReadState CtdlReadMessageBodyAsync(AsyncIO *IO)
 char *CtdlReadMessageBody(char *terminator,    /* token signalling EOT */
                          long tlen,
                          size_t maxlen,                /* maximum message length */
-                         char *exist,          /* if non-null, append to it;
+                         StrBuf *exist,                /* if non-null, append to it;
                                                   exist is ALWAYS freed  */
                          int crlf,             /* CRLF newlines instead of LF */
                          int *sock             /* socket handle or 0 for this session's client socket */
@@ -3863,16 +4123,16 @@ struct CtdlMessage *CtdlMakeMessage(
        if (my_email == NULL) my_email = "";
 
        if (!IsEmptyStr(my_email)) {
-               msg->cm_fields['P'] = strdup(my_email);
+               msg->cm_fields[eMessagePath] = strdup(my_email);
        }
        else {
                snprintf(buf, sizeof buf, "%s", author->fullname);
-               msg->cm_fields['P'] = strdup(buf);
+               msg->cm_fields[eMessagePath] = strdup(buf);
        }
-       convert_spaces_to_underscores(msg->cm_fields['P']);
+       convert_spaces_to_underscores(msg->cm_fields[eMessagePath]);
 
        snprintf(buf, sizeof buf, "%ld", (long)time(NULL));     /* timestamp */
-       msg->cm_fields['T'] = strdup(buf);
+       msg->cm_fields[eTimestamp] = strdup(buf);
 
        if ((fake_name != NULL) && (fake_name[0])) {            /* author */
                FakeAuthor = NewStrBufPlain (fake_name, -1);
@@ -3881,34 +4141,34 @@ struct CtdlMessage *CtdlMakeMessage(
                FakeAuthor = NewStrBufPlain (author->fullname, -1);
        }
        StrBufRFC2047encode(&FakeEncAuthor, FakeAuthor);
-       msg->cm_fields['A'] = SmashStrBuf(&FakeEncAuthor);
+       msg->cm_fields[eAuthor] = SmashStrBuf(&FakeEncAuthor);
        FreeStrBuf(&FakeAuthor);
 
        if (CC->room.QRflags & QR_MAILBOX) {            /* room */
-               msg->cm_fields['O'] = strdup(&CC->room.QRname[11]);
+               msg->cm_fields[eOriginalRoom] = strdup(&CC->room.QRname[11]);
        }
        else {
-               msg->cm_fields['O'] = strdup(CC->room.QRname);
+               msg->cm_fields[eOriginalRoom] = strdup(CC->room.QRname);
        }
 
-       msg->cm_fields['N'] = strdup(NODENAME);         /* nodename */
-       msg->cm_fields['H'] = strdup(HUMANNODE);                /* hnodename */
+       msg->cm_fields[eNodeName] = strdup(NODENAME);           /* nodename */
+       msg->cm_fields[eHumanNode] = strdup(HUMANNODE);         /* hnodename */
 
        if ((recipient != NULL) && (recipient[0] != 0)) {
-               msg->cm_fields['R'] = strdup(recipient);
+               msg->cm_fields[eRecipient] = strdup(recipient);
        }
        if ((recp_cc != NULL) && (recp_cc[0] != 0)) {
-               msg->cm_fields['Y'] = strdup(recp_cc);
+               msg->cm_fields[eCarbonCopY] = strdup(recp_cc);
        }
        if (dest_node[0] != 0) {
-               msg->cm_fields['D'] = strdup(dest_node);
+               msg->cm_fields[eDestination] = strdup(dest_node);
        }
 
        if (!IsEmptyStr(my_email)) {
-               msg->cm_fields['F'] = strdup(my_email);
+               msg->cm_fields[erFc822Addr] = strdup(my_email);
        }
        else if ( (author == &CC->user) && (!IsEmptyStr(CC->cs_inet_email)) ) {
-               msg->cm_fields['F'] = strdup(CC->cs_inet_email);
+               msg->cm_fields[erFc822Addr] = strdup(CC->cs_inet_email);
        }
 
        if (subject != NULL) {
@@ -3924,40 +4184,35 @@ struct CtdlMessage *CtdlMakeMessage(
                               (IsAscii = isascii(subject[i]) != 0 ))
                                i++;
                        if (IsAscii != 0)
-                               msg->cm_fields['U'] = strdup(subject);
+                               msg->cm_fields[eMsgSubject] = strdup(subject);
                        else /* ok, we've got utf8 in the string. */
                        {
-                               msg->cm_fields['U'] = rfc2047encode(subject, length);
+                               msg->cm_fields[eMsgSubject] = rfc2047encode(subject, length);
                        }
 
                }
        }
 
        if (supplied_euid != NULL) {
-               msg->cm_fields['E'] = strdup(supplied_euid);
+               msg->cm_fields[eExclusiveID] = strdup(supplied_euid);
        }
 
        if ((references != NULL) && (!IsEmptyStr(references))) {
-               if (msg->cm_fields['W'] != NULL)
-                       free(msg->cm_fields['W']);
-               msg->cm_fields['W'] = strdup(references);
+               if (msg->cm_fields[eWeferences] != NULL)
+                       free(msg->cm_fields[eWeferences]);
+               msg->cm_fields[eWeferences] = strdup(references);
        }
 
        if (preformatted_text != NULL) {
-               msg->cm_fields['M'] = preformatted_text;
+               msg->cm_fields[eMesageText] = preformatted_text;
        }
        else {
-               msg->cm_fields['M'] = CtdlReadMessageBody(HKEY("000"), config.c_maxmsglen, NULL, 0, 0);
+               msg->cm_fields[eMesageText] = CtdlReadMessageBody(HKEY("000"), config.c_maxmsglen, NULL, 0, 0);
        }
 
        return(msg);
 }
 
-extern int netconfig_check_roomaccess(
-       char *errmsgbuf, 
-       size_t n,
-       const char* RemoteIdentifier); /* TODO: find a smarter way */
-
 /*
  * Check to see whether we have permission to post a message in the current
  * room.  Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or
@@ -3992,7 +4247,7 @@ int CtdlDoIHavePermissionToPostInThisRoom(
                }
                if ((PostPublic!=POST_LMTP) &&(CC->room.QRflags2 & QR2_SMTP_PUBLIC) == 0) {
 
-                       return netconfig_check_roomaccess(errmsgbuf, n, RemoteIdentifier);
+                       return CtdlNetconfigCheckRoomaccess(errmsgbuf, n, RemoteIdentifier);
                }
                return (0);
 
@@ -4048,7 +4303,7 @@ int CtdlCheckInternetMailPermission(struct ctdluser *who) {
        /* User flagged ok? */
        if (who->flags & US_INTERNET) return(2);
 
-       /* Aide level access? */
+       /* Admin level access? */
        if (who->axlevel >= AxAideU) return(3);
 
        /* No mail for you! */
@@ -4160,8 +4415,8 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                alias(this_recp);
                alias(this_recp);
                mailtype = alias(this_recp);
-               j = 0;
-               for (j=0; !IsEmptyStr(&this_recp[j]); ++j) {
+
+               for (j = 0; !IsEmptyStr(&this_recp[j]); ++j) {
                        if (this_recp[j]=='_') {
                                this_recp_cooked[j] = ' ';
                        }
@@ -4639,7 +4894,7 @@ void cmd_ent0(char *entargs)
                 * to the actual mail address so others get a valid
                 * reply-to-header.
                 */
-               msg->cm_fields['V'] = strdup(valid->recp_orgroom);
+               msg->cm_fields[eenVelopeTo] = strdup(valid->recp_orgroom);
        }
 
        if (msg != NULL) {
@@ -4657,8 +4912,8 @@ void cmd_ent0(char *entargs)
                                client_write(HKEY("Internal error.\n"));
                        }
 
-                       if (msg->cm_fields['E'] != NULL) {
-                               cprintf("%s\n", msg->cm_fields['E']);
+                       if (msg->cm_fields[eExclusiveID] != NULL) {
+                               cprintf("%s\n", msg->cm_fields[eExclusiveID]);
                        } else {
                                cprintf("\n");
                        }
@@ -4703,12 +4958,12 @@ int CtdlDeleteMessages(char *room_name,         /* which room */
                        regcomp(&re, content_type, 0);
                        need_to_free_re = 1;
                }
-       MSG_syslog(LOG_DEBUG, "CtdlDeleteMessages(%s, %d msgs, %s)\n",
+       MSG_syslog(LOG_DEBUG, " CtdlDeleteMessages(%s, %d msgs, %s)\n",
                   room_name, num_dmsgnums, content_type);
 
        /* get room record, obtaining a lock... */
        if (CtdlGetRoomLock(&qrbuf, room_name) != 0) {
-               MSG_syslog(LOG_ERR, "CtdlDeleteMessages(): Room <%s> not found\n",
+               MSG_syslog(LOG_ERR, " CtdlDeleteMessages(): Room <%s> not found\n",
                           room_name);
                if (need_to_free_re) regfree(&re);
                return (0);     /* room not found */
@@ -4723,7 +4978,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                cdb_free(cdbfr);
        }
        if (num_msgs > 0) {
-               int have_contenttype = !IsEmptyStr(content_type);
+               int have_contenttype = (content_type != NULL) && !IsEmptyStr(content_type);
                int have_delmsgs = (num_dmsgnums == 0) || (dmsgnums == NULL);
                int have_more_del = 1;
 
@@ -4735,7 +4990,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                        StrBuf *dbg = NewStrBuf();
                        for (i = 0; i < num_dmsgnums; i++)
                                StrBufAppendPrintf(dbg, ", %ld", dmsgnums[i]);
-                       MSG_syslog(LOG_DEBUG, "Deleting before: %s", ChrPtr(dbg));
+                       MSG_syslog(LOG_DEBUG, " Deleting before: %s", ChrPtr(dbg));
                        FreeStrBuf(&dbg);
                }
 */
@@ -4743,7 +4998,6 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                while ((i < num_msgs) && (have_more_del)) {
                        delete_this = 0x00;
 
-
                        /* Set/clear a bit for each criterion */
 
                        /* 0 messages in the list or a null list means that we are
@@ -4754,6 +5008,10 @@ int CtdlDeleteMessages(char *room_name,          /* which room */
                        }
                        else {
                                while ((i < num_msgs) && (msglist[i] < dmsgnums[j])) i++;
+
+                               if (i >= num_msgs)
+                                       continue;
+
                                if (msglist[i] == dmsgnums[j]) {
                                        delete_this |= 0x01;
                                }
@@ -4782,7 +5040,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
                        StrBuf *dbg = NewStrBuf();
                        for (i = 0; i < num_deleted; i++)
                                StrBufAppendPrintf(dbg, ", %ld", dellist[i]);
-                       MSG_syslog(LOG_DEBUG, "Deleting: %s", ChrPtr(dbg));
+                       MSG_syslog(LOG_DEBUG, " Deleting: %s", ChrPtr(dbg));
                        FreeStrBuf(&dbg);
                }
 */
@@ -4814,7 +5072,7 @@ int CtdlDeleteMessages(char *room_name,           /* which room */
        /* Now free the memory we used, and go away. */
        if (msglist != NULL) free(msglist);
        if (dellist != NULL) free(dellist);
-       MSG_syslog(LOG_DEBUG, "%d message(s) deleted.\n", num_deleted);
+       MSG_syslog(LOG_DEBUG, " %d message(s) deleted.\n", num_deleted);
        if (need_to_free_re) regfree(&re);
        return (num_deleted);
 }
@@ -4931,7 +5189,7 @@ void cmd_move(char *args)
         */
        permit = 0;
 
-       /* Aides can move/copy */
+       /* Admins can move/copy */
        if (CC->user.axlevel >= AxAideU) permit = 1;
 
        /* Room aides can move/copy */
@@ -5323,13 +5581,13 @@ void CtdlWriteObject(char *req_room,                    /* Room to stuff it in */
        msg->cm_magic = CTDLMESSAGE_MAGIC;
        msg->cm_anon_type = MES_NORMAL;
        msg->cm_format_type = 4;
-       msg->cm_fields['A'] = strdup(CCC->user.fullname);
-       msg->cm_fields['O'] = strdup(req_room);
-       msg->cm_fields['N'] = strdup(config.c_nodename);
-       msg->cm_fields['H'] = strdup(config.c_humannode);
+       msg->cm_fields[eAuthor] = strdup(CCC->user.fullname);
+       msg->cm_fields[eOriginalRoom] = strdup(req_room);
+       msg->cm_fields[eNodeName] = strdup(config.c_nodename);
+       msg->cm_fields[eHumanNode] = strdup(config.c_humannode);
        msg->cm_flags = flags;
        
-       msg->cm_fields['M'] = encoded_message;
+       msg->cm_fields[eMesageText] = encoded_message;
 
        /* Create the requested room if we have to. */
        if (CtdlGetRoom(&qrbuf, roomname) != 0) {
@@ -5388,7 +5646,7 @@ char *CtdlGetSysConfig(char *sysconfname) {
        else {
                msg = CtdlFetchMessage(msgnum, 1);
                if (msg != NULL) {
-                       conf = strdup(msg->cm_fields['M']);
+                       conf = strdup(msg->cm_fields[eMesageText]);
                        CtdlFreeMessage(msg);
                }
                else {