fix dlen
[citadel.git] / citadel / server / msgbase.c
index 8a709492334a0d88cb3c586efefdb81b21964c8c..aaf4446c604f07f6b434bffa7dc905fd26e84109 100644 (file)
@@ -52,7 +52,7 @@ char *msgkeys[] = {
        "jrnl", // J -> eJournal
        "rep2", // K -> eReplyTo
        "list", // L -> eListID
-       "text", // M -> eMesageText
+       "text", // M -> eMessageText
        NULL,   // N (formerly used as eNodename)
        "room", // O -> eOriginalRoom
        "path", // P -> eMessagePath
@@ -121,7 +121,7 @@ eMsgField FieldOrder[]  = {
        eSuppressIdx ,
        eExtnotify   ,
 // Message text (MUST be last)
-       eMesageText 
+       eMessageText 
 // Not saved to disk: 
 //     eVltMsgNum
 //
@@ -1102,14 +1102,14 @@ 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 ( (CM_IsEmpty(ret, eMesageText)) && (with_body) ) {
+       if ( (CM_IsEmpty(ret, eMessageText)) && (with_body) ) {
                dmsgtext = cdb_fetch(CDB_BIGMSGS, &msgnum, sizeof(long));
                if (dmsgtext.ptr != NULL) {
-                       CM_SetField(ret, eMesageText, dmsgtext.ptr);
+                       CM_SetField(ret, eMessageText, dmsgtext.ptr);
                }
        }
-       if (CM_IsEmpty(ret, eMesageText)) {
-               CM_SetField(ret, eMesageText, "\r\n\r\n (no text)\r\n");
+       if (CM_IsEmpty(ret, eMessageText)) {
+               CM_SetField(ret, eMessageText, "\r\n\r\n (no text)\r\n");
        }
 
        return (ret);
@@ -1416,14 +1416,14 @@ int check_cached_msglist(long msgnum) {
 // Get a message off disk.  (returns om_* values found in msgbase.h)
 int CtdlOutputMsg(long msg_num,                // message number (local) to fetch
                int mode,               // how would you like that message?
-               int headers_only,       // eschew the message body?
+               int headers_only,       // If nonzero, skip the message body.  Also avoids loading it, if it's stored separately.
                int do_proto,           // do Citadel protocol responses?
-               int crlf,               // Use CRLF newlines instead of LF?
+               int crlf,               // If nonzero, terminate lines with CRLF instead of just LF
                char *section,          // NULL or a message/rfc822 section
                int flags,              // various flags; see msgbase.h
-               char **Author,
-               char **Address,
-               char **MessageID
+               char **Author,          // If non-NULL, allocate a string buffer and populate the display name (caller must free)
+               char **Address,         // If non-NULL, allocate a string buffer and populate the email address (caller must free)
+               char **MessageID        // If non-NULL, allocate a string buffer and populate the message ID (caller must free)
 ) {
        struct CtdlMessage *TheMessage = NULL;
        int retcode = CIT_OK;
@@ -1456,15 +1456,10 @@ int CtdlOutputMsg(long msg_num,         // message number (local) to fetch
                r = check_cached_msglist(msg_num);
        }
        if (r != om_ok) {
-               syslog(LOG_DEBUG, "msgbase: security check fail; message %ld is not in %s",
-                          msg_num, CC->room.QRname
-               );
+               syslog(LOG_DEBUG, "msgbase: security check fail; message %ld is not in %s", msg_num, CC->room.QRname);
                if (do_proto) {
                        if (r == om_access_denied) {
-                               cprintf("%d message %ld was not found in this room\n",
-                                       ERROR + HIGHER_ACCESS_REQUIRED,
-                                       msg_num
-                               );
+                               cprintf("%d message %ld was not found in this room\n", ERROR + HIGHER_ACCESS_REQUIRED, msg_num);
                        }
                }
                return(r);
@@ -1479,8 +1474,7 @@ int CtdlOutputMsg(long msg_num,           // message number (local) to fetch
        }
 
        if (TheMessage == NULL) {
-               if (do_proto) cprintf("%d Can't locate msg %ld on disk\n",
-                       ERROR + MESSAGE_NOT_FOUND, msg_num);
+               if (do_proto) cprintf("%d Can't locate msg %ld on disk\n", ERROR + MESSAGE_NOT_FOUND, msg_num);
                return(om_no_such_msg);
        }
 
@@ -1488,22 +1482,19 @@ 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(CM_RANGE(TheMessage, eMesageText),
+               mime_parser(CM_RANGE(TheMessage, eMessageText),
                            *extract_encapsulated_message,
                            NULL, NULL, (void *)&encap, 0
                        );
 
                if ((Author != NULL) && (*Author == NULL)) {
-                       long len;
-                       CM_GetAsField(TheMessage, eAuthor, Author, &len);
+                       *Author = strdup(TheMessage->cm_fields[eAuthor]);
                }
                if ((Address != NULL) && (*Address == NULL)) {  
-                       long len;
-                       CM_GetAsField(TheMessage, erFc822Addr, Address, &len);
+                       *Address = strdup(TheMessage->cm_fields[erFc822Addr]);
                }
                if ((MessageID != NULL) && (*MessageID == NULL)) {      
-                       long len;
-                       CM_GetAsField(TheMessage, emessageId, MessageID, &len);
+                       *MessageID = strdup(TheMessage->cm_fields[emessageId]);
                }
                CM_Free(TheMessage);
                TheMessage = NULL;
@@ -1566,13 +1557,13 @@ void OutputCtdlMsgHeaders(struct CtdlMessage *TheMessage, int do_proto) {
                else {
                        safestrncpy(display_name, buf, sizeof display_name);
                }
-               if ((is_room_aide())
-                   && ((TheMessage->cm_anon_type == MES_ANONONLY)
-                       || (TheMessage->cm_anon_type == MES_ANONOPT))) {
+               if (    (is_room_aide())
+                       && (    (TheMessage->cm_anon_type == MES_ANONONLY)
+                               || (TheMessage->cm_anon_type == MES_ANONOPT)
+                       )
+               ) {
                        size_t tmp = strlen(display_name);
-                       snprintf(&display_name[tmp],
-                                sizeof display_name - tmp,
-                                " [%s]", buf);
+                       snprintf(&display_name[tmp], sizeof display_name - tmp, " [%s]", buf);
                }
        }
 
@@ -1580,7 +1571,7 @@ void OutputCtdlMsgHeaders(struct CtdlMessage *TheMessage, int do_proto) {
        for (i=0; i< NDiskFields; ++i) {
                eMsgField Field;
                Field = FieldOrder[i];
-               if (Field != eMesageText) {
+               if (Field != eMessageText) {
                        if ( (!CM_IsEmpty(TheMessage, Field)) && (msgkeys[Field] != NULL) ) {
                                if ((Field == eenVelopeTo) || (Field == eRecipient) || (Field == eCarbonCopY)) {
                                        sanitize_truncated_recipient(TheMessage->cm_fields[Field]);
@@ -1712,7 +1703,7 @@ void OutputRFC822MsgHeaders(
 
                        case eExclusiveID:
                        case eJournal:
-                       case eMesageText:
+                       case eMessageText:
                        case eBig_message:
                        case eOriginalRoom:
                        case eErrorMsg:
@@ -1748,7 +1739,7 @@ void Dump_RFC822HeadersBody(
        char *mptr;
        int lfSent = 0;
 
-       mptr = TheMessage->cm_fields[eMesageText];
+       mptr = TheMessage->cm_fields[eMessageText];
 
        prev_ch = '\0';
        while (*mptr != '\0') {
@@ -1756,14 +1747,12 @@ void Dump_RFC822HeadersBody(
                        // do nothing
                }
                else {
-                       if ((!eoh) &&
-                           (*mptr == '\n'))
-                       {
+                       if ((!eoh) && (*mptr == '\n')) {
                                eoh = (*(mptr+1) == '\r') && (*(mptr+2) == '\n');
-                               if (!eoh)
+                               if (!eoh) {
                                        eoh = *(mptr+1) == '\n';
-                               if (eoh)
-                               {
+                               }
+                               if (eoh) {
                                        StartOfText = mptr;
                                        StartOfText = strchr(StartOfText, '\n');
                                        StartOfText = strchr(StartOfText, '\n');
@@ -1823,7 +1812,7 @@ void DumpFormatFixed(
        int xlline = 0;
        char *mptr;
 
-       mptr = TheMessage->cm_fields[eMesageText];
+       mptr = TheMessage->cm_fields[eMessageText];
        
        if (mode == MT_MIME) {
                cprintf("Content-type: text/plain\n\n");
@@ -1937,7 +1926,7 @@ int CtdlOutputPreLoadedMsg(
                }
                else {
                        // Parse the message text component
-                       mime_parser(CM_RANGE(TheMessage, eMesageText), *mime_download, NULL, NULL, NULL, 0);
+                       mime_parser(CM_RANGE(TheMessage, eMessageText), *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
                        if (CC->download_fp == NULL) {
@@ -1960,7 +1949,7 @@ int CtdlOutputPreLoadedMsg(
                else {
                        // Locate and parse the component specified by the caller
                        int found_it = 0;
-                       mime_parser(CM_RANGE(TheMessage, eMesageText), *mime_spew_section, NULL, NULL, (void *)&found_it, 0);
+                       mime_parser(CM_RANGE(TheMessage, eMessageText), *mime_spew_section, NULL, NULL, (void *)&found_it, 0);
 
                        // If section wasn't found, print an error
                        if (!found_it) {
@@ -1984,10 +1973,10 @@ int CtdlOutputPreLoadedMsg(
        }
 
        // nhdr=yes means that we're only displaying headers, no body
-       if ( (TheMessage->cm_anon_type == MES_ANONONLY)
-          && ((mode == MT_CITADEL) || (mode == MT_MIME))
-          && (do_proto)
-          ) {
+       if (    (TheMessage->cm_anon_type == MES_ANONONLY)
+               && ((mode == MT_CITADEL) || (mode == MT_MIME))
+               && (do_proto)
+       ) {
                cprintf("nhdr=yes\n");
        }
 
@@ -2019,12 +2008,13 @@ int CtdlOutputPreLoadedMsg(
        }
 
        if (mode == MT_RFC822) {
-               // Construct a fun message id
-               cprintf("Message-ID: <%s", mid);
-               if (strchr(mid, '@')==NULL) {
-                       cprintf("@%s", snode);
-               }
-               cprintf(">%s", nl);
+               // Make the message ID RFC2822 compliant
+               cprintf("Message-ID: <%s%s%s>%s",               // put it in angle brackets
+                       mid,
+                       (strchr(mid, '@') ? "" : "@"),          // if there is no domain part,
+                       (strchr(mid, '@') ? "" : snode),        // tack on ours.
+                       nl
+               );
 
                if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONONLY)) {
                        cprintf("From: \"----\" <x@x.org>%s", nl);
@@ -2053,18 +2043,14 @@ START_TEXT:
        if (TheMessage->cm_format_type == FMT_RFC822) {
                if ( (mode == MT_CITADEL) || (mode == MT_MIME) ) {
                        memset(&ma, 0, sizeof(struct ma_info));
-                       mime_parser(CM_RANGE(TheMessage, eMesageText),
+                       mime_parser(CM_RANGE(TheMessage, eMessageText),
                                (do_proto ? *list_this_part : NULL),
                                (do_proto ? *list_this_pref : NULL),
                                (do_proto ? *list_this_suff : NULL),
                                (void *)&ma, 1);
                }
                else if (mode == MT_RFC822) {   // unparsed RFC822 dump
-                       Dump_RFC822HeadersBody(
-                               TheMessage,
-                               headers_only,
-                               flags,
-                               nl, nlen);
+                       Dump_RFC822HeadersBody(TheMessage, headers_only, flags, nl, nlen);
                        goto DONE;
                }
        }
@@ -2078,11 +2064,9 @@ START_TEXT:
                if (do_proto) cprintf("text\n");
        }
 
-       if (TheMessage->cm_format_type == FMT_FIXED) 
-               DumpFormatFixed(
-                       TheMessage,
-                       mode,           // how would you like that message?
-                       nl, nlen);
+       if (TheMessage->cm_format_type == FMT_FIXED) {
+               DumpFormatFixed( TheMessage, mode, nl, nlen);
+       }
 
        // If the message on disk is format 0 (Citadel vari-format), we
        // output using the formatter at 80 columns.  This is the final output
@@ -2095,7 +2079,7 @@ START_TEXT:
                if (mode == MT_MIME) {
                        cprintf("Content-type: text/x-citadel-variformat\n\n");
                }
-               memfmout(TheMessage->cm_fields[eMesageText], nl);
+               memfmout(TheMessage->cm_fields[eMessageText], nl);
        }
 
        // If the message on disk is format 4 (MIME), we've gotta hand it
@@ -2111,15 +2095,15 @@ START_TEXT:
                        strcpy(ma.chosen_part, "1");
                        ma.chosen_pref = 9999;
                        ma.dont_decode = CC->msg4_dont_decode;
-                       mime_parser(CM_RANGE(TheMessage, eMesageText),
+                       mime_parser(CM_RANGE(TheMessage, eMessageText),
                                    *choose_preferred, *fixed_output_pre,
                                    *fixed_output_post, (void *)&ma, 1);
-                       mime_parser(CM_RANGE(TheMessage, eMesageText),
+                       mime_parser(CM_RANGE(TheMessage, eMessageText),
                                    *output_preferred, NULL, NULL, (void *)&ma, 1);
                }
                else {
                        ma.use_fo_hooks = 1;
-                       mime_parser(CM_RANGE(TheMessage, eMesageText),
+                       mime_parser(CM_RANGE(TheMessage, eMessageText),
                                    *fixed_output, *fixed_output_pre,
                                    *fixed_output_post, (void *)&ma, 0);
                }
@@ -2131,18 +2115,19 @@ DONE:   // now we're done
        return(om_ok);
 }
 
+
 // Save one or more message pointers into a specified room
 // (Returns 0 for success, nonzero for failure)
 // roomname may be NULL to use the current room
 //
-// Note that the 'supplied_msg' field may be set to NULL, in which case
+// Note that the 'msg_in' field may be set to NULL, in which case
 // the message will be fetched from disk, by number, if we need to perform
 // replication checks.  This adds an additional database read, so if the
 // caller already has the message in memory then it should be supplied.  (Obviously
 // this mode of operation only works if we're saving a single message.)
 //
 int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newmsgs,
-                       int do_repl_check, struct CtdlMessage *supplied_msg, int suppress_refcount_adj
+                       int do_repl_check, struct CtdlMessage *msg_in, int suppress_refcount_adj
 ) {
        int i, j, unique;
        char hold_rm[ROOMNAMELEN];
@@ -2167,7 +2152,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
        // Sanity checks
        if (newmsgidlist == NULL) return(ERROR + INTERNAL_ERROR);
        if (num_newmsgs < 1) return(ERROR + INTERNAL_ERROR);
-       if (num_newmsgs > 1) supplied_msg = NULL;
+       if (num_newmsgs > 1) msg_in = NULL;
 
        // Now the regular stuff
        if (CtdlGetRoomLock(&CC->room, ((roomname != NULL) ? roomname : CC->room.QRname) ) != 0) {
@@ -2230,8 +2215,8 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
                for (i=0; i<num_msgs_to_be_merged; ++i) {
                        msgid = msgs_to_be_merged[i];
        
-                       if (supplied_msg != NULL) {
-                               msg = supplied_msg;
+                       if (msg_in != NULL) {
+                               msg = msg_in;
                        }
                        else {
                                msg = CtdlFetchMessage(msgid, 0);
@@ -2246,7 +2231,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
                                }
 
                                // Free up the memory we may have allocated
-                               if (msg != supplied_msg) {
+                               if (msg != msg_in) {
                                        CM_Free(msg);
                                }
                        }
@@ -2283,8 +2268,8 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
 
 
 // This is the same as CtdlSaveMsgPointersInRoom() but it only accepts a single message.
-int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check, struct CtdlMessage *supplied_msg) {
-       return CtdlSaveMsgPointersInRoom(roomname, &msgid, 1, do_repl_check, supplied_msg, 0);
+int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check, struct CtdlMessage *msg_in) {
+       return CtdlSaveMsgPointersInRoom(roomname, &msgid, 1, do_repl_check, msg_in, 0);
 }
 
 
@@ -2293,58 +2278,52 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int do_repl_check, stru
 //
 // This is the back end for CtdlSubmitMsg() and should not be directly
 // called by server-side modules.
-long CtdlSaveThisMessage(struct CtdlMessage *msg, long msgid, int Reply) {
-       long retval;
-       struct ser_ret smr;
-       int is_bigmsg = 0;
-       char *holdM = NULL;
-       long holdMLen = 0;
+long CtdlSaveThisMessage(struct CtdlMessage *msg, long msgid) {
+       long error_count = 0;
+
+       // Serialize our data structure for storage in the database
+       struct ser_ret smr = CtdlSerializeMessage(msg);
 
-       // If the message is big, set its body aside for storage elsewhere and we hide the message body from the serializer
-       if (!CM_IsEmpty(msg, eMesageText) && msg->cm_lengths[eMesageText] > BIGMSG) {
-               is_bigmsg = 1;
-               holdM = msg->cm_fields[eMesageText];
-               msg->cm_fields[eMesageText] = NULL;
-               holdMLen = msg->cm_lengths[eMesageText];
-               msg->cm_lengths[eMesageText] = 0;
+       if (smr.len == 0) {
+               syslog(LOG_ERR, "msgbase: CtdlSaveMessage() unable to serialize message");
+               return (-1);
        }
 
-       // Serialize our data structure for storage in the database
-       CtdlSerializeMessage(&smr, msg);
+       // STORAGE STRATEGY:
+       // * If headers+content fit are <= 4K, store them together.  It's likely to be one
+       //   memory page, one disk sector, etc. so we benefit from a single disk operation.
+       // * If headers+content exceed 4K, store them separately so we don't end up fetching
+       //   many gigamegs of data when we're just scanning the headers.
+       // * We are using a threshold of 4000 bytes so that there's some room for overhead
+       //   if the database or OS adds any metadata to that one disk page.
+
+       if (smr.len <= 4000) {                                  // all together less than one page, store them together
+
+               if (cdb_store(CDB_MSGMAIN, &msgid, (int)sizeof(long), smr.ser, smr.len)) {
+                       ++error_count;
+               }
 
-       if (is_bigmsg) {
-               // put the message body back into the message
-               msg->cm_fields[eMesageText] = holdM;
-               msg->cm_lengths[eMesageText] = holdMLen;
        }
 
-       if (smr.len == 0) {
-               if (Reply) {
-                       cprintf("%d Unable to serialize message\n", ERROR + INTERNAL_ERROR);
+       else {                                                  // exceed one page, store headers in MSGMAIN, body in BIGMSGS
+
+               if (cdb_store(CDB_MSGMAIN, &msgid, (int)sizeof(long), smr.ser, (smr.msgstart - smr.ser))) {
+                       ++error_count;
                }
-               else {
-                       syslog(LOG_ERR, "msgbase: CtdlSaveMessage() unable to serialize message");
+
+               if (cdb_store(CDB_BIGMSGS, &msgid, (int)sizeof(long), smr.msgstart+1, (smr.len - (smr.msgstart - smr.ser) - 1) )) {
+                       ++error_count;
                }
-               return (-1L);
-       }
 
-       // Write our little bundle of joy into the message base
-       retval = cdb_store(CDB_MSGMAIN, &msgid, (int)sizeof(long), smr.ser, smr.len);
-       if (retval < 0) {
-               syslog(LOG_ERR, "msgbase: can't store message %ld: %ld", msgid, retval);
        }
-       else {
-               if (is_bigmsg) {
-                       retval = cdb_store(CDB_BIGMSGS, &msgid, (int)sizeof(long), holdM, (holdMLen + 1));
-                       if (retval < 0) {
-                               syslog(LOG_ERR, "msgbase: failed to store message body for msgid %ld: %ld", msgid, retval);
-                       }
-               }
+
+       if (error_count > 0) {
+               syslog(LOG_ERR, "msgbase: encountered %ld errors storing message %ld", error_count, msgid);
        }
 
        // Free the memory we used for the serialized message
        free(smr.ser);
-       return(retval);
+       return(error_count);
 }
 
 
@@ -2359,7 +2338,7 @@ long send_message(struct CtdlMessage *msg) {
 
        // Generate an ID if we don't have one already
        if (CM_IsEmpty(msg, emessageId)) {
-               msgidbuflen = snprintf(msgidbuf, sizeof msgidbuf, "%08lX-%08lX@%s",
+               msgidbuflen = snprintf(msgidbuf, sizeof msgidbuf, "%lX-%lX@%s",
                       (long unsigned int) time(NULL),
                       (long unsigned int) newmsgid,
                       CtdlGetConfigStr("c_fqdn")
@@ -2367,7 +2346,7 @@ long send_message(struct CtdlMessage *msg) {
                CM_SetField(msg, emessageId, msgidbuf);
        }
 
-       retval = CtdlSaveThisMessage(msg, newmsgid, 1);
+       retval = CtdlSaveThisMessage(msg, newmsgid);
 
        if (retval == 0) {
                retval = newmsgid;
@@ -2380,58 +2359,59 @@ long send_message(struct CtdlMessage *msg) {
 
 // Serialize a struct CtdlMessage into the format used on disk.
 // 
-// This function loads up a "struct ser_ret" (defined in server.h) which
+// This function returns 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 CtdlSerializeMessage(struct ser_ret *ret,         // return values
-                         struct CtdlMessage *msg)      // unserialized msg
-{
+struct ser_ret CtdlSerializeMessage(struct CtdlMessage *msg) {
+       struct ser_ret ret;
        size_t wlen;
        int i;
 
+       ret.len = 0;
+       ret.ser = NULL;
+       ret.msgstart = NULL;
+
        // Check for valid message format
        if (CM_IsValidMsg(msg) == 0) {
                syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() aborting due to invalid message");
-               ret->len = 0;
-               ret->ser = NULL;
-               return;
+               return(ret);
        }
 
-       ret->len = 3;
-       for (i=0; i < NDiskFields; ++i)
-               if (msg->cm_fields[FieldOrder[i]] != NULL)
-                       ret->len += msg->cm_lengths[FieldOrder[i]] + 2;
+       ret.len = 3;
+       assert(FieldOrder[NDiskFields-1] == eMessageText);              // Message text MUST be last!
+       for (i=0; i < NDiskFields; ++i) {
+               if (msg->cm_fields[FieldOrder[i]] != NULL) {
+                       ret.len += msg->cm_lengths[FieldOrder[i]] + 2;
+               }
+       }
 
-       ret->ser = malloc(ret->len);
-       if (ret->ser == NULL) {
-               syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() malloc(%ld) failed: %m", (long)ret->len);
-               ret->len = 0;
-               ret->ser = NULL;
-               return;
+       ret.ser = malloc(ret.len);
+       if (ret.ser == NULL) {
+               syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() malloc(%ld) failed: %m", (long)ret.len);
+               ret.len = 0;
+               ret.ser = NULL;
+               ret.msgstart = NULL;
+               return(ret);
        }
 
-       ret->ser[0] = 0xFF;
-       ret->ser[1] = msg->cm_anon_type;
-       ret->ser[2] = msg->cm_format_type;
+       ret.ser[0] = 0xFF;
+       ret.ser[1] = msg->cm_anon_type;
+       ret.ser[2] = msg->cm_format_type;
        wlen = 3;
 
        for (i=0; i < NDiskFields; ++i) {
                if (msg->cm_fields[FieldOrder[i]] != NULL) {
-                       ret->ser[wlen++] = (char)FieldOrder[i];
-
-                       memcpy(&ret->ser[wlen],
-                              msg->cm_fields[FieldOrder[i]],
-                              msg->cm_lengths[FieldOrder[i]] + 1);
-
+                       if (FieldOrder[i] == eMessageText) {
+                               ret.msgstart = &ret.ser[wlen];          // Make a note where the message text begins
+                       }
+                       ret.ser[wlen++] = (char)FieldOrder[i];
+                       memcpy(&ret.ser[wlen], msg->cm_fields[FieldOrder[i]], msg->cm_lengths[FieldOrder[i]] + 1);
                        wlen = wlen + msg->cm_lengths[FieldOrder[i]] + 1;
                }
        }
 
-       if (ret->len != wlen) {
-               syslog(LOG_ERR, "msgbase: ERROR; len=%ld wlen=%ld", (long)ret->len, (long)wlen);
-       }
-
-       return;
+       assert(ret.len == wlen);                                        // Make sure we measured it correctly
+       return(ret);
 }
 
 
@@ -2509,7 +2489,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       // message to save
        }
 
        // Learn about what's inside, because it's what's inside that counts
-       if (CM_IsEmpty(msg, eMesageText)) {
+       if (CM_IsEmpty(msg, eMessageText)) {
                syslog(LOG_ERR, "msgbase: ERROR; attempt to save message with NULL body");
                return(-2);
        }
@@ -2523,20 +2503,23 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     // message to save
                break;
        case 4:
                strcpy(content_type, "text/plain");
-               mptr = bmstrcasestr(msg->cm_fields[eMesageText], "Content-type:");
+               mptr = bmstrcasestr(msg->cm_fields[eMessageText], "Content-type:");
                if (mptr != NULL) {
                        char *aptr;
                        safestrncpy(content_type, &mptr[13], sizeof content_type);
                        string_trim(content_type);
                        aptr = content_type;
                        while (!IsEmptyStr(aptr)) {
-                               if ((*aptr == ';')
-                                   || (*aptr == ' ')
-                                   || (*aptr == 13)
-                                   || (*aptr == 10)) {
+                               if (    (*aptr == ';')
+                                       || (*aptr == ' ')
+                                       || (*aptr == 13)
+                                       || (*aptr == 10)
+                               ) {
                                        *aptr = 0;
                                }
-                               else aptr++;
+                               else {
+                                       aptr++;
+                               }
                        }
                }
        }
@@ -2783,13 +2766,13 @@ long quickie_message(char *from,
        if (!IsEmptyStr(room)) CM_SetField(msg, eOriginalRoom, room);
        if (!IsEmptyStr(to)) {
                CM_SetField(msg, eRecipient, to);
-               recp = validate_recipients(to, NULL, 0);
+               recp = validate_recipients(to, 0);
        }
        if (!IsEmptyStr(subject)) {
                CM_SetField(msg, eMsgSubject, subject);
        }
        if (!IsEmptyStr(text)) {
-               CM_SetField(msg, eMesageText, text);
+               CM_SetField(msg, eMessageText, text);
        }
 
        long msgnum = CtdlSubmitMsg(msg, recp, room);
@@ -2898,7 +2881,7 @@ struct CtdlMessage *CtdlMakeMessage(
         char *fake_name,                // who we're masquerading as
        char *my_email,                 // which of my email addresses to use (empty is ok)
         char *subject,                  // Subject (optional)
-       char *supplied_euid,            // ...or NULL if this is irrelevant
+       char *euid_in,          // ...or NULL if this is irrelevant
         char *preformatted_text,        // ...or NULL to read text from client
        char *references                // Thread references
 ) {
@@ -2918,8 +2901,8 @@ struct CtdlMessage *CtdlMakeMessage(
                (my_email)?strlen(my_email): 0,
                subject,                                // Subject (optional)
                (subject)?strlen(subject): 0,
-               supplied_euid,                          // ...or NULL if this is irrelevant
-               (supplied_euid)?strlen(supplied_euid):0,
+               euid_in,                                // ...or NULL if this is irrelevant
+               (euid_in)?strlen(euid_in):0,
                preformatted_text,                      // ...or NULL to read text from client
                (preformatted_text)?strlen(preformatted_text) : 0,
                references,                             // Thread references
@@ -2949,7 +2932,7 @@ struct CtdlMessage *CtdlMakeMessageLen(
        long myelen,
        char *subject,                  // Subject (optional)
        long subjlen,
-       char *supplied_euid,            // ...or NULL if this is irrelevant
+       char *euid_in,          // ...or NULL if this is irrelevant
        long euidlen,
        char *preformatted_text,        // ...or NULL to read text from client
        long textlen,
@@ -3024,10 +3007,12 @@ struct CtdlMessage *CtdlMakeMessageLen(
                        long IsAscii;
                        IsAscii = -1;
                        i = 0;
-                       while ((subject[i] != '\0') && (IsAscii = isascii(subject[i]) != 0 ))
+                       while ((subject[i] != '\0') && (IsAscii = isascii(subject[i]) != 0 )) {
                                i++;
-                       if (IsAscii != 0)
+                       }
+                       if (IsAscii != 0) {
                                CM_SetField(msg, eMsgSubject, subject);
+                       }
                        else {  // ok, we've got utf8 in the string.
                                char *rfc2047Subj;
                                rfc2047Subj = rfc2047encode(subject, length);
@@ -3038,7 +3023,7 @@ struct CtdlMessage *CtdlMakeMessageLen(
        }
 
        if (euidlen > 0) {
-               CM_SetField(msg, eExclusiveID, supplied_euid);
+               CM_SetField(msg, eExclusiveID, euid_in);
        }
 
        if (reflen > 0) {
@@ -3046,13 +3031,13 @@ struct CtdlMessage *CtdlMakeMessageLen(
        }
 
        if (preformatted_text != NULL) {
-               CM_SetField(msg, eMesageText, preformatted_text);
+               CM_SetField(msg, eMessageText, preformatted_text);
        }
        else {
                StrBuf *MsgBody;
                MsgBody = CtdlReadMessageBodyBuf(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0);
                if (MsgBody != NULL) {
-                       CM_SetAsFieldSB(msg, eMesageText, &MsgBody);
+                       CM_SetAsFieldSB(msg, eMessageText, &MsgBody);
                }
        }
 
@@ -3081,9 +3066,9 @@ int CtdlDeleteMessages(const char *room_name,     // which room
        int need_to_free_re = 0;
 
        if (content_type) if (!IsEmptyStr(content_type)) {
-                       regcomp(&re, content_type, 0);
-                       need_to_free_re = 1;
-               }
+               regcomp(&re, content_type, 0);
+               need_to_free_re = 1;
+       }
        syslog(LOG_DEBUG, "msgbase: CtdlDeleteMessages(%s, %d msgs, %s)", room_name, num_dmsgnums, content_type);
 
        // get room record, obtaining a lock...
@@ -3207,11 +3192,7 @@ void PutMetaData(struct MetaData *smibuf) {
 
        // Use the negative of the message number for the metadata db index
        TheIndex = (0L - smibuf->meta_msgnum);
-
-       cdb_store(CDB_MSGMAIN,
-                 &TheIndex, (int)sizeof(long),
-                 smibuf, (int)sizeof(struct MetaData)
-       );
+       cdb_store(CDB_MSGMAIN, &TheIndex, (int)sizeof(long), smibuf, (int)sizeof(struct MetaData));
 }
 
 
@@ -3319,7 +3300,7 @@ long CtdlWriteObject(char *req_room,                      // Room to stuff it in
        CM_SetField(msg, eOriginalRoom, req_room);
        msg->cm_flags = flags;
        
-       CM_SetAsFieldSB(msg, eMesageText, &encoded_message);
+       CM_SetAsFieldSB(msg, eMessageText, &encoded_message);
 
        // Create the requested room if we have to.
        if (CtdlGetRoom(&qrbuf, roomname) != 0) {