Move simple subtstitution functions to the message functions
[citadel.git] / citadel / msgbase.c
index b8b6d7c767cfccb0193e2e45adf95f295a9ccc66..405790cb43ded6e5b56871264ae5f2931eec99be 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"
@@ -71,18 +74,6 @@ void AdjRefCountList(long *msgnum, long nmsg, int incr);
 
 int MessageDebugEnabled = 0;
 
-#define DBGLOG(LEVEL) if ((LEVEL != LOG_DEBUG) || (MessageDebugEnabled != 0))
-#define CCCID CCC->cs_pid
-#define MSG_syslog(LEVEL, FORMAT, ...)         \
-       DBGLOG(LEVEL) syslog(LEVEL,             \
-                            "CC[%d]" FORMAT,   \
-                            CCCID, __VA_ARGS__)
-
-#define MSGM_syslog(LEVEL, FORMAT)             \
-       DBGLOG(LEVEL) syslog(LEVEL,             \
-                            "CC[%d]" FORMAT,   \
-                            CCCID)
-
 /*
  * These are the four-character field headers we use when outputting
  * messages in Citadel format (as opposed to RFC822 format).
@@ -97,32 +88,270 @@ 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);
+
+int CM_IsEmpty(struct CtdlMessage *Msg, eMsgField which)
+{
+       return !((Msg->cm_fields[which] != NULL) &&
+                (Msg->cm_fields[which][0] != '\0'));
+}
+
+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;
+       }
+}
+
+/*
+ * Returns 1 if the supplied pointer points to a valid Citadel message.
+ * If the pointer is NULL or the magic number check fails, returns 0.
+ */
+int CM_IsValidMsg(struct CtdlMessage *msg) {
+       if (msg == NULL)
+               return 0;
+       if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
+               struct CitContext *CCC = CC;
+               MSGM_syslog(LOG_WARNING, "CM_IsValidMsg() -- self-check failed\n");
+               return 0;
+       }
+       return 1;
+}
+
+void CM_FreeContents(struct CtdlMessage *msg)
+{
+       int i;
+
+       for (i = 0; i < 256; ++i)
+               if (msg->cm_fields[i] != NULL) {
+                       free(msg->cm_fields[i]);
+               }
+
+       msg->cm_magic = 0;      /* just in case */
+}
+/*
+ * 'Destructor' for struct CtdlMessage
+ */
+void CM_Free(struct CtdlMessage *msg)
+{
+       if (CM_IsValidMsg(msg) == 0) 
+       {
+               if (msg != NULL) free (msg);
+               return;
+       }
+       CM_FreeContents(msg);
+       free(msg);
+}
+
+int CM_DupField(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 * CM_Duplicate(struct CtdlMessage *OrgMsg)
+{
+       int i;
+       struct CtdlMessage *NewMsg;
+
+       if (CM_IsValidMsg(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 (!CM_DupField(i, OrgMsg, NewMsg))
+                       {
+                               CM_Free(NewMsg);
+                               return NULL;
+                       }
+               }
+       }
+
+       return NewMsg;
+}
+
+
+
 /*
  * This function is self explanatory.
  * (What can I say, I'm in a weird mood today...)
@@ -287,13 +516,13 @@ 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'] : "")
+               (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"),
+               (!CM_IsEmpty(msg, eAuthor) ? msg->cm_fields[eAuthor] : ""),
+               (!CM_IsEmpty(msg, eNodeName) ? msg->cm_fields[eNodeName] : ""),
+               (!CM_IsEmpty(msg, erFc822Addr) ? msg->cm_fields[erFc822Addr] : ""),
+               (!CM_IsEmpty(msg, eMsgSubject) ? msg->cm_fields[eMsgSubject] : "")
        );
-       CtdlFreeMessage(msg);
+       CM_Free(msg);
 }
 
 /*
@@ -311,9 +540,9 @@ 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"));
-       CtdlFreeMessage(msg);
+               (!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""),
+               (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"));
+       CM_Free(msg);
 }
 
 
@@ -382,7 +611,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;
@@ -713,7 +942,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                                        if (CtdlMsgCmp(msg, compare)) {
                                                msglist[a] = 0L;
                                        }
-                                       CtdlFreeMessage(msg);
+                                       CM_Free(msg);
                                }
                        }
                }
@@ -901,12 +1130,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);
                                }
                        }
                }
@@ -923,48 +1152,11 @@ void cmd_msgs(char *cmdbuf)
                           template,
                           CallBack,
                           NULL);
-       if (template != NULL) CtdlFreeMessage(template);
+       if (template != NULL) CM_Free(template);
        cprintf("000\n");
 }
 
 
-
-
-/* 
- * help_subst()  -  support routine for help file viewer
- */
-void help_subst(char *strbuf, char *source, char *dest)
-{
-       char workbuf[SIZ];
-       int p;
-
-       while (p = pattern2(strbuf, source), (p >= 0)) {
-               strcpy(workbuf, &strbuf[p + strlen(source)]);
-               strcpy(&strbuf[p], dest);
-               strcat(strbuf, workbuf);
-       }
-}
-
-
-void do_help_subst(char *buffer)
-{
-       char buf2[16];
-
-       help_subst(buffer, "^nodename", config.c_nodename);
-       help_subst(buffer, "^humannode", config.c_humannode);
-       help_subst(buffer, "^fqdn", config.c_fqdn);
-       help_subst(buffer, "^username", CC->user.fullname);
-       snprintf(buf2, sizeof buf2, "%ld", CC->user.usernum);
-       help_subst(buffer, "^usernum", buf2);
-       help_subst(buffer, "^sysadm", config.c_sysadm);
-       help_subst(buffer, "^variantname", CITADEL);
-       snprintf(buf2, sizeof buf2, "%d", config.c_maxsessions);
-       help_subst(buffer, "^maxsessions", buf2);
-       help_subst(buffer, "^bbsdir", ctdl_message_dir);
-}
-
-
-
 /*
  * memfmout()  -  Citadel text formatter and paginator.
  *          Although the original purpose of this routine was to format
@@ -1145,7 +1337,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));
@@ -1211,6 +1403,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;
@@ -1240,13 +1433,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'));
 
@@ -1257,21 +1452,20 @@ 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 ( (CM_IsEmpty(ret, eMesageText)) && (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 (CM_IsEmpty(ret, eMesageText)) {
+               CM_SetField(ret, eMesageText, HKEY("\r\n\r\n (no text)\r\n"));
        }
 
        /* Perform "before read" hooks (aborting if any return nonzero) */
        if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) {
-               CtdlFreeMessage(ret);
+               CM_Free(ret);
                return NULL;
        }
 
