// Implements the message store.
//
-// Copyright (c) 1987-2022 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 distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-
+// This program is open source software. Use, duplication, or disclosure
+// is subject to the terms of the GNU General Public License version 3.
#include <stdlib.h>
#include <unistd.h>
#include "euidindex.h"
#include "msgbase.h"
#include "journaling.h"
+#include "modules/fulltext/serv_fulltext.h"
struct addresses_to_be_filed *atbf = NULL;
"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
eMsgField FieldOrder[] = {
-/* Important fields */
+// Important fields
emessageId ,
eMessagePath ,
eTimestamp ,
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);
}
-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);
}
char buf[128];
long len;
len = snprintf(buf, sizeof(buf), "%ld", lvalue);
- CM_SetField(Msg, which, buf, len);
+ CM_SetField(Msg, which, 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]);
// Retrieve the "seen" message list for the current room.
void CtdlGetSeen(char *buf, int which_set) {
- visit vbuf;
+ struct visit vbuf;
// Learn about the user and room in question
CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
void CtdlSetSeen(long *target_msgnums, int num_target_msgnums,
int target_setting, int which_set,
struct ctdluser *which_user, struct ctdlroom *which_room) {
- struct cdbdata *cdbfr;
int i, k;
int is_seen = 0;
int was_seen = 0;
long lo = (-1L);
long hi = (-1L);
- visit vbuf;
+ struct visit vbuf;
long *msglist;
int num_msgs = 0;
StrBuf *vset;
CtdlGetRelationship(&vbuf, which_user, which_room);
// Load the message list
- cdbfr = cdb_fetch(CDB_MSGLISTS, &which_room->QRnumber, sizeof(long));
- if (cdbfr != NULL) {
- msglist = (long *) cdbfr->ptr;
- cdbfr->ptr = NULL; // CtdlSetSeen() now owns this memory
- num_msgs = cdbfr->len / sizeof(long);
- cdb_free(cdbfr);
- }
- else {
- return; // No messages at all? No further action.
+ num_msgs = CtdlFetchMsgList(which_room->QRnumber, &msglist);
+ if (num_msgs <= 0) {
+ free(msglist);
+ return;
}
-
+
is_set = malloc(num_msgs * sizeof(char));
memset(is_set, 0, (num_msgs * sizeof(char)) );
vset = NewStrBuf();
}
-
-#if 0 // This is a special diagnostic section. Do not allow it to run during normal operation.
- syslog(LOG_DEBUG, "There are %d messages in the room.\n", num_msgs);
- for (i=0; i<num_msgs; ++i) {
- if ((i > 0) && (msglist[i] <= msglist[i-1])) abort();
- }
- syslog(LOG_DEBUG, "We are twiddling %d of them.\n", num_target_msgnums);
- for (k=0; k<num_target_msgnums; ++k) {
- if ((k > 0) && (target_msgnums[k] <= target_msgnums[k-1])) abort();
- }
-#endif
-
// Translate the existing sequence set into an array of booleans
setstr = NewStrBuf();
lostr = NewStrBuf();
vset = new_set;
}
- // Decide which message set we're manipulating
+ // Decide which message set we're manipulating. Zero the buffers so they compress well.
switch (which_set) {
case ctdlsetseen_seen:
+ memset(vbuf.v_seen, 0, sizeof vbuf.v_seen);
safestrncpy(vbuf.v_seen, ChrPtr(vset), sizeof vbuf.v_seen);
break;
case ctdlsetseen_answered:
+ memset(vbuf.v_answered, 0, sizeof vbuf.v_seen);
safestrncpy(vbuf.v_answered, ChrPtr(vset), sizeof vbuf.v_answered);
break;
}
void *userdata)
{
int a, i, j;
- visit vbuf;
- struct cdbdata *cdbfr;
+ struct visit vbuf;
long *msglist = NULL;
int num_msgs = 0;
int num_processed = 0;
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);
}
// 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);
}
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;
}
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) {
}
}
- /* 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<orig_num_msgs; ++i) {
- for (j=0; j<num_search_msgs; ++j) {
- if (msglist[i] == search_msgs[j]) {
+ for (j=0; j<array_len(search); ++j) {
+ long smsgnum;
+ memcpy(&smsgnum, array_get_element_at(search, j), sizeof(long));
+ if (msglist[i] == smsgnum) {
msglist[num_msgs++] = msglist[i];
}
}
}
}
else {
- num_msgs = 0; /* No messages qualify */
+ num_msgs = 0; // No messages qualify
}
- if (search_msgs != NULL) free(search_msgs);
+ if (search != NULL) array_free(search);
- /* Now that we've purged messages which don't contain the search
- * string, treat a MSGS_SEARCH just like a MSGS_ALL from this
- * point on.
- */
+ // Now that we've purged messages which don't contain the search
+ // string, treat a MSGS_SEARCH just like a MSGS_ALL from this
+ // point on.
mode = MSGS_ALL;
}
- /*
- * Now iterate through the message list, according to the
- * criteria supplied by the caller.
- */
+ // Now iterate through the message list, according to the
+ // criteria supplied by the caller.
if (num_msgs > 0)
for (a = 0; a < num_msgs; ++a) {
if (server_shutting_down) {
}
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);
}
-/*
- * 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;
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;
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;
}
-/*
- * 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)
}
-/*
- * 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)
}
-/*
- * 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)
}
-/*
- * 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,
{
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;
}
-/*
- * 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)
which = field_header;
len = strlen(mptr);
- CM_SetField(ret, which, mptr, len);
+ CM_SetField(ret, which, mptr);
mptr += len + 1; // advance to next field
// 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;
}
// 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);
}
if (!strcasecmp(cbtype, "text/html")) {
- ptr = html_to_ascii(content, length, 80);
+ ptr = html_to_ascii(content, length, 80, 0);
wlen = strlen(ptr);
client_write(ptr, wlen);
if ((wlen > 0) && (ptr[wlen-1] != '\n')) {
for (i=0; i<num_tokens(CC->preferred_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,
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);
}
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);
}
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))
- {
+ 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);
}
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);
}
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, "<unknown>", sizeof display_name);
if (!CM_IsEmpty(TheMessage, eAuthor)) {
strcpy(buf, TheMessage->cm_fields[eAuthor]);
}
}
- /* 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]);
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.
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,
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) {
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;
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) &&
}
-/* 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;
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");
}
}
- /* 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';
}
}
-/*
- * 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];
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);
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) {
strcpy(luser, "");
strcpy(fuser, "");
strcpy(snode, "");
- if (mode == MT_RFC822)
+ if (mode == MT_RFC822) {
OutputRFC822MsgHeaders(
TheMessage,
flags,
luser, sizeof(luser),
fuser, sizeof(fuser),
snode, sizeof(snode)
- );
-
+ );
+ }
for (i=0; !IsEmptyStr(&suser[i]); ++i) {
suser[i] = tolower(suser[i]);
}
if (mode == MT_RFC822) {
- /* Construct a fun message id */
+ // Construct a fun message id
cprintf("Message-ID: <%s", mid);
if (strchr(mid, '@')==NULL) {
cprintf("@%s", snode);
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);
}
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),
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
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);
}
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;
- /* 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 */
- 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<num_newmsgs; ++i) {
unique = 1;
if (num_msgs > 0) for (j=0; j<num_msgs; ++j) {
syslog(LOG_DEBUG, "msgbase: %d unique messages to be merged", num_msgs_to_be_merged);
- /*
- * Now merge the new messages
- */
+ // Now merge the new messages
msglist = realloc(msglist, (sizeof(long) * (num_msgs + num_msgs_to_be_merged)) );
if (msglist == NULL) {
syslog(LOG_ALERT, "msgbase: ERROR; can't realloc message list!");
free(msgs_to_be_merged);
+ abort(); // FIXME
return (ERROR + INTERNAL_ERROR);
}
memcpy(&msglist[num_msgs], msgs_to_be_merged, (sizeof(long) * num_msgs_to_be_merged) );
num_msgs += num_msgs_to_be_merged;
- /* Sort the message list, so all the msgid's are in order */
+ // Sort the message list, so all the msgid's are in order
num_msgs = sort_msglist(msglist, num_msgs);
- /* Determine the highest message number */
+ // Determine the highest message number
highest_msg = msglist[num_msgs - 1];
- /* Write it back to disk. */
- cdb_store(CDB_MSGLISTS, &CC->room.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");
if (msg != NULL) {
ReplicationChecks(msg);
- /* If the message has an Exclusive ID, index that... */
+ // If the message has an Exclusive ID, index that...
if (!CM_IsEmpty(msg, eExclusiveID)) {
index_message_by_euid(msg->cm_fields[eExclusiveID], &CC->room, msgid);
}
- /* Free up the memory we may have allocated */
+ // Free up the memory we may have allocated
if (msg != supplied_msg) {
CM_Free(msg);
}
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)
-{
+// 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);
}
-/*
- * 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 %d 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);
}
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);
+ (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);
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);
}
-/*
- * 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;
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);
}
}
else {
- CM_SetField(msg, eMessagePath, HKEY("unknown"));
+ CM_SetField(msg, eMessagePath, "unknown");
}
}
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);
}
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);
- striplt(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);
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);
}
}
- /* ...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);
}
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!");
- abort();
+ exit(CTDLEXIT_REDIRECT);
}
CC->redirect_buffer = NewStrBufPlain(NULL, SIZ);
CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, QP_EADDR);
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);
}
}
- /* 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; i<num_tokens(recps->recp_room, '|'); ++i) {
extract_token(recipient, recps->recp_room, i, '|', sizeof recipient);
}
}
- /* Bump this user's messages posted counter. */
- syslog(LOG_DEBUG, "msgbase: updating user");
- CtdlLockGetCurrentUser();
- CC->user.posted = CC->user.posted + 1;
- CtdlPutCurrentUserLock();
-
- /* 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);
CM_SetFieldLONG(msg, eVltMsgNum, newmsgid);
- /* If this is private, local mail, make a copy in the
- * recipient's mailbox and bump the reference count.
- */
+ // If this is private, local mail, make a copy in the recipient's mailbox and bump the reference count.
if ((recps != NULL) && (recps->num_local > 0)) {
char *pch;
int ntokens;
if (CtdlGetUser(&userbuf, recipient) == 0) {
CtdlMailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM);
CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg);
- CtdlBumpNewMailCounter(userbuf.usernum);
+ CtdlBumpNewMailCounter(userbuf.usernum); // if this user is logged in, tell them they have new mail.
PerformMessageHooks(msg, recps, EVT_AFTERUSRMBOXSAVE);
}
else {
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);
}
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;
}
}
}
- /*
- * 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);
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,
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]);
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);
+ CM_SetField(msg, eRecipient, to);
recp = validate_recipients(to, NULL, 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);
}
-/*
- * 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
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;
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;
}
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 *supplied_euid, // ...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, // ...or NULL if this is irrelevant
(supplied_euid)?strlen(supplied_euid):0,
- preformatted_text, /* ...or NULL to read text from client */
+ 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 *supplied_euid, // ...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;
msg->cm_anon_type = type;
msg->cm_format_type = format_type;
- if (recipient != NULL) rcplen = striplt(recipient);
- if (recp_cc != NULL) cclen = striplt(recp_cc);
+ 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);
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) {
long length;
- length = striplt(subject);
+ length = string_trim(subject);
if (length > 0) {
long i;
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));
}
if (euidlen > 0) {
- CM_SetField(msg, eExclusiveID, supplied_euid, euidlen);
+ CM_SetField(msg, eExclusiveID, supplied_euid);
}
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);
}
}
}
-/*
- * 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"
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 */
- 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;
num_msgs = sort_msglist(msglist, num_msgs);
- if (num_dmsgnums > 1)
+ if (num_dmsgnums > 1) {
num_dmsgnums = sort_msglist(dmsgnums, num_dmsgnums);
-/*
- {
- StrBuf *dbg = NewStrBuf();
- for (i = 0; i < num_dmsgnums; i++)
- StrBufAppendPrintf(dbg, ", %ld", dmsgnums[i]);
- syslog(LOG_DEBUG, "msgbase: Deleting before: %s", ChrPtr(dbg));
- FreeStrBuf(&dbg);
- }
-*/
- i = 0; j = 0;
+ }
+ i = 0;
+ j = 0;
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;
}
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; i<num_deleted; ++i) {
PerformDeleteHooks(qrbuf.QRname, dellist[i]);
}
AdjRefCountList(dellist, num_deleted, -1);
}
- /* Now free the memory we used, and go away. */
+ // Now free the memory we used, and go away.
if (msglist != NULL) free(msglist);
if (dellist != NULL) free(dellist);
syslog(LOG_DEBUG, "msgbase: %d message(s) deleted", num_deleted);
}
-/*
- * GetMetaData() - Get the supplementary record for a message
- */
-void GetMetaData(struct MetaData *smibuf, long msgnum)
-{
- struct cdbdata *cdbsmi;
+// GetMetaData() - Get the supplementary record for a message
+void GetMetaData(struct MetaData *smibuf, long msgnum) {
+ struct cdbdata cdbsmi;
long TheIndex;
memset(smibuf, 0, sizeof(struct MetaData));
smibuf->meta_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++) {
}
-/*
- * 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;
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];
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";
}