X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fserver%2Fmsgbase.c;h=8582fba301bed1b1f50209c45b5384ec34a04187;hb=HEAD;hp=3588ac8d08af9f79052c87fcfc425bb372c5d2fa;hpb=5d3c7139faafbfdc5db4b45bdf9ef7105d4593b1;p=citadel.git diff --git a/citadel/server/msgbase.c b/citadel/server/msgbase.c index 3588ac8d0..aaf4446c6 100644 --- a/citadel/server/msgbase.c +++ b/citadel/server/msgbase.c @@ -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); } @@ -1494,16 +1488,13 @@ int CtdlOutputMsg(long msg_num, // message number (local) to fetch ); 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); } } @@ -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'); @@ -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: \"----\" %s", nl); @@ -2060,11 +2050,7 @@ START_TEXT: (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 @@ -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; icm_lengths[eMessageText] > BIGMSG) { - is_bigmsg = 1; - holdM = msg->cm_fields[eMessageText]; - msg->cm_fields[eMessageText] = NULL; - holdMLen = msg->cm_lengths[eMessageText]; - msg->cm_lengths[eMessageText] = 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 (is_bigmsg) { - // put the message body back into the message - msg->cm_fields[eMessageText] = holdM; - msg->cm_lengths[eMessageText] = holdMLen; + if (smr.len == 0) { + syslog(LOG_ERR, "msgbase: CtdlSaveMessage() unable to serialize message"); + return (-1); } - if (smr.len == 0) { - if (Reply) { - cprintf("%d Unable to serialize message\n", ERROR + INTERNAL_ERROR); - } - else { - syslog(LOG_ERR, "msgbase: CtdlSaveMessage() unable to serialize message"); + // 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; } - 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); - } + + 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; + } + + if (cdb_store(CDB_BIGMSGS, &msgid, (int)sizeof(long), smr.msgstart+1, (smr.len - (smr.msgstart - smr.ser) - 1) )) { + ++error_count; } + + } + + 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); } @@ -2358,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") @@ -2366,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; @@ -2387,15 +2367,18 @@ struct ser_ret CtdlSerializeMessage(struct CtdlMessage *msg) { 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(ret); } 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; @@ -2407,6 +2390,7 @@ struct ser_ret CtdlSerializeMessage(struct CtdlMessage *msg) { syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() malloc(%ld) failed: %m", (long)ret.len); ret.len = 0; ret.ser = NULL; + ret.msgstart = NULL; return(ret); } @@ -2417,16 +2401,16 @@ struct ser_ret CtdlSerializeMessage(struct CtdlMessage *msg) { for (i=0; i < NDiskFields; ++i) { if (msg->cm_fields[FieldOrder[i]] != NULL) { + 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); - } - + assert(ret.len == wlen); // Make sure we measured it correctly return(ret); } @@ -2526,13 +2510,16 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, // message to save 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++; + } } } } @@ -2779,7 +2766,7 @@ 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); @@ -2894,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 ) { @@ -2914,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 @@ -2945,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, @@ -3020,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); @@ -3034,7 +3023,7 @@ struct CtdlMessage *CtdlMakeMessageLen( } if (euidlen > 0) { - CM_SetField(msg, eExclusiveID, supplied_euid); + CM_SetField(msg, eExclusiveID, euid_in); } if (reflen > 0) { @@ -3077,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... @@ -3203,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)); }