@@ -1279,46 +1473,6 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body)
 }
 
 
-/*
- * Returns 1 if the supplied pointer points to a valid Citadel message.
- * If the pointer is NULL or the magic number check fails, returns 0.
- */
-int is_valid_message(struct CtdlMessage *msg) {
-       if (msg == NULL)
-               return 0;
-       if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
-               struct CitContext *CCC = CC;
-               MSGM_syslog(LOG_WARNING, "is_valid_message() -- self-check failed\n");
-               return 0;
-       }
-       return 1;
-}
-
-void CtdlFreeMessageContents(struct CtdlMessage *msg)
-{
-       int i;
-
-       for (i = 0; i < 256; ++i)
-               if (msg->cm_fields[i] != NULL) {
-                       free(msg->cm_fields[i]);
-               }
-
-       msg->cm_magic = 0;      /* just in case */
-}
-/*
- * 'Destructor' for struct CtdlMessage
- */
-void CtdlFreeMessage(struct CtdlMessage *msg)
-{
-       if (is_valid_message(msg) == 0) 
-       {
-               if (msg != NULL) free (msg);
-               return;
-       }
-       CtdlFreeMessageContents(msg);
-       free(msg);
-}
-
 
 /*
  * Pre callback function for multipart/alternative
@@ -1666,11 +1820,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;
 
@@ -1739,12 +1895,23 @@ 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
                );
-               CtdlFreeMessage(TheMessage);
+
+               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;
+               }
+               CM_Free(TheMessage);
                TheMessage = NULL;
 
                if (encap.msg) {
@@ -1757,19 +1924,34 @@ 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);
-       CtdlFreeMessage(TheMessage);
+       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;
+       }
+
+       CM_Free(TheMessage);
 
        return(retcode);
 }
@@ -1796,7 +1978,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);
@@ -1930,16 +2112,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 (!CM_IsEmpty(TheMessage, eAuthor)) {
+               strcpy(buf, TheMessage->cm_fields[eAuthor]);
                if (TheMessage->cm_anon_type == MES_ANONONLY) {
                        safestrncpy(display_name, "****", sizeof display_name);
                }
@@ -1963,35 +2144,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) {
-                               suppress_f = 1;
-                       }
+       if (!CM_IsEmpty(TheMessage, eNodeName) &&
+           (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 ( (!CM_IsEmpty(TheMessage, Field))
+                            && (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]
                                                );
                                }
                        }
@@ -2022,7 +2205,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);
                        }
@@ -2036,7 +2219,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') {
@@ -2054,7 +2237,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",
@@ -2098,7 +2281,7 @@ void OutputRFC822MsgHeaders(
                                        }
                                }
                        }
-                       else if (i == 'K') {
+                       else if (i == eReplyTo) {
                                hptr = mptr;
                                while ((*hptr != '\0') && isspace(*hptr))
                                        hptr ++;
@@ -2130,7 +2313,7 @@ void Dump_RFC822HeadersBody(
        int nllen = strlen(nl);
        char *mptr;
 
-       mptr = TheMessage->cm_fields['M'];
+       mptr = TheMessage->cm_fields[eMesageText];
 
 
        prev_ch = '\0';
@@ -2190,7 +2373,6 @@ void Dump_RFC822HeadersBody(
        }
        if (outlen > 0) {
                client_write(outbuf, outlen);
-               outlen = 0;
        }
 }
 
@@ -2212,7 +2394,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");
@@ -2304,7 +2486,7 @@ int CtdlOutputPreLoadedMsg(
        strcpy(mid, "unknown");
        nl = (crlf ? "\r\n" : "\n");
 
-       if (!is_valid_message(TheMessage)) {
+       if (!CM_IsValidMsg(TheMessage)) {
                MSGM_syslog(LOG_ERR,
                            "ERROR: invalid preloaded message for output\n");
                cit_backtrace ();
@@ -2314,8 +2496,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) && (!CM_IsEmpty(TheMessage, eenVelopeTo)) ) {
+               memset(TheMessage->cm_fields[eenVelopeTo], ' ', strlen(TheMessage->cm_fields[eenVelopeTo]));
        }
                
        /* Are we downloading a MIME component? */
