X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmsgbase.c;h=9d4451309fd8e831bd3618b67779097a9b7efa8c;hb=dbaf416c5e665af584ad1db3109cc5486fdfa96f;hp=776680b6df7cd5dd04f16bd04979fe23cf07cff6;hpb=ab472129567331427686fd2dc4dacf61afe58ff9;p=citadel.git diff --git a/citadel/msgbase.c b/citadel/msgbase.c index 776680b6d..9d4451309 100644 --- a/citadel/msgbase.c +++ b/citadel/msgbase.c @@ -737,7 +737,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, free(msglist); return -1; } - msg = CtdlFetchMessage(msglist[a], 1); + msg = CtdlFetchMessage(msglist[a], 1, 1); if (msg != NULL) { if (CtdlMsgCmp(msg, compare)) { msglist[a] = 0L; @@ -1083,33 +1083,18 @@ void mime_spew_section(char *name, char *filename, char *partnum, char *disp, } } - -/* - * Load a message from disk into memory. - * This is used by CtdlOutputMsg() and other fetch functions. - * - * NOTE: Caller is responsible for freeing the returned CtdlMessage struct - * using the CtdlMessageFree() function. - */ -struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) +struct CtdlMessage *CtdlDeserializeMessage(long msgnum, int with_body, const char *Buffer, long Length) { struct CitContext *CCC = CC; - struct cdbdata *dmsgtext; struct CtdlMessage *ret = NULL; - char *mptr; - char *upper_bound; + const char *mptr; + const char *upper_bound; cit_uint8_t ch; cit_uint8_t field_header; eMsgField which; - MSG_syslog(LOG_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body); - dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); - if (dmsgtext == NULL) { - MSG_syslog(LOG_ERR, "CtdlFetchMessage(%ld, %d) Failed!\n", msgnum, with_body); - return NULL; - } - mptr = dmsgtext->ptr; - upper_bound = mptr + dmsgtext->len; + mptr = Buffer; + upper_bound = Buffer + Length; /* Parse the three bytes that begin EVERY message on disk. * The first is always 0xFF, the on-disk magic number. @@ -1119,7 +1104,6 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) ch = *mptr++; if (ch != 255) { MSG_syslog(LOG_ERR, "Message %ld appears to be corrupted.\n", msgnum); - cdb_free(dmsgtext); return NULL; } ret = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); @@ -1135,21 +1119,67 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) * have just processed the 'M' (message text) field. */ do { + field_header = '\0'; long len; + + /* work around possibly buggy messages: */ + while (field_header == '\0') + { + if (mptr >= upper_bound) { + break; + } + field_header = *mptr++; + } if (mptr >= upper_bound) { break; } - field_header = *mptr++; which = field_header; len = strlen(mptr); + CM_SetField(ret, which, mptr, len); mptr += len + 1; /* advance to next field */ } while ((mptr < upper_bound) && (field_header != 'M')); + return (ret); +} + + +/* + * Load a message from disk into memory. + * This is used by CtdlOutputMsg() and other fetch functions. + * + * NOTE: Caller is responsible for freeing the returned CtdlMessage struct + * using the CtdlMessageFree() function. + */ +struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body, int run_msg_hooks) +{ + struct CitContext *CCC = CC; + struct cdbdata *dmsgtext; + struct CtdlMessage *ret = NULL; + + MSG_syslog(LOG_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body); + dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); + if (dmsgtext == NULL) { + MSG_syslog(LOG_ERR, "CtdlFetchMessage(%ld, %d) Failed!\n", msgnum, with_body); + return NULL; + } + + if (dmsgtext->ptr[dmsgtext->len - 1] != '\0') + { + MSG_syslog(LOG_ERR, "CtdlFetchMessage(%ld, %d) Forcefully terminating message!!\n", msgnum, with_body); + dmsgtext->ptr[dmsgtext->len - 1] = '\0'; + } + + ret = CtdlDeserializeMessage(msgnum, with_body, dmsgtext->ptr, dmsgtext->len); + cdb_free(dmsgtext); + if (ret == NULL) { + return NULL; + } + /* Always make sure there's something in the msg text field. If * it's NULL, the message text is most likely stored separately, * so go ahead and fetch that. Failing that, just set a dummy @@ -1158,7 +1188,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) if ( (CM_IsEmpty(ret, eMesageText)) && (with_body) ) { dmsgtext = cdb_fetch(CDB_BIGMSGS, &msgnum, sizeof(long)); if (dmsgtext != NULL) { - CM_SetAsField(ret, eMesageText, &dmsgtext->ptr, dmsgtext->len); + CM_SetAsField(ret, eMesageText, &dmsgtext->ptr, dmsgtext->len - 1); cdb_free(dmsgtext); } } @@ -1167,7 +1197,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) } /* Perform "before read" hooks (aborting if any return nonzero) */ - if (PerformMessageHooks(ret, NULL, EVT_BEFOREREAD) > 0) { + if (run_msg_hooks && (PerformMessageHooks(ret, NULL, EVT_BEFOREREAD) > 0)) { CM_Free(ret); return NULL; } @@ -1504,14 +1534,15 @@ int check_cached_msglist(long msgnum) { * */ 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 do_proto, /* do Citadel protocol responses? */ - int crlf, /* Use CRLF newlines instead of LF? */ - char *section, /* NULL or a message/rfc822 section */ - int flags, /* various flags; see msgbase.h */ - char **Author, - char **Address + 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? */ + char *section, /* NULL or a message/rfc822 section */ + int flags, /* various flags; see msgbase.h */ + char **Author, + char **Address, + char **MessageID ) { struct CitContext *CCC = CC; struct CtdlMessage *TheMessage = NULL; @@ -1566,10 +1597,10 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ * request that we don't even bother loading the body into memory. */ if (headers_only == HEADERS_FAST) { - TheMessage = CtdlFetchMessage(msg_num, 0); + TheMessage = CtdlFetchMessage(msg_num, 0, 1); } else { - TheMessage = CtdlFetchMessage(msg_num, 1); + TheMessage = CtdlFetchMessage(msg_num, 1, 1); } if (TheMessage == NULL) { @@ -1599,6 +1630,11 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ long len; CM_GetAsField(TheMessage, erFc822Addr, Address, &len); } + if ((MessageID != NULL) && (*MessageID == NULL)) + { + long len; + CM_GetAsField(TheMessage, emessageId, MessageID, &len); + } CM_Free(TheMessage); TheMessage = NULL; @@ -1638,6 +1674,11 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ long len; CM_GetAsField(TheMessage, erFc822Addr, Address, &len); } + if ((MessageID != NULL) && (*MessageID == NULL)) + { + long len; + CM_GetAsField(TheMessage, emessageId, MessageID, &len); + } CM_Free(TheMessage); @@ -1739,28 +1780,28 @@ void OutputRFC822MsgHeaders( char *mpptr = NULL; char *hptr; - for (i = 0; i < 256; ++i) { - if (TheMessage->cm_fields[i]) { - mptr = mpptr = TheMessage->cm_fields[i]; - - if (i == eAuthor) { + for (i = 0; i < NDiskFields; ++i) { + if (TheMessage->cm_fields[FieldOrder[i]]) { + mptr = mpptr = TheMessage->cm_fields[FieldOrder[i]]; + switch (FieldOrder[i]) { + case eAuthor: safestrncpy(luser, mptr, sizeof_luser); safestrncpy(suser, mptr, sizeof_suser); - } - else if (i == 'Y') { + break; + case eCarbonCopY: if ((flags & QP_EADDR) != 0) { mptr = qp_encode_email_addrs(mptr); } sanitize_truncated_recipient(mptr); cprintf("CC: %s%s", mptr, nl); - } - else if (i == 'P') { + break; + case eMessagePath: cprintf("Return-Path: %s%s", mptr, nl); - } - else if (i == eListID) { + break; + case eListID: cprintf("List-ID: %s%s", mptr, nl); - } - else if (i == 'V') { + break; + case eenVelopeTo: if ((flags & QP_EADDR) != 0) mptr = qp_encode_email_addrs(mptr); hptr = mptr; @@ -1768,22 +1809,25 @@ void OutputRFC822MsgHeaders( hptr ++; if (!IsEmptyStr(hptr)) cprintf("Envelope-To: %s%s", hptr, nl); - } - else if (i == 'U') { + break; + case eMsgSubject: cprintf("Subject: %s%s", mptr, nl); subject_found = 1; - } - else if (i == 'I') + break; + case emessageId: safestrncpy(mid, mptr, sizeof_mid); /// TODO: detect @ here and copy @nodename in if not found. - else if (i == erFc822Addr) + break; + case erFc822Addr: safestrncpy(fuser, mptr, sizeof_fuser); - /* else if (i == 'O') + /* case eOriginalRoom: cprintf("X-Citadel-Room: %s%s", - mptr, nl); */ - else if (i == 'N') + mptr, nl) + break; + ; */ + case eNodeName: safestrncpy(snode, mptr, sizeof_snode); - else if (i == 'R') - { + break; + case eRecipient: if (haschar(mptr, '@') == 0) { sanitize_truncated_recipient(mptr); @@ -1799,13 +1843,13 @@ void OutputRFC822MsgHeaders( cprintf("To: %s", mptr); cprintf("%s", nl); } - } - else if (i == 'T') { + break; + case eTimestamp: datestring(datestamp, sizeof datestamp, atol(mptr), DATESTRING_RFC822); cprintf("Date: %s%s", datestamp, nl); - } - else if (i == 'W') { + break; + case eWeferences: cprintf("References: "); k = num_tokens(mptr, '|'); for (j=0; jcm_lengths[eMesageText] > BIGMSG) { - is_bigmsg = 1; - holdM = msg->cm_fields[eMesageText]; - msg->cm_fields[eMesageText] = NULL; - holdMLen = msg->cm_lengths[eMesageText]; - msg->cm_lengths[eMesageText] = 0; - } + /* + * If 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 */ CtdlSerializeMessage(&smr, msg); 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) { - cprintf("%d Unable to serialize message\n", - ERROR + INTERNAL_ERROR); + if (Reply) { + cprintf("%d Unable to serialize message\n", + ERROR + INTERNAL_ERROR); + } + else { + MSGM_syslog(LOG_ERR, "CtdlSaveMessage() unable to serialize message"); + + } return (-1L); } /* Write our little bundle of joy into the message base */ - if (cdb_store(CDB_MSGMAIN, &newmsgid, (int)sizeof(long), - smr.ser, smr.len) < 0) { - MSGM_syslog(LOG_ERR, "Can't store message\n"); - retval = 0L; - } else { + retval = cdb_store(CDB_MSGMAIN, &msgid, (int)sizeof(long), + smr.ser, smr.len); + if (retval < 0) { + MSG_syslog(LOG_ERR, "Can't store message %ld: %ld", msgid, retval); + } + else { if (is_bigmsg) { - cdb_store(CDB_BIGMSGS, - &newmsgid, - (int)sizeof(long), - holdM, - (holdMLen + 1) + retval = cdb_store(CDB_BIGMSGS, + &msgid, + (int)sizeof(long), + holdM, + (holdMLen + 1) ); + if (retval < 0) { + MSG_syslog(LOG_ERR, "failed to store message body for msgid %ld: %ld", + msgid, retval); + } } - retval = newmsgid; } /* Free the memory we used for the serialized message */ free(smr.ser); + return(retval); +} + +long send_message(struct CtdlMessage *msg) { + long newmsgid; + long retval; + char msgidbuf[256]; + long msgidbuflen; + + /* Get a new message number */ + newmsgid = get_new_message_number(); + + /* 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, + config.c_fqdn + ); + + CM_SetField(msg, emessageId, msgidbuf, msgidbuflen); + } + + retval = CtdlSaveThisMessage(msg, newmsgid, 1); + + if (retval == 0) { + retval = newmsgid; + } + /* Return the *local* message ID to the caller * (even if we're storing an incoming network message) */ @@ -2527,7 +2615,7 @@ long send_message(struct CtdlMessage *msg) { * serialized message in memory. THE LATTER MUST BE FREED BY THE CALLER. */ void CtdlSerializeMessage(struct ser_ret *ret, /* return values */ - struct CtdlMessage *msg) /* unserialized msg */ + struct CtdlMessage *msg) /* unserialized msg */ { struct CitContext *CCC = CC; size_t wlen; @@ -2731,7 +2819,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ MSG_syslog(LOG_INFO, "Final selection: %s (%s)\n", actual_rm, room); if (strcasecmp(actual_rm, CCC->room.QRname)) { /* CtdlGetRoom(&CCC->room, actual_rm); */ - CtdlUserGoto(actual_rm, 0, 1, NULL, NULL); + CtdlUserGoto(actual_rm, 0, 1, NULL, NULL, NULL, NULL); } /* @@ -2821,9 +2909,9 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* Bump this user's messages posted counter. */ MSGM_syslog(LOG_DEBUG, "Updating user\n"); - CtdlGetUserLock(&CCC->user, CCC->curr_user); + CtdlLockGetCurrentUser(); CCC->user.posted = CCC->user.posted + 1; - CtdlPutUserLock(&CCC->user); + CtdlPutCurrentUserLock(); /* Decide where bounces need to be delivered */ if ((recps != NULL) && (recps->bounce_to == NULL)) @@ -2878,7 +2966,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* Go back to the room we started from */ MSG_syslog(LOG_DEBUG, "Returning to original room %s\n", hold_rm); if (strcasecmp(hold_rm, CCC->room.QRname)) - CtdlUserGoto(hold_rm, 0, 1, NULL, NULL); + CtdlUserGoto(hold_rm, 0, 1, NULL, NULL, NULL, NULL); /* * Any addresses to harvest for someone's address book? @@ -3001,7 +3089,7 @@ void flood_protect_quickie_message(const char *from, const char *subject, int nCriterions, const char **CritStr, - long *CritStrLen, + const long *CritStrLen, long ccid, long ioid, time_t NOW) @@ -3012,7 +3100,8 @@ void flood_protect_quickie_message(const char *from, StrBuf *guid; char timestamp[64]; long tslen; - time_t tsday = NOW / (8*60*60); /* just care for a day... */ + static const time_t tsday = (8*60*60); /* just care for a day... */ + time_t seenstamp; tslen = snprintf(timestamp, sizeof(timestamp), "%ld", tsday); MD5Init(&md5context); @@ -3031,29 +3120,35 @@ void flood_protect_quickie_message(const char *from, if (StrLength(guid) > 40) StrBufCutAt(guid, 40, NULL); - if (CheckIfAlreadySeen("FPAideMessage", - guid, - NOW, - tsday, - eUpdate, - ccid, - ioid)!= 0) + seenstamp = CheckIfAlreadySeen("FPAideMessage", + guid, + NOW, + tsday, + eUpdate, + ccid, + ioid); + if ((seenstamp > 0) && (seenstamp < tsday)) { FreeStrBuf(&guid); /* yes, we did. flood protection kicks in. */ syslog(LOG_DEBUG, - "not sending message again\n"); + "not sending message again - %ld < %ld \n", seenstamp, tsday); return; } - FreeStrBuf(&guid); - /* no, this message isn't sent recently; go ahead. */ - quickie_message(from, - fromaddr, - to, - room, - text, - format_type, - subject); + else + { + syslog(LOG_DEBUG, + "sending message. %ld >= %ld", seenstamp, tsday); + FreeStrBuf(&guid); + /* no, this message isn't sent recently; go ahead. */ + quickie_message(from, + fromaddr, + to, + room, + text, + format_type, + subject); + } } @@ -3208,6 +3303,11 @@ eReadState CtdlReadMessageBodyAsync(AsyncIO *IO) IO->SendBuf.fd); fd = fopen(fn, "a+"); + if (fd == NULL) { + syslog(LOG_EMERG, "failed to open file %s: %s", fn, strerror(errno)); + cit_backtrace(); + exit(1); + } #endif ReadMsg = IO->ReadMsg; @@ -3289,7 +3389,7 @@ eReadState CtdlReadMessageBodyAsync(AsyncIO *IO) if (MsgFinished) return eReadSuccess; else - return eAbort; + return eReadFail; } @@ -3429,6 +3529,7 @@ struct CtdlMessage *CtdlMakeMessageLen( } StrBufRFC2047encode(&FakeEncAuthor, FakeAuthor); CM_SetAsFieldSB(msg, eAuthor, &FakeEncAuthor); + FreeStrBuf(&FakeAuthor); if (CCC->room.QRflags & QR_MAILBOX) { /* room */ CM_SetField(msg, eOriginalRoom, &CCC->room.QRname[11], strlen(&CCC->room.QRname[11])); @@ -3489,9 +3590,10 @@ struct CtdlMessage *CtdlMakeMessageLen( CM_SetField(msg, eMesageText, preformatted_text, textlen); } else { - preformatted_text = CtdlReadMessageBody(HKEY("000"), config.c_maxmsglen, NULL, 0, 0); - if (preformatted_text != NULL) { - CM_SetField(msg, eMesageText, preformatted_text, strlen(preformatted_text)); + StrBuf *MsgBody; + MsgBody = CtdlReadMessageBodyBuf(HKEY("000"), config.c_maxmsglen, NULL, 0, 0); + if (MsgBody != NULL) { + CM_SetAsFieldSB(msg, eMesageText, &MsgBody); } } @@ -3505,7 +3607,7 @@ struct CtdlMessage *CtdlMakeMessageLen( * API function to delete messages which match a set of criteria * (returns the actual number of messages deleted) */ -int CtdlDeleteMessages(char *room_name, /* which room */ +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" */ char *content_type /* or "" for any. regular expressions expected. */