X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fserver%2Fmsgbase.c;h=8582fba301bed1b1f50209c45b5384ec34a04187;hb=HEAD;hp=d6dc230f8c4d7dfe6a5a628ee6961b6701637afb;hpb=d1c43a6b9f20d1ba2d8581a90fcc1ea825b9bec8;p=citadel.git diff --git a/citadel/server/msgbase.c b/citadel/server/msgbase.c index d6dc230f8..aaf4446c6 100644 --- a/citadel/server/msgbase.c +++ b/citadel/server/msgbase.c @@ -1,9 +1,9 @@ // Implements the message store. // -// Copyright (c) 1987-2023 by the citadel.org team +// Copyright (c) 1987-2024 by the citadel.org team // -// This program is open source software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License version 3. +// This program is open source software. Use, duplication, or disclosure +// is subject to the terms of the GNU General Public License version 3. #include #include @@ -24,6 +24,7 @@ #include "euidindex.h" #include "msgbase.h" #include "journaling.h" +#include "modules/fulltext/serv_fulltext.h" struct addresses_to_be_filed *atbf = NULL; @@ -51,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 @@ -93,7 +94,7 @@ void FillMsgKeyLookupTable(void) { eMsgField FieldOrder[] = { -/* Important fields */ +// Important fields emessageId , eMessagePath , eTimestamp , @@ -101,29 +102,29 @@ eMsgField FieldOrder[] = { erFc822Addr , eOriginalRoom, eRecipient , -/* Semi-important fields */ +// Semi-important fields eBig_message , eExclusiveID , eWeferences , eJournal , -/* G is not used yet */ +// G is not used yet eReplyTo , eListID , -/* Q is not used yet */ +// Q is not used yet eenVelopeTo , -/* X is not used yet */ -/* Z is not used yet */ +// X is not used yet +// Z is not used yet eCarbonCopY , eMsgSubject , -/* internal only */ +// internal only eErrorMsg , eSuppressIdx , eExtnotify , -/* Message text (MUST be last) */ - eMesageText -/* Not saved to disk: - eVltMsgNum -*/ +// Message text (MUST be last) + eMessageText +// Not saved to disk: +// eVltMsgNum +// }; static const long NDiskFields = sizeof(FieldOrder) / sizeof(eMsgField); @@ -134,17 +135,12 @@ int CM_IsEmpty(struct CtdlMessage *Msg, eMsgField which) { } -void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length) { +void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf) { if (Msg->cm_fields[which] != NULL) { - free (Msg->cm_fields[which]); - } - if (length < 0) { // You can set the length to -1 to have CM_SetField measure it for you - length = strlen(buf); + 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'; - Msg->cm_lengths[which] = length; + Msg->cm_fields[which] = strdup(buf); + Msg->cm_lengths[which] = strlen(buf); } @@ -152,7 +148,7 @@ 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); + CM_SetField(Msg, which, buf); } @@ -234,6 +230,7 @@ void CM_PrependToField(struct CtdlMessage *Msg, eMsgField which, const char *buf } +// This is like CM_SetField() except the caller is transferring ownership of the supplied memory to the message void CM_SetAsField(struct CtdlMessage *Msg, eMsgField which, char **buf, long length) { if (Msg->cm_fields[which] != NULL) { free (Msg->cm_fields[which]); @@ -446,9 +443,7 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums, // Load the message list num_msgs = CtdlFetchMsgList(which_room->QRnumber, &msglist); if (num_msgs <= 0) { - if (msglist != NULL) { - free(msglist); - } + free(msglist); return; } @@ -612,7 +607,6 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, { int a, i, j; struct visit vbuf; - struct cdbdata *cdbfr; long *msglist = NULL; int num_msgs = 0; int num_processed = 0; @@ -622,11 +616,10 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, int is_seen = 0; long lastold = 0L; int printed_lastold = 0; - int num_search_msgs = 0; - long *search_msgs = NULL; regex_t re; int need_to_free_re = 0; regmatch_t pm; + Array *search = NULL; if ((content_type) && (!IsEmptyStr(content_type))) { regcomp(&re, content_type, 0); @@ -652,37 +645,28 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, } // Load the message list - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); - if (cdbfr == NULL) { + num_msgs = CtdlFetchMsgList(CC->room.QRnumber, &msglist); + if (num_msgs <= 0) { + free(msglist); if (need_to_free_re) regfree(&re); return 0; // No messages at all? No further action. } - msglist = (long *) cdbfr->ptr; - num_msgs = cdbfr->len / sizeof(long); - - cdbfr->ptr = NULL; // clear this so that cdb_free() doesn't free it - cdb_free(cdbfr); // we own this memory now - - /* - * Now begin the traversal. - */ - if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) { + // Now begin the traversal. + if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) if (msglist[a] > 0) { - /* If the caller is looking for a specific MIME type, filter - * out all messages which are not of the type requested. - */ + // If the caller is looking for a specific MIME type, filter + // out all messages which are not of the type requested. if ((content_type != NULL) && (!IsEmptyStr(content_type))) { - /* This call to GetMetaData() sits inside this loop - * so that we only do the extra database read per msg - * if we need to. Doing the extra read all the time - * really kills the server. If we ever need to use - * metadata for another search criterion, we need to - * move the read somewhere else -- but still be smart - * enough to only do the read if the caller has - * specified something that will need it. - */ + // This call to GetMetaData() sits inside this loop + // so that we only do the extra database read per msg + // if we need to. Doing the extra read all the time + // really kills the server. If we ever need to use + // metadata for another search criterion, we need to + // move the read somewhere else -- but still be smart + // enough to only do the read if the caller has + // specified something that will need it. if (server_shutting_down) { if (need_to_free_re) regfree(&re); free(msglist); @@ -690,7 +674,6 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, } GetMetaData(&smi, msglist[a]); - /* if (strcasecmp(smi.meta_content_type, content_type)) { old non-regex way */ if (regexec(&re, smi.meta_content_type, 1, &pm, 0) != 0) { msglist[a] = 0L; } @@ -699,9 +682,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, num_msgs = sort_msglist(msglist, num_msgs); - /* If a template was supplied, filter out the messages which - * don't match. (This could induce some delays!) - */ + // If a template was supplied, filter out the messages which don't match. (This could induce some delays!) if (num_msgs > 0) { if (compare != NULL) { for (a = 0; a < num_msgs; ++a) { @@ -721,55 +702,52 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, } } - /* If a search string was specified, get a message list from - * the full text index and remove messages which aren't on both - * lists. - * - * How this works: - * Since the lists are sorted and strictly ascending, and the - * output list is guaranteed to be shorter than or equal to the - * input list, we overwrite the bottom of the input list. This - * eliminates the need to memmove big chunks of the list over and - * over again. - */ + // If a search string was specified, get a message list from + // the full text index and remove messages which aren't on both + // lists. + // + // How this works: + // Since the lists are sorted and strictly ascending, and the + // output list is guaranteed to be shorter than or equal to the + // input list, we overwrite the bottom of the input list. This + // eliminates the need to memmove big chunks of the list over and + // over again. if ( (num_msgs > 0) && (mode == MSGS_SEARCH) && (search_string) ) { - /* Call search module via hook mechanism. - * NULL means use any search function available. - * otherwise replace with a char * to name of search routine - */ - CtdlModuleDoSearch(&num_search_msgs, &search_msgs, search_string, "fulltext"); + // Call search module via hook mechanism. + // NULL means use any search function available. + // otherwise replace with a char * to name of search routine + search = CtdlFullTextSearch(search_string); - if (num_search_msgs > 0) { + if (array_len(search) > 0) { int orig_num_msgs; orig_num_msgs = num_msgs; num_msgs = 0; for (i=0; i 0) for (a = 0; a < num_msgs; ++a) { if (server_shutting_down) { @@ -813,9 +791,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, } if (need_to_free_re) regfree(&re); - /* - * We cache the most recent msglist in order to do security checks later - */ + // We cache the most recent msglist in order to do security checks later if (CC->client_socket > 0) { if (CC->cached_msglist != NULL) { free(CC->cached_msglist); @@ -831,16 +807,14 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, } -/* - * memfmout() - Citadel text formatter and paginator. - * Although the original purpose of this routine was to format - * text to the reader's screen width, all we're really using it - * for here is to format text out to 80 columns before sending it - * to the client. The client software may reformat it again. - */ +// memfmout() - Citadel text formatter and paginator. +// Although the original purpose of this routine was to format +// text to the reader's screen width, all we're really using it +// for here is to format text out to 80 columns before sending it +// to the client. The client software MAY reformat it again. void memfmout( - char *mptr, /* where are we going to get our text from? */ - const char *nl /* string to terminate lines with */ + char *mptr, // where are we going to get our text from? + const char *nl // string to terminate lines with ) { int column = 0; unsigned char ch = 0; @@ -865,10 +839,10 @@ void memfmout( column = 0; } else if (ch == '\r') { - /* Ignore carriage returns. Newlines are always LF or CRLF but never CR. */ + // Ignore carriage returns. Newlines are always LF or CRLF but never CR. } else if (isspace(ch)) { - if (column > 72) { /* Beyond 72 columns, break on the next space */ + if (column > 72) { // Beyond 72 columns, break on the next space if (client_write(outbuf, len) == -1) { syslog(LOG_ERR, "msgbase: memfmout() aborting due to write failure"); return; @@ -888,7 +862,7 @@ void memfmout( else { outbuf[len++] = ch; ++column; - if (column > 1000) { /* Beyond 1000 columns, break anywhere */ + if (column > 1000) { // Beyond 1000 columns, break anywhere if (client_write(outbuf, len) == -1) { syslog(LOG_ERR, "msgbase: memfmout() aborting due to write failure"); return; @@ -913,9 +887,7 @@ void memfmout( } -/* - * Callback function for mime parser that simply lists the part - */ +// Callback function for mime parser that simply lists the part void list_this_part(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, char *cbid, void *cbuserdata) @@ -937,9 +909,7 @@ void list_this_part(char *name, char *filename, char *partnum, char *disp, } -/* - * Callback function for multipart prefix - */ +// Callback function for multipart prefix void list_this_pref(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, char *cbid, void *cbuserdata) @@ -957,9 +927,7 @@ void list_this_pref(char *name, char *filename, char *partnum, char *disp, } -/* - * Callback function for multipart sufffix - */ +// Callback function for multipart sufffix void list_this_suff(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, char *cbid, void *cbuserdata) @@ -976,10 +944,8 @@ void list_this_suff(char *name, char *filename, char *partnum, char *disp, } -/* - * Callback function for mime parser that opens a section for downloading - * we use serv_files function here: - */ +// Callback function for mime parser that opens a section for downloading +// we use serv_files function here: extern void OpenCmdResult(char *filename, const char *mime_type); void mime_download(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, @@ -987,7 +953,7 @@ void mime_download(char *name, char *filename, char *partnum, char *disp, { int rv = 0; - /* Silently go away if there's already a download open. */ + // Silently go away if there's already a download open. if (CC->download_fp != NULL) return; @@ -1018,10 +984,8 @@ void mime_download(char *name, char *filename, char *partnum, char *disp, } -/* - * Callback function for mime parser that outputs a section all at once. - * We can specify the desired section by part number *or* content-id. - */ +// Callback function for mime parser that outputs a section all at once. +// We can specify the desired section by part number *or* content-id. void mime_spew_section(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, char *cbid, void *cbuserdata) @@ -1096,7 +1060,7 @@ struct CtdlMessage *CtdlDeserializeMessage(long msgnum, int with_body, const cha which = field_header; len = strlen(mptr); - CM_SetField(ret, which, mptr, len); + CM_SetField(ret, which, mptr); mptr += len + 1; // advance to next field @@ -1112,25 +1076,23 @@ struct CtdlMessage *CtdlDeserializeMessage(long msgnum, int with_body, const cha // using the CM_Free(); function. // struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) { - struct cdbdata *dmsgtext; + struct cdbdata dmsgtext; struct CtdlMessage *ret = NULL; syslog(LOG_DEBUG, "msgbase: CtdlFetchMessage(%ld, %d)", msgnum, with_body); dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); - if (dmsgtext == NULL) { + if (dmsgtext.ptr == NULL) { syslog(LOG_ERR, "msgbase: message #%ld was not found", msgnum); return NULL; } - if (dmsgtext->ptr[dmsgtext->len - 1] != '\0') { + if (dmsgtext.ptr[dmsgtext.len - 1] != '\0') { + // FIXME LMDB cannot write to immutable memory syslog(LOG_ERR, "msgbase: CtdlFetchMessage(%ld, %d) Forcefully terminating message!!", msgnum, with_body); - dmsgtext->ptr[dmsgtext->len - 1] = '\0'; + dmsgtext.ptr[dmsgtext.len - 1] = '\0'; } - ret = CtdlDeserializeMessage(msgnum, with_body, dmsgtext->ptr, dmsgtext->len); - - cdb_free(dmsgtext); - + ret = CtdlDeserializeMessage(msgnum, with_body, dmsgtext.ptr, dmsgtext.len); if (ret == NULL) { return NULL; } @@ -1140,15 +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 != NULL) { - CM_SetAsField(ret, eMesageText, &dmsgtext->ptr, dmsgtext->len - 1); - cdb_free(dmsgtext); + if (dmsgtext.ptr != NULL) { + CM_SetField(ret, eMessageText, dmsgtext.ptr); } } - if (CM_IsEmpty(ret, eMesageText)) { - CM_SetField(ret, eMesageText, HKEY("\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); @@ -1328,7 +1289,7 @@ void output_preferred(char *name, for (i=0; ipreferred_formats, '|'); ++i) { extract_token(buf, CC->preferred_formats, i, '|', sizeof buf); if (!strcasecmp(buf, cbtype)) { - /* Yeah! Go! W00t!! */ + // Yeah! Go! W00t!! if (ma->dont_decode == 0) rc = mime_decode_now (content, length, @@ -1455,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; @@ -1487,34 +1448,24 @@ int CtdlOutputMsg(long msg_num, // message number (local) to fetch return(r); } - /* - * Check to make sure the message is actually IN this room - */ + // Check to make sure the message is actually IN this room r = check_cached_msglist(msg_num); if (r == om_access_denied) { - /* Not in the cache? We get ONE shot to check it again. */ + // Not in the cache? We get ONE shot to check it again. CtdlForEachMessage(MSGS_ALL, 0L, NULL, NULL, NULL, NULL, NULL); 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); } - /* - * Fetch the message from disk. If we're in HEADERS_FAST mode, - * request that we don't even bother loading the body into memory. - */ + // Fetch the message from disk. If we're in HEADERS_FAST mode, request that we don't even bother loading the body into memory. if (headers_only == HEADERS_FAST) { TheMessage = CtdlFetchMessage(msg_num, 0); } @@ -1523,36 +1474,27 @@ 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); } - /* Here is the weird form of this command, to process only an - * encapsulated message/rfc822 section. - */ + // Here is the weird form of this command, to process only an encapsulated message/rfc822 section. 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); + if ((Author != NULL) && (*Author == NULL)) { + *Author = strdup(TheMessage->cm_fields[eAuthor]); } - if ((Address != NULL) && (*Address == NULL)) - { - long len; - CM_GetAsField(TheMessage, erFc822Addr, Address, &len); + if ((Address != NULL) && (*Address == NULL)) { + *Address = strdup(TheMessage->cm_fields[erFc822Addr]); } - if ((MessageID != NULL) && (*MessageID == NULL)) - { - long len; - CM_GetAsField(TheMessage, emessageId, MessageID, &len); + if ((MessageID != NULL) && (*MessageID == NULL)) { + *MessageID = strdup(TheMessage->cm_fields[emessageId]); } CM_Free(TheMessage); TheMessage = NULL; @@ -1560,41 +1502,33 @@ int CtdlOutputMsg(long msg_num, // message number (local) to fetch if (encap.msg) { encap.msg[encap.msglen] = 0; TheMessage = convert_internet_message(encap.msg); - encap.msg = NULL; /* no free() here, TheMessage owns it now */ + encap.msg = NULL; // no free() here, TheMessage owns it now - /* Now we let it fall through to the bottom of this - * function, because TheMessage now contains the - * encapsulated message instead of the top-level - * message. Isn't that neat? - */ + // Now we let it fall through to the bottom of this function, because TheMessage now contains the + // 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); + 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 */ - if (retcode == CIT_OK) + // Ok, output the message now + if (retcode == CIT_OK) { retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf, flags); - if ((Author != NULL) && (*Author == NULL)) - { + } + if ((Author != NULL) && (*Author == NULL)) { long len; CM_GetAsField(TheMessage, eAuthor, Author, &len); } - if ((Address != NULL) && (*Address == NULL)) - { + if ((Address != NULL) && (*Address == NULL)) { long len; CM_GetAsField(TheMessage, erFc822Addr, Address, &len); } - if ((MessageID != NULL) && (*MessageID == NULL)) - { + if ((MessageID != NULL) && (*MessageID == NULL)) { long len; CM_GetAsField(TheMessage, emessageId, MessageID, &len); } @@ -1610,7 +1544,7 @@ void OutputCtdlMsgHeaders(struct CtdlMessage *TheMessage, int do_proto) { char buf[SIZ]; char display_name[256]; - /* begin header processing loop for Citadel message format */ + // begin header processing loop for Citadel message format safestrncpy(display_name, "", sizeof display_name); if (!CM_IsEmpty(TheMessage, eAuthor)) { strcpy(buf, TheMessage->cm_fields[eAuthor]); @@ -1623,21 +1557,21 @@ 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); } } - /* Now spew the header fields in the order we like them. */ + // Now spew the header fields in the order we like them. 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]); @@ -1647,13 +1581,13 @@ void OutputCtdlMsgHeaders(struct CtdlMessage *TheMessage, int do_proto) { cprintf("%s=%s\n", msgkeys[Field], display_name); } } - /* Masquerade display name if needed */ + // Masquerade display name if needed else { if (do_proto) { cprintf("%s=%s\n", msgkeys[Field], TheMessage->cm_fields[Field]); } } - /* Give the client a hint about whether the message originated locally */ + // Give the client a hint about whether the message originated locally if (Field == erFc822Addr) { if (IsDirectory(TheMessage->cm_fields[Field] ,0)) { cprintf("locl=yes\n"); // message originated locally. @@ -1670,7 +1604,7 @@ void OutputCtdlMsgHeaders(struct CtdlMessage *TheMessage, int do_proto) { void OutputRFC822MsgHeaders( struct CtdlMessage *TheMessage, - int flags, /* should the message be exported clean */ + int flags, // should the message be exported clean const char *nl, int nlen, char *mid, long sizeof_mid, char *suser, long sizeof_suser, @@ -1769,14 +1703,14 @@ void OutputRFC822MsgHeaders( case eExclusiveID: case eJournal: - case eMesageText: + case eMessageText: case eBig_message: case eOriginalRoom: case eErrorMsg: case eSuppressIdx: case eExtnotify: case eVltMsgNum: - /* these don't map to mime message headers. */ + // these don't map to mime message headers. break; } if (mptr != mpptr) { @@ -1792,8 +1726,8 @@ void OutputRFC822MsgHeaders( void Dump_RFC822HeadersBody( struct CtdlMessage *TheMessage, - int headers_only, /* eschew the message body? */ - int flags, /* should the bessage be exported clean? */ + int headers_only, // eschew the message body? + int flags, // should the bessage be exported clean? const char *nl, int nlen) { cit_uint8_t prev_ch; @@ -1805,22 +1739,20 @@ void Dump_RFC822HeadersBody( char *mptr; int lfSent = 0; - mptr = TheMessage->cm_fields[eMesageText]; + mptr = TheMessage->cm_fields[eMessageText]; prev_ch = '\0'; while (*mptr != '\0') { if (*mptr == '\r') { - /* do nothing */ + // 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'); @@ -1866,13 +1798,12 @@ void Dump_RFC822HeadersBody( } -/* If the format type on disk is 1 (fixed-format), then we want - * everything to be output completely literally ... regardless of - * what message transfer format is in use. - */ +// If the format type on disk is 1 (fixed-format), then we want +// everything to be output completely literally ... regardless of +// what message transfer format is in use. void DumpFormatFixed( struct CtdlMessage *TheMessage, - int mode, /* how would you like that message? */ + int mode, // how would you like that message? const char *nl, int nllen) { cit_uint8_t ch; @@ -1881,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"); @@ -1909,7 +1840,7 @@ void DumpFormatFixed( } } - /* if we reach the outer bounds of our buffer, abort without respect for what we purge. */ + // if we reach the outer bounds of our buffer, abort without respect for what we purge. if (xlline && ((isspace(ch)) || (buflen > SIZ - nllen - 2))) { ch = '\r'; } @@ -1938,26 +1869,23 @@ void DumpFormatFixed( } -/* - * Get a message off disk. (returns om_* values found in msgbase.h) - */ +// Get a message off disk. (returns om_* values found in msgbase.h) int CtdlOutputPreLoadedMsg( struct CtdlMessage *TheMessage, - int mode, /* how would you like that message? */ - int headers_only, /* eschew the message body? */ - int do_proto, /* do Citadel protocol responses? */ - int crlf, /* Use CRLF newlines instead of LF? */ - int flags /* should the bessage be exported clean? */ + int mode, // how would you like that message? + int headers_only, // eschew the message body? + int do_proto, // do Citadel protocol responses? + int crlf, // Use CRLF newlines instead of LF? + int flags // should the bessage be exported clean? ) { int i; - const char *nl; /* newline string */ + const char *nl; // newline string int nlen; struct ma_info ma; - /* Buffers needed for RFC822 translation. These are all filled - * using functions that are bounds-checked, and therefore we can - * make them substantially smaller than SIZ. - */ + // Buffers needed for RFC822 translation. These are all filled + // using functions that are bounds-checked, and therefore we can + // make them substantially smaller than SIZ. char suser[1024]; char luser[1024]; char fuser[1024]; @@ -1978,35 +1906,33 @@ int CtdlOutputPreLoadedMsg( return(om_no_such_msg); } - /* 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. - */ + // 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) && (!CM_IsEmpty(TheMessage, eenVelopeTo)) ) { memset(TheMessage->cm_fields[eenVelopeTo], ' ', TheMessage->cm_lengths[eenVelopeTo]); } - /* Are we downloading a MIME component? */ + // Are we downloading a MIME component? if (mode == MT_DOWNLOAD) { if (TheMessage->cm_format_type != FMT_RFC822) { - if (do_proto) - cprintf("%d This is not a MIME message.\n", - ERROR + ILLEGAL_VALUE); - } else if (CC->download_fp != NULL) { - if (do_proto) cprintf( - "%d You already have a download open.\n", - ERROR + RESOURCE_BUSY); - } else { - /* Parse the message text component */ - mime_parser(CM_RANGE(TheMessage, eMesageText), - *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 (do_proto) { + cprintf("%d This is not a MIME message.\n", ERROR + ILLEGAL_VALUE); + } + } + else if (CC->download_fp != NULL) { + if (do_proto) { + cprintf( "%d You already have a download open.\n", ERROR + RESOURCE_BUSY); + } + } + else { + // Parse the message text component + 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) { - if (do_proto) cprintf( - "%d Section %s not found.\n", - ERROR + FILE_NOT_FOUND, - CC->download_desired_section); + if (do_proto) { + cprintf( "%d Section %s not found.\n", ERROR + FILE_NOT_FOUND, CC->download_desired_section); + } } } return((CC->download_fp != NULL) ? om_ok : om_mime_error); @@ -2023,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) { @@ -2047,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"); } @@ -2063,7 +1989,7 @@ int CtdlOutputPreLoadedMsg( strcpy(luser, ""); strcpy(fuser, ""); strcpy(snode, ""); - if (mode == MT_RFC822) + if (mode == MT_RFC822) { OutputRFC822MsgHeaders( TheMessage, flags, @@ -2073,8 +1999,8 @@ int CtdlOutputPreLoadedMsg( luser, sizeof(luser), fuser, sizeof(fuser), snode, sizeof(snode) - ); - + ); + } for (i=0; !IsEmptyStr(&suser[i]); ++i) { suser[i] = tolower(suser[i]); @@ -2082,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: \"----\" %s", nl); @@ -2102,7 +2029,7 @@ int CtdlOutputPreLoadedMsg( cprintf("From: \"%s\" <%s@%s>%s", luser, suser, snode, nl); } - /* Blank line signifying RFC822 end-of-headers */ + // Blank line signifying RFC822 end-of-headers if (TheMessage->cm_format_type != FMT_RFC822) { cprintf("%s", nl); } @@ -2116,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; } } @@ -2141,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 @@ -2158,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 @@ -2174,38 +2095,39 @@ 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); } } -DONE: /* now we're done */ +DONE: // now we're done if (do_proto) cprintf("000\n"); 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]; @@ -2227,41 +2149,24 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms strcpy(hold_rm, CC->room.QRname); - /* Sanity checks */ + // 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) { + // Now the regular stuff + if (CtdlGetRoomLock(&CC->room, ((roomname != NULL) ? roomname : CC->room.QRname) ) != 0) { syslog(LOG_ERR, "msgbase: no such room <%s>", roomname); return(ERROR + ROOM_NOT_FOUND); } - msgs_to_be_merged = malloc(sizeof(long) * num_newmsgs); num_msgs_to_be_merged = 0; + num_msgs = CtdlFetchMsgList(CC->room.QRnumber, &msglist); - - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); - if (cdbfr == NULL) { - msglist = NULL; - num_msgs = 0; - } - else { - msglist = (long *) cdbfr->ptr; - cdbfr->ptr = NULL; // CtdlSaveMsgPointerInRoom() now owns this memory (this needs attention if we move to LMDB) - num_msgs = cdbfr->len / sizeof(long); - cdb_free(cdbfr); - } - - - /* Create a list of msgid's which were supplied by the caller, but do - * not already exist in the target room. It is absolutely taboo to - * have more than one reference to the same message in a room. - */ + // Create a list of msgid's which were supplied by the caller, but do + // not already exist in the target room. It is absolutely taboo to + // have more than one reference to the same message in a room. for (i=0; i 0) for (j=0; jroom.QRnumber, (int)sizeof(long), - msglist, (int)(num_msgs * sizeof(long))); + // Write it back to disk. + cdb_store(CDB_MSGLISTS, &CC->room.QRnumber, (int)sizeof(long), msglist, (int)(num_msgs * sizeof(long))); - /* Free up the memory we used. */ + // Free up the memory we used. free(msglist); - /* Update the highest-message pointer and unlock the room. */ + // Update the highest-message pointer and unlock the room. CC->room.QRhighest = highest_msg; CtdlPutRoomLock(&CC->room); - /* Perform replication checks if necessary */ + // Perform replication checks if necessary if ( (DoesThisRoomNeedEuidIndexing(&CC->room)) && (do_repl_check) ) { syslog(LOG_DEBUG, "msgbase: CtdlSaveMsgPointerInRoom() doing repl checks"); for (i=0; icm_fields[eExclusiveID], &CC->room, msgid); } - /* Free up the memory we may have allocated */ - if (msg != supplied_msg) { + // Free up the memory we may have allocated + if (msg != msg_in) { CM_Free(msg); } } @@ -2340,112 +2243,87 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms syslog(LOG_DEBUG, "msgbase: CtdlSaveMsgPointerInRoom() skips repl checks"); } - /* Submit this room for processing by hooks */ + // Submit this room for processing by hooks int total_roomhook_errors = PerformRoomHooks(&CC->room); if (total_roomhook_errors) { syslog(LOG_WARNING, "msgbase: room hooks returned %d errors", total_roomhook_errors); } - /* Go back to the room we were in before we wandered here... */ + // Go back to the room we were in before we wandered here... CtdlGetRoom(&CC->room, hold_rm); - /* Bump the reference count for all messages which were merged */ + // Bump the reference count for all messages which were merged if (!suppress_refcount_adj) { AdjRefCountList(msgs_to_be_merged, num_msgs_to_be_merged, +1); } - /* Free up memory... */ + // Free up memory... if (msgs_to_be_merged != NULL) { free(msgs_to_be_merged); } - /* Return success. */ + // Return success. return (0); } -/* - * 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); +// 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 *msg_in) { + return CtdlSaveMsgPointersInRoom(roomname, &msgid, 1, do_repl_check, msg_in, 0); } -/* - * Message base operation to save a new message to the message store - * (returns new message number) - * - * 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; +// Message base operation to save a new message to the message store +// (returns new message number) +// +// This is the back end for CtdlSubmitMsg() and should not be directly +// called by server-side modules. +long CtdlSaveThisMessage(struct CtdlMessage *msg, long msgid) { + long error_count = 0; - /* - * 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; + // Serialize our data structure for storage in the database + struct ser_ret smr = CtdlSerializeMessage(msg); + + 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 the memory we used for the serialized message free(smr.ser); - - return(retval); + return(error_count); } @@ -2455,98 +2333,90 @@ long send_message(struct CtdlMessage *msg) { char msgidbuf[256]; long msgidbuflen; - /* Get a new message number */ + // Get a new message number newmsgid = get_new_message_number(); - /* Generate an ID if we don't have one already */ + // Generate an ID if we don't have one already if (CM_IsEmpty(msg, emessageId)) { - msgidbuflen = snprintf(msgidbuf, sizeof msgidbuf, "%08lX-%08lX@%s", - (long unsigned int) time(NULL), - (long unsigned int) newmsgid, - CtdlGetConfigStr("c_fqdn") - ); - - CM_SetField(msg, emessageId, msgidbuf, msgidbuflen); + msgidbuflen = snprintf(msgidbuf, sizeof msgidbuf, "%lX-%lX@%s", + (long unsigned int) time(NULL), + (long unsigned int) newmsgid, + CtdlGetConfigStr("c_fqdn") + ); + CM_SetField(msg, emessageId, msgidbuf); } - retval = CtdlSaveThisMessage(msg, newmsgid, 1); + retval = CtdlSaveThisMessage(msg, newmsgid); if (retval == 0) { retval = newmsgid; } - /* Return the *local* message ID to the caller - * (even if we're storing an incoming network message) - */ + // Return the *local* message ID to the caller (even if we're storing a remotely originated message) return(retval); } -/* - * Serialize a struct CtdlMessage into the format used on disk. - * - * 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 CtdlSerializeMessage(struct ser_ret *ret, /* return values */ - struct CtdlMessage *msg) /* unserialized msg */ -{ +// Serialize a struct CtdlMessage into the format used on disk. +// +// 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. +struct ser_ret CtdlSerializeMessage(struct CtdlMessage *msg) { + struct ser_ret ret; size_t wlen; int i; - /* - * Check for valid message format - */ + 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); } -/* - * 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. - */ +// 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. void ReplicationChecks(struct CtdlMessage *msg) { long old_msgnum = (-1L); @@ -2554,13 +2424,10 @@ void ReplicationChecks(struct CtdlMessage *msg) { syslog(LOG_DEBUG, "msgbase: performing replication checks in <%s>", CC->room.QRname); - /* No exclusive id? Don't do anything. */ + // No exclusive id? Don't do anything. if (msg == NULL) return; if (CM_IsEmpty(msg, eExclusiveID)) return; - /*syslog(LOG_DEBUG, "msgbase: exclusive ID: <%s> for room <%s>", - msg->cm_fields[eExclusiveID], CC->room.QRname);*/ - old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CC->room); if (old_msgnum > 0L) { syslog(LOG_DEBUG, "msgbase: ReplicationChecks() replacing message %ld", old_msgnum); @@ -2569,17 +2436,15 @@ void ReplicationChecks(struct CtdlMessage *msg) { } -/* - * Save a message to disk and submit it into the delivery system. - */ -long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ - struct recptypes *recps, /* recipients (if mail) */ - const char *force /* force a particular room? */ +// Save a message to disk and submit it into the delivery system. +long CtdlSubmitMsg(struct CtdlMessage *msg, // message to save + struct recptypes *recps, // recipients (if mail) + const char *force // force a particular room? ) { char hold_rm[ROOMNAMELEN]; char actual_rm[ROOMNAMELEN]; char force_room[ROOMNAMELEN]; - char content_type[SIZ]; /* We have to learn this */ + char content_type[SIZ]; // We have to learn this char recipient[SIZ]; char bounce_to[1024]; const char *room; @@ -2594,17 +2459,14 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ int qualified_for_journaling = 0; syslog(LOG_DEBUG, "msgbase: CtdlSubmitMsg() called"); - if (CM_IsValidMsg(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 this message has no timestamp, we take the liberty of giving it one, right now. if (CM_IsEmpty(msg, eTimestamp)) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); } - /* If this message has no path, we generate one. - */ + // If this message has no path, we generate one. if (CM_IsEmpty(msg, eMessagePath)) { if (!CM_IsEmpty(msg, eAuthor)) { CM_CopyField(msg, eMessagePath, eAuthor); @@ -2615,7 +2477,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } else { - CM_SetField(msg, eMessagePath, HKEY("unknown")); + CM_SetField(msg, eMessagePath, "unknown"); } } @@ -2626,8 +2488,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ strcpy(force_room, force); } - /* Learn about what's inside, because it's what's inside that counts */ - if (CM_IsEmpty(msg, eMesageText)) { + // Learn about what's inside, because it's what's inside that counts + if (CM_IsEmpty(msg, eMessageText)) { syslog(LOG_ERR, "msgbase: ERROR; attempt to save message with NULL body"); return(-2); } @@ -2641,25 +2503,28 @@ 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++; + } } } } - /* Goto the correct room */ + // Goto the correct room room = (recps) ? CC->room.QRname : SENTITEMS; syslog(LOG_DEBUG, "msgbase: selected room %s", room); strcpy(hold_rm, CC->room.QRname); @@ -2668,7 +2533,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ strcpy(actual_rm, SENTITEMS); } - /* If the user is a twit, move to the twit room for posting */ + // If the user is a twit, move to the twit room for posting if (TWITDETECT) { if (CC->user.axlevel == AxProbU) { strcpy(hold_rm, actual_rm); @@ -2677,7 +2542,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } - /* ...or if this message is destined for Aide> then go there. */ + // ...or if this message is destined for Aide> then go there. if (!IsEmptyStr(force_room)) { strcpy(actual_rm, force_room); } @@ -2687,50 +2552,42 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ CtdlUserGoto(actual_rm, 0, 1, NULL, NULL, NULL, NULL); } - /* - * If this message has no O (room) field, generate one. - */ + // If this message has no O (room) field, generate one. if (CM_IsEmpty(msg, eOriginalRoom) && !IsEmptyStr(CC->room.QRname)) { - CM_SetField(msg, eOriginalRoom, CC->room.QRname, -1); + CM_SetField(msg, eOriginalRoom, CC->room.QRname); } - /* Perform "before save" hooks (aborting if any return nonzero) */ + // Perform "before save" hooks (aborting if any return nonzero) syslog(LOG_DEBUG, "msgbase: performing before-save hooks"); if (PerformMessageHooks(msg, recps, EVT_BEFORESAVE) > 0) return(-3); - /* - * If this message has an Exclusive ID, and the room is replication - * checking enabled, then do replication checks. - */ + // If this message has an Exclusive ID, and the room is replication + // checking enabled, then do replication checks. if (DoesThisRoomNeedEuidIndexing(&CC->room)) { ReplicationChecks(msg); } - /* Save it to disk */ + // Save it to disk syslog(LOG_DEBUG, "msgbase: saving to disk"); newmsgid = send_message(msg); if (newmsgid <= 0L) return(-5); - /* Write a supplemental message info record. This doesn't have to - * be a critical section because nobody else knows about this message - * yet. - */ + // Write a supplemental message info record. This doesn't have to be + // a critical section because nobody else knows about this message yet. syslog(LOG_DEBUG, "msgbase: creating metadata record"); memset(&smi, 0, sizeof(struct MetaData)); smi.meta_msgnum = newmsgid; smi.meta_refcount = 0; safestrncpy(smi.meta_content_type, content_type, sizeof smi.meta_content_type); - /* - * Measure how big this message will be when rendered as RFC822. - * We do this for two reasons: - * 1. We need the RFC822 length for the new metadata record, so the - * POP and IMAP services don't have to calculate message lengths - * while the user is waiting (multiplied by potentially hundreds - * or thousands of messages). - * 2. If journaling is enabled, we will need an RFC822 version of the - * message to attach to the journalized copy. - */ + // Measure how big this message will be when rendered as RFC822. + // We do this for two reasons: + // 1. We need the RFC822 length for the new metadata record, so the + // POP and IMAP services don't have to calculate message lengths + // while the user is waiting (multiplied by potentially hundreds + // or thousands of messages). + // 2. If journaling is enabled, we will need an RFC822 version of the + // message to attach to the journalized copy. if (CC->redirect_buffer != NULL) { syslog(LOG_ALERT, "msgbase: CC->redirect_buffer is not NULL during message submission!"); exit(CTDLEXIT_REDIRECT); @@ -2743,13 +2600,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ PutMetaData(&smi); - /* Now figure out where to store the pointers */ + // Now figure out where to store the pointers syslog(LOG_DEBUG, "msgbase: storing pointers"); - /* If this is being done by the networker delivering a private - * message, we want to BYPASS saving the sender's copy (because there - * is no local sender; it would otherwise go to the Trashcan). - */ + // If this is being done by the networker delivering a private + // message, we want to BYPASS saving the sender's copy (because there + // is no local sender; it would otherwise go to the Trashcan). if ((!CC->internal_pgm) || (recps == NULL)) { if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 1, msg) != 0) { syslog(LOG_ERR, "msgbase: ERROR saving message pointer %ld in %s", newmsgid, actual_rm); @@ -2757,12 +2613,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } - /* For internet mail, drop a copy in the outbound queue room */ + // For internet mail, drop a copy in the outbound queue room if ((recps != NULL) && (recps->num_internet > 0)) { CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0, msg); } - /* If other rooms are specified, drop them there too. */ + // If other rooms are specified, drop them there too. if ((recps != NULL) && (recps->num_room > 0)) { for (i=0; irecp_room, '|'); ++i) { extract_token(recipient, recps->recp_room, i, '|', sizeof recipient); @@ -2771,7 +2627,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } - /* Decide where bounces need to be delivered */ + // Decide where bounces need to be delivered if ((recps != NULL) && (recps->bounce_to == NULL)) { if (CC->logged_in) { strcpy(bounce_to, CC->user.fullname); @@ -2809,21 +2665,19 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ recps->recp_local = pch; } - /* Perform "after save" hooks */ + // Perform "after save" hooks syslog(LOG_DEBUG, "msgbase: performing after-save hooks"); PerformMessageHooks(msg, recps, EVT_AFTERSAVE); CM_FlushField(msg, eVltMsgNum); - /* Go back to the room we started from */ + // Go back to the room we started from syslog(LOG_DEBUG, "msgbase: returning to original room %s", hold_rm); if (strcasecmp(hold_rm, CC->room.QRname)) { CtdlUserGoto(hold_rm, 0, 1, NULL, NULL, NULL, NULL); } - /* - * Any addresses to harvest for someone's address book? - */ + // Any addresses to harvest for someone's address book? if ( (CC->logged_in) && (recps != NULL) ) { collected_addresses = harvest_collected_addresses(msg); } @@ -2839,9 +2693,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ end_critical_section(S_ATBF); } - /* - * Determine whether this message qualifies for journaling. - */ + // Determine whether this message qualifies for journaling. if (!CM_IsEmpty(msg, eJournal)) { qualified_for_journaling = 0; } @@ -2857,11 +2709,9 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } - /* - * Do we have to perform journaling? If so, hand off the saved - * RFC822 version will be handed off to the journaler for background - * submit. Otherwise, we have to free the memory ourselves. - */ + // Do we have to perform journaling? If so, hand off the saved + // RFC822 version will be handed off to the journaler for background + // submit. Otherwise, we have to free the memory ourselves. if (saved_rfc822_version != NULL) { if (qualified_for_journaling) { JournalBackgroundSubmit(msg, saved_rfc822_version, recps); @@ -2874,14 +2724,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ if ((recps != NULL) && (recps->bounce_to == bounce_to)) recps->bounce_to = NULL; - /* Done. */ + // Done. return(newmsgid); } -/* - * Convenience function for generating small administrative messages. - */ +// Convenience function for generating small administrative messages. long quickie_message(char *from, char *fromaddr, char *to, @@ -2900,11 +2748,11 @@ long quickie_message(char *from, msg->cm_format_type = format_type; if (!IsEmptyStr(from)) { - CM_SetField(msg, eAuthor, from, -1); + CM_SetField(msg, eAuthor, from); } else if (!IsEmptyStr(fromaddr)) { char *pAt; - CM_SetField(msg, eAuthor, fromaddr, -1); + CM_SetField(msg, eAuthor, fromaddr); pAt = strchr(msg->cm_fields[eAuthor], '@'); if (pAt != NULL) { CM_CutFieldAt(msg, eAuthor, pAt - msg->cm_fields[eAuthor]); @@ -2914,17 +2762,17 @@ long quickie_message(char *from, msg->cm_fields[eAuthor] = strdup("Citadel"); } - if (!IsEmptyStr(fromaddr)) CM_SetField(msg, erFc822Addr, fromaddr, -1); - if (!IsEmptyStr(room)) CM_SetField(msg, eOriginalRoom, room, -1); + if (!IsEmptyStr(fromaddr)) CM_SetField(msg, erFc822Addr, fromaddr); + if (!IsEmptyStr(room)) CM_SetField(msg, eOriginalRoom, room); if (!IsEmptyStr(to)) { - CM_SetField(msg, eRecipient, to, -1); - recp = validate_recipients(to, NULL, 0); + CM_SetField(msg, eRecipient, to); + recp = validate_recipients(to, 0); } if (!IsEmptyStr(subject)) { - CM_SetField(msg, eMsgSubject, subject, -1); + CM_SetField(msg, eMsgSubject, subject); } if (!IsEmptyStr(text)) { - CM_SetField(msg, eMesageText, text, -1); + CM_SetField(msg, eMessageText, text); } long msgnum = CtdlSubmitMsg(msg, recp, room); @@ -2934,9 +2782,7 @@ long quickie_message(char *from, } -/* - * Back end function used by CtdlMakeMessage() and similar functions - */ +// Back end function used by CtdlMakeMessage() and similar functions StrBuf *CtdlReadMessageBodyBuf(char *terminator, // token signalling EOT long tlen, size_t maxlen, // maximum message length @@ -2957,12 +2803,12 @@ StrBuf *CtdlReadMessageBodyBuf(char *terminator, // token signalling EOT Message = NewStrBufDup(exist); } - /* Do we need to change leading ".." to "." for SMTP escaping? */ + // Do we need to change leading ".." to "." for SMTP escaping? if ((tlen == 1) && (*terminator == '.')) { dotdot = 1; } - /* read in the lines of message text one by one */ + // read in the lines of message text one by one do { if (CtdlClientGetLine(LineBuf) < 0) { finished = 1; @@ -2978,14 +2824,14 @@ StrBuf *CtdlReadMessageBodyBuf(char *terminator, // token signalling EOT StrBufAppendBufPlain(LineBuf, HKEY("\n"), 0); } - /* Unescape SMTP-style input of two dots at the beginning of the line */ + // Unescape SMTP-style input of two dots at the beginning of the line if ((dotdot) && (StrLength(LineBuf) > 1) && (ChrPtr(LineBuf)[0] == '.')) { StrBufCutLeft(LineBuf, 1); } StrBufAppendBuf(Message, LineBuf, 0); } - /* if we've hit the max msg length, flush the rest */ + // if we've hit the max msg length, flush the rest if (StrLength(Message) >= maxlen) { flushing = 1; } @@ -3026,73 +2872,71 @@ char *CtdlReadMessageBody(char *terminator, // token signalling EOT struct CtdlMessage *CtdlMakeMessage( - struct ctdluser *author, /* author's user structure */ - char *recipient, /* NULL if it's not mail */ - char *recp_cc, /* NULL if it's not mail */ - char *room, /* room where it's going */ - int type, /* see MES_ types in header file */ - int format_type, /* variformat, plain text, MIME... */ - 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 *preformatted_text, /* ...or NULL to read text from client */ - char *references /* Thread references */ + struct ctdluser *author, // author's user structure + char *recipient, // NULL if it's not mail + char *recp_cc, // NULL if it's not mail + char *room, // room where it's going + int type, // see MES_ types in header file + int format_type, // variformat, plain text, MIME... + 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 *euid_in, // ...or NULL if this is irrelevant + char *preformatted_text, // ...or NULL to read text from client + char *references // Thread references ) { return CtdlMakeMessageLen( - author, /* author's user structure */ - recipient, /* NULL if it's not mail */ + author, // author's user structure + recipient, // NULL if it's not mail (recipient)?strlen(recipient) : 0, - recp_cc, /* NULL if it's not mail */ + recp_cc, // NULL if it's not mail (recp_cc)?strlen(recp_cc): 0, - room, /* room where it's going */ + room, // room where it's going (room)?strlen(room): 0, - type, /* see MES_ types in header file */ - format_type, /* variformat, plain text, MIME... */ - fake_name, /* who we're masquerading as */ + type, // see MES_ types in header file + format_type, // variformat, plain text, MIME... + fake_name, // who we're masquerading as (fake_name)?strlen(fake_name): 0, - my_email, /* which of my email addresses to use (empty is ok) */ + my_email, // which of my email addresses to use (empty is ok) (my_email)?strlen(my_email): 0, - subject, /* Subject (optional) */ + subject, // Subject (optional) (subject)?strlen(subject): 0, - supplied_euid, /* ...or NULL if this is irrelevant */ - (supplied_euid)?strlen(supplied_euid):0, - preformatted_text, /* ...or NULL to read text from client */ + 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 */ + references, // Thread references (references)?strlen(references):0); } -/* - * Build a binary message to be saved on disk. - * (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 CM_Free() is called.) - */ +// Build a binary message to be saved on disk. +// (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 CM_Free() is called.) struct CtdlMessage *CtdlMakeMessageLen( - struct ctdluser *author, /* author's user structure */ - char *recipient, /* NULL if it's not mail */ + struct ctdluser *author, // author's user structure + char *recipient, // NULL if it's not mail long rcplen, - char *recp_cc, /* NULL if it's not mail */ + char *recp_cc, // NULL if it's not mail long cclen, - char *room, /* room where it's going */ + char *room, // room where it's going long roomlen, - int type, /* see MES_ types in header file */ - int format_type, /* variformat, plain text, MIME... */ - char *fake_name, /* who we're masquerading as */ + int type, // see MES_ types in header file + int format_type, // variformat, plain text, MIME... + char *fake_name, // who we're masquerading as long fnlen, - char *my_email, /* which of my email addresses to use (empty is ok) */ + char *my_email, // which of my email addresses to use (empty is ok) long myelen, - char *subject, /* Subject (optional) */ + 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 */ + char *preformatted_text, // ...or NULL to read text from client long textlen, - char *references, /* Thread references */ + char *references, // Thread references long reflen ) { long blen; @@ -3110,17 +2954,17 @@ struct CtdlMessage *CtdlMakeMessageLen( if (recipient != NULL) rcplen = string_trim(recipient); if (recp_cc != NULL) cclen = string_trim(recp_cc); - /* Path or Return-Path */ + // Path or Return-Path if (myelen > 0) { - CM_SetField(msg, eMessagePath, my_email, myelen); + CM_SetField(msg, eMessagePath, my_email); } else if (!IsEmptyStr(author->fullname)) { - CM_SetField(msg, eMessagePath, author->fullname, -1); + CM_SetField(msg, eMessagePath, author->fullname); } convert_spaces_to_underscores(msg->cm_fields[eMessagePath]); blen = snprintf(buf, sizeof buf, "%ld", (long)time(NULL)); - CM_SetField(msg, eTimestamp, buf, blen); + CM_SetField(msg, eTimestamp, buf); if (fnlen > 0) { FakeAuthor = NewStrBufPlain (fake_name, fnlen); @@ -3133,26 +2977,26 @@ struct CtdlMessage *CtdlMakeMessageLen( FreeStrBuf(&FakeAuthor); if (!!IsEmptyStr(CC->room.QRname)) { - if (CC->room.QRflags & QR_MAILBOX) { /* room */ - CM_SetField(msg, eOriginalRoom, &CC->room.QRname[11], -1); + if (CC->room.QRflags & QR_MAILBOX) { // room + CM_SetField(msg, eOriginalRoom, &CC->room.QRname[11]); } else { - CM_SetField(msg, eOriginalRoom, CC->room.QRname, -1); + CM_SetField(msg, eOriginalRoom, CC->room.QRname); } } if (rcplen > 0) { - CM_SetField(msg, eRecipient, recipient, rcplen); + CM_SetField(msg, eRecipient, recipient); } if (cclen > 0) { - CM_SetField(msg, eCarbonCopY, recp_cc, cclen); + CM_SetField(msg, eCarbonCopY, recp_cc); } if (myelen > 0) { - CM_SetField(msg, erFc822Addr, my_email, myelen); + CM_SetField(msg, erFc822Addr, my_email); } else if ( (author == &CC->user) && (!IsEmptyStr(CC->cs_inet_email)) ) { - CM_SetField(msg, erFc822Addr, CC->cs_inet_email, -1); + CM_SetField(msg, erFc822Addr, CC->cs_inet_email); } if (subject != NULL) { @@ -3163,13 +3007,13 @@ 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) - CM_SetField(msg, eMsgSubject, subject, subjlen); - else /* ok, we've got utf8 in the string. */ - { + } + if (IsAscii != 0) { + CM_SetField(msg, eMsgSubject, subject); + } + else { // ok, we've got utf8 in the string. char *rfc2047Subj; rfc2047Subj = rfc2047encode(subject, length); CM_SetAsField(msg, eMsgSubject, &rfc2047Subj, strlen(rfc2047Subj)); @@ -3179,21 +3023,21 @@ struct CtdlMessage *CtdlMakeMessageLen( } if (euidlen > 0) { - CM_SetField(msg, eExclusiveID, supplied_euid, euidlen); + CM_SetField(msg, eExclusiveID, euid_in); } if (reflen > 0) { - CM_SetField(msg, eWeferences, references, reflen); + CM_SetField(msg, eWeferences, references); } if (preformatted_text != NULL) { - CM_SetField(msg, eMesageText, preformatted_text, textlen); + 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); } } @@ -3201,10 +3045,8 @@ struct CtdlMessage *CtdlMakeMessageLen( } -/* - * API function to delete messages which match a set of criteria - * (returns the actual number of messages deleted) - */ +// API function to delete messages which match a set of criteria +// (returns the actual number of messages deleted) int CtdlDeleteMessages(const char *room_name, // which room long *dmsgnums, // array of msg numbers to be deleted int num_dmsgnums, // number of msgs to be deleted, or 0 for "any" @@ -3224,27 +3066,21 @@ 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... */ + // get room record, obtaining a lock... if (CtdlGetRoomLock(&qrbuf, room_name) != 0) { syslog(LOG_ERR, "msgbase: CtdlDeleteMessages(): Room <%s> not found", room_name); if (need_to_free_re) regfree(&re); - return(0); /* room not found */ + return(0); // room not found } - cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long)); - if (cdbfr != NULL) { - dellist = malloc(cdbfr->len); - msglist = (long *) cdbfr->ptr; - cdbfr->ptr = NULL; // CtdlDeleteMessages() now owns this memory (this needs attention if we move to LMDB) - num_msgs = cdbfr->len / sizeof(long); - cdb_free(cdbfr); - } + num_msgs = CtdlFetchMsgList(qrbuf.QRnumber, &msglist); if (num_msgs > 0) { + dellist = malloc(num_msgs * sizeof(long)); int have_contenttype = (content_type != NULL) && !IsEmptyStr(content_type); int have_delmsgs = (num_dmsgnums == 0) || (dmsgnums == NULL); int have_more_del = 1; @@ -3258,11 +3094,10 @@ int CtdlDeleteMessages(const char *room_name, // which room while ((i < num_msgs) && (have_more_del)) { delete_this = 0x00; - /* Set/clear a bit for each criterion */ + // Set/clear a bit for each criterion - /* 0 messages in the list or a null list means that we are - * interested in deleting any messages which meet the other criteria. - */ + // 0 messages in the list or a null list means that we are + // interested in deleting any messages which meet the other criteria. if (have_delmsgs) { delete_this |= 0x01; } @@ -3284,52 +3119,44 @@ int CtdlDeleteMessages(const char *room_name, // which room if (regexec(&re, smi.meta_content_type, 1, &pm, 0) == 0) { delete_this |= 0x02; } - } else { + } + else { delete_this |= 0x02; } - /* Delete message only if all bits are set */ + // Delete message only if all bits are set if (delete_this == 0x03) { dellist[num_deleted++] = msglist[i]; msglist[i] = 0L; } i++; } -/* - { - StrBuf *dbg = NewStrBuf(); - for (i = 0; i < num_deleted; i++) - StrBufAppendPrintf(dbg, ", %ld", dellist[i]); - syslog(LOG_DEBUG, "msgbase: Deleting: %s", ChrPtr(dbg)); - FreeStrBuf(&dbg); - } -*/ + num_msgs = sort_msglist(msglist, num_msgs); - cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, (int)sizeof(long), - msglist, (int)(num_msgs * sizeof(long))); + cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, (int)sizeof(long), msglist, (int)(num_msgs * sizeof(long))); - if (num_msgs > 0) + if (num_msgs > 0) { qrbuf.QRhighest = msglist[num_msgs - 1]; - else + } + else { qrbuf.QRhighest = 0; + } } CtdlPutRoomLock(&qrbuf); - /* Go through the messages we pulled out of the index, and decrement - * their reference counts by 1. If this is the only room the message - * was in, the reference count will reach zero and the message will - * automatically be deleted from the database. We do this in a - * separate pass because there might be plug-in hooks getting called, - * and we don't want that happening during an S_ROOMS critical - * section. - */ + // Go through the messages we pulled out of the index, and decrement + // their reference counts by 1. If this is the only room the message + // was in, the reference count will reach zero and the message will + // automatically be deleted from the database. We do this in a + // separate pass because there might be plug-in hooks getting called, + // and we don't want that happening during an S_ROOMS critical section. if (num_deleted) { for (i=0; imeta_msgnum = msgnum; - smibuf->meta_refcount = 1; /* Default reference count is 1 */ + smibuf->meta_refcount = 1; // Default reference count is 1 - /* Use the negative of the message number for its supp record index */ + // Use the negative of the message number for its supp record index TheIndex = (0L - msgnum); cdbsmi = cdb_fetch(CDB_MSGMAIN, &TheIndex, sizeof(long)); - if (cdbsmi == NULL) { - return; /* record not found; leave it alone */ + if (cdbsmi.ptr == NULL) { + return; // record not found; leave it alone } - memcpy(smibuf, cdbsmi->ptr, - ((cdbsmi->len > sizeof(struct MetaData)) ? - sizeof(struct MetaData) : cdbsmi->len) - ); - cdb_free(cdbsmi); + memcpy(smibuf, cdbsmi.ptr, ((cdbsmi.len > sizeof(struct MetaData)) ? sizeof(struct MetaData) : cdbsmi.len)); return; } -/* - * PutMetaData() - (re)write supplementary record for a message - */ -void PutMetaData(struct MetaData *smibuf) -{ +// PutMetaData() - (re)write supplementary record for a message +void PutMetaData(struct MetaData *smibuf) { long TheIndex; - /* Use the negative of the message number for the metadata db index */ + // 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)); } -/* - * Convenience function to process a big block of AdjRefCount() operations - */ -void AdjRefCountList(long *msgnum, long nmsg, int incr) -{ +// Convenience function to process a big block of AdjRefCount() operations +void AdjRefCountList(long *msgnum, long nmsg, int incr) { long i; for (i = 0; i < nmsg; i++) { @@ -3396,18 +3206,15 @@ void AdjRefCountList(long *msgnum, long nmsg, int incr) } -/* - * AdjRefCount - adjust the reference count for a message. We need to delete from disk any message whose reference count reaches zero. - */ -void AdjRefCount(long msgnum, int incr) -{ +// AdjRefCount - adjust the reference count for a message. +// We need to delete from disk any message whose reference count reaches zero. +void AdjRefCount(long msgnum, int incr) { struct MetaData smi; long delnum; - /* This is a *tight* critical section; please keep it that way, as - * it may get called while nested in other critical sections. - * Complicating this any further will surely cause deadlock! - */ + // This is a *tight* critical section; please keep it that way, as + // it may get called while nested in other critical sections. + // Complicating this any further will surely cause deadlock! begin_critical_section(S_SUPPMSGMAIN); GetMetaData(&smi, msgnum); smi.meta_refcount += incr; @@ -3415,38 +3222,34 @@ void AdjRefCount(long msgnum, int incr) end_critical_section(S_SUPPMSGMAIN); syslog(LOG_DEBUG, "msgbase: AdjRefCount() msg %ld ref count delta %+d, is now %d", msgnum, incr, smi.meta_refcount); - /* If the reference count is now zero, delete both the message and its metadata record. - */ + // If the reference count is now zero, delete both the message and its metadata record. if (smi.meta_refcount == 0) { syslog(LOG_DEBUG, "msgbase: deleting message <%ld>", msgnum); - /* Call delete hooks with NULL room to show it has gone altogether */ + // Call delete hooks with NULL room to show it has gone altogether PerformDeleteHooks(NULL, msgnum); - /* Remove from message base */ + // Remove from message base delnum = msgnum; cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long)); - cdb_delete(CDB_BIGMSGS, &delnum, (int)sizeof(long)); + cdb_delete(CDB_BIGMSGS, &delnum, (int)sizeof(long)); // There might not be a bigmsgs. Not an error. - /* Remove metadata record */ + // Remove metadata record delnum = (0L - msgnum); cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long)); } } -/* - * Write a generic object to this room - * - * Returns the message number of the written object, in case you need it. - */ -long CtdlWriteObject(char *req_room, /* Room to stuff it in */ - char *content_type, /* MIME type of this object */ - char *raw_message, /* Data to be written */ - off_t raw_length, /* Size of raw_message */ - struct ctdluser *is_mailbox, /* Mailbox room? */ - int is_binary, /* Is encoding necessary? */ - unsigned int flags /* Internal save flags */ +// Write a generic object to this room +// Returns the message number of the written object, in case you need it. +long CtdlWriteObject(char *req_room, // Room to stuff it in + char *content_type, // MIME type of this object + char *raw_message, // Data to be written + off_t raw_length, // Size of raw_message + struct ctdluser *is_mailbox, // Mailbox room? + int is_binary, // Is encoding necessary? + unsigned int flags // Internal save flags ) { struct ctdlroom qrbuf; char roomname[ROOMNAMELEN]; @@ -3493,33 +3296,33 @@ long 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; - CM_SetField(msg, eAuthor, CC->user.fullname, -1); - CM_SetField(msg, eOriginalRoom, req_room, -1); + CM_SetField(msg, eAuthor, CC->user.fullname); + 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. */ + // Create the requested room if we have to. if (CtdlGetRoom(&qrbuf, roomname) != 0) { CtdlCreateRoom(roomname, ( (is_mailbox != NULL) ? 5 : 3 ), "", 0, 1, 0, VIEW_BBS); } - /* Now write the data */ + // Now write the data long new_msgnum = CtdlSubmitMsg(msg, NULL, roomname); CM_Free(msg); return new_msgnum; } -/************************************************************************/ -/* MODULE INITIALIZATION */ -/************************************************************************/ +// ************************************************************************/ +// * MODULE INITIALIZATION */ +// ************************************************************************/ char *ctdl_module_init_msgbase(void) { if (!threading) { FillMsgKeyLookupTable(); } - /* return our module id for the log */ + // return our module id for the log return "msgbase"; }