@@ -2330,7 +2512,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
@@ -2357,7 +2539,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
                         */
@@ -2455,7 +2637,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),
@@ -2496,7 +2678,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");
@@ -2549,7 +2731,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;
 }
 
@@ -2565,7 +2747,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);
 }
 
 
@@ -2594,7 +2776,7 @@ void cmd_msg3(char *cmdbuf)
        }
 
        serialize_message(&smr, msg);
-       CtdlFreeMessage(msg);
+       CM_Free(msg);
 
        if (smr.len == 0) {
                cprintf("%d Unable to serialize message\n",
@@ -2619,7 +2801,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);
 }
 
 
@@ -2652,7 +2834,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);
 }                      
 
 
@@ -2668,8 +2850,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);
+}
 
 
 /*
@@ -2761,6 +2943,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;
@@ -2800,13 +2984,13 @@ 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 (!CM_IsEmpty(msg, eExclusiveID)) {
+                                       index_message_by_euid(msg->cm_fields[eExclusiveID], &CCC->room, msgid);
                                }
 
                                /* Free up the memory we may have allocated */
                                if (msg != supplied_msg) {
-                                       CtdlFreeMessage(msg);
+                                       CM_Free(msg);
                                }
                        }
        
@@ -2864,29 +3048,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 (CM_IsEmpty(msg, emessageId)) {
+               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 (!CM_IsEmpty(msg, eMesageText)) {
+               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;
                }
        }
 
