X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmsgbase.c;h=4f5c4046f0bdea2291a6203ae88a7d86f0d631ea;hb=ece8da4e0b7e2b371494b7c58a53041da6773d6b;hp=107b1d5cb2a9fa32f73f34aea6d27c433d4d4fda;hpb=d593027b916b25577f92be5403ee7ff0eaa30d74;p=citadel.git diff --git a/citadel/msgbase.c b/citadel/msgbase.c index 107b1d5cb..4f5c4046f 100644 --- a/citadel/msgbase.c +++ b/citadel/msgbase.c @@ -2,7 +2,22 @@ * $Id$ * * Implements the message store. + * + * Copyright (c) 1987-2009 by the citadel.org team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "sysdep.h" @@ -54,6 +69,8 @@ #include "serv_network.h" #include "threads.h" +#include "ctdl_module.h" + long config_msgnum; struct addresses_to_be_filed *atbf = NULL; @@ -89,7 +106,8 @@ char *msgkeys[] = { "hnod", "msgn", "jrnl", - NULL, NULL, + NULL, + "list", "text", "node", "room", @@ -278,6 +296,25 @@ void headers_listing(long msgnum, void *userdata) CtdlFreeMessage(msg); } +/* + * Back end for the MSGS command: output EUID header. + */ +void headers_euid(long msgnum, void *userdata) +{ + struct CtdlMessage *msg; + + msg = CtdlFetchMessage(msgnum, 0); + if (msg == NULL) { + cprintf("%ld||\n", msgnum); + return; + } + + cprintf("%ld|%s|\n", msgnum, (msg->cm_fields['E'] ? msg->cm_fields['E'] : "")); + CtdlFreeMessage(msg); +} + + + /* Determine if a given message matches the fields in a message template. @@ -297,6 +334,8 @@ int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) { for (i='A'; i<='Z'; ++i) { if (template->cm_fields[i] != NULL) { if (msg->cm_fields[i] == NULL) { + /* Considered equal if temmplate is empty string */ + if (IsEmptyStr(template->cm_fields[i])) continue; return 1; } if (strcasecmp(msg->cm_fields[i], @@ -334,40 +373,47 @@ 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, j, k; + int i, k; int is_seen = 0; int was_seen = 0; long lo = (-1L); long hi = (-1L); - long t = (-1L); - int trimming = 0; struct visit vbuf; long *msglist; int num_msgs = 0; - char vset[SIZ]; + StrBuf *vset; + StrBuf *setstr; + StrBuf *lostr; + StrBuf *histr; + const char *pvset; char *is_set; /* actually an array of booleans */ - int num_sets; - int s; - char setstr[SIZ], lostr[SIZ], histr[SIZ]; - size_t tmp; /* Don't bother doing *anything* if we were passed a list of zero messages */ if (num_target_msgnums < 1) { return; } - CtdlLogPrintf(CTDL_DEBUG, "CtdlSetSeen(%d msgs starting with %ld, %d, %d)\n", + /* If no room was specified, we go with the current room. */ + if (!which_room) { + which_room = &CC->room; + } + + /* If no user was specified, we go with the current user. */ + if (!which_user) { + which_user = &CC->user; + } + + CtdlLogPrintf(CTDL_DEBUG, "CtdlSetSeen(%d msgs starting with %ld, %s, %d) in <%s>\n", num_target_msgnums, target_msgnums[0], - target_setting, which_set); + (target_setting ? "SET" : "CLEAR"), + which_set, + which_room->QRname); /* Learn about the user and room in question */ - CtdlGetRelationship(&vbuf, - ((which_user != NULL) ? which_user : &CC->user), - ((which_room != NULL) ? which_room : &CC->room) - ); + CtdlGetRelationship(&vbuf, which_user, which_room); /* Load the message list */ - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); + 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 */ @@ -382,33 +428,52 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums, /* Decide which message set we're manipulating */ switch(which_set) { - case ctdlsetseen_seen: - safestrncpy(vset, vbuf.v_seen, sizeof vset); - break; - case ctdlsetseen_answered: - safestrncpy(vset, vbuf.v_answered, sizeof vset); - break; + case ctdlsetseen_seen: + vset = NewStrBufPlain(vbuf.v_seen, -1); + break; + case ctdlsetseen_answered: + vset = NewStrBufPlain(vbuf.v_answered, -1); + break; + default: + vset = NewStrBuf(); } - /* CtdlLogPrintf(CTDL_DEBUG, "before optimize: %s\n", vset); */ + +#if 0 /* This is a special diagnostic section. Do not allow it to run during normal operation. */ + CtdlLogPrintf(CTDL_DEBUG, "There are %d messages in the room.\n", num_msgs); + for (i=0; i 0) && (msglist[i] <= msglist[i-1])) abort(); + } + CtdlLogPrintf(CTDL_DEBUG, "We are twiddling %d of them.\n", num_target_msgnums); + for (k=0; k 0) && (target_msgnums[k] <= target_msgnums[k-1])) abort(); + } +#endif + + CtdlLogPrintf(CTDL_DEBUG, "before update: %s\n", ChrPtr(vset)); /* Translate the existing sequence set into an array of booleans */ - num_sets = num_tokens(vset, ','); - for (s=0; s= 2) { - extract_token(histr, setstr, 1, ':', sizeof histr); - if (!strcmp(histr, "*")) { - snprintf(histr, sizeof histr, "%ld", LONG_MAX); - } + setstr = NewStrBuf(); + lostr = NewStrBuf(); + histr = NewStrBuf(); + pvset = NULL; + while (StrBufExtract_NextToken(setstr, vset, &pvset, ',') >= 0) { + + StrBufExtract_token(lostr, setstr, 0, ':'); + if (StrBufNum_tokens(setstr, ':') >= 2) { + StrBufExtract_token(histr, setstr, 1, ':'); + } + else { + FlushStrBuf(histr); + StrBufAppendBuf(histr, lostr, 0); + } + lo = StrTol(lostr); + if (!strcmp(ChrPtr(histr), "*")) { + hi = LONG_MAX; } else { - strcpy(histr, lostr); + hi = StrTol(histr); } - lo = atol(lostr); - hi = atol(histr); for (i = 0; i < num_msgs; ++i) { if ((msglist[i] >= lo) && (msglist[i] <= hi)) { @@ -416,85 +481,114 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums, } } } + FreeStrBuf(&setstr); + FreeStrBuf(&lostr); + FreeStrBuf(&histr); + /* Now translate the array of booleans back into a sequence set */ - strcpy(vset, ""); - lo = (-1L); - hi = (-1L); + FlushStrBuf(vset); + was_seen = 0; + lo = (-1); + hi = (-1); for (i=0; i sizeof vset) { - remove_token(vset, 0, ','); - trimming = 1; - if (j--) break; /* loop no more than 9 times */ + if (StrLength(vset) > 0) { + StrBufAppendBufPlain(vset, HKEY(","), 0); } - if ( (trimming) && (which_set == ctdlsetseen_seen) ) { - t = atol(vset); - if (t<2) t=2; - --t; - snprintf(lostr, sizeof lostr, - "1:%ld,%s", t, vset); - safestrncpy(vset, lostr, sizeof vset); + if (lo == hi) { + StrBufAppendPrintf(vset, "%ld", hi); + } + else { + StrBufAppendPrintf(vset, "%ld:%ld", lo, hi); } - /* end trim-o-matic code */ + } - tmp = strlen(vset); - if (tmp > 0) { - strcat(vset, ","); - ++tmp; + if ((is_seen) && (i == num_msgs - 1)) { + if (StrLength(vset) > 0) { + StrBufAppendBufPlain(vset, HKEY(","), 0); } - if (lo == hi) { - snprintf(&vset[tmp], (sizeof vset) - tmp, - "%ld", lo); + if ((i==0) || (was_seen == 0)) { + StrBufAppendPrintf(vset, "%ld", msglist[i]); } else { - snprintf(&vset[tmp], (sizeof vset) - tmp, - "%ld:%ld", lo, hi); + StrBufAppendPrintf(vset, "%ld:%ld", lo, msglist[i]); } - lo = (-1L); - hi = (-1L); } + was_seen = is_seen; } + /* + * We will have to stuff this string back into a 4096 byte buffer, so if it's + * larger than that now, truncate it by removing tokens from the beginning. + * The limit of 100 iterations is there to prevent an infinite loop in case + * something unexpected happens. + */ + int number_of_truncations = 0; + while ( (StrLength(vset) > SIZ) && (number_of_truncations < 100) ) { + StrBufRemove_token(vset, 0, ','); + ++number_of_truncations; + } + + /* + * If we're truncating the sequence set of messages marked with the 'seen' flag, + * we want the earliest messages (the truncated ones) to be marked, not unmarked. + * Otherwise messages at the beginning will suddenly appear to be 'unseen'. + */ + if ( (which_set == ctdlsetseen_seen) && (number_of_truncations > 0) ) { + StrBuf *first_tok; + first_tok = NewStrBuf(); + StrBufExtract_token(first_tok, vset, 0, ','); + StrBufRemove_token(vset, 0, ','); + + if (StrBufNum_tokens(first_tok, ':') > 1) { + StrBufRemove_token(first_tok, 0, ':'); + } + + StrBuf *new_set; + new_set = NewStrBuf(); + StrBufAppendBufPlain(new_set, HKEY("1:"), 0); + StrBufAppendBuf(new_set, first_tok, 0); + StrBufAppendBufPlain(new_set, HKEY(":"), 0); + StrBufAppendBuf(new_set, vset, 0); + + FreeStrBuf(&vset); + FreeStrBuf(&first_tok); + vset = new_set; + } + + CtdlLogPrintf(CTDL_DEBUG, " after update: %s\n", ChrPtr(vset)); + /* Decide which message set we're manipulating */ switch (which_set) { case ctdlsetseen_seen: - safestrncpy(vbuf.v_seen, vset, sizeof vbuf.v_seen); + safestrncpy(vbuf.v_seen, ChrPtr(vset), sizeof vbuf.v_seen); break; case ctdlsetseen_answered: - safestrncpy(vbuf.v_answered, vset, - sizeof vbuf.v_answered); + safestrncpy(vbuf.v_answered, ChrPtr(vset), sizeof vbuf.v_answered); break; } - free(is_set); - /* CtdlLogPrintf(CTDL_DEBUG, " after optimize: %s\n", vset); */ + free(is_set); free(msglist); - CtdlSetRelationship(&vbuf, - ((which_user != NULL) ? which_user : &CC->user), - ((which_room != NULL) ? which_room : &CC->room) - ); + CtdlSetRelationship(&vbuf, which_user, which_room); + FreeStrBuf(&vset); } @@ -505,7 +599,7 @@ void CtdlSetSeen(long *target_msgnums, int num_target_msgnums, int CtdlForEachMessage(int mode, long ref, char *search_string, char *content_type, struct CtdlMessage *compare, - void (*CallBack) (long, void *), + ForEachMsgCallback CallBack, void *userdata) { @@ -527,13 +621,13 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, int need_to_free_re = 0; regmatch_t pm; - if (content_type) if (!IsEmptyStr(content_type)) { + if ((content_type) && (!IsEmptyStr(content_type))) { regcomp(&re, content_type, 0); need_to_free_re = 1; } /* Learn about the user and room in question */ - getuser(&CC->user, CC->curr_user); + CtdlGetUser(&CC->user, CC->curr_user); CtdlGetRelationship(&vbuf, &CC->user, &CC->room); /* Load the message list */ @@ -555,7 +649,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, /* 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) if (!IsEmptyStr(content_type)) { + 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 @@ -698,14 +792,26 @@ void cmd_msgs(char *cmdbuf) int i; int with_template = 0; struct CtdlMessage *template = NULL; - int with_headers = 0; char search_string[1024]; + ForEachMsgCallback CallBack; extract_token(which, cmdbuf, 0, '|', sizeof which); cm_ref = extract_int(cmdbuf, 1); extract_token(search_string, cmdbuf, 1, '|', sizeof search_string); with_template = extract_int(cmdbuf, 2); - with_headers = extract_int(cmdbuf, 3); + switch (extract_int(cmdbuf, 3)) + { + default: + case MSG_HDRS_BRIEF: + CallBack = simple_listing; + break; + case MSG_HDRS_ALL: + CallBack = headers_listing; + break; + case MSG_HDRS_EUID: + CallBack = headers_euid; + break; + } strcat(which, " "); if (!strncasecmp(which, "OLD", 3)) @@ -744,7 +850,7 @@ void cmd_msgs(char *cmdbuf) template->cm_magic = CTDLMESSAGE_MAGIC; template->cm_anon_type = MES_NORMAL; - while(client_getln(buf, sizeof buf), strcmp(buf,"000")) { + while(client_getln(buf, sizeof buf) >= 0 && strcmp(buf,"000")) { extract_token(tfield, buf, 0, '|', sizeof tfield); extract_token(tvalue, buf, 1, '|', sizeof tvalue); for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) { @@ -761,13 +867,12 @@ void cmd_msgs(char *cmdbuf) } CtdlForEachMessage(mode, - ( (mode == MSGS_SEARCH) ? 0 : cm_ref ), - ( (mode == MSGS_SEARCH) ? search_string : NULL ), - NULL, - template, - (with_headers ? headers_listing : simple_listing), - NULL - ); + ( (mode == MSGS_SEARCH) ? 0 : cm_ref ), + ( (mode == MSGS_SEARCH) ? search_string : NULL ), + NULL, + template, + CallBack, + NULL); if (template != NULL) CtdlFreeMessage(template); cprintf("000\n"); } @@ -901,14 +1006,14 @@ void memfmout( */ void list_this_part(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) + char *cbid, void *cbuserdata) { struct ma_info *ma; ma = (struct ma_info *)cbuserdata; if (ma->is_ma == 0) { - cprintf("part=%s|%s|%s|%s|%s|%ld\n", - name, filename, partnum, disp, cbtype, (long)length); + cprintf("part=%s|%s|%s|%s|%s|%ld|%s\n", + name, filename, partnum, disp, cbtype, (long)length, cbid); } } @@ -917,7 +1022,7 @@ void list_this_part(char *name, char *filename, char *partnum, char *disp, */ void list_this_pref(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) + char *cbid, void *cbuserdata) { struct ma_info *ma; @@ -936,7 +1041,7 @@ void list_this_pref(char *name, char *filename, char *partnum, char *disp, */ void list_this_suff(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) + char *cbid, void *cbuserdata) { struct ma_info *ma; @@ -955,47 +1060,55 @@ void list_this_suff(char *name, char *filename, char *partnum, char *disp, */ void mime_download(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { + 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; - /* ...or if this is not the desired section */ - if (strcasecmp(CC->download_desired_section, partnum)) - return; - - CC->download_fp = tmpfile(); - if (CC->download_fp == NULL) - return; - - fwrite(content, length, 1, CC->download_fp); - fflush(CC->download_fp); - rewind(CC->download_fp); - - OpenCmdResult(filename, cbtype); + if ( + (!IsEmptyStr(partnum) && (!strcasecmp(CC->download_desired_section, partnum))) + || (!IsEmptyStr(cbid) && (!strcasecmp(CC->download_desired_section, cbid))) + ) { + CC->download_fp = tmpfile(); + if (CC->download_fp == NULL) + return; + + rv = fwrite(content, length, 1, CC->download_fp); + fflush(CC->download_fp); + rewind(CC->download_fp); + + OpenCmdResult(filename, cbtype); + } } /* - * Callback function for mime parser that outputs a section all at once + * 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, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { int *found_it = (int *)cbuserdata; - /* ...or if this is not the desired section */ - if (strcasecmp(CC->download_desired_section, partnum)) - return; - - *found_it = 1; - - cprintf("%d %d\n", BINARY_FOLLOWS, (int)length); - client_write(content, length); + if ( + (!IsEmptyStr(partnum) && (!strcasecmp(CC->download_desired_section, partnum))) + || (!IsEmptyStr(cbid) && (!strcasecmp(CC->download_desired_section, cbid))) + ) { + *found_it = 1; + cprintf("%d %d|-1|%s|%s\n", + BINARY_FOLLOWS, + (int)length, + filename, + cbtype + ); + client_write(content, length); + } } @@ -1137,7 +1250,7 @@ void CtdlFreeMessage(struct CtdlMessage *msg) */ void fixed_output_pre(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, char *encoding, - void *cbuserdata) + char *cbid, void *cbuserdata) { struct ma_info *ma; @@ -1157,7 +1270,7 @@ void fixed_output_pre(char *name, char *filename, char *partnum, char *disp, */ void fixed_output_post(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { struct ma_info *ma; @@ -1177,7 +1290,7 @@ void fixed_output_post(char *name, char *filename, char *partnum, char *disp, */ void fixed_output(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { char *ptr; char *wptr; @@ -1250,7 +1363,7 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp, */ void choose_preferred(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { char buf[1024]; int i; @@ -1280,7 +1393,7 @@ void choose_preferred(char *name, char *filename, char *partnum, char *disp, */ void output_preferred(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { int i; char buf[128]; @@ -1328,7 +1441,7 @@ void output_preferred(char *name, char *filename, char *partnum, char *disp, /* No translations required or possible: output as text/plain */ cprintf("Content-type: text/plain\n\n"); fixed_output(name, filename, partnum, disp, content, cbtype, cbcharset, - length, encoding, cbuserdata); + length, encoding, cbid, cbuserdata); } @@ -1344,7 +1457,7 @@ struct encapmsg { */ void extract_encapsulated_message(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, char *cbcharset, size_t length, - char *encoding, void *cbuserdata) + char *encoding, char *cbid, void *cbuserdata) { struct encapmsg *encap; @@ -1363,40 +1476,58 @@ void extract_encapsulated_message(char *name, char *filename, char *partnum, cha + + +int CtdlDoIHavePermissionToReadMessagesInThisRoom(void) { + if ((!(CC->logged_in)) && (!(CC->internal_pgm))) { + return(om_not_logged_in); + } + return(om_ok); +} + + /* * Get a message off disk. (returns om_* values found in msgbase.h) * */ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ - int mode, /* how would you like that message? */ - int headers_only, /* eschew the message body? */ - int do_proto, /* do Citadel protocol responses? */ - int crlf, /* Use CRLF newlines instead of LF? */ - char *section /* NULL or a message/rfc822 section */ + 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 */ ) { struct CtdlMessage *TheMessage = NULL; int retcode = om_no_such_msg; struct encapmsg encap; + int r; - CtdlLogPrintf(CTDL_DEBUG, "CtdlOutputMsg() msgnum=%ld, mode=%d, section=%s\n", + CtdlLogPrintf(CTDL_DEBUG, "CtdlOutputMsg(msgnum=%ld, mode=%d, section=%s)\n", msg_num, mode, (section ? section : "<>") ); - if ((!(CC->logged_in)) && (!(CC->internal_pgm))) { - if (do_proto) cprintf("%d Not logged in.\n", - ERROR + NOT_LOGGED_IN); - return(om_not_logged_in); + r = CtdlDoIHavePermissionToReadMessagesInThisRoom(); + if (r != om_ok) { + if (do_proto) { + if (r == om_not_logged_in) { + cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); + } + else { + cprintf("%d An unknown error has occurred.\n", ERROR); + } + } + return(r); } /* FIXME: check message id against msglist for this room */ /* - * Fetch the message from disk. If we're in any sort of headers - * only 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) || (headers_only == HEADERS_ONLY) ) { + if (headers_only == HEADERS_FAST) { TheMessage = CtdlFetchMessage(msg_num, 0); } else { @@ -1444,13 +1575,154 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ } /* Ok, output the message now */ - retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf); + retcode = CtdlOutputPreLoadedMsg(TheMessage, mode, headers_only, do_proto, crlf, flags); CtdlFreeMessage(TheMessage); return(retcode); } +char *qp_encode_email_addrs(char *source) +{ + char user[256], node[256], name[256]; + const char headerStr[] = "=?UTF-8?Q?"; + char *Encoded; + char *EncodedName; + char *nPtr; + int need_to_encode = 0; + long SourceLen; + long EncodedMaxLen; + long nColons = 0; + long *AddrPtr; + long *AddrUtf8; + long nAddrPtrMax = 50; + long nmax; + int InQuotes = 0; + int i, n; + + if (source == NULL) return source; + if (IsEmptyStr(source)) return source; + + AddrPtr = malloc (sizeof (long) * nAddrPtrMax); + AddrUtf8 = malloc (sizeof (long) * nAddrPtrMax); + memset(AddrUtf8, 0, sizeof (long) * nAddrPtrMax); + *AddrPtr = 0; + i = 0; + while (!IsEmptyStr (&source[i])) { + if (nColons >= nAddrPtrMax){ + long *ptr; + + ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2); + memcpy (ptr, AddrPtr, sizeof (long) * nAddrPtrMax); + free (AddrPtr), AddrPtr = ptr; + + ptr = (long *) malloc(sizeof (long) * nAddrPtrMax * 2); + memset(&ptr[nAddrPtrMax], 0, + sizeof (long) * nAddrPtrMax); + + memcpy (ptr, AddrUtf8, sizeof (long) * nAddrPtrMax); + free (AddrUtf8), AddrUtf8 = ptr; + nAddrPtrMax *= 2; + } + if (((unsigned char) source[i] < 32) || + ((unsigned char) source[i] > 126)) { + need_to_encode = 1; + AddrUtf8[nColons] = 1; + } + if (source[i] == '"') + InQuotes = !InQuotes; + if (!InQuotes && source[i] == ',') { + AddrPtr[nColons] = i; + nColons++; + } + i++; + } + if (need_to_encode == 0) { + free(AddrPtr); + free(AddrUtf8); + return source; + } + + SourceLen = i; + EncodedMaxLen = nColons * (sizeof(headerStr) + 3) + SourceLen * 3; + Encoded = (char*) malloc (EncodedMaxLen); + + for (i = 0; i < nColons; i++) + source[AddrPtr[i]++] = '\0'; + + nPtr = Encoded; + *nPtr = '\0'; + for (i = 0; i < nColons && nPtr != NULL; i++) { + nmax = EncodedMaxLen - (nPtr - Encoded); + if (AddrUtf8[i]) { + process_rfc822_addr(&source[AddrPtr[i]], + user, + node, + name); + /* TODO: libIDN here ! */ + if (IsEmptyStr(name)) { + n = snprintf(nPtr, nmax, + (i==0)?"%s@%s" : ",%s@%s", + user, node); + } + else { + EncodedName = rfc2047encode(name, strlen(name)); + n = snprintf(nPtr, nmax, + (i==0)?"%s <%s@%s>" : ",%s <%s@%s>", + EncodedName, user, node); + free(EncodedName); + } + } + else { + n = snprintf(nPtr, nmax, + (i==0)?"%s" : ",%s", + &source[AddrPtr[i]]); + } + if (n > 0 ) + nPtr += n; + else { + char *ptr, *nnPtr; + ptr = (char*) malloc(EncodedMaxLen * 2); + memcpy(ptr, Encoded, EncodedMaxLen); + nnPtr = ptr + (nPtr - Encoded), nPtr = nnPtr; + free(Encoded), Encoded = ptr; + EncodedMaxLen *= 2; + i--; /* do it once more with properly lengthened buffer */ + } + } + for (i = 0; i < nColons; i++) + source[--AddrPtr[i]] = ','; + free(AddrUtf8); + free(AddrPtr); + return Encoded; +} + + +/* If the last item in a list of recipients was truncated to a partial address, + * remove it completely in order to avoid choking libSieve + */ +void sanitize_truncated_recipient(char *str) +{ + if (!str) return; + if (num_tokens(str, ',') < 2) return; + + int len = strlen(str); + if (len < 900) return; + if (len > 998) str[998] = 0; + + char *cptr = strrchr(str, ','); + if (!cptr) return; + + char *lptr = strchr(cptr, '<'); + char *rptr = strchr(cptr, '>'); + + if ( (lptr) && (rptr) && (rptr > lptr) ) return; + + *cptr = 0; +} + + + /* * Get a message off disk. (returns om_* values found in msgbase.h) */ @@ -1459,14 +1731,15 @@ int CtdlOutputPreLoadedMsg( 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 crlf, /* Use CRLF newlines instead of LF? */ + int flags /* should the bessage be exported clean? */ ) { int i, j, k; char buf[SIZ]; - cit_uint8_t ch; + cit_uint8_t ch, prev_ch; char allkeys[30]; char display_name[256]; - char *mptr; + char *mptr, *mpptr; char *nl; /* newline string */ int suppress_f = 0; int subject_found = 0; @@ -1480,7 +1753,6 @@ int CtdlOutputPreLoadedMsg( char luser[100]; char fuser[100]; char snode[100]; - char lnode[100]; char mid[100]; char datestamp[100]; @@ -1494,9 +1766,17 @@ int CtdlOutputPreLoadedMsg( if (!is_valid_message(TheMessage)) { CtdlLogPrintf(CTDL_ERR, "ERROR: invalid preloaded message for output\n"); + cit_backtrace (); 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. + */ + if ( (flags & SUPPRESS_ENV_TO) && (TheMessage->cm_fields['V'] != NULL) ) { + memset(TheMessage->cm_fields['V'], ' ', strlen(TheMessage->cm_fields['V'])); + } + /* Are we downloading a MIME component? */ if (mode == MT_DOWNLOAD) { if (TheMessage->cm_format_type != FMT_RFC822) { @@ -1563,7 +1843,7 @@ int CtdlOutputPreLoadedMsg( /* nhdr=yes means that we're only displaying headers, no body */ if ( (TheMessage->cm_anon_type == MES_ANONONLY) - && (mode == MT_CITADEL) + && ((mode == MT_CITADEL) || (mode == MT_MIME)) && (do_proto) ) { cprintf("nhdr=yes\n"); @@ -1604,7 +1884,7 @@ int CtdlOutputPreLoadedMsg( if (haschar(TheMessage->cm_fields['N'], '.') == 0) { suppress_f = 1; } - + /* Now spew the header fields in the order we like them. */ safestrncpy(allkeys, FORDER, sizeof allkeys); for (i=0; icm_fields[k] != NULL) && (msgkeys[k] != NULL) ) { + if ((k == 'V') || (k == 'R') || (k == 'Y')) { + sanitize_truncated_recipient(TheMessage->cm_fields[k]); + } if (k == 'A') { if (do_proto) cprintf("%s=%s\n", msgkeys[k], @@ -1639,23 +1922,31 @@ int CtdlOutputPreLoadedMsg( strcpy(luser, ""); strcpy(fuser, ""); strcpy(snode, NODENAME); - strcpy(lnode, HUMANNODE); if (mode == MT_RFC822) { for (i = 0; i < 256; ++i) { if (TheMessage->cm_fields[i]) { - mptr = TheMessage->cm_fields[i]; - + mptr = mpptr = TheMessage->cm_fields[i]; + if (i == 'A') { safestrncpy(luser, mptr, sizeof luser); safestrncpy(suser, mptr, sizeof suser); } else if (i == 'Y') { + 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') { cprintf("Return-Path: %s%s", mptr, nl); } + else if (i == 'L') { + cprintf("List-ID: %s%s", mptr, nl); + } else if (i == 'V') { + if ((flags & QP_EADDR) != 0) + mptr = qp_encode_email_addrs(mptr); cprintf("Envelope-To: %s%s", mptr, nl); } else if (i == 'U') { @@ -1664,8 +1955,6 @@ int CtdlOutputPreLoadedMsg( } else if (i == 'I') safestrncpy(mid, mptr, sizeof mid); - else if (i == 'H') - safestrncpy(lnode, mptr, sizeof lnode); else if (i == 'F') safestrncpy(fuser, mptr, sizeof fuser); /* else if (i == 'O') @@ -1677,11 +1966,18 @@ int CtdlOutputPreLoadedMsg( { if (haschar(mptr, '@') == 0) { - cprintf("To: %s@%s%s", mptr, config.c_fqdn, nl); + sanitize_truncated_recipient(mptr); + cprintf("To: %s@%s", mptr, config.c_fqdn); + cprintf("%s", nl); } else { - cprintf("To: %s%s", mptr, nl); + if ((flags & QP_EADDR) != 0) { + mptr = qp_encode_email_addrs(mptr); + } + sanitize_truncated_recipient(mptr); + cprintf("To: %s", mptr); + cprintf("%s", nl); } } else if (i == 'T') { @@ -1703,6 +1999,8 @@ int CtdlOutputPreLoadedMsg( } } } + if (mptr != mpptr) + free (mptr); } } if (subject_found == 0) { @@ -1740,8 +2038,6 @@ int CtdlOutputPreLoadedMsg( cprintf("From: \"%s\" <%s@%s>%s", luser, suser, snode, nl); } - cprintf("Organization: %s%s", lnode, nl); - /* Blank line signifying RFC822 end-of-headers */ if (TheMessage->cm_format_type != FMT_RFC822) { cprintf("%s", nl); @@ -1775,6 +2071,7 @@ START_TEXT: char outbuf[1024]; int outlen = 0; int nllen = strlen(nl); + prev_ch = 0; while (ch=*mptr, ch!=0) { if (ch==13) { /* do nothing */ @@ -1794,6 +2091,14 @@ START_TEXT: } } } + if (flags & ESC_DOT) + { + if ((prev_ch == 10) && (ch == '.') && ((*(mptr+1) == 13) || (*(mptr+1) == 10))) + { + outbuf[outlen++] = '.'; + } + } + prev_ch = ch; ++mptr; if (outlen > 1000) { client_write(outbuf, outlen); @@ -1906,7 +2211,7 @@ void cmd_msg0(char *cmdbuf) msgid = extract_long(cmdbuf, 0); headers_only = extract_int(cmdbuf, 1); - CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL); + CtdlOutputMsg(msgid, MT_CITADEL, headers_only, 1, 0, NULL, 0); return; } @@ -1922,7 +2227,7 @@ void cmd_msg2(char *cmdbuf) msgid = extract_long(cmdbuf, 0); headers_only = extract_int(cmdbuf, 1); - CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL); + CtdlOutputMsg(msgid, MT_RFC822, headers_only, 1, 1, NULL, 0); } @@ -1976,7 +2281,7 @@ void cmd_msg4(char *cmdbuf) msgid = extract_long(cmdbuf, 0); extract_token(section, cmdbuf, 1, '|', sizeof section); - CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) ); + CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0, (section[0] ? section : NULL) , 0); } @@ -2009,7 +2314,7 @@ void cmd_opna(char *cmdbuf) extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section); safestrncpy(CC->download_desired_section, desired_section, sizeof CC->download_desired_section); - CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL); + CtdlOutputMsg(msgid, MT_DOWNLOAD, 0, 1, 1, NULL, 0); } @@ -2025,7 +2330,7 @@ void cmd_dlat(char *cmdbuf) extract_token(desired_section, cmdbuf, 1, '|', sizeof desired_section); safestrncpy(CC->download_desired_section, desired_section, sizeof CC->download_desired_section); - CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL); + CtdlOutputMsg(msgid, MT_SPEW_SECTION, 0, 1, 1, NULL, 0); } @@ -2068,7 +2373,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms if (num_newmsgs > 1) supplied_msg = NULL; /* Now the regular stuff */ - if (lgetroom(&CC->room, + if (CtdlGetRoomLock(&CC->room, ((roomname != NULL) ? roomname : CC->room.QRname) ) != 0) { CtdlLogPrintf(CTDL_ERR, "No such room <%s>\n", roomname); @@ -2135,7 +2440,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms /* Update the highest-message pointer and unlock the room. */ CC->room.QRhighest = highest_msg; - lputroom(&CC->room); + CtdlPutRoomLock(&CC->room); /* Perform replication checks if necessary */ if ( (DoesThisRoomNeedEuidIndexing(&CC->room)) && (do_repl_check) ) { @@ -2176,7 +2481,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms PerformRoomHooks(&CC->room); /* Go back to the room we were in before we wandered here... */ - getroom(&CC->room, hold_rm); + CtdlGetRoom(&CC->room, hold_rm); /* Bump the reference count for all messages which were merged */ for (i=0; i for room <%s>\n", msg->cm_fields['E'], CC->room.QRname);*/ - old_msgnum = locate_message_by_euid(msg->cm_fields['E'], &CC->room); + old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields['E'], &CC->room); if (old_msgnum > 0L) { CtdlLogPrintf(CTDL_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum); CtdlDeleteMessages(CC->room.QRname, &old_msgnum, 1, ""); @@ -2407,8 +2712,9 @@ void ReplicationChecks(struct CtdlMessage *msg) { * Save a message to disk and submit it into the delivery system. */ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ - struct recptypes *recps, /* recipients (if mail) */ - char *force /* force a particular room? */ + struct recptypes *recps, /* recipients (if mail) */ + char *force, /* force a particular room? */ + int flags /* should the message be exported clean? */ ) { char submit_filename[128]; char generated_timestamp[32]; @@ -2433,7 +2739,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ struct addresses_to_be_filed *aptr = NULL; char *saved_rfc822_version = NULL; int qualified_for_journaling = 0; - struct CitContext *CCC = CC; /* CachedCitContext - performance boost */ + CitContext *CCC = CC; /* CachedCitContext - performance boost */ + char bounce_to[1024] = ""; + size_t tmp = 0; + int rv = 0; CtdlLogPrintf(CTDL_DEBUG, "CtdlSubmitMsg() called\n"); if (is_valid_message(msg) == 0) return(-1); /* self check */ @@ -2526,8 +2835,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ CtdlLogPrintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm); if (strcasecmp(actual_rm, CCC->room.QRname)) { - /* getroom(&CCC->room, actual_rm); */ - usergoto(actual_rm, 0, 1, NULL, NULL); + /* CtdlGetRoom(&CCC->room, actual_rm); */ + CtdlUserGoto(actual_rm, 0, 1, NULL, NULL); } /* @@ -2582,7 +2891,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ CCC->redirect_buffer = malloc(SIZ); CCC->redirect_len = 0; CCC->redirect_alloc = SIZ; - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1); + CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1, QP_EADDR); smi.meta_rfc822_length = CCC->redirect_len; saved_rfc822_version = CCC->redirect_buffer; CCC->redirect_buffer = NULL; @@ -2606,14 +2915,12 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } /* For internet mail, drop a copy in the outbound queue room */ - if (recps != NULL) - if (recps->num_internet > 0) { + if ((recps != NULL) && (recps->num_internet > 0)) { CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0, msg); } /* If other rooms are specified, drop them there too. */ - if (recps != NULL) - if (recps->num_room > 0) + if ((recps != NULL) && (recps->num_room > 0)) for (i=0; irecp_room, '|'); ++i) { extract_token(recipient, recps->recp_room, i, '|', sizeof recipient); @@ -2623,26 +2930,36 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* Bump this user's messages posted counter. */ CtdlLogPrintf(CTDL_DEBUG, "Updating user\n"); - lgetuser(&CCC->user, CCC->curr_user); + CtdlGetUserLock(&CCC->user, CCC->curr_user); CCC->user.posted = CCC->user.posted + 1; - lputuser(&CCC->user); + CtdlPutUserLock(&CCC->user); + + /* Decide where bounces need to be delivered */ + if ((recps != NULL) && (recps->bounce_to != NULL)) { + safestrncpy(bounce_to, recps->bounce_to, sizeof bounce_to); + } + else if (CCC->logged_in) { + snprintf(bounce_to, sizeof bounce_to, "%s@%s", CCC->user.fullname, config.c_nodename); + } + else { + snprintf(bounce_to, sizeof bounce_to, "%s@%s", msg->cm_fields['A'], msg->cm_fields['N']); + } /* If this is private, local mail, make a copy in the * recipient's mailbox and bump the reference count. */ - if (recps != NULL) - if (recps->num_local > 0) + if ((recps != NULL) && (recps->num_local > 0)) for (i=0; irecp_local, '|'); ++i) { extract_token(recipient, recps->recp_local, i, '|', sizeof recipient); CtdlLogPrintf(CTDL_DEBUG, "Delivering private local mail to <%s>\n", recipient); - if (getuser(&userbuf, recipient) == 0) { + if (CtdlGetUser(&userbuf, recipient) == 0) { // Add a flag so the Funambol module knows its mail msg->cm_fields['W'] = strdup(recipient); - MailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM); + CtdlMailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM); CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg); - BumpNewMailCounter(userbuf.usernum); + CtdlBumpNewMailCounter(userbuf.usernum); if (!IsEmptyStr(config.c_funambol_host) || !IsEmptyStr(config.c_pager_program)) { /* Generate a instruction message for the Funambol notification * server, in the same style as the SMTP queue @@ -2651,9 +2968,9 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ instr = malloc(instr_alloc); snprintf(instr, instr_alloc, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n" - "bounceto|%s@%s\n", + "bounceto|%s\n", SPOOLMIME, newmsgid, (long)time(NULL), - msg->cm_fields['A'], msg->cm_fields['N'] + bounce_to ); imsg = malloc(sizeof(struct CtdlMessage)); @@ -2665,7 +2982,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ imsg->cm_fields['J'] = strdup("do not journal"); imsg->cm_fields['M'] = instr; /* imsg owns this memory now */ imsg->cm_fields['W'] = strdup(recipient); - CtdlSubmitMsg(imsg, NULL, FNBL_QUEUE_ROOM); + CtdlSubmitMsg(imsg, NULL, FNBL_QUEUE_ROOM, 0); CtdlFreeMessage(imsg); } } @@ -2688,8 +3005,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ * node. We'll revisit this again in a year or so when everyone has * a network spool receiver that can handle the new style messages. */ - if (recps != NULL) - if (recps->num_ignet > 0) + if ((recps != NULL) && (recps->num_ignet > 0)) for (i=0; irecp_ignet, '|'); ++i) { extract_token(recipient, recps->recp_ignet, i, '|', sizeof recipient); @@ -2709,7 +3025,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ (long) getpid(), CCC->cs_pid, ++seqnum); network_fp = fopen(submit_filename, "wb+"); if (network_fp != NULL) { - fwrite(smr.ser, smr.len, 1, network_fp); + rv = fwrite(smr.ser, smr.len, 1, network_fp); fclose(network_fp); } free(smr.ser); @@ -2724,27 +3040,31 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* Go back to the room we started from */ CtdlLogPrintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm); if (strcasecmp(hold_rm, CCC->room.QRname)) - usergoto(hold_rm, 0, 1, NULL, NULL); + CtdlUserGoto(hold_rm, 0, 1, NULL, NULL); /* For internet mail, generate delivery instructions. * Yes, this is recursive. Deal with it. Infinite recursion does * not happen because the delivery instructions message does not * contain a recipient. */ - if (recps != NULL) - if (recps->num_internet > 0) { + if ((recps != NULL) && (recps->num_internet > 0)) { CtdlLogPrintf(CTDL_DEBUG, "Generating delivery instructions\n"); instr_alloc = 1024; instr = malloc(instr_alloc); snprintf(instr, instr_alloc, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n" - "bounceto|%s@%s\n", + "bounceto|%s\n", SPOOLMIME, newmsgid, (long)time(NULL), - msg->cm_fields['A'], msg->cm_fields['N'] + bounce_to ); + if (recps->envelope_from != NULL) { + tmp = strlen(instr); + snprintf(&instr[tmp], instr_alloc-tmp, "envelope_from|%s\n", recps->envelope_from); + } + for (i=0; irecp_internet, '|'); ++i) { - size_t tmp = strlen(instr); + tmp = strlen(instr); extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient); if ((tmp + strlen(recipient) + 32) > instr_alloc) { instr_alloc = instr_alloc * 2; @@ -2761,7 +3081,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ imsg->cm_fields['A'] = strdup("Citadel"); imsg->cm_fields['J'] = strdup("do not journal"); imsg->cm_fields['M'] = instr; /* imsg owns this memory now */ - CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); + CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR); CtdlFreeMessage(imsg); } @@ -2773,14 +3093,14 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } if (collected_addresses != NULL) { - begin_critical_section(S_ATBF); aptr = (struct addresses_to_be_filed *) malloc(sizeof(struct addresses_to_be_filed)); - aptr->next = atbf; - MailboxName(actual_rm, sizeof actual_rm, + CtdlMailboxName(actual_rm, sizeof actual_rm, &CCC->user, USERCONTACTSROOM); aptr->roomname = strdup(actual_rm); aptr->collected_addresses = collected_addresses; + begin_critical_section(S_ATBF); + aptr->next = atbf; atbf = aptr; end_critical_section(S_ATBF); } @@ -2823,13 +3143,17 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ +void aide_message (char *text, char *subject) +{ + quickie_message("Citadel",NULL,NULL,AIDEROOM,text,FMT_CITADEL,subject); +} /* * Convenience function for generating small administrative messages. */ -void quickie_message(char *from, char *fromaddr, char *to, char *room, char *text, - int format_type, char *subject) +void quickie_message(const char *from, const char *fromaddr, char *to, char *room, const char *text, + int format_type, const char *subject) { struct CtdlMessage *msg; struct recptypes *recp = NULL; @@ -2865,7 +3189,7 @@ void quickie_message(char *from, char *fromaddr, char *to, char *room, char *tex } msg->cm_fields['M'] = strdup(text); - CtdlSubmitMsg(msg, recp, room); + CtdlSubmitMsg(msg, recp, room, 0); CtdlFreeMessage(msg); if (recp != NULL) free_recipients(recp); } @@ -2995,7 +3319,8 @@ struct CtdlMessage *CtdlMakeMessage( 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 *preformatted_text, /* ...or NULL to read text from client */ + char *references /* Thread references */ ) { char dest_node[256]; char buf[1024]; @@ -3010,8 +3335,8 @@ struct CtdlMessage *CtdlMakeMessage( /* Don't confuse the poor folks if it's not routed mail. */ strcpy(dest_node, ""); - striplt(recipient); - striplt(recp_cc); + if (recipient != NULL) striplt(recipient); + if (recp_cc != NULL) striplt(recp_cc); /* Path or Return-Path */ if (my_email == NULL) my_email = ""; @@ -3028,10 +3353,12 @@ struct CtdlMessage *CtdlMakeMessage( snprintf(buf, sizeof buf, "%ld", (long)time(NULL)); /* timestamp */ msg->cm_fields['T'] = strdup(buf); - if (fake_name[0]) /* author */ + if ((fake_name != NULL) && (fake_name[0])) { /* author */ msg->cm_fields['A'] = strdup(fake_name); - else + } + else { msg->cm_fields['A'] = strdup(author->fullname); + } if (CC->room.QRflags & QR_MAILBOX) { /* room */ msg->cm_fields['O'] = strdup(&CC->room.QRname[11]); @@ -3043,10 +3370,10 @@ struct CtdlMessage *CtdlMakeMessage( msg->cm_fields['N'] = strdup(NODENAME); /* nodename */ msg->cm_fields['H'] = strdup(HUMANNODE); /* hnodename */ - if (recipient[0] != 0) { + if ((recipient != NULL) && (recipient[0] != 0)) { msg->cm_fields['R'] = strdup(recipient); } - if (recp_cc[0] != 0) { + if ((recp_cc != NULL) && (recp_cc[0] != 0)) { msg->cm_fields['Y'] = strdup(recp_cc); } if (dest_node[0] != 0) { @@ -3086,6 +3413,12 @@ struct CtdlMessage *CtdlMakeMessage( msg->cm_fields['E'] = strdup(supplied_euid); } + if (references != NULL) { + if (!IsEmptyStr(references)) { + msg->cm_fields['W'] = strdup(references); + } + } + if (preformatted_text != NULL) { msg->cm_fields['M'] = preformatted_text; } @@ -3323,7 +3656,7 @@ struct recptypes *validate_recipients(char *supplied_recipients, strcat(ret->recp_room, this_recp); } else if ( (!strncasecmp(this_recp, "room_", 5)) - && (!getroom(&tempQR, &this_recp_cooked[5])) ) { + && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) { /* Save room so we can restore it later */ tempQR2 = CC->room; @@ -3352,7 +3685,7 @@ struct recptypes *validate_recipients(char *supplied_recipients, CC->room = tempQR2; } - else if (getuser(&tempUS, this_recp) == 0) { + else if (CtdlGetUser(&tempUS, this_recp) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); if (!IsEmptyStr(ret->recp_local)) { @@ -3360,7 +3693,7 @@ struct recptypes *validate_recipients(char *supplied_recipients, } strcat(ret->recp_local, this_recp); } - else if (getuser(&tempUS, this_recp_cooked) == 0) { + else if (CtdlGetUser(&tempUS, this_recp_cooked) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); if (!IsEmptyStr(ret->recp_local)) { @@ -3469,6 +3802,8 @@ void free_recipients(struct recptypes *valid) { if (valid->recp_ignet != NULL) free(valid->recp_ignet); if (valid->recp_room != NULL) free(valid->recp_room); if (valid->display_recp != NULL) free(valid->display_recp); + if (valid->bounce_to != NULL) free(valid->bounce_to); + if (valid->envelope_from != NULL) free(valid->envelope_from); free(valid); } @@ -3503,6 +3838,8 @@ void cmd_ent0(char *entargs) int i, j; char buf[256]; int newuseremail_ok = 0; + char references[SIZ]; + char *ptr; unbuffer_output(); @@ -3525,6 +3862,10 @@ void cmd_ent0(char *entargs) break; } extract_token(newuseremail, entargs, 10, '|', sizeof newuseremail); + extract_token(references, entargs, 11, '|', sizeof references); + for (ptr=references; *ptr != 0; ++ptr) { + if (*ptr == '!') *ptr = '|'; + } /* first check to make sure the request is valid. */ @@ -3685,9 +4026,9 @@ void cmd_ent0(char *entargs) * is set, or if there is one or more Internet email recipients. */ if (CC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1; - if (valid_to) if (valid_to->num_internet > 0) subject_required = 1; - if (valid_cc) if (valid_cc->num_internet > 0) subject_required = 1; - if (valid_bcc) if (valid_bcc->num_internet > 0) subject_required = 1; + if ((valid_to) && (valid_to->num_internet > 0)) subject_required = 1; + if ((valid_cc) && (valid_cc->num_internet > 0)) subject_required = 1; + if ((valid_bcc) && (valid_bcc->num_internet > 0)) subject_required = 1; /* If we're only checking the validity of the request, return * success without creating the message. @@ -3718,7 +4059,7 @@ void cmd_ent0(char *entargs) CC->room.QRname, anonymous, format_type, newusername, newuseremail, subject, ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL), - NULL); + NULL, references); /* Put together one big recipients struct containing to/cc/bcc all in * one. This is for the envelope. @@ -3746,7 +4087,7 @@ void cmd_ent0(char *entargs) free(all_recps); if (msg != NULL) { - msgnum = CtdlSubmitMsg(msg, valid, ""); + msgnum = CtdlSubmitMsg(msg, valid, "", QP_EADDR); if (do_confirm) { cprintf("%ld\n", msgnum); @@ -3805,7 +4146,7 @@ int CtdlDeleteMessages(char *room_name, /* which room */ room_name, num_dmsgnums, content_type); /* get room record, obtaining a lock... */ - if (lgetroom(&qrbuf, room_name) != 0) { + if (CtdlGetRoomLock(&qrbuf, room_name) != 0) { CtdlLogPrintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n", room_name); if (need_to_free_re) regfree(&re); @@ -3862,7 +4203,7 @@ int CtdlDeleteMessages(char *room_name, /* which room */ qrbuf.QRhighest = msglist[num_msgs - 1]; } - lputroom(&qrbuf); + 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 @@ -3947,19 +4288,6 @@ void cmd_dele(char *args) } -/* - * Back end API function for moves and deletes (multiple messages) - */ -int CtdlCopyMsgsToRoom(long *msgnums, int num_msgs, char *dest) { - int err; - - err = CtdlSaveMsgPointersInRoom(dest, msgnums, num_msgs, 1, NULL); - if (err != 0) return(err); - - return(0); -} - - /* @@ -3992,13 +4320,17 @@ void cmd_move(char *args) targ[ROOMNAMELEN - 1] = 0; is_copy = extract_int(args, 2); - if (getroom(&qtemp, targ) != 0) { - cprintf("%d '%s' does not exist.\n", - ERROR + ROOM_NOT_FOUND, targ); + if (CtdlGetRoom(&qtemp, targ) != 0) { + cprintf("%d '%s' does not exist.\n", ERROR + ROOM_NOT_FOUND, targ); return; } - getuser(&CC->user, CC->curr_user); + if (!strcasecmp(qtemp.QRname, CC->room.QRname)) { + cprintf("%d Source and target rooms are the same.\n", ERROR + ALREADY_EXISTS); + return; + } + + CtdlGetUser(&CC->user, CC->curr_user); CtdlRoomAccess(&qtemp, &CC->user, &ra, NULL); /* Check for permission to perform this operation. @@ -4049,7 +4381,7 @@ void cmd_move(char *args) /* * Do the copy */ - err = CtdlCopyMsgsToRoom(msgs, num_msgs, targ); + err = CtdlSaveMsgPointersInRoom(targ, msgs, num_msgs, 1, NULL); if (err != 0) { cprintf("%d Cannot store message(s) in %s: error %d\n", err, targ, err); @@ -4121,6 +4453,7 @@ void PutMetaData(struct MetaData *smibuf) void AdjRefCount(long msgnum, int incr) { struct arcq new_arcq; + int rv = 0; begin_critical_section(S_SUPPMSGMAIN); if (arcfp == NULL) { @@ -4150,7 +4483,7 @@ void AdjRefCount(long msgnum, int incr) new_arcq.arcq_msgnum = msgnum; new_arcq.arcq_delta = incr; - fwrite(&new_arcq, sizeof(struct arcq), 1, arcfp); + rv = fwrite(&new_arcq, sizeof(struct arcq), 1, arcfp); fflush(arcfp); return; @@ -4172,7 +4505,7 @@ int TDAP_ProcessAdjRefCountQueue(void) struct arcq arcq_rec; int num_records_processed = 0; - snprintf(file_arcq_temp, sizeof file_arcq_temp, "%s2", file_arcq); + snprintf(file_arcq_temp, sizeof file_arcq_temp, "%s.%04x", file_arcq, rand()); begin_critical_section(S_SUPPMSGMAIN); if (arcfp != NULL) { @@ -4235,7 +4568,7 @@ void TDAP_AdjRefCount(long msgnum, int incr) smi.meta_refcount += incr; PutMetaData(&smi); end_critical_section(S_SUPPMSGMAIN); - CtdlLogPrintf(CTDL_DEBUG, "msg %ld ref count delta %d, is now %d\n", + CtdlLogPrintf(CTDL_DEBUG, "msg %ld ref count delta %+d, is now %d\n", msgnum, incr, smi.meta_refcount); /* If the reference count is now zero, delete the message @@ -4265,50 +4598,33 @@ void TDAP_AdjRefCount(long msgnum, int incr) * Note: this could be much more efficient. Right now we use two temporary * files, and still pull the message into memory as with all others. */ -void CtdlWriteObject(char *req_room, /* Room to stuff it in */ - char *content_type, /* MIME type of this object */ - char *tempfilename, /* Where to fetch it from */ +void 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? */ - int is_unique, /* Del others of this type? */ - unsigned int flags /* Internal save flags */ + int is_binary, /* Is encoding necessary? */ + int is_unique, /* Del others of this type? */ + unsigned int flags /* Internal save flags */ ) { - FILE *fp; struct ctdlroom qrbuf; char roomname[ROOMNAMELEN]; struct CtdlMessage *msg; - - char *raw_message = NULL; char *encoded_message = NULL; - off_t raw_length = 0; if (is_mailbox != NULL) { - MailboxName(roomname, sizeof roomname, is_mailbox, req_room); + CtdlMailboxName(roomname, sizeof roomname, is_mailbox, req_room); } else { safestrncpy(roomname, req_room, sizeof(roomname)); } - fp = fopen(tempfilename, "rb"); - if (fp == NULL) { - CtdlLogPrintf(CTDL_CRIT, "Cannot open %s: %s\n", - tempfilename, strerror(errno)); - return; - } - fseek(fp, 0L, SEEK_END); - raw_length = ftell(fp); - rewind(fp); CtdlLogPrintf(CTDL_DEBUG, "Raw length is %ld\n", (long)raw_length); - raw_message = malloc((size_t)raw_length + 2); - fread(raw_message, (size_t)raw_length, 1, fp); - fclose(fp); - if (is_binary) { - encoded_message = malloc((size_t) - (((raw_length * 134) / 100) + 4096 ) ); + encoded_message = malloc((size_t) (((raw_length * 134) / 100) + 4096 ) ); } else { encoded_message = malloc((size_t)(raw_length + 4096)); @@ -4336,7 +4652,6 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ ); } else { - raw_message[raw_length] = 0; memcpy( &encoded_message[strlen(encoded_message)], raw_message, @@ -4344,8 +4659,6 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ ); } - free(raw_message); - CtdlLogPrintf(CTDL_DEBUG, "Allocating\n"); msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); @@ -4361,8 +4674,8 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ msg->cm_fields['M'] = encoded_message; /* Create the requested room if we have to. */ - if (getroom(&qrbuf, roomname) != 0) { - create_room(roomname, + if (CtdlGetRoom(&qrbuf, roomname) != 0) { + CtdlCreateRoom(roomname, ( (is_mailbox != NULL) ? 5 : 3 ), "", 0, 1, 0, VIEW_BBS); } @@ -4375,7 +4688,7 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ ); } /* Now write the data */ - CtdlSubmitMsg(msg, NULL, roomname); + CtdlSubmitMsg(msg, NULL, roomname, 0); CtdlFreeMessage(msg); } @@ -4397,8 +4710,8 @@ char *CtdlGetSysConfig(char *sysconfname) { char buf[SIZ]; strcpy(hold_rm, CC->room.QRname); - if (getroom(&CC->room, SYSCONFIGROOM) != 0) { - getroom(&CC->room, hold_rm); + if (CtdlGetRoom(&CC->room, SYSCONFIGROOM) != 0) { + CtdlGetRoom(&CC->room, hold_rm); return NULL; } @@ -4425,7 +4738,7 @@ char *CtdlGetSysConfig(char *sysconfname) { } } - getroom(&CC->room, hold_rm); + CtdlGetRoom(&CC->room, hold_rm); if (conf != NULL) do { extract_token(buf, conf, 0, '\n', sizeof buf); @@ -4435,20 +4748,9 @@ char *CtdlGetSysConfig(char *sysconfname) { return(conf); } -void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) { - char temp[PATH_MAX]; - FILE *fp; - - CtdlMakeTempFileName(temp, sizeof temp); - - fp = fopen(temp, "w"); - if (fp == NULL) return; - fprintf(fp, "%s", sysconfdata); - fclose(fp); - /* this handy API function does all the work for us */ - CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0); - unlink(temp); +void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) { + CtdlWriteObject(SYSCONFIGROOM, sysconfname, sysconfdata, (strlen(sysconfdata)+1), NULL, 0, 1, 0); } @@ -4498,3 +4800,29 @@ void cmd_isme(char *argbuf) { } } + + +/*****************************************************************************/ +/* MODULE INITIALIZATION STUFF */ +/*****************************************************************************/ + +CTDL_MODULE_INIT(msgbase) +{ + if (!threading) { + CtdlRegisterProtoHook(cmd_msgs, "MSGS", "Output a list of messages in the current room"); + CtdlRegisterProtoHook(cmd_msg0, "MSG0", "Output a message in plain text format"); + CtdlRegisterProtoHook(cmd_msg2, "MSG2", "Output a message in RFC822 format"); + CtdlRegisterProtoHook(cmd_msg3, "MSG3", "Output a message in raw format (deprecated)"); + CtdlRegisterProtoHook(cmd_msg4, "MSG4", "Output a message in the client's preferred format"); + CtdlRegisterProtoHook(cmd_msgp, "MSGP", "Select preferred format for MSG4 output"); + CtdlRegisterProtoHook(cmd_opna, "OPNA", "Open an attachment for download"); + CtdlRegisterProtoHook(cmd_dlat, "DLAT", "Download an attachment"); + CtdlRegisterProtoHook(cmd_ent0, "ENT0", "Enter a message"); + CtdlRegisterProtoHook(cmd_dele, "DELE", "Delete a message"); + CtdlRegisterProtoHook(cmd_move, "MOVE", "Move or copy a message to another room"); + CtdlRegisterProtoHook(cmd_isme, "ISME", "Determine whether an email address belongs to a user"); + } + + /* return our Subversion id for the Log */ + return "$Id$"; +}