/*
* Implements the message store.
*
- * Copyright (c) 1987-2017 by the citadel.org team
+ * Copyright (c) 1987-2018 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.
*/
+#include <stdlib.h>
+#include <unistd.h>
#include <stdio.h>
#include <regex.h>
+#include <sys/stat.h>
#include <libcitadel.h>
-
#include "md5.h"
-
#include "ctdl_module.h"
#include "citserver.h"
#include "control.h"
#include "genstamp.h"
#include "room_ops.h"
#include "user_ops.h"
-
#include "internet_addressing.h"
#include "euidindex.h"
#include "msgbase.h"
NULL,
"from", /* A -> eAuthor */
NULL, /* B -> eBig_message */
- NULL, /* C -> eRemoteRoom */
- NULL, /* D -> eDestination */
+ NULL, /* C -> eRemoteRoom FIXME no more ignet */
+ NULL, /* D -> eDestination FIXME no more ignet */
"exti", /* E -> eXclusivID */
"rfca", /* F -> erFc822Addr */
NULL, /* G */
- "hnod", /* H -> eHumanNode */
+ "hnod", /* H -> eHumanNode FIXME no more ignet */
"msgn", /* I -> emessageId */
"jrnl", /* J -> eJournal */
"rep2", /* K -> eReplyTo */
"list", /* L -> eListID */
"text", /* M -> eMesageText */
- "node", /* N -> eNodeName */
+ "node", /* N -> eNodeName FIXME no more ignet */
"room", /* O -> eOriginalRoom */
"path", /* P -> eMessagePath */
NULL, /* Q */
"rcpt", /* R -> eRecipient */
- "spec", /* S -> eSpecialField */
+ "spec", /* S -> eSpecialField FIXME we might not be using this anymore */
"time", /* T -> eTimestamp */
"subj", /* U -> eMsgSubject */
"nvto", /* V -> eenVelopeTo */
static const long NDiskFields = sizeof(FieldOrder) / sizeof(eMsgField);
+
int CM_IsEmpty(struct CtdlMessage *Msg, eMsgField which)
{
- return !((Msg->cm_fields[which] != NULL) &&
- (Msg->cm_fields[which][0] != '\0'));
+ return !((Msg->cm_fields[which] != NULL) && (Msg->cm_fields[which][0] != '\0'));
}
+
void CM_SetField(struct CtdlMessage *Msg, eMsgField which, const char *buf, long length)
{
- if (Msg->cm_fields[which] != NULL)
+ if (Msg->cm_fields[which] != NULL) {
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;
}
+
void CM_SetFieldLONG(struct CtdlMessage *Msg, eMsgField which, long lvalue)
{
char buf[128];
len = snprintf(buf, sizeof(buf), "%ld", lvalue);
CM_SetField(Msg, which, buf, len);
}
+
+
void CM_CutFieldAt(struct CtdlMessage *Msg, eMsgField WhichToCut, long maxlen)
{
if (Msg->cm_fields[WhichToCut] == NULL)
}
}
+
void CM_FlushField(struct CtdlMessage *Msg, eMsgField which)
{
if (Msg->cm_fields[which] != NULL)
Msg->cm_fields[which] = NULL;
Msg->cm_lengths[which] = 0;
}
+
+
void CM_Flush(struct CtdlMessage *Msg)
{
int i;
- if (CM_IsValidMsg(Msg) == 0)
+ if (CM_IsValidMsg(Msg) == 0) {
return;
+ }
- for (i = 0; i < 256; ++i)
- {
+ for (i = 0; i < 256; ++i) {
CM_FlushField(Msg, i);
}
}
+
void CM_CopyField(struct CtdlMessage *Msg, eMsgField WhichToPutTo, eMsgField WhichtToCopy)
{
long len;
- if (Msg->cm_fields[WhichToPutTo] != NULL)
+ if (Msg->cm_fields[WhichToPutTo] != NULL) {
free (Msg->cm_fields[WhichToPutTo]);
+ }
- if (Msg->cm_fields[WhichtToCopy] != NULL)
- {
+ if (Msg->cm_fields[WhichtToCopy] != NULL) {
len = Msg->cm_lengths[WhichtToCopy];
Msg->cm_fields[WhichToPutTo] = malloc(len + 1);
memcpy(Msg->cm_fields[WhichToPutTo], Msg->cm_fields[WhichtToCopy], len);
Msg->cm_fields[WhichToPutTo][len] = '\0';
Msg->cm_lengths[WhichToPutTo] = len;
}
- else
- {
+ else {
Msg->cm_fields[WhichToPutTo] = NULL;
Msg->cm_lengths[WhichToPutTo] = 0;
}
}
}
+
void CM_SetAsField(struct CtdlMessage *Msg, eMsgField which, char **buf, long length)
{
- if (Msg->cm_fields[which] != NULL)
+ if (Msg->cm_fields[which] != NULL) {
free (Msg->cm_fields[which]);
+ }
Msg->cm_fields[which] = *buf;
*buf = NULL;
Msg->cm_lengths[which] = length;
}
+
void CM_SetAsFieldSB(struct CtdlMessage *Msg, eMsgField which, StrBuf **buf)
{
- if (Msg->cm_fields[which] != NULL)
+ if (Msg->cm_fields[which] != NULL) {
free (Msg->cm_fields[which]);
+ }
Msg->cm_lengths[which] = StrLength(*buf);
Msg->cm_fields[which] = SmashStrBuf(buf);
}
+
void CM_GetAsField(struct CtdlMessage *Msg, eMsgField which, char **ret, long *retlen)
{
- if (Msg->cm_fields[which] != NULL)
- {
+ if (Msg->cm_fields[which] != NULL) {
*retlen = Msg->cm_lengths[which];
*ret = Msg->cm_fields[which];
Msg->cm_fields[which] = NULL;
Msg->cm_lengths[which] = 0;
}
- else
- {
+ else {
*ret = NULL;
*retlen = 0;
}
}
+
/*
* Returns 1 if the supplied pointer points to a valid Citadel message.
* If the pointer is NULL or the magic number check fails, returns 0.
*/
int CM_IsValidMsg(struct CtdlMessage *msg) {
- if (msg == NULL)
+ if (msg == NULL) {
return 0;
+ }
if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) {
syslog(LOG_WARNING, "msgbase: CM_IsValidMsg() self-check failed");
return 0;
return 1;
}
+
void CM_FreeContents(struct CtdlMessage *msg)
{
int i;
msg->cm_magic = 0; /* just in case */
}
+
+
/*
* 'Destructor' for struct CtdlMessage
*/
void CM_Free(struct CtdlMessage *msg)
{
- if (CM_IsValidMsg(msg) == 0)
- {
+ if (CM_IsValidMsg(msg) == 0) {
if (msg != NULL) free (msg);
return;
}
free(msg);
}
+
int CM_DupField(eMsgField i, struct CtdlMessage *OrgMsg, struct CtdlMessage *NewMsg)
{
long len;
len = OrgMsg->cm_lengths[i];
NewMsg->cm_fields[i] = malloc(len + 1);
- if (NewMsg->cm_fields[i] == NULL)
+ if (NewMsg->cm_fields[i] == NULL) {
return 0;
+ }
memcpy(NewMsg->cm_fields[i], OrgMsg->cm_fields[i], len);
NewMsg->cm_fields[i][len] = '\0';
NewMsg->cm_lengths[i] = len;
return 1;
}
+
struct CtdlMessage * CM_Duplicate(struct CtdlMessage *OrgMsg)
{
int i;
struct CtdlMessage *NewMsg;
- if (CM_IsValidMsg(OrgMsg) == 0)
+ if (CM_IsValidMsg(OrgMsg) == 0) {
return NULL;
+ }
NewMsg = (struct CtdlMessage *)malloc(sizeof(struct CtdlMessage));
- if (NewMsg == NULL)
+ if (NewMsg == NULL) {
return NULL;
+ }
memcpy(NewMsg, OrgMsg, sizeof(struct CtdlMessage));
memset(&NewMsg->cm_fields, 0, sizeof(char*) * 256);
- for (i = 0; i < 256; ++i)
- {
- if (OrgMsg->cm_fields[i] != NULL)
- {
- if (!CM_DupField(i, OrgMsg, NewMsg))
- {
+ for (i = 0; i < 256; ++i) {
+ if (OrgMsg->cm_fields[i] != NULL) {
+ if (!CM_DupField(i, OrgMsg, NewMsg)) {
CM_Free(NewMsg);
return NULL;
}
}
-
-
-
/* Determine if a given message matches the fields in a message template.
* Return 0 for a successful match.
*/
) {
CC->download_fp = tmpfile();
if (CC->download_fp == NULL) {
- syslog(LOG_EMERG, "msgbase: mime_download() couldn't write: %s", strerror(errno));
+ syslog(LOG_EMERG, "msgbase: mime_download() couldn't write: %m");
cprintf("%d cannot open temporary file: %s\n", ERROR + INTERNAL_ERROR, strerror(errno));
return;
}
rv = fwrite(content, length, 1, CC->download_fp);
if (rv <= 0) {
- syslog(LOG_EMERG, "msgbase: mime_download() Couldn't write: %s", strerror(errno));
+ syslog(LOG_EMERG, "msgbase: mime_download() Couldn't write: %m");
cprintf("%d unable to write tempfile.\n", ERROR + TOO_BIG);
fclose(CC->download_fp);
CC->download_fp = NULL;
if (!CM_IsValidMsg(TheMessage)) {
syslog(LOG_ERR, "msgbase: error; invalid preloaded message for output");
- cit_backtrace ();
return(om_no_such_msg);
}
ret->ser = malloc(ret->len);
if (ret->ser == NULL) {
- syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() malloc(%ld) failed: %s",
- (long)ret->len, strerror(errno)
- );
+ syslog(LOG_ERR, "msgbase: CtdlSerializeMessage() malloc(%ld) failed: %m", (long)ret->len);
ret->len = 0;
ret->ser = NULL;
return;
/* Decide where bounces need to be delivered */
if ((recps != NULL) && (recps->bounce_to == NULL))
{
- if (CC->logged_in)
- snprintf(bounce_to, sizeof bounce_to, "%s@%s",
- CC->user.fullname, CtdlGetConfigStr("c_nodename"));
- else
- snprintf(bounce_to, sizeof bounce_to, "%s@%s",
- msg->cm_fields[eAuthor], msg->cm_fields[eNodeName]);
+ if (CC->logged_in) {
+ snprintf(bounce_to, sizeof bounce_to, "%s@%s", CC->user.fullname, CtdlGetConfigStr("c_nodename"));
+ }
+ else {
+ snprintf(bounce_to, sizeof bounce_to, "%s@%s", msg->cm_fields[eAuthor], msg->cm_fields[eNodeName]);
+ }
recps->bounce_to = bounce_to;
}
size_t maxlen, /* maximum message length */
StrBuf *exist, /* if non-null, append to it;
exist is ALWAYS freed */
- int crlf, /* CRLF newlines instead of LF */
- int *sock /* socket handle or 0 for this session's client socket */
+ int crlf /* CRLF newlines instead of LF */
)
{
StrBuf *Message;
/* read in the lines of message text one by one */
do {
- if (sock != NULL) {
- if ((CtdlSockGetLine(sock, LineBuf, 5) < 0) ||
- (*sock == -1))
- finished = 1;
- }
- else {
- if (CtdlClientGetLine(LineBuf) < 0) finished = 1;
+ if (CtdlClientGetLine(LineBuf) < 0) {
+ finished = 1;
}
- if ((StrLength(LineBuf) == tlen) &&
- (!strcmp(ChrPtr(LineBuf), terminator)))
+ if ((StrLength(LineBuf) == tlen) && (!strcmp(ChrPtr(LineBuf), terminator))) {
finished = 1;
-
+ }
if ( (!flushing) && (!finished) ) {
if (crlf) {
StrBufAppendBufPlain(LineBuf, HKEY("\r\n"), 0);
return Message;
}
-void DeleteAsyncMsg(ReadAsyncMsg **Msg)
-{
- if (*Msg == NULL)
- return;
- FreeStrBuf(&(*Msg)->MsgBuf);
-
- free(*Msg);
- *Msg = NULL;
-}
-
-ReadAsyncMsg *NewAsyncMsg(const char *terminator, /* token signalling EOT */
- long tlen,
- size_t maxlen, /* maximum message length */
- size_t expectlen, /* if we expect a message, how long should it be? */
- StrBuf *exist, /* if non-null, append to it;
- exist is ALWAYS freed */
- long eLen, /* length of exist */
- int crlf /* CRLF newlines instead of LF */
- )
-{
- ReadAsyncMsg *NewMsg;
-
- NewMsg = (ReadAsyncMsg *)malloc(sizeof(ReadAsyncMsg));
- memset(NewMsg, 0, sizeof(ReadAsyncMsg));
-
- if (exist == NULL) {
- long len;
-
- if (expectlen == 0) {
- len = 4 * SIZ;
- }
- else {
- len = expectlen + 10;
- }
- NewMsg->MsgBuf = NewStrBufPlain(NULL, len);
- }
- else {
- NewMsg->MsgBuf = NewStrBufDup(exist);
- }
- /* Do we need to change leading ".." to "." for SMTP escaping? */
- if ((tlen == 1) && (*terminator == '.')) {
- NewMsg->dodot = 1;
- }
-
- NewMsg->terminator = terminator;
- NewMsg->tlen = tlen;
-
- NewMsg->maxlen = maxlen;
-
- NewMsg->crlf = crlf;
-
- return NewMsg;
-}
-
-/*
- * Back end function used by CtdlMakeMessage() and similar functions
- */
-eReadState CtdlReadMessageBodyAsync(AsyncIO *IO)
-{
- ReadAsyncMsg *ReadMsg;
- int MsgFinished = 0;
- eReadState Finished = eMustReadMore;
-
-#ifdef BIGBAD_IODBG
- char fn [SIZ];
- FILE *fd;
- const char *pch = ChrPtr(IO->SendBuf.Buf);
- const char *pchh = IO->SendBuf.ReadWritePointer;
- long nbytes;
-
- if (pchh == NULL)
- pchh = pch;
-
- nbytes = StrLength(IO->SendBuf.Buf) - (pchh - pch);
- snprintf(fn, SIZ, "/tmp/foolog_ev_%s.%d",
- ((CitContext*)(IO->CitContext))->ServiceName,
- IO->SendBuf.fd);
-
- fd = fopen(fn, "a+");
- if (fd == NULL) {
- syslog(LOG_ERR, "%s: %s", fn, strerror(errno));
- cit_backtrace();
- exit(1);
- }
-#endif
-
- ReadMsg = IO->ReadMsg;
-
- /* read in the lines of message text one by one */
- do {
- Finished = StrBufChunkSipLine(IO->IOBuf, &IO->RecvBuf);
-
- switch (Finished) {
- case eMustReadMore: /// read new from socket...
-#ifdef BIGBAD_IODBG
- if (IO->RecvBuf.ReadWritePointer != NULL) {
- nbytes = StrLength(IO->RecvBuf.Buf) - (IO->RecvBuf.ReadWritePointer - ChrPtr(IO->RecvBuf.Buf));
- fprintf(fd, "Read; Line unfinished: %ld Bytes still in buffer [", nbytes);
-
- fwrite(IO->RecvBuf.ReadWritePointer, nbytes, 1, fd);
-
- fprintf(fd, "]\n");
- } else {
- fprintf(fd, "BufferEmpty! \n");
- }
- fclose(fd);
-#endif
- return Finished;
- break;
- case eBufferNotEmpty: /* shouldn't happen... */
- case eReadSuccess: /// done for now...
- break;
- case eReadFail: /// WHUT?
- ///todo: shut down!
- break;
- }
-
-
- if ((StrLength(IO->IOBuf) == ReadMsg->tlen) &&
- (!strcmp(ChrPtr(IO->IOBuf), ReadMsg->terminator))) {
- MsgFinished = 1;
-#ifdef BIGBAD_IODBG
- fprintf(fd, "found Terminator; Message Size: %d\n", StrLength(ReadMsg->MsgBuf));
-#endif
- }
- else if (!ReadMsg->flushing) {
-
-#ifdef BIGBAD_IODBG
- fprintf(fd, "Read Line: [%d][%s]\n", StrLength(IO->IOBuf), ChrPtr(IO->IOBuf));
-#endif
-
- /* Unescape SMTP-style input of two dots at the beginning of the line */
- if ((ReadMsg->dodot) &&
- (StrLength(IO->IOBuf) == 2) && /* TODO: do we just unescape lines with two dots or any line? */
- (!strcmp(ChrPtr(IO->IOBuf), "..")))
- {
-#ifdef BIGBAD_IODBG
- fprintf(fd, "UnEscaped!\n");
-#endif
- StrBufCutLeft(IO->IOBuf, 1);
- }
-
- if (ReadMsg->crlf) {
- StrBufAppendBufPlain(IO->IOBuf, HKEY("\r\n"), 0);
- }
- else {
- StrBufAppendBufPlain(IO->IOBuf, HKEY("\n"), 0);
- }
-
- StrBufAppendBuf(ReadMsg->MsgBuf, IO->IOBuf, 0);
- }
-
- /* if we've hit the max msg length, flush the rest */
- if (StrLength(ReadMsg->MsgBuf) >= ReadMsg->maxlen) ReadMsg->flushing = 1;
-
- } while (!MsgFinished);
-
-#ifdef BIGBAD_IODBG
- fprintf(fd, "Done with reading; %s.\n, ",
- (MsgFinished)?"Message Finished": "FAILED");
- fclose(fd);
-#endif
- if (MsgFinished)
- return eReadSuccess;
- else
- return eReadFail;
-}
-
/*
* Back end function used by CtdlMakeMessage() and similar functions
size_t maxlen, /* maximum message length */
StrBuf *exist, /* if non-null, append to it;
exist is ALWAYS freed */
- int crlf, /* CRLF newlines instead of LF */
- int *sock /* socket handle or 0 for this session's client socket */
+ int crlf /* CRLF newlines instead of LF */
)
{
StrBuf *Message;
tlen,
maxlen,
exist,
- crlf,
- sock);
+ crlf
+ );
if (Message == NULL)
return NULL;
else
}
else {
StrBuf *MsgBody;
- MsgBody = CtdlReadMessageBodyBuf(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0, 0);
+ MsgBody = CtdlReadMessageBodyBuf(HKEY("000"), CtdlGetConfigLong("c_maxmsglen"), NULL, 0);
if (MsgBody != NULL) {
CM_SetAsFieldSB(msg, eMesageText, &MsgBody);
}
}
-
-
/*
* GetMetaData() - Get the supplementary record for a message
*/
if (cdbsmi == NULL) {
return; /* record not found; go with defaults */
}
- memcpy(smibuf, cdbsmi->ptr,
+ memcpy(smibuf, cdbsmi->ptr, // FIXME can we do this without a memcpy?
((cdbsmi->len > sizeof(struct MetaData)) ?
sizeof(struct MetaData) : cdbsmi->len));
cdb_free(cdbsmi);
new_arcq.arcq_delta = incr;
rv = fwrite(&new_arcq, sizeof(struct arcq), 1, arcfp);
if (rv == -1) {
- syslog(LOG_EMERG, "%s: %s", file_arcq, strerror(errno));
+ syslog(LOG_EMERG, "%s: %m", file_arcq);
}
fflush(arcfp);
struct arcq *new_arcq;
int rv = 0;
- syslog(LOG_DEBUG, "msgbase: AdjRefCountList() msg %ld ref count delta %+d", nmsg, incr);
-
begin_critical_section(S_SUPPMSGMAIN);
if (arcfp == NULL) {
arcfp = fopen(file_arcq, "ab+");
the_size = sizeof(struct arcq) * nmsg;
new_arcq = malloc(the_size);
for (i = 0; i < nmsg; i++) {
+ syslog(LOG_DEBUG, "msgbase: AdjRefCountList() msg %ld ref count delta %+d", msgnum[i], incr);
new_arcq[i].arcq_msgnum = msgnum[i];
new_arcq[i].arcq_delta = incr;
}
{
rv = fwrite(new_arcq + offset, 1, the_size - offset, arcfp);
if (rv == -1) {
- syslog(LOG_EMERG, "%s: %s", file_arcq, strerror(errno));
+ syslog(LOG_ERR, "%s: %m", file_arcq);
}
else {
offset += rv;
r = link(file_arcq, file_arcq_temp);
if (r != 0) {
- syslog(LOG_ERR, "%s: %s", file_arcq_temp, strerror(errno));
+ syslog(LOG_ERR, "%s: %m", file_arcq_temp);
end_critical_section(S_SUPPMSGMAIN);
return(num_records_processed);
}
fp = fopen(file_arcq_temp, "rb");
if (fp == NULL) {
- syslog(LOG_ERR, "%s: %s", file_arcq_temp, strerror(errno));
+ syslog(LOG_ERR, "%s: %m", file_arcq_temp);
return(num_records_processed);
}
fclose(fp);
r = unlink(file_arcq_temp);
if (r != 0) {
- syslog(LOG_ERR, "%s: %s", file_arcq_temp, strerror(errno));
+ syslog(LOG_ERR, "%s: %m", file_arcq_temp);
}
return(num_records_processed);