@@ -2894,7 +3079,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) {
@@ -2944,12 +3129,14 @@ 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
         */
-       if (is_valid_message(msg) == 0) {
+       if (CM_IsValidMsg(msg) == 0) {
                MSGM_syslog(LOG_ERR, "serialize_message() aborting due to invalid message\n");
                ret->len = 0;
                ret->ser = NULL;
@@ -2957,9 +3144,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) {
@@ -2975,12 +3165,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);
@@ -2990,47 +3187,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.
@@ -3046,12 +3202,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 (CM_IsEmpty(msg, 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, "");
@@ -3070,7 +3226,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];
@@ -3098,41 +3253,40 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
        int rv = 0;
 
        MSGM_syslog(LOG_DEBUG, "CtdlSubmitMsg() called\n");
-       if (is_valid_message(msg) == 0) return(-1);     /* self check */
+       if (CM_IsValidMsg(msg) == 0) return(-1);        /* self check */
 
        /* 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 (CM_IsEmpty(msg, eTimestamp)) {
+               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 (CM_IsEmpty(msg, eMessagePath)) {
+               if (!CM_IsEmpty(msg, eAuthor)) {
+                       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 (CM_IsEmpty(msg, eMesageText)) {
                MSGM_syslog(LOG_ERR, "ERROR: attempt to save message with NULL body\n");
                return(-2);
        }
@@ -3146,7 +3300,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);
@@ -3196,8 +3350,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 (CM_IsEmpty(msg, eOriginalRoom)) {
+               CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname));
        }
 
        /* Perform "before save" hooks (aborting if any return nonzero) */
@@ -3292,7 +3446,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
@@ -3300,8 +3454,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) {
@@ -3312,13 +3468,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));
@@ -3326,13 +3486,13 @@ 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);
+                                       CM_Free(imsg);
                                }
                        }
                        else {
@@ -3343,12 +3503,10 @@ 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);
+
+       CM_SetFieldLONG(msg, eVltMsgNum, newmsgid);
        PerformMessageHooks(msg, EVT_AFTERSAVE);
-       free(msg->cm_fields['3']);
-       msg->cm_fields['3'] = NULL;
+       CM_FlushField(msg, eVltMsgNum);
 
        /* 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
@@ -3363,12 +3521,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) {
@@ -3388,10 +3546,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 */
@@ -3447,12 +3605,12 @@ 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);
+               CM_Free(imsg);
        }
 
        /*
@@ -3478,7 +3636,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        /*
         * Determine whether this message qualifies for journaling.
         */
-       if (msg->cm_fields['J'] != NULL) {
+       if (!CM_IsEmpty(msg, eJournal)) {
                qualified_for_journaling = 0;
        }
        else {
@@ -3512,18 +3670,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;
@@ -3535,35 +3691,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);
+       CM_Free(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);
+}
 
 
 /*
@@ -3572,7 +3791,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 */
@@ -3589,8 +3808,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? */
@@ -3653,7 +3871,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 */
@@ -3676,8 +3894,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 == '.')) {
@@ -3810,7 +4027,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 */
@@ -3836,7 +4053,7 @@ char *CtdlReadMessageBody(char *terminator,       /* token signalling EOT */
  * (NOTE: if you supply 'preformatted_text', the buffer you give it
  * will become part of the message.  This means you are no longer
  * responsible for managing that memory -- it will be freed along with
- * the rest of the fields when CtdlFreeMessage() is called.)
+ * the rest of the fields when CM_Free() is called.)
  */
 
 struct CtdlMessage *CtdlMakeMessage(
@@ -3875,16 +4092,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);
@@ -3893,34 +4110,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) {
@@ -3936,40 +4153,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
@@ -4004,7 +4216,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);
 
