Move Message struct manipulation functions into one place
[citadel.git] / citadel / msgbase.c
index 6320144f4c58203a947ed5c6c4ee24ef8538bba6..30d83361f3b4ab073476b615ae3f10a045359655 100644 (file)
@@ -116,6 +116,242 @@ char *msgkeys[] = {
        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 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);
+}
+
+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;
+}
+
+
+
 /*
  * This function is self explanatory.
  * (What can I say, I'm in a weird mood today...)
@@ -280,11 +516,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'] : "")
+               (!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);
 }
@@ -304,8 +540,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"));
+               (!CM_IsEmpty(msg, eExclusiveID) ? msg->cm_fields[eExclusiveID] : ""),
+               (!CM_IsEmpty(msg, eTimestamp) ? msg->cm_fields[eTimestamp] : "0"));
        CtdlFreeMessage(msg);
 }
 
@@ -894,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);
                                }
                        }
                }
@@ -1138,7 +1374,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));
@@ -1204,6 +1440,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;
@@ -1233,13 +1470,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'));
 
@@ -1250,16 +1489,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 ( (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) */
@@ -1272,46 +1510,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
@@ -1734,7 +1932,7 @@ 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
@@ -1742,13 +1940,13 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
 
                if ((Author != NULL) && (*Author == NULL))
                {
-                       *Author = TheMessage->cm_fields['A'];
-                       TheMessage->cm_fields['A'] = NULL;
+                       *Author = TheMessage->cm_fields[eAuthor];
+                       TheMessage->cm_fields[eAuthor] = NULL;
                }
                if ((Address != NULL) && (*Address == NULL))
                {       
-                       *Address = TheMessage->cm_fields['F'];
-                       TheMessage->cm_fields['F'] = NULL;
+                       *Address = TheMessage->cm_fields[erFc822Addr];
+                       TheMessage->cm_fields[erFc822Addr] = NULL;
                }
                CtdlFreeMessage(TheMessage);
                TheMessage = NULL;
@@ -1763,7 +1961,6 @@ 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) {
@@ -1782,13 +1979,13 @@ int CtdlOutputMsg(long msg_num,         /* message number (local) to fetch */
                retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf, flags);
        if ((Author != NULL) && (*Author == NULL))
        {
-               *Author = TheMessage->cm_fields['A'];
-               TheMessage->cm_fields['A'] = NULL;
+               *Author = TheMessage->cm_fields[eAuthor];
+               TheMessage->cm_fields[eAuthor] = NULL;
        }
        if ((Address != NULL) && (*Address == NULL))
        {       
-               *Address = TheMessage->cm_fields['F'];
-               TheMessage->cm_fields['F'] = NULL;
+               *Address = TheMessage->cm_fields[erFc822Addr];
+               TheMessage->cm_fields[erFc822Addr] = NULL;
        }
 
        CtdlFreeMessage(TheMessage);
@@ -1952,16 +2149,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);
                }
@@ -1985,35 +2181,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]
                                                );
                                }
                        }
@@ -2044,7 +2242,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);
                        }
@@ -2058,7 +2256,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') {
@@ -2076,7 +2274,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",
@@ -2120,7 +2318,7 @@ void OutputRFC822MsgHeaders(
                                        }
                                }
                        }
-                       else if (i == 'K') {
+                       else if (i == eReplyTo) {
                                hptr = mptr;
                                while ((*hptr != '\0') && isspace(*hptr))
                                        hptr ++;
@@ -2152,7 +2350,7 @@ void Dump_RFC822HeadersBody(
        int nllen = strlen(nl);
        char *mptr;
 
-       mptr = TheMessage->cm_fields['M'];
+       mptr = TheMessage->cm_fields[eMesageText];
 
 
        prev_ch = '\0';
@@ -2233,7 +2431,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");
@@ -2335,8 +2533,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? */
@@ -2351,7 +2549,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
@@ -2378,7 +2576,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
                         */
@@ -2476,7 +2674,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),
@@ -2517,7 +2715,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");
@@ -2823,8 +3021,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 (!CM_IsEmpty(msg, eExclusiveID)) {
+                                       index_message_by_euid(msg->cm_fields[eExclusiveID], &CCC->room, msgid);
                                }
 
                                /* Free up the memory we may have allocated */
@@ -2887,29 +3085,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;
                }
        }
 
