X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmsgbase.c;h=e83a6d36bcca1229285cc4292bf2993202889f4e;hb=9d6ac635379521753572f0641718f58f154b2aa3;hp=e1f88781de7029513201aed871e1ac7281710ecb;hpb=744c57519f693b69cd2f5b602f26ba209bdf53ab;p=citadel.git diff --git a/citadel/msgbase.c b/citadel/msgbase.c index e1f88781d..e83a6d36b 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" @@ -31,6 +46,7 @@ #include #include #include +#include #include "citadel.h" #include "server.h" #include "serv_extensions.h" @@ -44,21 +60,16 @@ #include "file_ops.h" #include "config.h" #include "control.h" -#include "tools.h" -#include "mime_parser.h" -#include "html.h" #include "genstamp.h" #include "internet_addressing.h" -#include "serv_fulltext.h" -#include "vcard.h" #include "euidindex.h" #include "journaling.h" #include "citadel_dirs.h" +#include "clientsocket.h" #include "serv_network.h" +#include "threads.h" -#ifdef HAVE_LIBSIEVE -# include "serv_sieve.h" -#endif /* HAVE_LIBSIEVE */ +#include "ctdl_module.h" long config_msgnum; struct addresses_to_be_filed *atbf = NULL; @@ -95,7 +106,8 @@ char *msgkeys[] = { "hnod", "msgn", "jrnl", - NULL, NULL, + NULL, + "list", "text", "node", "room", @@ -106,7 +118,7 @@ char *msgkeys[] = { "time", "subj", NULL, - NULL, + "wefw", NULL, "cccc", NULL @@ -188,7 +200,7 @@ int alias(char *name) } if (strcasecmp(original_name, name)) { - lprintf(CTDL_INFO, "%s is being forwarded to %s\n", original_name, name); + CtdlLogPrintf(CTDL_INFO, "%s is being forwarded to %s\n", original_name, name); } /* Change "user @ xxx" to "user" if xxx is an alias for this host */ @@ -196,7 +208,7 @@ int alias(char *name) if (name[a] == '@') { if (CtdlHostAlias(&name[a+1]) == hostalias_localhost) { name[a] = 0; - lprintf(CTDL_INFO, "Changed to <%s>\n", name); + CtdlLogPrintf(CTDL_INFO, "Changed to <%s>\n", name); } } } @@ -303,6 +315,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], @@ -340,40 +354,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; } - lprintf(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 */ @@ -388,33 +409,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(); } - /* lprintf(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 { - strcpy(histr, lostr); + FlushStrBuf(histr); + StrBufAppendBuf(histr, lostr, 0); + } + lo = StrTol(lostr); + if (!strcmp(ChrPtr(histr), "*")) { + hi = LONG_MAX; + } + else { + hi = StrTol(histr); } - lo = atol(lostr); - hi = atol(histr); for (i = 0; i < num_msgs; ++i) { if ((msglist[i] >= lo) && (msglist[i] <= hi)) { @@ -422,85 +462,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 (lo == hi) { + StrBufAppendPrintf(vset, "%ld", hi); } - 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); + 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); - /* lprintf(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); } @@ -533,7 +602,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, int need_to_free_re = 0; regmatch_t pm; - if (content_type) if (strlen(content_type) > 0) { + if ((content_type) && (!IsEmptyStr(content_type))) { regcomp(&re, content_type, 0); need_to_free_re = 1; } @@ -546,9 +615,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); if (cdbfr != NULL) { msglist = (long *) cdbfr->ptr; - cdbfr->ptr = NULL; /* CtdlForEachMessage() now owns this memory */ num_msgs = cdbfr->len / sizeof(long); - cdb_free(cdbfr); } else { if (need_to_free_re) regfree(&re); return 0; /* No messages at all? No further action. */ @@ -563,7 +630,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 (strlen(content_type) > 0) { + 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 @@ -614,7 +681,13 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, * over again. */ if ( (num_msgs > 0) && (mode == MSGS_SEARCH) && (search_string) ) { - ft_search(&num_search_msgs, &search_msgs, 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"); + if (num_search_msgs > 0) { int orig_num_msgs; @@ -678,7 +751,7 @@ int CtdlForEachMessage(int mode, long ref, char *search_string, ++num_processed; } } - free(msglist); /* Clean up */ + cdb_free(cdbfr); /* Clean up */ if (need_to_free_re) regfree(&re); return num_processed; } @@ -746,7 +819,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) { @@ -807,7 +880,7 @@ void do_help_subst(char *buffer) help_subst(buffer, "^variantname", CITADEL); snprintf(buf2, sizeof buf2, "%d", config.c_maxsessions); help_subst(buffer, "^maxsessions", buf2); - help_subst(buffer, "^bbsdir", ctdl_bbsbase_dir); + help_subst(buffer, "^bbsdir", ctdl_message_dir); } @@ -903,14 +976,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); } } @@ -919,7 +992,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; @@ -938,7 +1011,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; @@ -957,47 +1030,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); + } } @@ -1018,7 +1099,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) cit_uint8_t ch; cit_uint8_t field_header; - lprintf(CTDL_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body); + CtdlLogPrintf(CTDL_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body); dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); if (dmsgtext == NULL) { @@ -1034,9 +1115,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) */ ch = *mptr++; if (ch != 255) { - lprintf(CTDL_ERR, - "Message %ld appears to be corrupted.\n", - msgnum); + CtdlLogPrintf(CTDL_ERR, "Message %ld appears to be corrupted.\n", msgnum); cdb_free(dmsgtext); return NULL; } @@ -1099,7 +1178,7 @@ int is_valid_message(struct CtdlMessage *msg) { if (msg == NULL) return 0; if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) { - lprintf(CTDL_WARNING, "is_valid_message() -- self-check failed\n"); + CtdlLogPrintf(CTDL_WARNING, "is_valid_message() -- self-check failed\n"); return 0; } return 1; @@ -1141,12 +1220,12 @@ 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; ma = (struct ma_info *)cbuserdata; - lprintf(CTDL_DEBUG, "fixed_output_pre() type=<%s>\n", cbtype); + CtdlLogPrintf(CTDL_DEBUG, "fixed_output_pre() type=<%s>\n", cbtype); if (!strcasecmp(cbtype, "multipart/alternative")) { ++ma->is_ma; ma->did_print = 0; @@ -1161,12 +1240,12 @@ 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; ma = (struct ma_info *)cbuserdata; - lprintf(CTDL_DEBUG, "fixed_output_post() type=<%s>\n", cbtype); + CtdlLogPrintf(CTDL_DEBUG, "fixed_output_post() type=<%s>\n", cbtype); if (!strcasecmp(cbtype, "multipart/alternative")) { --ma->is_ma; ma->did_print = 0; @@ -1181,7 +1260,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; @@ -1190,7 +1269,7 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp, ma = (struct ma_info *)cbuserdata; - lprintf(CTDL_DEBUG, + CtdlLogPrintf(CTDL_DEBUG, "fixed_output() part %s: %s (%s) (%ld bytes)\n", partnum, filename, cbtype, (long)length); @@ -1199,14 +1278,13 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp, * we've already printed another section, skip this one. */ if ( (ma->is_ma) && (ma->did_print) ) { - lprintf(CTDL_DEBUG, "Skipping part %s (%s)\n", - partnum, cbtype); + CtdlLogPrintf(CTDL_DEBUG, "Skipping part %s (%s)\n", partnum, cbtype); return; } ma->did_print = 1; if ( (!strcasecmp(cbtype, "text/plain")) - || (strlen(cbtype)==0) ) { + || (IsEmptyStr(cbtype)) ) { wptr = content; if (length > 0) { client_write(wptr, length); @@ -1255,7 +1333,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; @@ -1263,14 +1341,18 @@ void choose_preferred(char *name, char *filename, char *partnum, char *disp, ma = (struct ma_info *)cbuserdata; - if (ma->is_ma > 0) { - for (i=0; ipreferred_formats, '|'); ++i) { - extract_token(buf, CC->preferred_formats, i, '|', sizeof buf); - if ( (!strcasecmp(buf, cbtype)) && (!ma->freeze) ) { - if (i < ma->chosen_pref) { - safestrncpy(ma->chosen_part, partnum, sizeof ma->chosen_part); - ma->chosen_pref = i; - } + // NOTE: REMOVING THIS CONDITIONAL FIXES BUG 220 + // http://bugzilla.citadel.org/show_bug.cgi?id=220 + // I don't know if there are any side effects! Please TEST TEST TEST + //if (ma->is_ma > 0) { + + for (i=0; ipreferred_formats, '|'); ++i) { + extract_token(buf, CC->preferred_formats, i, '|', sizeof buf); + if ( (!strcasecmp(buf, cbtype)) && (!ma->freeze) ) { + if (i < ma->chosen_pref) { + CtdlLogPrintf(CTDL_DEBUG, "Setting chosen part: <%s>\n", partnum); + safestrncpy(ma->chosen_part, partnum, sizeof ma->chosen_part); + ma->chosen_pref = i; } } } @@ -1281,7 +1363,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]; @@ -1307,12 +1389,12 @@ void output_preferred(char *name, char *filename, char *partnum, char *disp, ++add_newline; } cprintf("Content-type: %s", cbtype); - if (strlen(cbcharset) > 0) { + if (!IsEmptyStr(cbcharset)) { cprintf("; charset=%s", cbcharset); } cprintf("\nContent-length: %d\n", (int)(length + add_newline) ); - if (strlen(encoding) > 0) { + if (!IsEmptyStr(encoding)) { cprintf("Content-transfer-encoding: %s\n", encoding); } else { @@ -1329,7 +1411,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); } @@ -1345,7 +1427,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; @@ -1364,40 +1446,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; - lprintf(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 { @@ -1413,7 +1513,7 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ /* Here is the weird form of this command, to process only an * encapsulated message/rfc822 section. */ - if (section) if (strlen(section)>0) if (strcmp(section, "0")) { + 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(TheMessage->cm_fields['M'], @@ -1445,32 +1545,171 @@ 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) - * */ 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 crlf, /* Use CRLF newlines instead of LF? */ + int flags /* should the bessage be exported clean? */ ) { - int i, k; + 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; @@ -1484,11 +1723,10 @@ int CtdlOutputPreLoadedMsg( char luser[100]; char fuser[100]; char snode[100]; - char lnode[100]; char mid[100]; char datestamp[100]; - lprintf(CTDL_DEBUG, "CtdlOutputPreLoadedMsg(TheMessage=%s, %d, %d, %d, %d\n", + CtdlLogPrintf(CTDL_DEBUG, "CtdlOutputPreLoadedMsg(TheMessage=%s, %d, %d, %d, %d\n", ((TheMessage == NULL) ? "NULL" : "not null"), mode, headers_only, do_proto, crlf); @@ -1496,11 +1734,19 @@ int CtdlOutputPreLoadedMsg( nl = (crlf ? "\r\n" : "\n"); if (!is_valid_message(TheMessage)) { - lprintf(CTDL_ERR, + 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) { @@ -1567,7 +1813,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,11 +1850,11 @@ int CtdlOutputPreLoadedMsg( */ suppress_f = 0; if (TheMessage->cm_fields['N'] != NULL) - if (strlen(TheMessage->cm_fields['N']) > 0) + if (!IsEmptyStr(TheMessage->cm_fields['N'])) 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], @@ -1643,23 +1892,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') { @@ -1668,8 +1925,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') @@ -1678,12 +1933,44 @@ int CtdlOutputPreLoadedMsg( else if (i == 'N') safestrncpy(snode, mptr, sizeof snode); else if (i == 'R') - cprintf("To: %s%s", mptr, nl); + { + if (haschar(mptr, '@') == 0) + { + sanitize_truncated_recipient(mptr); + cprintf("To: %s@%s", mptr, config.c_fqdn); + cprintf("%s", nl); + } + else + { + 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') { datestring(datestamp, sizeof datestamp, atol(mptr), DATESTRING_RFC822); cprintf("Date: %s%s", datestamp, nl); } + else if (i == 'W') { + cprintf("References: "); + k = num_tokens(mptr, '|'); + for (j=0; j", buf); + if (j == (k-1)) { + cprintf("%s", nl); + } + else { + cprintf(" "); + } + } + } + if (mptr != mpptr) + free (mptr); } } if (subject_found == 0) { @@ -1691,7 +1978,7 @@ int CtdlOutputPreLoadedMsg( } } - for (i=0; icm_anon_type == MES_ANONOPT)) { cprintf("From: \"anonymous\" %s", nl); } - else if (strlen(fuser) > 0) { + else if (!IsEmptyStr(fuser)) { cprintf("From: \"%s\" <%s>%s", luser, fuser, nl); } else { 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); @@ -1756,6 +2041,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 */ @@ -1775,6 +2061,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); @@ -1804,22 +2098,27 @@ START_TEXT: * what message transfer format is in use. */ if (TheMessage->cm_format_type == FMT_FIXED) { + int buflen; if (mode == MT_MIME) { cprintf("Content-type: text/plain\n\n"); } - strcpy(buf, ""); + *buf = '\0'; + buflen = 0; while (ch = *mptr++, ch > 0) { if (ch == 13) ch = 10; - if ((ch == 10) || (strlen(buf) > 250)) { + if ((ch == 10) || (buflen > 250)) { + buf[buflen] = '\0'; cprintf("%s%s", buf, nl); - strcpy(buf, ""); + *buf = '\0'; + buflen = 0; } else { - buf[strlen(buf) + 1] = 0; - buf[strlen(buf)] = ch; + buf[buflen] = ch; + buflen++; } } - if (strlen(buf) > 0) + buf[buflen] = '\0'; + if (!IsEmptyStr(buf)) cprintf("%s%s", buf, nl); } @@ -1853,7 +2152,7 @@ START_TEXT: *choose_preferred, *fixed_output_pre, *fixed_output_post, (void *)&ma, 0); mime_parser(mptr, NULL, - *output_preferred, NULL, NULL, (void *)&ma, 0); + *output_preferred, NULL, NULL, (void *)&ma, CC->msg4_dont_decode); } else { ma.use_fo_hooks = 1; @@ -1882,7 +2181,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; } @@ -1898,7 +2197,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); } @@ -1952,7 +2251,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); } @@ -1962,9 +2261,14 @@ void cmd_msg4(char *cmdbuf) */ void cmd_msgp(char *cmdbuf) { - safestrncpy(CC->preferred_formats, cmdbuf, - sizeof(CC->preferred_formats)); - cprintf("%d ok\n", CIT_OK); + if (!strcasecmp(cmdbuf, "dont_decode")) { + CC->msg4_dont_decode = 1; + cprintf("%d MSG4 will not pre-decode messages.\n", CIT_OK); + } + else { + safestrncpy(CC->preferred_formats, cmdbuf, sizeof(CC->preferred_formats)); + cprintf("%d Preferred MIME formats have been set.\n", CIT_OK); + } } @@ -1980,7 +2284,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); } @@ -1996,7 +2300,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); } @@ -2027,7 +2331,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms long *msgs_to_be_merged = NULL; int num_msgs_to_be_merged = 0; - lprintf(CTDL_DEBUG, + CtdlLogPrintf(CTDL_DEBUG, "CtdlSaveMsgPointersInRoom(room=%s, num_msgs=%d, repl=%d)\n", roomname, num_newmsgs, do_repl_check); @@ -2039,10 +2343,10 @@ 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) { - lprintf(CTDL_ERR, "No such room <%s>\n", roomname); + CtdlLogPrintf(CTDL_ERR, "No such room <%s>\n", roomname); return(ERROR + ROOM_NOT_FOUND); } @@ -2079,14 +2383,14 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms } } - lprintf(9, "%d unique messages to be merged\n", num_msgs_to_be_merged); + CtdlLogPrintf(9, "%d unique messages to be merged\n", num_msgs_to_be_merged); /* * Now merge the new messages */ msglist = realloc(msglist, (sizeof(long) * (num_msgs + num_msgs_to_be_merged)) ); if (msglist == NULL) { - lprintf(CTDL_ALERT, "ERROR: can't realloc message list!\n"); + CtdlLogPrintf(CTDL_ALERT, "ERROR: can't realloc message list!\n"); } memcpy(&msglist[num_msgs], msgs_to_be_merged, (sizeof(long) * num_msgs_to_be_merged) ); num_msgs += num_msgs_to_be_merged; @@ -2106,11 +2410,11 @@ 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) ) { - lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom() doing repl checks\n"); + CtdlLogPrintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom() doing repl checks\n"); for (i=0; iroom); /* 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; ilen = 0; ret->ser = NULL; return; @@ -2283,7 +2587,7 @@ void serialize_message(struct ser_ret *ret, /* return values */ ret->ser = malloc(ret->len); if (ret->ser == NULL) { - lprintf(CTDL_ERR, "serialize_message() malloc(%ld) failed: %s\n", + CtdlLogPrintf(CTDL_ERR, "serialize_message() malloc(%ld) failed: %s\n", (long)ret->len, strerror(errno)); ret->len = 0; ret->ser = NULL; @@ -2301,13 +2605,50 @@ void serialize_message(struct ser_ret *ret, /* return values */ safestrncpy((char *)&ret->ser[wlen], msg->cm_fields[(int)forder[i]], fieldlen+1); wlen = wlen + fieldlen + 1; } - if (ret->len != wlen) lprintf(CTDL_ERR, "ERROR: len=%ld wlen=%ld\n", + if (ret->len != wlen) CtdlLogPrintf(CTDL_ERR, "ERROR: len=%ld wlen=%ld\n", (long)ret->len, (long)wlen); return; } +/* + * Serialize a struct CtdlMessage into the format used on disk and network. + * + * 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 dump_message(struct CtdlMessage *msg, /* unserialized msg */ + long Siz) /* how many chars ? */ +{ + size_t wlen; + int i; + static char *forder = FORDER; + char *buf; + + /* + * Check for valid message format + */ + if (is_valid_message(msg) == 0) { + CtdlLogPrintf(CTDL_ERR, "dump_message() aborting due to invalid message\n"); + return; + } + + buf = (char*) malloc (Siz + 1); + + wlen = 3; + + for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) { + snprintf (buf, Siz, " msg[%c] = %s ...\n", (char) forder[i], + msg->cm_fields[(int)forder[i]]); + client_write (buf, strlen(buf)); + } + + return; +} + + /* * Check to see if any messages already exist in the current room which @@ -2318,19 +2659,19 @@ void ReplicationChecks(struct CtdlMessage *msg) { if (DoesThisRoomNeedEuidIndexing(&CC->room) == 0) return; - lprintf(CTDL_DEBUG, "Performing replication checks in <%s>\n", + CtdlLogPrintf(CTDL_DEBUG, "Performing replication checks in <%s>\n", CC->room.QRname); /* No exclusive id? Don't do anything. */ if (msg == NULL) return; if (msg->cm_fields['E'] == NULL) return; - if (strlen(msg->cm_fields['E']) == 0) return; - /*lprintf(CTDL_DEBUG, "Exclusive ID: <%s> for room <%s>\n", + if (IsEmptyStr(msg->cm_fields['E'])) return; + /*CtdlLogPrintf(CTDL_DEBUG, "Exclusive ID: <%s> for room <%s>\n", msg->cm_fields['E'], CC->room.QRname);*/ old_msgnum = locate_message_by_euid(msg->cm_fields['E'], &CC->room); if (old_msgnum > 0L) { - lprintf(CTDL_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum); + CtdlLogPrintf(CTDL_DEBUG, "ReplicationChecks() replacing message %ld\n", old_msgnum); CtdlDeleteMessages(CC->room.QRname, &old_msgnum, 1, ""); } } @@ -2341,8 +2682,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 bessage be exported clean? */ ) { char submit_filename[128]; char generated_timestamp[32]; @@ -2367,8 +2709,12 @@ 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 */ + char bounce_to[1024] = ""; + size_t tmp = 0; + int rv = 0; - lprintf(CTDL_DEBUG, "CtdlSubmitMsg() called\n"); + CtdlLogPrintf(CTDL_DEBUG, "CtdlSubmitMsg() called\n"); if (is_valid_message(msg) == 0) return(-1); /* self check */ /* If this message has no timestamp, we take the liberty of @@ -2384,7 +2730,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ if (msg->cm_fields['P'] == NULL) { if (msg->cm_fields['A'] != NULL) { msg->cm_fields['P'] = strdup(msg->cm_fields['A']); - for (a=0; acm_fields['P']); ++a) { + for (a=0; !IsEmptyStr(&msg->cm_fields['P'][a]); ++a) { if (isspace(msg->cm_fields['P'][a])) { msg->cm_fields['P'][a] = ' '; } @@ -2404,7 +2750,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* Learn about what's inside, because it's what's inside that counts */ if (msg->cm_fields['M'] == NULL) { - lprintf(CTDL_ERR, "ERROR: attempt to save message with NULL body\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR: attempt to save message with NULL body\n"); return(-2); } @@ -2419,68 +2765,71 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ strcpy(content_type, "text/plain"); mptr = bmstrcasestr(msg->cm_fields['M'], "Content-type:"); if (mptr != NULL) { + char *aptr; safestrncpy(content_type, &mptr[13], sizeof content_type); striplt(content_type); - for (a = 0; a < strlen(content_type); ++a) { - if ((content_type[a] == ';') - || (content_type[a] == ' ') - || (content_type[a] == 13) - || (content_type[a] == 10)) { - content_type[a] = 0; + aptr = content_type; + while (!IsEmptyStr(aptr)) { + if ((*aptr == ';') + || (*aptr == ' ') + || (*aptr == 13) + || (*aptr == 10)) { + *aptr = 0; } + else aptr++; } } } /* Goto the correct room */ - lprintf(CTDL_DEBUG, "Selected room %s\n", (recps) ? CC->room.QRname : SENTITEMS); - strcpy(hold_rm, CC->room.QRname); - strcpy(actual_rm, CC->room.QRname); + CtdlLogPrintf(CTDL_DEBUG, "Selected room %s\n", (recps) ? CCC->room.QRname : SENTITEMS); + strcpy(hold_rm, CCC->room.QRname); + strcpy(actual_rm, CCC->room.QRname); if (recps != NULL) { strcpy(actual_rm, SENTITEMS); } /* If the user is a twit, move to the twit room for posting */ if (TWITDETECT) { - if (CC->user.axlevel == 2) { + if (CCC->user.axlevel == 2) { strcpy(hold_rm, actual_rm); strcpy(actual_rm, config.c_twitroom); - lprintf(CTDL_DEBUG, "Diverting to twit room\n"); + CtdlLogPrintf(CTDL_DEBUG, "Diverting to twit room\n"); } } /* ...or if this message is destined for Aide> then go there. */ - if (strlen(force_room) > 0) { + if (!IsEmptyStr(force_room)) { strcpy(actual_rm, force_room); } - lprintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm); - if (strcasecmp(actual_rm, CC->room.QRname)) { - /* getroom(&CC->room, actual_rm); */ - usergoto(actual_rm, 0, 1, NULL, NULL); + CtdlLogPrintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm); + if (strcasecmp(actual_rm, CCC->room.QRname)) { + /* CtdlGetRoom(&CCC->room, actual_rm); */ + CtdlUserGoto(actual_rm, 0, 1, NULL, NULL); } /* * If this message has no O (room) field, generate one. */ if (msg->cm_fields['O'] == NULL) { - msg->cm_fields['O'] = strdup(CC->room.QRname); + msg->cm_fields['O'] = strdup(CCC->room.QRname); } /* Perform "before save" hooks (aborting if any return nonzero) */ - lprintf(CTDL_DEBUG, "Performing before-save hooks\n"); + CtdlLogPrintf(CTDL_DEBUG, "Performing before-save hooks\n"); if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-3); /* * If this message has an Exclusive ID, and the room is replication * checking enabled, then do replication checks. */ - if (DoesThisRoomNeedEuidIndexing(&CC->room)) { + if (DoesThisRoomNeedEuidIndexing(&CCC->room)) { ReplicationChecks(msg); } /* Save it to disk */ - lprintf(CTDL_DEBUG, "Saving to disk\n"); + CtdlLogPrintf(CTDL_DEBUG, "Saving to disk\n"); newmsgid = send_message(msg); if (newmsgid <= 0L) return(-5); @@ -2488,7 +2837,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ * be a critical section because nobody else knows about this message * yet. */ - lprintf(CTDL_DEBUG, "Creating MetaData record\n"); + CtdlLogPrintf(CTDL_DEBUG, "Creating MetaData record\n"); memset(&smi, 0, sizeof(struct MetaData)); smi.meta_msgnum = newmsgid; smi.meta_refcount = 0; @@ -2505,67 +2854,75 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ * 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) { - lprintf(CTDL_ALERT, "CC->redirect_buffer is not NULL during message submission!\n"); + if (CCC->redirect_buffer != NULL) { + CtdlLogPrintf(CTDL_ALERT, "CCC->redirect_buffer is not NULL during message submission!\n"); abort(); } - CC->redirect_buffer = malloc(SIZ); - CC->redirect_len = 0; - CC->redirect_alloc = SIZ; - CtdlOutputPreLoadedMsg(msg, MT_RFC822, HEADERS_ALL, 0, 1); - smi.meta_rfc822_length = CC->redirect_len; - saved_rfc822_version = CC->redirect_buffer; - CC->redirect_buffer = NULL; - CC->redirect_len = 0; - CC->redirect_alloc = 0; + CCC->redirect_buffer = malloc(SIZ); + CCC->redirect_len = 0; + CCC->redirect_alloc = SIZ; + 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; + CCC->redirect_len = 0; + CCC->redirect_alloc = 0; PutMetaData(&smi); /* Now figure out where to store the pointers */ - lprintf(CTDL_DEBUG, "Storing pointers\n"); + CtdlLogPrintf(CTDL_DEBUG, "Storing pointers\n"); /* 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 ((!CCC->internal_pgm) || (recps == NULL)) { if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 1, msg) != 0) { - lprintf(CTDL_ERR, "ERROR saving message pointer!\n"); + CtdlLogPrintf(CTDL_ERR, "ERROR saving message pointer!\n"); CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0, msg); } } /* 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); - lprintf(CTDL_DEBUG, "Delivering to room <%s>\n", recipient); + CtdlLogPrintf(CTDL_DEBUG, "Delivering to room <%s>\n", recipient); CtdlSaveMsgPointerInRoom(recipient, newmsgid, 0, msg); } /* Bump this user's messages posted counter. */ - lprintf(CTDL_DEBUG, "Updating user\n"); - lgetuser(&CC->user, CC->curr_user); - CC->user.posted = CC->user.posted + 1; - lputuser(&CC->user); + CtdlLogPrintf(CTDL_DEBUG, "Updating user\n"); + lgetuser(&CCC->user, CCC->curr_user); + CCC->user.posted = CCC->user.posted + 1; + lputuser(&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); - lprintf(CTDL_DEBUG, "Delivering private local mail to <%s>\n", + CtdlLogPrintf(CTDL_DEBUG, "Delivering private local mail to <%s>\n", recipient); if (getuser(&userbuf, recipient) == 0) { // Add a flag so the Funambol module knows its mail @@ -2573,7 +2930,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ MailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM); CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg); BumpNewMailCounter(userbuf.usernum); - if (strlen(config.c_funambol_host) > 0) { + 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 */ @@ -2581,9 +2938,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)); @@ -2595,19 +2952,19 @@ 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); } } else { - lprintf(CTDL_DEBUG, "No user <%s>\n", recipient); + CtdlLogPrintf(CTDL_DEBUG, "No user <%s>\n", recipient); CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0, msg); } } /* Perform "after save" hooks */ - lprintf(CTDL_DEBUG, "Performing after-save hooks\n"); + CtdlLogPrintf(CTDL_DEBUG, "Performing after-save hooks\n"); PerformMessageHooks(msg, EVT_AFTERSAVE); /* For IGnet mail, we have to save a new copy into the spooler for @@ -2618,8 +2975,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); @@ -2636,10 +2992,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ snprintf(submit_filename, sizeof submit_filename, "%s/netmail.%04lx.%04x.%04x", ctdl_netin_dir, - (long) getpid(), CC->cs_pid, ++seqnum); + (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); @@ -2652,29 +3008,33 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } /* Go back to the room we started from */ - lprintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm); - if (strcasecmp(hold_rm, CC->room.QRname)) - usergoto(hold_rm, 0, 1, NULL, NULL); + CtdlLogPrintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm); + if (strcasecmp(hold_rm, CCC->room.QRname)) + 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) { - lprintf(CTDL_DEBUG, "Generating delivery instructions\n"); + 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; @@ -2691,26 +3051,26 @@ 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); } /* * Any addresses to harvest for someone's address book? */ - if ( (CC->logged_in) && (recps != NULL) ) { + if ( (CCC->logged_in) && (recps != NULL) ) { collected_addresses = harvest_collected_addresses(msg); } 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, - &CC->user, USERCONTACTSROOM); + &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); } @@ -2753,13 +3113,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; @@ -2788,14 +3152,14 @@ void quickie_message(char *from, char *fromaddr, char *to, char *room, char *tex msg->cm_fields['N'] = strdup(NODENAME); if (to != NULL) { msg->cm_fields['R'] = strdup(to); - recp = validate_recipients(to); + recp = validate_recipients(to, NULL, 0); } if (subject != NULL) { msg->cm_fields['U'] = strdup(subject); } msg->cm_fields['M'] = strdup(text); - CtdlSubmitMsg(msg, recp, room); + CtdlSubmitMsg(msg, recp, room, 0); CtdlFreeMessage(msg); if (recp != NULL) free_recipients(recp); } @@ -2809,7 +3173,8 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ size_t maxlen, /* maximum message length */ char *exist, /* if non-null, append to it; exist is ALWAYS freed */ - int crlf /* CRLF newlines instead of LF */ + int crlf, /* CRLF newlines instead of LF */ + int sock /* socket handle or 0 for this session's client socket */ ) { char buf[1024]; int linelen; @@ -2849,7 +3214,12 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ /* read in the lines of message text one by one */ do { - if (client_getln(buf, (sizeof buf - 3)) < 1) finished = 1; + if (sock > 0) { + if (sock_getln(sock, buf, (sizeof buf - 3)) < 0) finished = 1; + } + else { + if (client_getln(buf, (sizeof buf - 3)) < 1) finished = 1; + } if (!strcmp(buf, terminator)) finished = 1; if (crlf) { strcat(buf, "\r\n"); @@ -2877,7 +3247,7 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ } else { buffer_len = (buffer_len * 2); m = ptr; - lprintf(CTDL_DEBUG, "buffer_len is now %ld\n", (long)buffer_len); + CtdlLogPrintf(CTDL_DEBUG, "buffer_len is now %ld\n", (long)buffer_len); } } @@ -2919,12 +3289,12 @@ 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]; struct CtdlMessage *msg; - int i; msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); @@ -2935,32 +3305,30 @@ 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 = ""; - if (strlen(my_email) > 0) { + if (!IsEmptyStr(my_email)) { msg->cm_fields['P'] = strdup(my_email); } else { snprintf(buf, sizeof buf, "%s", author->fullname); msg->cm_fields['P'] = strdup(buf); } - for (i=0; (msg->cm_fields['P'][i]!=0); ++i) { - if (isspace(msg->cm_fields['P'][i])) { - msg->cm_fields['P'][i] = '_'; - } - } + convert_spaces_to_underscores(msg->cm_fields['P']); 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]); @@ -2972,20 +3340,20 @@ 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) { msg->cm_fields['D'] = strdup(dest_node); } - if (strlen(my_email) > 0) { + if (!IsEmptyStr(my_email)) { msg->cm_fields['F'] = strdup(my_email); } - else if ( (author == &CC->user) && (strlen(CC->cs_inet_email) > 0) ) { + else if ( (author == &CC->user) && (!IsEmptyStr(CC->cs_inet_email)) ) { msg->cm_fields['F'] = strdup(CC->cs_inet_email); } @@ -3015,11 +3383,17 @@ 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; } else { - msg->cm_fields['M'] = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0); + msg->cm_fields['M'] = CtdlReadMessageBody("000", config.c_maxmsglen, NULL, 0, 0); } return(msg); @@ -3031,13 +3405,65 @@ struct CtdlMessage *CtdlMakeMessage( * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or * returns 0 on success. */ -int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf, size_t n) { +int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf, + size_t n, + const char* RemoteIdentifier, + int PostPublic) { int ra; - if (!(CC->logged_in)) { + if (!(CC->logged_in) && + (PostPublic == POST_LOGGED_IN)) { snprintf(errmsgbuf, n, "Not logged in."); return (ERROR + NOT_LOGGED_IN); } + else if (PostPublic == CHECK_EXISTANCE) { + return (0); // We're Evaling whether a recipient exists + } + else if (!(CC->logged_in)) { + + if ((CC->room.QRflags & QR_READONLY)) { + snprintf(errmsgbuf, n, "Not logged in."); + return (ERROR + NOT_LOGGED_IN); + } + if (CC->room.QRflags2 & QR2_MODERATED) { + snprintf(errmsgbuf, n, "Not logged in Moderation feature not yet implemented!"); + return (ERROR + NOT_LOGGED_IN); + } + if ((PostPublic!=POST_LMTP) &&(CC->room.QRflags2 & QR2_SMTP_PUBLIC) == 0) { + SpoolControl *sc; + char filename[SIZ]; + int found; + + if (RemoteIdentifier == NULL) + { + snprintf(errmsgbuf, n, "Need sender to permit access."); + return (ERROR + USERNAME_REQUIRED); + } + + assoc_file_name(filename, sizeof filename, &CC->room, ctdl_netcfg_dir); + begin_critical_section(S_NETCONFIGS); + if (!read_spoolcontrol_file(&sc, filename)) + { + end_critical_section(S_NETCONFIGS); + snprintf(errmsgbuf, n, + "This mailing list only accepts posts from subscribers."); + return (ERROR + NO_SUCH_USER); + } + end_critical_section(S_NETCONFIGS); + found = is_recipient (sc, RemoteIdentifier); + free_spoolcontrol_struct(&sc); + if (found) { + return (0); + } + else { + snprintf(errmsgbuf, n, + "This mailing list only accepts posts from subscribers."); + return (ERROR + NO_SUCH_USER); + } + } + return (0); + + } if ((CC->user.axlevel < 2) && ((CC->room.QRflags & QR_MAILBOX) == 0)) { @@ -3089,7 +3515,9 @@ int CtdlCheckInternetMailPermission(struct ctdluser *who) { * * Caller needs to free the result using free_recipients() */ -struct recptypes *validate_recipients(char *supplied_recipients) { +struct recptypes *validate_recipients(char *supplied_recipients, + const char *RemoteIdentifier, + int Flags) { struct recptypes *ret; char *recipients = NULL; char this_recp[256]; @@ -3101,6 +3529,9 @@ struct recptypes *validate_recipients(char *supplied_recipients) { int invalid; struct ctdluser tempUS; struct ctdlroom tempQR; + struct ctdlroom tempQR2; + int err = 0; + char errmsg[SIZ]; int in_quotes = 0; /* Initialize */ @@ -3139,7 +3570,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) { ret->recptypes_magic = RECPTYPES_MAGIC; /* Change all valid separator characters to commas */ - for (i=0; i 0) { + while (!IsEmptyStr(recipients)) { for (i=0; i<=strlen(recipients); ++i) { if (recipients[i] == '\"') in_quotes = 1 - in_quotes; @@ -3165,12 +3596,15 @@ struct recptypes *validate_recipients(char *supplied_recipients) { } striplt(this_recp); - lprintf(CTDL_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp); + if (IsEmptyStr(this_recp)) + break; + CtdlLogPrintf(CTDL_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp); ++num_recps; mailtype = alias(this_recp); mailtype = alias(this_recp); mailtype = alias(this_recp); - for (j=0; j<=strlen(this_recp); ++j) { + j = 0; + for (j=0; !IsEmptyStr(&this_recp[j]); ++j) { if (this_recp[j]=='_') { this_recp_cooked[j] = ' '; } @@ -3178,21 +3612,53 @@ struct recptypes *validate_recipients(char *supplied_recipients) { this_recp_cooked[j] = this_recp[j]; } } + this_recp_cooked[j] = '\0'; invalid = 0; + errmsg[0] = 0; switch(mailtype) { case MES_LOCAL: if (!strcasecmp(this_recp, "sysop")) { ++ret->num_room; strcpy(this_recp, config.c_aideroom); - if (strlen(ret->recp_room) > 0) { + if (!IsEmptyStr(ret->recp_room)) { strcat(ret->recp_room, "|"); } strcat(ret->recp_room, this_recp); } + else if ( (!strncasecmp(this_recp, "room_", 5)) + && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) { + + /* Save room so we can restore it later */ + tempQR2 = CC->room; + CC->room = tempQR; + + /* Check permissions to send mail to this room */ + err = CtdlDoIHavePermissionToPostInThisRoom(errmsg, + sizeof errmsg, + RemoteIdentifier, + Flags + ); + if (err) + { + ++ret->num_error; + invalid = 1; + } + else { + ++ret->num_room; + if (!IsEmptyStr(ret->recp_room)) { + strcat(ret->recp_room, "|"); + } + strcat(ret->recp_room, &this_recp_cooked[5]); + } + + /* Restore room in case something needs it */ + CC->room = tempQR2; + + } else if (getuser(&tempUS, this_recp) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); - if (strlen(ret->recp_local) > 0) { + if (!IsEmptyStr(ret->recp_local)) { strcat(ret->recp_local, "|"); } strcat(ret->recp_local, this_recp); @@ -3200,19 +3666,11 @@ struct recptypes *validate_recipients(char *supplied_recipients) { else if (getuser(&tempUS, this_recp_cooked) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); - if (strlen(ret->recp_local) > 0) { + if (!IsEmptyStr(ret->recp_local)) { strcat(ret->recp_local, "|"); } strcat(ret->recp_local, this_recp); } - else if ( (!strncasecmp(this_recp, "room_", 5)) - && (!getroom(&tempQR, &this_recp_cooked[5])) ) { - ++ret->num_room; - if (strlen(ret->recp_room) > 0) { - strcat(ret->recp_room, "|"); - } - strcat(ret->recp_room, &this_recp_cooked[5]); - } else { ++ret->num_error; invalid = 1; @@ -3231,7 +3689,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) { } else { ++ret->num_internet; - if (strlen(ret->recp_internet) > 0) { + if (!IsEmptyStr(ret->recp_internet)) { strcat(ret->recp_internet, "|"); } strcat(ret->recp_internet, this_recp); @@ -3239,7 +3697,7 @@ struct recptypes *validate_recipients(char *supplied_recipients) { break; case MES_IGNET: ++ret->num_ignet; - if (strlen(ret->recp_ignet) > 0) { + if (!IsEmptyStr(ret->recp_ignet)) { strcat(ret->recp_ignet, "|"); } strcat(ret->recp_ignet, this_recp); @@ -3250,20 +3708,21 @@ struct recptypes *validate_recipients(char *supplied_recipients) { break; } if (invalid) { - if (strlen(ret->errormsg) == 0) { - snprintf(append, sizeof append, - "Invalid recipient: %s", - this_recp); + if (IsEmptyStr(errmsg)) { + snprintf(append, sizeof append, "Invalid recipient: %s", this_recp); } else { - snprintf(append, sizeof append, ", %s", this_recp); + snprintf(append, sizeof append, "%s", errmsg); } - if ( (strlen(ret->errormsg) + strlen(append)) < SIZ) { + if ( (strlen(ret->errormsg) + strlen(append) + 3) < SIZ) { + if (!IsEmptyStr(ret->errormsg)) { + strcat(ret->errormsg, "; "); + } strcat(ret->errormsg, append); } } else { - if (strlen(ret->display_recp) == 0) { + if (IsEmptyStr(ret->display_recp)) { strcpy(append, this_recp); } else { @@ -3281,12 +3740,12 @@ struct recptypes *validate_recipients(char *supplied_recipients) { strcpy(ret->errormsg, "No recipients specified."); } - lprintf(CTDL_DEBUG, "validate_recipients()\n"); - lprintf(CTDL_DEBUG, " local: %d <%s>\n", ret->num_local, ret->recp_local); - lprintf(CTDL_DEBUG, " room: %d <%s>\n", ret->num_room, ret->recp_room); - lprintf(CTDL_DEBUG, " inet: %d <%s>\n", ret->num_internet, ret->recp_internet); - lprintf(CTDL_DEBUG, " ignet: %d <%s>\n", ret->num_ignet, ret->recp_ignet); - lprintf(CTDL_DEBUG, " error: %d <%s>\n", ret->num_error, ret->errormsg); + CtdlLogPrintf(CTDL_DEBUG, "validate_recipients()\n"); + CtdlLogPrintf(CTDL_DEBUG, " local: %d <%s>\n", ret->num_local, ret->recp_local); + CtdlLogPrintf(CTDL_DEBUG, " room: %d <%s>\n", ret->num_room, ret->recp_room); + CtdlLogPrintf(CTDL_DEBUG, " inet: %d <%s>\n", ret->num_internet, ret->recp_internet); + CtdlLogPrintf(CTDL_DEBUG, " ignet: %d <%s>\n", ret->num_ignet, ret->recp_ignet); + CtdlLogPrintf(CTDL_DEBUG, " error: %d <%s>\n", ret->num_error, ret->errormsg); free(recipients); return(ret); @@ -3303,7 +3762,7 @@ void free_recipients(struct recptypes *valid) { } if (valid->recptypes_magic != RECPTYPES_MAGIC) { - lprintf(CTDL_EMERG, "Attempt to call free_recipients() on some other data type!\n"); + CtdlLogPrintf(CTDL_EMERG, "Attempt to call free_recipients() on some other data type!\n"); abort(); } @@ -3313,6 +3772,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); } @@ -3347,6 +3808,8 @@ void cmd_ent0(char *entargs) int i, j; char buf[256]; int newuseremail_ok = 0; + char references[SIZ]; + char *ptr; unbuffer_output(); @@ -3369,10 +3832,14 @@ 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. */ - err = CtdlDoIHavePermissionToPostInThisRoom(errmsg, sizeof errmsg); + err = CtdlDoIHavePermissionToPostInThisRoom(errmsg, sizeof errmsg, NULL, POST_LOGGED_IN); if (err) { cprintf("%d %s\n", err, errmsg); @@ -3381,7 +3848,7 @@ void cmd_ent0(char *entargs) /* Check some other permission type things. */ - if (strlen(newusername) == 0) { + if (IsEmptyStr(newusername)) { strcpy(newusername, CC->user.fullname); } if ( (CC->user.axlevel < 6) @@ -3396,15 +3863,15 @@ void cmd_ent0(char *entargs) } - if (strlen(newuseremail) == 0) { + if (IsEmptyStr(newuseremail)) { newuseremail_ok = 1; } - if (strlen(newuseremail) > 0) { + if (!IsEmptyStr(newuseremail)) { if (!strcasecmp(newuseremail, CC->cs_inet_email)) { newuseremail_ok = 1; } - else if (strlen(CC->cs_inet_other_emails) > 0) { + else if (!IsEmptyStr(CC->cs_inet_other_emails)) { j = num_tokens(CC->cs_inet_other_emails, '|'); for (i=0; ics_inet_other_emails, i, '|', sizeof buf); @@ -3440,24 +3907,24 @@ void cmd_ent0(char *entargs) strcpy(bcc, ""); } - valid_to = validate_recipients(recp); + valid_to = validate_recipients(recp, NULL, 0); if (valid_to->num_error > 0) { - cprintf("%d Invalid recipient (To)\n", ERROR + NO_SUCH_USER); + cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_to->errormsg); free_recipients(valid_to); return; } - valid_cc = validate_recipients(cc); + valid_cc = validate_recipients(cc, NULL, 0); if (valid_cc->num_error > 0) { - cprintf("%d Invalid recipient (CC)\n", ERROR + NO_SUCH_USER); + cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_cc->errormsg); free_recipients(valid_to); free_recipients(valid_cc); return; } - valid_bcc = validate_recipients(bcc); + valid_bcc = validate_recipients(bcc, NULL, 0); if (valid_bcc->num_error > 0) { - cprintf("%d Invalid recipient (BCC)\n", ERROR + NO_SUCH_USER); + cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid_bcc->errormsg); free_recipients(valid_to); free_recipients(valid_cc); free_recipients(valid_bcc); @@ -3529,9 +3996,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. @@ -3561,28 +4028,28 @@ void cmd_ent0(char *entargs) msg = CtdlMakeMessage(&CC->user, recp, cc, CC->room.QRname, anonymous, format_type, newusername, newuseremail, subject, - ((strlen(supplied_euid) > 0) ? supplied_euid : NULL), - NULL); + ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL), + NULL, references); /* Put together one big recipients struct containing to/cc/bcc all in * one. This is for the envelope. */ char *all_recps = malloc(SIZ * 3); strcpy(all_recps, recp); - if (strlen(cc) > 0) { - if (strlen(all_recps) > 0) { + if (!IsEmptyStr(cc)) { + if (!IsEmptyStr(all_recps)) { strcat(all_recps, ","); } strcat(all_recps, cc); } - if (strlen(bcc) > 0) { - if (strlen(all_recps) > 0) { + if (!IsEmptyStr(bcc)) { + if (!IsEmptyStr(all_recps)) { strcat(all_recps, ","); } strcat(all_recps, bcc); } - if (strlen(all_recps) > 0) { - valid = validate_recipients(all_recps); + if (!IsEmptyStr(all_recps)) { + valid = validate_recipients(all_recps, NULL, 0); } else { valid = NULL; @@ -3590,7 +4057,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); @@ -3641,16 +4108,16 @@ int CtdlDeleteMessages(char *room_name, /* which room */ regmatch_t pm; int need_to_free_re = 0; - if (content_type) if (strlen(content_type) > 0) { + if (content_type) if (!IsEmptyStr(content_type)) { regcomp(&re, content_type, 0); need_to_free_re = 1; } - lprintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %d msgs, %s)\n", + CtdlLogPrintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %d msgs, %s)\n", room_name, num_dmsgnums, content_type); /* get room record, obtaining a lock... */ - if (lgetroom(&qrbuf, room_name) != 0) { - lprintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n", + if (CtdlGetRoomLock(&qrbuf, room_name) != 0) { + CtdlLogPrintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n", room_name); if (need_to_free_re) regfree(&re); return (0); /* room not found */ @@ -3684,7 +4151,7 @@ int CtdlDeleteMessages(char *room_name, /* which room */ } } - if (strlen(content_type) == 0) { + if (IsEmptyStr(content_type)) { delete_this |= 0x02; } else { GetMetaData(&smi, msglist[i]); @@ -3706,7 +4173,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 @@ -3724,7 +4191,7 @@ int CtdlDeleteMessages(char *room_name, /* which room */ /* Now free the memory we used, and go away. */ if (msglist != NULL) free(msglist); if (dellist != NULL) free(dellist); - lprintf(CTDL_DEBUG, "%d message(s) deleted.\n", num_deleted); + CtdlLogPrintf(CTDL_DEBUG, "%d message(s) deleted.\n", num_deleted); if (need_to_free_re) regfree(&re); return (num_deleted); } @@ -3791,19 +4258,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); -} - - /* @@ -3836,9 +4290,13 @@ 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; + } + + if (!strcasecmp(qtemp.QRname, CC->room.QRname)) { + cprintf("%d Source and target rooms are the same.\n", ERROR + ALREADY_EXISTS); return; } @@ -3868,6 +4326,10 @@ void cmd_move(char *args) /* Permit message removal from collaborative delete rooms */ if (CC->room.QRflags2 & QR2_COLLABDEL) permit = 1; + /* Users allowed to post into the target room may move into it too. */ + if ((CC->room.QRflags & QR_MAILBOX) && + (qtemp.QRflags & UA_POSTALLOWED)) permit = 1; + /* User must have access to target room */ if (!(ra & UA_KNOWN)) permit = 0; @@ -3889,7 +4351,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); @@ -3961,6 +4423,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) { @@ -3970,7 +4433,7 @@ void AdjRefCount(long msgnum, int incr) /* msgnum < 0 means that we're trying to close the file */ if (msgnum < 0) { - lprintf(CTDL_DEBUG, "Closing the AdjRefCount queue file\n"); + CtdlLogPrintf(CTDL_DEBUG, "Closing the AdjRefCount queue file\n"); begin_critical_section(S_SUPPMSGMAIN); if (arcfp != NULL) { fclose(arcfp); @@ -3990,7 +4453,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; @@ -4012,7 +4475,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) { @@ -4022,7 +4485,7 @@ int TDAP_ProcessAdjRefCountQueue(void) r = link(file_arcq, file_arcq_temp); if (r != 0) { - lprintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); + CtdlLogPrintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); end_critical_section(S_SUPPMSGMAIN); return(num_records_processed); } @@ -4032,7 +4495,7 @@ int TDAP_ProcessAdjRefCountQueue(void) fp = fopen(file_arcq_temp, "rb"); if (fp == NULL) { - lprintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); + CtdlLogPrintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); return(num_records_processed); } @@ -4044,7 +4507,7 @@ int TDAP_ProcessAdjRefCountQueue(void) fclose(fp); r = unlink(file_arcq_temp); if (r != 0) { - lprintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); + CtdlLogPrintf(CTDL_CRIT, "%s: %s\n", file_arcq_temp, strerror(errno)); } return(num_records_processed); @@ -4075,19 +4538,17 @@ void TDAP_AdjRefCount(long msgnum, int incr) smi.meta_refcount += incr; PutMetaData(&smi); end_critical_section(S_SUPPMSGMAIN); - lprintf(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 * (and its supplementary record as well). */ if (smi.meta_refcount == 0) { - lprintf(CTDL_DEBUG, "Deleting message <%ld>\n", msgnum); - - /* Remove from fulltext index */ - if (config.c_enable_fulltext) { - ft_index_message(msgnum, 0); - } + CtdlLogPrintf(CTDL_DEBUG, "Deleting message <%ld>\n", msgnum); + + /* Call delete hooks with NULL room to show it has gone altogether */ + PerformDeleteHooks(NULL, msgnum); /* Remove from message base */ delnum = msgnum; @@ -4107,24 +4568,21 @@ 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); @@ -4133,24 +4591,10 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ safestrncpy(roomname, req_room, sizeof(roomname)); } - fp = fopen(tempfilename, "rb"); - if (fp == NULL) { - lprintf(CTDL_CRIT, "Cannot open %s: %s\n", - tempfilename, strerror(errno)); - return; - } - fseek(fp, 0L, SEEK_END); - raw_length = ftell(fp); - rewind(fp); - lprintf(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); + CtdlLogPrintf(CTDL_DEBUG, "Raw length is %ld\n", (long)raw_length); 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)); @@ -4173,11 +4617,11 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ CtdlEncodeBase64( &encoded_message[strlen(encoded_message)], raw_message, - (int)raw_length + (int)raw_length, + 0 ); } else { - raw_message[raw_length] = 0; memcpy( &encoded_message[strlen(encoded_message)], raw_message, @@ -4185,9 +4629,7 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ ); } - free(raw_message); - - lprintf(CTDL_DEBUG, "Allocating\n"); + CtdlLogPrintf(CTDL_DEBUG, "Allocating\n"); msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; @@ -4202,8 +4644,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); } @@ -4211,12 +4653,12 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ * other objects of this type that are currently in the room. */ if (is_unique) { - lprintf(CTDL_DEBUG, "Deleted %d other msgs of this type\n", + CtdlLogPrintf(CTDL_DEBUG, "Deleted %d other msgs of this type\n", CtdlDeleteMessages(roomname, NULL, 0, content_type) ); } /* Now write the data */ - CtdlSubmitMsg(msg, NULL, roomname); + CtdlSubmitMsg(msg, NULL, roomname, 0); CtdlFreeMessage(msg); } @@ -4238,8 +4680,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; } @@ -4266,30 +4708,19 @@ 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); strcpy(conf, &conf[strlen(buf)+1]); - } while ( (strlen(conf)>0) && (strlen(buf)>0) ); + } while ( (!IsEmptyStr(conf)) && (!IsEmptyStr(buf)) ); 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); } @@ -4301,7 +4732,7 @@ int CtdlIsMe(char *addr, int addr_buf_len) struct recptypes *recp; int i; - recp = validate_recipients(addr); + recp = validate_recipients(addr, NULL, 0); if (recp == NULL) return(0); if (recp->num_local == 0) { @@ -4339,3 +4770,27 @@ void cmd_isme(char *argbuf) { } } + + +/*****************************************************************************/ +/* MODULE INITIALIZATION STUFF */ +/*****************************************************************************/ + +CTDL_MODULE_INIT(msgbase) +{ + 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$"; +}