@@ -4060,7 +4272,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! */
@@ -4169,11 +4381,11 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                ++num_recps;
 
                strcpy(org_recp, this_recp);
+               alias(this_recp);
+               alias(this_recp);
                mailtype = alias(this_recp);
-               mailtype = 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] = ' ';
                        }
@@ -4386,7 +4598,6 @@ void cmd_ent0(char *entargs)
        char subject[SIZ];
        int subject_required = 0;
        int do_confirm = 0;
-       int verbose_reply = 0;
        long msgnum;
        int i, j;
        char buf[256];
@@ -4405,7 +4616,6 @@ void cmd_ent0(char *entargs)
        do_confirm = extract_int(entargs, 6);
        extract_token(cc, entargs, 7, '|', sizeof cc);
        extract_token(bcc, entargs, 8, '|', sizeof bcc);
-       verbose_reply = extract_int(entargs, 9);
        switch(CC->room.QRdefaultview) {
        case VIEW_NOTES:
        case VIEW_WIKI:
@@ -4653,39 +4863,33 @@ 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) {
                msgnum = CtdlSubmitMsg(msg, valid, "", QP_EADDR);
-               if (verbose_reply)
-               {
-                       if (StrLength(CCC->StatusMessage)>0)
-                       {
-                               StrBufAppendBufPlain(CCC->StatusMessage, HKEY("\n000\n"), 0);
-                               cputbuf(CCC->StatusMessage);
-                       }
-                       else
-                               client_write(HKEY("\n000\n"));
-               }
-
                if (do_confirm) {
                        cprintf("%ld\n", msgnum);
-                       if (msgnum >= 0L) {
+
+                       if (StrLength(CCC->StatusMessage) > 0) {
+                               cprintf("%s\n", ChrPtr(CCC->StatusMessage));
+                       }
+                       else if (msgnum >= 0L) {
                                client_write(HKEY("Message accepted.\n"));
                        }
                        else {
                                client_write(HKEY("Internal error.\n"));
                        }
-                       if (msg->cm_fields['E'] != NULL) {
-                               cprintf("%s\n", msg->cm_fields['E']);
+
+                       if (!CM_IsEmpty(msg, eExclusiveID)) {
+                               cprintf("%s\n", msg->cm_fields[eExclusiveID]);
                        } else {
                                cprintf("\n");
                        }
                        cprintf("000\n");
                }
 
-               CtdlFreeMessage(msg);
+               CM_Free(msg);
        }
        if (valid != NULL) {
                free_recipients(valid);
@@ -4723,12 +4927,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 */
@@ -4743,7 +4947,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;
 
@@ -4755,7 +4959,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);
                }
 */
@@ -4763,7 +4967,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
@@ -4774,6 +4977,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;
                                }
@@ -4802,7 +5009,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);
                }
 */
@@ -4834,7 +5041,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);
 }
@@ -4951,7 +5158,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 */
@@ -5343,13 +5550,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) {
@@ -5367,7 +5574,7 @@ void CtdlWriteObject(char *req_room,                      /* Room to stuff it in */
        }
        /* Now write the data */
        CtdlSubmitMsg(msg, NULL, roomname, 0);
-       CtdlFreeMessage(msg);
+       CM_Free(msg);
 }
 
 
@@ -5408,8 +5615,8 @@ char *CtdlGetSysConfig(char *sysconfname) {
        else {
                msg = CtdlFetchMessage(msgnum, 1);
                if (msg != NULL) {
-                       conf = strdup(msg->cm_fields['M']);
-                       CtdlFreeMessage(msg);
+                       conf = strdup(msg->cm_fields[eMesageText]);
+                       CM_Free(msg);
                }
                else {
                        conf = NULL;