@@ -2917,7 +3116,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) {
@@ -2967,7 +3166,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
@@ -2980,9 +3181,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) {
@@ -2998,12 +3202,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);
@@ -3028,12 +3239,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, "");
@@ -3052,7 +3263,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];
@@ -3085,36 +3295,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 (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);
        }
@@ -3128,7 +3337,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);
@@ -3178,8 +3387,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) */
@@ -3274,7 +3483,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
@@ -3282,8 +3491,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) {
@@ -3294,13 +3505,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));
@@ -3308,11 +3523,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);
                                }
@@ -3325,12 +3540,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
@@ -3345,12 +3558,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) {
@@ -3370,10 +3583,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 */
@@ -3429,10 +3642,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);
        }
@@ -3460,7 +3673,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 {
@@ -3499,7 +3712,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
  */
 void quickie_message(const char *from,
                     const char *fromaddr,
-                    char *to,
+                    const char *to,
                     char *room,
                     const char *text, 
                     int format_type,
@@ -3515,29 +3728,29 @@ void quickie_message(const char *from,
        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);
@@ -3546,25 +3759,25 @@ void quickie_message(const char *from,
 
 void flood_protect_quickie_message(const char *from,
                                   const char *fromaddr,
-                                  char *to,
+                                  const char *to,
                                   char *room,
                                   const char *text, 
                                   int format_type,
                                   const char *subject,
                                   int nCriterions,
                                   const char **CritStr,
-                                  long *CritStrLen)
+                                  long *CritStrLen,
+                                  long ccid,
+                                  long ioid,
+                                  time_t NOW)
 {
        int i;
-       struct UseTable ut;
        u_char rawdigest[MD5_DIGEST_LEN];
        struct MD5Context md5context;
        StrBuf *guid;
-       struct cdbdata *cdbut;
        char timestamp[64];
        long tslen;
-       time_t ts = time(NULL);
-       time_t tsday = ts / (8*60*60); /* just care for a day... */
+       time_t tsday = NOW / (8*60*60); /* just care for a day... */
 
        tslen = snprintf(timestamp, sizeof(timestamp), "%ld", tsday);
        MD5Init(&md5context);
@@ -3582,27 +3795,22 @@ void flood_protect_quickie_message(const char *from,
        StrBufAppendBufPlain(guid, HKEY("_fldpt"), 0);
        if (StrLength(guid) > 40)
                StrBufCutAt(guid, 40, NULL);
-       /* Find out if we've already sent a similar message */
-       memcpy(ut.ut_msgid, SKEY(guid));
-       ut.ut_timestamp = ts;
-
-       cdbut = cdb_fetch(CDB_USETABLE, SKEY(guid));
 
-       if (cdbut != 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");
-               cdb_free(cdbut);
+               return;
        }
-
-       /* rewrite the record anyway, to update the timestamp */
-       cdb_store(CDB_USETABLE,
-                 SKEY(guid),
-                 &ut, sizeof(struct UseTable) );
-       
        FreeStrBuf(&guid);
-
-       if (cdbut != NULL) return;
        /* no, this message isn't sent recently; go ahead. */
        quickie_message(from,
                        fromaddr,
@@ -3921,16 +4129,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);
@@ -3939,34 +4147,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) {
@@ -3982,30 +4190,30 @@ 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);
@@ -4692,7 +4900,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) {
@@ -4710,8 +4918,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 (!CM_IsEmpty(msg, eExclusiveID)) {
+                               cprintf("%s\n", msg->cm_fields[eExclusiveID]);
                        } else {
                                cprintf("\n");
                        }
@@ -4756,12 +4964,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 */
@@ -4788,7 +4996,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);
                }
 */
@@ -4796,7 +5004,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
@@ -4807,6 +5014,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;
                                }
@@ -4835,7 +5046,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);
                }
 */
@@ -4867,7 +5078,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);
 }
@@ -5376,13 +5587,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) {
@@ -5441,7 +5652,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 {