X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=citadel%2Fmsgbase.c;h=98dbafc2714bd5275acc4754d20c546036254e6d;hb=29a51c8f1017be11c4d0c9e63104ae7300459eea;hp=f13e75aac6fda049f025e04f5da6ed6769c3a758;hpb=f582ce612a254f8e55275c56335d2a234aab8170;p=citadel.git diff --git a/citadel/msgbase.c b/citadel/msgbase.c index f13e75aac..98dbafc27 100644 --- a/citadel/msgbase.c +++ b/citadel/msgbase.c @@ -29,14 +29,13 @@ #include #include -#include #include #include #include #include #include "citadel.h" #include "server.h" -#include "dynloader.h" +#include "serv_extensions.h" #include "database.h" #include "msgbase.h" #include "support.h" @@ -59,38 +58,50 @@ extern struct config config; long config_msgnum; + +/* + * This really belongs in serv_network.c, but I don't know how to export + * symbols between modules. + */ +struct FilterList *filterlist = NULL; + + +/* + * These are the four-character field headers we use when outputting + * messages in Citadel format (as opposed to RFC822 format). + */ char *msgkeys[] = { - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", "", "", "", "", "", "", "", - "", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, "from", - "", "", "", + NULL, NULL, NULL, "exti", "rfca", - "", + NULL, "hnod", "msgn", - "", "", "", + NULL, NULL, NULL, "text", "node", "room", "path", - "", + NULL, "rcpt", "spec", "time", "subj", - "", - "", - "", - "", - "" + NULL, + NULL, + NULL, + NULL, + NULL }; /* @@ -158,23 +169,24 @@ int alias(char *name) strcpy(name, bbb); } fclose(fp); - lprintf(7, "Mail is being forwarded to %s\n", name); + + /* Hit the Global Address Book */ + if (CtdlDirectoryLookup(aaa, name) == 0) { + strcpy(name, aaa); + } + + lprintf(CTDL_INFO, "Mail is being forwarded to %s\n", name); /* Change "user @ xxx" to "user" if xxx is an alias for this host */ for (a=0; a\n", name); + lprintf(CTDL_INFO, "Changed to <%s>\n", name); } } } - /* Hit the Global Address Book */ - if (CtdlDirectoryLookup(aaa, name) == 0) { - strcpy(name, aaa); - } - /* determine local or remote type, see citadel.h */ at = haschar(name, '@'); if (at == 0) return(MES_LOCAL); /* no @'s - local address */ @@ -199,11 +211,11 @@ int alias(char *name) extract_token(buf, ignetcfg, i, '\n'); extract_token(testnode, buf, 0, '|'); if (!strcasecmp(node, testnode)) { - phree(ignetcfg); + free(ignetcfg); return(MES_IGNET); } } - phree(ignetcfg); + free(ignetcfg); /* * Then try nodes that are two or more hops away. @@ -213,11 +225,11 @@ int alias(char *name) extract_token(buf, ignetmap, i, '\n'); extract_token(testnode, buf, 0, '|'); if (!strcasecmp(node, testnode)) { - phree(ignetmap); + free(ignetmap); return(MES_IGNET); } } - phree(ignetmap); + free(ignetmap); /* If we get to this point it's an invalid node name */ return (MES_ERROR); @@ -229,6 +241,11 @@ void get_mm(void) FILE *fp; fp = fopen("citadel.control", "r"); + if (fp == NULL) { + lprintf(CTDL_CRIT, "Cannot open citadel.control: %s\n", + strerror(errno)); + exit(errno); + } fread((char *) &CitControl, sizeof(struct CitControl), 1, fp); fclose(fp); } @@ -271,10 +288,28 @@ int CtdlMsgCmp(struct CtdlMessage *msg, struct CtdlMessage *template) { } + /* - * Manipulate the "seen msgs" string. + * Retrieve the "seen" message list for the current room. */ -void CtdlSetSeen(long target_msgnum, int target_setting) { +void CtdlGetSeen(char *buf, int which_set) { + struct visit vbuf; + + /* Learn about the user and room in question */ + CtdlGetRelationship(&vbuf, &CC->user, &CC->room); + + if (which_set == ctdlsetseen_seen) + safestrncpy(buf, vbuf.v_seen, SIZ); + if (which_set == ctdlsetseen_answered) + safestrncpy(buf, vbuf.v_answered, SIZ); +} + + + +/* + * Manipulate the "seen msgs" string (or other message set strings) + */ +void CtdlSetSeen(long target_msgnum, int target_setting, int which_set) { char newseen[SIZ]; struct cdbdata *cdbfr; int i; @@ -285,16 +320,18 @@ void CtdlSetSeen(long target_msgnum, int target_setting) { struct visit vbuf; long *msglist; int num_msgs = 0; + char vset[SIZ]; + + lprintf(CTDL_DEBUG, "CtdlSetSeen(%ld, %d, %d)\n", + target_msgnum, target_setting, which_set); /* Learn about the user and room in question */ - get_mm(); - getuser(&CC->usersupp, CC->curr_user); - CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom); + CtdlGetRelationship(&vbuf, &CC->user, &CC->room); /* Load the message list */ - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long)); + cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); if (cdbfr != NULL) { - msglist = mallok(cdbfr->len); + msglist = malloc(cdbfr->len); memcpy(msglist, cdbfr->ptr, cdbfr->len); num_msgs = cdbfr->len / sizeof(long); cdb_free(cdbfr); @@ -302,7 +339,11 @@ void CtdlSetSeen(long target_msgnum, int target_setting) { return; /* No messages at all? No further action. */ } - lprintf(9, "before optimize: %s\n", vbuf.v_seen); + /* Decide which message set we're manipulating */ + if (which_set == ctdlsetseen_seen) strcpy(vset, vbuf.v_seen); + if (which_set == ctdlsetseen_answered) strcpy(vset, vbuf.v_answered); + + lprintf(CTDL_DEBUG, "before optimize: %s\n", vset); strcpy(newseen, ""); for (i=0; i SIZ) { strcpy(newseen, &newseen[20]); newseen[0] = '*'; } - if (strlen(newseen) > 0) strcat(newseen, ","); + tmp = strlen(newseen); + if (tmp > 0) { + strcat(newseen, ","); + tmp++; + } if (lo == hi) { - sprintf(&newseen[strlen(newseen)], "%ld", lo); + snprintf(&newseen[tmp], sizeof newseen - tmp, + "%ld", lo); } else { - sprintf(&newseen[strlen(newseen)], "%ld:%ld", - lo, hi); + snprintf(&newseen[tmp], sizeof newseen - tmp, + "%ld:%ld", lo, hi); } lo = (-1L); hi = (-1L); @@ -341,10 +389,13 @@ void CtdlSetSeen(long target_msgnum, int target_setting) { was_seen = is_seen; } - safestrncpy(vbuf.v_seen, newseen, SIZ); - lprintf(9, " after optimize: %s\n", vbuf.v_seen); - phree(msglist); - CtdlSetRelationship(&vbuf, &CC->usersupp, &CC->quickroom); + /* Decide which message set we're manipulating */ + if (which_set == ctdlsetseen_seen) strcpy(vbuf.v_seen, newseen); + if (which_set == ctdlsetseen_answered) strcpy(vbuf.v_answered, newseen); + + lprintf(CTDL_DEBUG, " after optimize: %s\n", newseen); + free(msglist); + CtdlSetRelationship(&vbuf, &CC->user, &CC->room); } @@ -353,7 +404,6 @@ void CtdlSetSeen(long target_msgnum, int target_setting) { * current room. (Returns the number of messages processed.) */ int CtdlForEachMessage(int mode, long ref, - int moderation_level, char *content_type, struct CtdlMessage *compare, void (*CallBack) (long, void *), @@ -375,13 +425,13 @@ int CtdlForEachMessage(int mode, long ref, /* Learn about the user and room in question */ get_mm(); - getuser(&CC->usersupp, CC->curr_user); - CtdlGetRelationship(&vbuf, &CC->usersupp, &CC->quickroom); + getuser(&CC->user, CC->curr_user); + CtdlGetRelationship(&vbuf, &CC->user, &CC->room); /* Load the message list */ - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long)); + cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); if (cdbfr != NULL) { - msglist = mallok(cdbfr->len); + msglist = malloc(cdbfr->len); memcpy(msglist, cdbfr->ptr, cdbfr->len); num_msgs = cdbfr->len / sizeof(long); cdb_free(cdbfr); @@ -394,19 +444,23 @@ int CtdlForEachMessage(int mode, long ref, * Now begin the traversal. */ if (num_msgs > 0) for (a = 0; a < num_msgs; ++a) { - GetMetaData(&smi, msglist[a]); - - /* Filter out messages that are moderated below the level - * currently being viewed at. - */ - if (smi.meta_mod < moderation_level) { - msglist[a] = 0L; - } /* 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) { + + /* This call to GetMetaData() sits inside this loop + * so that we only do the extra database read per msg + * if we need to. Doing the extra read all the time + * really kills the server. If we ever need to use + * metadata for another search criterion, we need to + * move the read somewhere else -- but still be smart + * enough to only do the read if the caller has + * specified something that will need it. + */ + GetMetaData(&smi, msglist[a]); + if (strcasecmp(smi.meta_content_type, content_type)) { msglist[a] = 0L; } @@ -421,7 +475,7 @@ int CtdlForEachMessage(int mode, long ref, if (num_msgs > 0) { if (compare != NULL) { for (a = 0; a < num_msgs; ++a) { - msg = CtdlFetchMessage(msglist[a]); + msg = CtdlFetchMessage(msglist[a], 1); if (msg != NULL) { if (CtdlMsgCmp(msg, compare)) { msglist[a] = 0L; @@ -454,7 +508,7 @@ int CtdlForEachMessage(int mode, long ref, || ((mode == MSGS_EQ) && (thismsg == ref)) ) ) { - if ((mode == MSGS_NEW) && (CC->usersupp.flags & US_LASTOLD) && (lastold > 0L) && (printed_lastold == 0) && (!is_seen)) { + if ((mode == MSGS_NEW) && (CC->user.flags & US_LASTOLD) && (lastold > 0L) && (printed_lastold == 0) && (!is_seen)) { if (CallBack) CallBack(lastold, userdata); printed_lastold = 1; @@ -464,7 +518,7 @@ int CtdlForEachMessage(int mode, long ref, ++num_processed; } } - phree(msglist); /* Clean up */ + free(msglist); /* Clean up */ return num_processed; } @@ -509,10 +563,11 @@ void cmd_msgs(char *cmdbuf) } if (with_template) { + unbuffer_output(); cprintf("%d Send template then receive message list\n", START_CHAT_MODE); template = (struct CtdlMessage *) - mallok(sizeof(struct CtdlMessage)); + malloc(sizeof(struct CtdlMessage)); memset(template, 0, sizeof(struct CtdlMessage)); while(client_gets(buf), strcmp(buf,"000")) { extract(tfield, buf, 0); @@ -520,17 +575,17 @@ void cmd_msgs(char *cmdbuf) for (i='A'; i<='Z'; ++i) if (msgkeys[i]!=NULL) { if (!strcasecmp(tfield, msgkeys[i])) { template->cm_fields[i] = - strdoop(tvalue); + strdup(tvalue); } } } + buffer_output(); } else { cprintf("%d Message list...\n", LISTING_FOLLOWS); } CtdlForEachMessage(mode, cm_ref, - CC->usersupp.moderation_filter, NULL, template, simple_listing, NULL); if (template != NULL) CtdlFreeMessage(template); cprintf("000\n"); @@ -562,13 +617,14 @@ void do_help_subst(char *buffer) help_subst(buffer, "^nodename", config.c_nodename); help_subst(buffer, "^humannode", config.c_humannode); help_subst(buffer, "^fqdn", config.c_fqdn); - help_subst(buffer, "^username", CC->usersupp.fullname); - sprintf(buf2, "%ld", CC->usersupp.usernum); + help_subst(buffer, "^username", CC->user.fullname); + snprintf(buf2, sizeof buf2, "%ld", CC->user.usernum); help_subst(buffer, "^usernum", buf2); help_subst(buffer, "^sysadm", config.c_sysadm); help_subst(buffer, "^variantname", CITADEL); - sprintf(buf2, "%d", config.c_maxsessions); + snprintf(buf2, sizeof buf2, "%d", config.c_maxsessions); help_subst(buffer, "^maxsessions", buf2); + help_subst(buffer, "^bbsdir", BBSDIR); } @@ -589,7 +645,7 @@ void memfmout( int a, b, c; int real = 0; int old = 0; - CIT_UBYTE ch; + cit_uint8_t ch; char aaa[140]; char buffer[SIZ]; @@ -673,6 +729,26 @@ void list_this_part(char *name, char *filename, char *partnum, char *disp, name, filename, partnum, disp, cbtype, (long)length); } +/* + * Callback function for multipart prefix + */ +void list_this_pref(char *name, char *filename, char *partnum, char *disp, + void *content, char *cbtype, size_t length, char *encoding, + void *cbuserdata) +{ + cprintf("pref=%s|%s\n", partnum, cbtype); +} + +/* + * Callback function for multipart sufffix + */ +void list_this_suff(char *name, char *filename, char *partnum, char *disp, + void *content, char *cbtype, size_t length, char *encoding, + void *cbuserdata) +{ + cprintf("suff=%s|%s\n", partnum, cbtype); +} + /* * Callback function for mime parser that opens a section for downloading @@ -710,15 +786,17 @@ void mime_download(char *name, char *filename, char *partnum, char *disp, * NOTE: Caller is responsible for freeing the returned CtdlMessage struct * using the CtdlMessageFree() function. */ -struct CtdlMessage *CtdlFetchMessage(long msgnum) +struct CtdlMessage *CtdlFetchMessage(long msgnum, int with_body) { struct cdbdata *dmsgtext; struct CtdlMessage *ret = NULL; char *mptr; - CIT_UBYTE ch; - CIT_UBYTE field_header; + cit_uint8_t ch; + cit_uint8_t field_header; size_t field_length; + lprintf(CTDL_DEBUG, "CtdlFetchMessage(%ld, %d)\n", msgnum, with_body); + dmsgtext = cdb_fetch(CDB_MSGMAIN, &msgnum, sizeof(long)); if (dmsgtext == NULL) { return NULL; @@ -732,11 +810,13 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum) */ ch = *mptr++; if (ch != 255) { - lprintf(5, "Message %ld appears to be corrupted.\n", msgnum); + lprintf(CTDL_ERR, + "Message %ld appears to be corrupted.\n", + msgnum); cdb_free(dmsgtext); return NULL; } - ret = (struct CtdlMessage *) mallok(sizeof(struct CtdlMessage)); + ret = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); memset(ret, 0, sizeof(struct CtdlMessage)); ret->cm_magic = CTDLMESSAGE_MAGIC; @@ -753,7 +833,7 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum) if (field_length == 0) break; field_header = *mptr++; - ret->cm_fields[field_header] = mallok(field_length); + ret->cm_fields[field_header] = malloc(field_length + 1); strcpy(ret->cm_fields[field_header], mptr); while (*mptr++ != 0); /* advance to next field */ @@ -762,9 +842,21 @@ struct CtdlMessage *CtdlFetchMessage(long msgnum) cdb_free(dmsgtext); - /* Always make sure there's something in the msg text field */ - if (ret->cm_fields['M'] == NULL) - ret->cm_fields['M'] = strdoop("\n"); + /* Always make sure there's something in the msg text field. If + * it's NULL, the message text is most likely stored separately, + * so go ahead and fetch that. Failing that, just set a dummy + * body so other code doesn't barf. + */ + if ( (ret->cm_fields['M'] == NULL) && (with_body) ) { + dmsgtext = cdb_fetch(CDB_BIGMSGS, &msgnum, sizeof(long)); + if (dmsgtext != NULL) { + ret->cm_fields['M'] = strdup(dmsgtext->ptr); + cdb_free(dmsgtext); + } + } + if (ret->cm_fields['M'] == NULL) { + ret->cm_fields['M'] = strdup("\n"); + } /* Perform "before read" hooks (aborting if any return nonzero) */ if (PerformMessageHooks(ret, EVT_BEFOREREAD) > 0) { @@ -784,7 +876,7 @@ int is_valid_message(struct CtdlMessage *msg) { if (msg == NULL) return 0; if ((msg->cm_magic) != CTDLMESSAGE_MAGIC) { - lprintf(3, "is_valid_message() -- self-check failed\n"); + lprintf(CTDL_WARNING, "is_valid_message() -- self-check failed\n"); return 0; } return 1; @@ -802,11 +894,11 @@ void CtdlFreeMessage(struct CtdlMessage *msg) for (i = 0; i < 256; ++i) if (msg->cm_fields[i] != NULL) { - phree(msg->cm_fields[i]); + free(msg->cm_fields[i]); } msg->cm_magic = 0; /* just in case */ - phree(msg); + free(msg); } @@ -824,9 +916,9 @@ void fixed_output_pre(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, size_t length, char *encoding, void *cbuserdata) { - lprintf(9, "fixed_output_pre() type=<%s>\n", cbtype); + lprintf(CTDL_DEBUG, "fixed_output_pre() type=<%s>\n", cbtype); if (!strcasecmp(cbtype, "multipart/alternative")) { - ma->is_ma = 1; + ++ma->is_ma; ma->did_print = 0; return; } @@ -839,9 +931,9 @@ void fixed_output_post(char *name, char *filename, char *partnum, char *disp, void *content, char *cbtype, size_t length, char *encoding, void *cbuserdata) { - lprintf(9, "fixed_output_post() type=<%s>\n", cbtype); + lprintf(CTDL_DEBUG, "fixed_output_post() type=<%s>\n", cbtype); if (!strcasecmp(cbtype, "multipart/alternative")) { - ma->is_ma = 0; + --ma->is_ma; ma->did_print = 0; return; } @@ -857,44 +949,37 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp, char *ptr; char *wptr; size_t wlen; - CIT_UBYTE ch = 0; - lprintf(9, "fixed_output() type=<%s>\n", cbtype); + lprintf(CTDL_DEBUG, "fixed_output() type=<%s>\n", cbtype); /* * If we're in the middle of a multipart/alternative scope and * we've already printed another section, skip this one. */ if ( (ma->is_ma == 1) && (ma->did_print == 1) ) { - lprintf(9, "Skipping part %s (%s)\n", partnum, cbtype); + lprintf(CTDL_DEBUG, "Skipping part %s (%s)\n", partnum, cbtype); return; } ma->did_print = 1; if ( (!strcasecmp(cbtype, "text/plain")) || (strlen(cbtype)==0) ) { - wlen = length; wptr = content; - while (wlen--) { - ch = *wptr++; - /********** - if (ch==10) cprintf("\r\n"); - else cprintf("%c", ch); - **********/ - cprintf("%c", ch); + if (length > 0) { + client_write(wptr, length); + if (wptr[length-1] != '\n') { + cprintf("\n"); + } } - if (ch != '\n') cprintf("\n"); } else if (!strcasecmp(cbtype, "text/html")) { ptr = html_to_ascii(content, 80, 0); wlen = strlen(ptr); - wptr = ptr; - while (wlen--) { - ch = *wptr++; - if (ch==10) cprintf("\r\n"); - else cprintf("%c", ch); + client_write(ptr, wlen); + if (ptr[wlen-1] != '\n') { + cprintf("\n"); } - phree(ptr); + free(ptr); } else if (strncasecmp(cbtype, "multipart/", 10)) { cprintf("Part %s: %s (%s) (%ld bytes)\r\n", @@ -902,6 +987,78 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp, } } +/* + * The client is elegant and sophisticated and wants to be choosy about + * MIME content types, so figure out which multipart/alternative part + * we're going to send. + */ +void choose_preferred(char *name, char *filename, char *partnum, char *disp, + void *content, char *cbtype, size_t length, char *encoding, + void *cbuserdata) +{ + char buf[SIZ]; + int i; + + if (ma->is_ma > 0) { + for (i=0; ipreferred_formats, '|'); ++i) { + extract(buf, CC->preferred_formats, i); + if (!strcasecmp(buf, cbtype)) { + strcpy(ma->chosen_part, partnum); + } + } + } +} + +/* + * Now that we've chosen our preferred part, output it. + */ +void output_preferred(char *name, char *filename, char *partnum, char *disp, + void *content, char *cbtype, size_t length, char *encoding, + void *cbuserdata) +{ + int i; + char buf[SIZ]; + int add_newline = 0; + char *text_content; + + /* This is not the MIME part you're looking for... */ + if (strcasecmp(partnum, ma->chosen_part)) return; + + /* If the content-type of this part is in our preferred formats + * list, we can simply output it verbatim. + */ + for (i=0; ipreferred_formats, '|'); ++i) { + extract(buf, CC->preferred_formats, i); + if (!strcasecmp(buf, cbtype)) { + /* Yeah! Go! W00t!! */ + + text_content = (char *)content; + if (text_content[length-1] != '\n') { + ++add_newline; + } + + cprintf("Content-type: %s\n", cbtype); + cprintf("Content-length: %d\n", + (int)(length + add_newline) ); + if (strlen(encoding) > 0) { + cprintf("Content-transfer-encoding: %s\n", encoding); + } + else { + cprintf("Content-transfer-encoding: 7bit\n"); + } + cprintf("\n"); + client_write(content, length); + if (add_newline) cprintf("\n"); + return; + } + } + + /* No translations required or possible: output as text/plain */ + cprintf("Content-type: text/plain\n\n"); + fixed_output(name, filename, partnum, disp, content, cbtype, + length, encoding, cbuserdata); +} + /* * Get a message off disk. (returns om_* values found in msgbase.h) @@ -913,40 +1070,35 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ int do_proto, /* do Citadel protocol responses? */ int crlf /* Use CRLF newlines instead of LF? */ ) { - struct CtdlMessage *TheMessage; - int retcode; + struct CtdlMessage *TheMessage = NULL; + int retcode = om_no_such_msg; - lprintf(7, "CtdlOutputMsg() msgnum=%ld, mode=%d\n", + lprintf(CTDL_DEBUG, "CtdlOutputMsg() msgnum=%ld, mode=%d\n", msg_num, mode); - TheMessage = NULL; - 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); } - /* FIXME ... small security issue - * We need to check to make sure the requested message is actually - * in the current room, and set msg_ok to 1 only if it is. This - * functionality is currently missing because I'm in a hurry to replace - * broken production code with nonbroken pre-beta code. :( -- ajc - * - if (!msg_ok) { - if (do_proto) cprintf("%d Message %ld is not in this room.\n", - ERROR, msg_num); - return(om_no_such_msg); - } - */ + /* FIXME: check message id against msglist for this room */ /* - * Fetch the message from disk + * Fetch the message from disk. If we're in sooper-fast headers + * only mode, request that we don't even bother loading the body + * into memory. */ - TheMessage = CtdlFetchMessage(msg_num); + if (headers_only == HEADERS_FAST) { + TheMessage = CtdlFetchMessage(msg_num, 0); + } + else { + TheMessage = CtdlFetchMessage(msg_num, 1); + } + if (TheMessage == NULL) { if (do_proto) cprintf("%d Can't locate msg %ld on disk\n", - ERROR, msg_num); + ERROR + MESSAGE_NOT_FOUND, msg_num); return(om_no_such_msg); } @@ -955,6 +1107,7 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ headers_only, do_proto, crlf); CtdlFreeMessage(TheMessage); + return(retcode); } @@ -963,7 +1116,8 @@ int CtdlOutputMsg(long msg_num, /* message number (local) to fetch */ * Get a message off disk. (returns om_* values found in msgbase.h) * */ -int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, +int CtdlOutputPreLoadedMsg( + struct CtdlMessage *TheMessage, long msg_num, int mode, /* how would you like that message? */ int headers_only, /* eschew the message body? */ @@ -971,12 +1125,14 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, int crlf /* Use CRLF newlines instead of LF? */ ) { int i, k; - char buf[1024]; - CIT_UBYTE ch; + char buf[SIZ]; + cit_uint8_t ch; char allkeys[SIZ]; char display_name[SIZ]; char *mptr; char *nl; /* newline string */ + int suppress_f = 0; + int subject_found = 0; /* buffers needed for RFC822 translation */ char suser[SIZ]; @@ -988,11 +1144,17 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, char datestamp[SIZ]; /* */ - sprintf(mid, "%ld", msg_num); + lprintf(CTDL_DEBUG, "CtdlOutputPreLoadedMsg(TheMessage=%s, %ld, %d, %d, %d, %d\n", + ((TheMessage == NULL) ? "NULL" : "not null"), + msg_num, + mode, headers_only, do_proto, crlf); + + snprintf(mid, sizeof mid, "%ld", msg_num); nl = (crlf ? "\r\n" : "\n"); if (!is_valid_message(TheMessage)) { - lprintf(1, "ERROR: invalid preloaded message for output\n"); + lprintf(CTDL_ERR, + "ERROR: invalid preloaded message for output\n"); return(om_no_such_msg); } @@ -1001,11 +1163,11 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, if (TheMessage->cm_format_type != FMT_RFC822) { if (do_proto) cprintf("%d This is not a MIME message.\n", - ERROR); + ERROR + ILLEGAL_VALUE); } else if (CC->download_fp != NULL) { if (do_proto) cprintf( "%d You already have a download open.\n", - ERROR); + ERROR + RESOURCE_BUSY); } else { /* Parse the message text component */ mptr = TheMessage->cm_fields['M']; @@ -1028,22 +1190,20 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, /* now for the user-mode message reading loops */ if (do_proto) cprintf("%d Message %ld:\n", LISTING_FOLLOWS, msg_num); - /* Tell the client which format type we're using. If this is a - * MIME message, *lie* about it and tell the user it's fixed-format. - */ - if (mode == MT_CITADEL) { - if (TheMessage->cm_format_type == FMT_RFC822) { - if (do_proto) cprintf("type=1\n"); - } - else { - if (do_proto) cprintf("type=%d\n", - TheMessage->cm_format_type); - } + /* Does the caller want to skip the headers? */ + if (headers_only == HEADERS_NONE) goto START_TEXT; + + /* Tell the client which format type we're using. */ + if ( (mode == MT_CITADEL) && (do_proto) ) { + cprintf("type=%d\n", TheMessage->cm_format_type); } /* nhdr=yes means that we're only displaying headers, no body */ - if ((TheMessage->cm_anon_type == MES_ANONONLY) && (mode == MT_CITADEL)) { - if (do_proto) cprintf("nhdr=yes\n"); + if ( (TheMessage->cm_anon_type == MES_ANONONLY) + && (mode == MT_CITADEL) + && (do_proto) + ) { + cprintf("nhdr=yes\n"); } /* begin header processing loop for Citadel message format */ @@ -1053,7 +1213,6 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, strcpy(display_name, ""); if (TheMessage->cm_fields['A']) { strcpy(buf, TheMessage->cm_fields['A']); - PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG); if (TheMessage->cm_anon_type == MES_ANONONLY) { strcpy(display_name, "****"); } @@ -1066,21 +1225,39 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, if ((is_room_aide()) && ((TheMessage->cm_anon_type == MES_ANONONLY) || (TheMessage->cm_anon_type == MES_ANONOPT))) { - sprintf(&display_name[strlen(display_name)], - " [%s]", buf); + size_t tmp = strlen(display_name); + snprintf(&display_name[tmp], + sizeof display_name - tmp, + " [%s]", buf); } } + /* Don't show Internet address for users on the + * local Citadel network. + */ + suppress_f = 0; + if (TheMessage->cm_fields['N'] != NULL) + if (strlen(TheMessage->cm_fields['N']) > 0) + if (haschar(TheMessage->cm_fields['N'], '.') == 0) { + suppress_f = 1; + } + + /* Now spew the header fields in the order we like them. */ strcpy(allkeys, FORDER); for (i=0; icm_fields[k] != NULL) { + if ( (TheMessage->cm_fields[k] != NULL) + && (msgkeys[k] != NULL) ) { if (k == 'A') { if (do_proto) cprintf("%s=%s\n", msgkeys[k], display_name); } + else if ((k == 'F') && (suppress_f)) { + /* do nothing */ + } + /* Masquerade display name if needed */ else { if (do_proto) cprintf("%s=%s\n", msgkeys[k], @@ -1101,44 +1278,41 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, strcpy(snode, NODENAME); strcpy(lnode, HUMANNODE); if (mode == MT_RFC822) { - cprintf("X-UIDL: %ld%s", msg_num, nl); for (i = 0; i < 256; ++i) { if (TheMessage->cm_fields[i]) { mptr = TheMessage->cm_fields[i]; if (i == 'A') { - strcpy(luser, mptr); - strcpy(suser, mptr); + safestrncpy(luser, mptr, sizeof luser); + safestrncpy(suser, mptr, sizeof suser); } -/**** - "Path:" removed for now because it confuses brain-dead Microsoft shitware - into thinking that mail messages are newsgroup messages instead. When we - add NNTP support back into Citadel we'll have to add code to only output - this field when appropriate. - else if (i == 'P') { - cprintf("Path: %s%s", mptr, nl); - } - ****/ - else if (i == 'U') + else if (i == 'U') { cprintf("Subject: %s%s", mptr, nl); + subject_found = 1; + } else if (i == 'I') - strcpy(mid, mptr); + safestrncpy(mid, mptr, sizeof mid); else if (i == 'H') - strcpy(lnode, mptr); + safestrncpy(lnode, mptr, sizeof lnode); + else if (i == 'F') + safestrncpy(fuser, mptr, sizeof fuser); else if (i == 'O') cprintf("X-Citadel-Room: %s%s", mptr, nl); else if (i == 'N') - strcpy(snode, mptr); + safestrncpy(snode, mptr, sizeof snode); else if (i == 'R') cprintf("To: %s%s", mptr, nl); else if (i == 'T') { - datestring(datestamp, atol(mptr), - DATESTRING_RFC822 ); + datestring(datestamp, sizeof datestamp, + atol(mptr), DATESTRING_RFC822); cprintf("Date: %s%s", datestamp, nl); } } } + if (subject_found == 0) { + cprintf("Subject: (no subject)%s", nl); + } } for (i=0; i%s", nl); - PerformUserHooks(luser, (-1L), EVT_OUTPUTMSG); - - if (strlen(fuser) > 0) { + if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONONLY)) { + cprintf("From: x@x.org (----)%s", nl); + } + else if (!is_room_aide() && (TheMessage->cm_anon_type == MES_ANONOPT)) { + cprintf("From: x@x.org (anonymous)%s", nl); + } + else if (strlen(fuser) > 0) { cprintf("From: %s (%s)%s", fuser, luser, nl); } else { @@ -1168,55 +1346,80 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, } cprintf("Organization: %s%s", lnode, nl); + + /* Blank line signifying RFC822 end-of-headers */ + if (TheMessage->cm_format_type != FMT_RFC822) { + cprintf("%s", nl); + } } /* end header processing loop ... at this point, we're in the text */ - +START_TEXT: + if (headers_only == HEADERS_FAST) goto DONE; mptr = TheMessage->cm_fields['M']; /* Tell the client about the MIME parts in this message */ if (TheMessage->cm_format_type == FMT_RFC822) { - if (mode == MT_CITADEL) { + if ( (mode == MT_CITADEL) || (mode == MT_MIME) ) { mime_parser(mptr, NULL, - *list_this_part, NULL, NULL, + *list_this_part, + *list_this_pref, + *list_this_suff, NULL, 0); } - else if (mode == MT_MIME) { /* list parts only */ - mime_parser(mptr, NULL, - *list_this_part, NULL, NULL, - NULL, 0); - if (do_proto) cprintf("000\n"); - return(om_ok); - } else if (mode == MT_RFC822) { /* unparsed RFC822 dump */ /* FIXME ... we have to put some code in here to avoid * printing duplicate header information when both * Citadel and RFC822 headers exist. Preference should * probably be given to the RFC822 headers. */ + int done_rfc822_hdrs = 0; while (ch=*(mptr++), ch!=0) { - if (ch==13) ; - else if (ch==10) cprintf("%s", nl); - else cprintf("%c", ch); + if (ch==13) { + /* do nothing */ + } + else if (ch==10) { + if (!done_rfc822_hdrs) { + if (headers_only != HEADERS_NONE) { + cprintf("%s", nl); + } + } + else { + if (headers_only != HEADERS_ONLY) { + cprintf("%s", nl); + } + } + if ((*(mptr) == 13) || (*(mptr) == 10)) { + done_rfc822_hdrs = 1; + } + } + else { + if (done_rfc822_hdrs) { + if (headers_only != HEADERS_NONE) { + cprintf("%c", ch); + } + } + else { + if (headers_only != HEADERS_ONLY) { + cprintf("%c", ch); + } + } + if ((*mptr == 13) || (*mptr == 10)) { + done_rfc822_hdrs = 1; + } + } } - if (do_proto) cprintf("000\n"); - return(om_ok); + goto DONE; } } - if (headers_only) { - if (do_proto) cprintf("000\n"); - return(om_ok); + if (headers_only == HEADERS_ONLY) { + goto DONE; } /* signify start of msg text */ - if (mode == MT_CITADEL) + if ( (mode == MT_CITADEL) || (mode == MT_MIME) ) { if (do_proto) cprintf("text\n"); - if (mode == MT_RFC822) { - if (TheMessage->cm_fields['U'] == NULL) { - cprintf("Subject: (no subject)%s", nl); - } - cprintf("%s", nl); } /* If the format type on disk is 1 (fixed-format), then we want @@ -1224,6 +1427,9 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, * what message transfer format is in use. */ if (TheMessage->cm_format_type == FMT_FIXED) { + if (mode == MT_MIME) { + cprintf("Content-type: text/plain\n\n"); + } strcpy(buf, ""); while (ch = *mptr++, ch > 0) { if (ch == 13) @@ -1248,6 +1454,9 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, * message to the reader's screen width. */ if (TheMessage->cm_format_type == FMT_CITADEL) { + if (mode == MT_MIME) { + cprintf("Content-type: text/x-citadel-variformat\n\n"); + } memfmout(80, mptr, 0, nl); } @@ -1259,12 +1468,23 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, if (TheMessage->cm_format_type == FMT_RFC822) { CtdlAllocUserData(SYM_MA_INFO, sizeof(struct ma_info)); memset(ma, 0, sizeof(struct ma_info)); - mime_parser(mptr, NULL, - *fixed_output, *fixed_output_pre, *fixed_output_post, - NULL, 0); + + if (mode == MT_MIME) { + strcpy(ma->chosen_part, "1"); + mime_parser(mptr, NULL, + *choose_preferred, *fixed_output_pre, + *fixed_output_post, NULL, 0); + mime_parser(mptr, NULL, + *output_preferred, NULL, NULL, NULL, 0); + } + else { + mime_parser(mptr, NULL, + *fixed_output, *fixed_output_pre, + *fixed_output_post, NULL, 0); + } } - /* now we're done */ +DONE: /* now we're done */ if (do_proto) cprintf("000\n"); return(om_ok); } @@ -1277,7 +1497,7 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, void cmd_msg0(char *cmdbuf) { long msgid; - int headers_only = 0; + int headers_only = HEADERS_ALL; msgid = extract_long(cmdbuf, 0); headers_only = extract_int(cmdbuf, 1); @@ -1293,7 +1513,7 @@ void cmd_msg0(char *cmdbuf) void cmd_msg2(char *cmdbuf) { long msgid; - int headers_only = 0; + int headers_only = HEADERS_ALL; msgid = extract_long(cmdbuf, 0); headers_only = extract_int(cmdbuf, 1); @@ -1314,15 +1534,15 @@ void cmd_msg3(char *cmdbuf) if (CC->internal_pgm == 0) { cprintf("%d This command is for internal programs only.\n", - ERROR); + ERROR + HIGHER_ACCESS_REQUIRED); return; } msgnum = extract_long(cmdbuf, 0); - msg = CtdlFetchMessage(msgnum); + msg = CtdlFetchMessage(msgnum, 1); if (msg == NULL) { cprintf("%d Message %ld not found.\n", - ERROR, msgnum); + ERROR + MESSAGE_NOT_FOUND, msgnum); return; } @@ -1331,19 +1551,19 @@ void cmd_msg3(char *cmdbuf) if (smr.len == 0) { cprintf("%d Unable to serialize message\n", - ERROR+INTERNAL_ERROR); + ERROR + INTERNAL_ERROR); return; } cprintf("%d %ld\n", BINARY_FOLLOWS, (long)smr.len); - client_write(smr.ser, smr.len); - phree(smr.ser); + client_write((char *)smr.ser, (int)smr.len); + free(smr.ser); } /* - * display a message (mode 4 - MIME) (FIXME ... still evolving, not complete) + * Display a message using MIME content types */ void cmd_msg4(char *cmdbuf) { @@ -1353,6 +1573,19 @@ void cmd_msg4(char *cmdbuf) CtdlOutputMsg(msgid, MT_MIME, 0, 1, 0); } + + +/* + * Client tells us its preferred message format(s) + */ +void cmd_msgp(char *cmdbuf) +{ + safestrncpy(CC->preferred_formats, cmdbuf, + sizeof(CC->preferred_formats)); + cprintf("%d ok\n", CIT_OK); +} + + /* * Open a component of a MIME message as a download file */ @@ -1383,55 +1616,56 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) { long highest_msg = 0L; struct CtdlMessage *msg = NULL; - lprintf(9, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n", + lprintf(CTDL_DEBUG, "CtdlSaveMsgPointerInRoom(%s, %ld, %d)\n", roomname, msgid, flags); - strcpy(hold_rm, CC->quickroom.QRname); + strcpy(hold_rm, CC->room.QRname); /* We may need to check to see if this message is real */ if ( (flags & SM_VERIFY_GOODNESS) || (flags & SM_DO_REPL_CHECK) ) { - msg = CtdlFetchMessage(msgid); + msg = CtdlFetchMessage(msgid, 1); if (msg == NULL) return(ERROR + ILLEGAL_VALUE); } /* Perform replication checks if necessary */ if ( (flags & SM_DO_REPL_CHECK) && (msg != NULL) ) { - if (getroom(&CC->quickroom, - ((roomname != NULL) ? roomname : CC->quickroom.QRname) ) + if (getroom(&CC->room, + ((roomname != NULL) ? roomname : CC->room.QRname) ) != 0) { - lprintf(9, "No such room <%s>\n", roomname); + lprintf(CTDL_ERR, "No such room <%s>\n", roomname); if (msg != NULL) CtdlFreeMessage(msg); return(ERROR + ROOM_NOT_FOUND); } if (ReplicationChecks(msg) != 0) { - getroom(&CC->quickroom, hold_rm); + getroom(&CC->room, hold_rm); if (msg != NULL) CtdlFreeMessage(msg); - lprintf(9, "Did replication, and newer exists\n"); + lprintf(CTDL_DEBUG, + "Did replication, and newer exists\n"); return(0); } } /* Now the regular stuff */ - if (lgetroom(&CC->quickroom, - ((roomname != NULL) ? roomname : CC->quickroom.QRname) ) + if (lgetroom(&CC->room, + ((roomname != NULL) ? roomname : CC->room.QRname) ) != 0) { - lprintf(9, "No such room <%s>\n", roomname); + lprintf(CTDL_ERR, "No such room <%s>\n", roomname); if (msg != NULL) CtdlFreeMessage(msg); return(ERROR + ROOM_NOT_FOUND); } - cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long)); + cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long)); if (cdbfr == NULL) { msglist = NULL; num_msgs = 0; } else { - msglist = mallok(cdbfr->len); + msglist = malloc(cdbfr->len); if (msglist == NULL) - lprintf(3, "ERROR malloc msglist!\n"); + lprintf(CTDL_ALERT, "ERROR malloc msglist!\n"); num_msgs = cdbfr->len / sizeof(long); memcpy(msglist, cdbfr->ptr, cdbfr->len); cdb_free(cdbfr); @@ -1444,20 +1678,20 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) { */ if (num_msgs > 0) for (i=0; iquickroom); /* unlock the room */ - getroom(&CC->quickroom, hold_rm); + lputroom(&CC->room); /* unlock the room */ + getroom(&CC->room, hold_rm); if (msg != NULL) CtdlFreeMessage(msg); + free(msglist); return(ERROR + ALREADY_EXISTS); } } /* Now add the new message */ ++num_msgs; - msglist = reallok(msglist, - (num_msgs * sizeof(long))); + msglist = realloc(msglist, (num_msgs * sizeof(long))); if (msglist == NULL) { - lprintf(3, "ERROR: can't realloc message list!\n"); + lprintf(CTDL_ALERT, "ERROR: can't realloc message list!\n"); } msglist[num_msgs - 1] = msgid; @@ -1468,16 +1702,16 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) { highest_msg = msglist[num_msgs - 1]; /* Write it back to disk. */ - cdb_store(CDB_MSGLISTS, &CC->quickroom.QRnumber, sizeof(long), - msglist, num_msgs * sizeof(long)); + cdb_store(CDB_MSGLISTS, &CC->room.QRnumber, (int)sizeof(long), + msglist, (int)(num_msgs * sizeof(long))); /* Free up the memory we used. */ - phree(msglist); + free(msglist); /* Update the highest-message pointer and unlock the room. */ - CC->quickroom.QRhighest = highest_msg; - lputroom(&CC->quickroom); - getroom(&CC->quickroom, hold_rm); + CC->room.QRhighest = highest_msg; + lputroom(&CC->room); + getroom(&CC->room, hold_rm); /* Bump the reference count for this message. */ if ((flags & SM_DONT_BUMP_REF)==0) { @@ -1492,56 +1726,71 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) { /* - * Message base operation to send a message to the master file + * Message base operation to save a new message to the message store * (returns new message number) * * This is the back end for CtdlSubmitMsg() and should not be directly * called by server-side modules. * */ -long send_message(struct CtdlMessage *msg, /* pointer to buffer */ - FILE *save_a_copy) /* save a copy to disk? */ -{ +long send_message(struct CtdlMessage *msg) { long newmsgid; long retval; char msgidbuf[SIZ]; struct ser_ret smr; + int is_bigmsg = 0; + char *holdM = NULL; /* Get a new message number */ newmsgid = get_new_message_number(); - sprintf(msgidbuf, "%ld@%s", newmsgid, config.c_fqdn); + snprintf(msgidbuf, sizeof msgidbuf, "%ld@%s", newmsgid, config.c_fqdn); /* Generate an ID if we don't have one already */ if (msg->cm_fields['I']==NULL) { - msg->cm_fields['I'] = strdoop(msgidbuf); + msg->cm_fields['I'] = strdup(msgidbuf); } - + + /* If the message is big, set its body aside for storage elsewhere */ + if (msg->cm_fields['M'] != NULL) { + if (strlen(msg->cm_fields['M']) > BIGMSG) { + is_bigmsg = 1; + holdM = msg->cm_fields['M']; + msg->cm_fields['M'] = NULL; + } + } + + /* Serialize our data structure for storage in the database */ serialize_message(&smr, msg); + if (is_bigmsg) { + msg->cm_fields['M'] = holdM; + } + if (smr.len == 0) { cprintf("%d Unable to serialize message\n", - ERROR+INTERNAL_ERROR); + ERROR + INTERNAL_ERROR); return (-1L); } /* Write our little bundle of joy into the message base */ - if (cdb_store(CDB_MSGMAIN, &newmsgid, sizeof(long), + if (cdb_store(CDB_MSGMAIN, &newmsgid, (int)sizeof(long), smr.ser, smr.len) < 0) { - lprintf(2, "Can't store message\n"); + lprintf(CTDL_ERR, "Can't store message\n"); retval = 0L; } else { + if (is_bigmsg) { + cdb_store(CDB_BIGMSGS, + &newmsgid, + (int)sizeof(long), + holdM, + (strlen(holdM) + 1) + ); + } retval = newmsgid; } - /* If the caller specified that a copy should be saved to a particular - * file handle, do that now too. - */ - if (save_a_copy != NULL) { - fwrite(smr.ser, smr.len, 1, save_a_copy); - } - /* Free the memory we used for the serialized message */ - phree(smr.ser); + free(smr.ser); /* Return the *local* message ID to the caller * (even if we're storing an incoming network message) @@ -1572,8 +1821,8 @@ void serialize_message(struct ser_ret *ret, /* return values */ ret->len = ret->len + strlen(msg->cm_fields[(int)forder[i]]) + 2; - lprintf(9, "serialize_message() calling malloc(%ld)\n", (long)ret->len); - ret->ser = mallok(ret->len); + lprintf(CTDL_DEBUG, "serialize_message() calling malloc(%ld)\n", (long)ret->len); + ret->ser = malloc(ret->len); if (ret->ser == NULL) { ret->len = 0; return; @@ -1586,10 +1835,10 @@ void serialize_message(struct ser_ret *ret, /* return values */ for (i=0; i<26; ++i) if (msg->cm_fields[(int)forder[i]] != NULL) { ret->ser[wlen++] = (char)forder[i]; - strcpy(&ret->ser[wlen], msg->cm_fields[(int)forder[i]]); + strcpy((char *)&ret->ser[wlen], msg->cm_fields[(int)forder[i]]); wlen = wlen + strlen(msg->cm_fields[(int)forder[i]]) + 1; } - if (ret->len != wlen) lprintf(3, "ERROR: len=%ld wlen=%ld\n", + if (ret->len != wlen) lprintf(CTDL_ERR, "ERROR: len=%ld wlen=%ld\n", (long)ret->len, (long)wlen); return; @@ -1604,8 +1853,8 @@ void check_repl(long msgnum, void *userdata) { struct CtdlMessage *msg; time_t timestamp = (-1L); - lprintf(9, "check_repl() found message %ld\n", msgnum); - msg = CtdlFetchMessage(msgnum); + lprintf(CTDL_DEBUG, "check_repl() found message %ld\n", msgnum); + msg = CtdlFetchMessage(msgnum, 1); if (msg == NULL) return; if (msg->cm_fields['T'] != NULL) { timestamp = atol(msg->cm_fields['T']); @@ -1614,18 +1863,18 @@ void check_repl(long msgnum, void *userdata) { if (timestamp > msg_repl->highest) { msg_repl->highest = timestamp; /* newer! */ - lprintf(9, "newer!\n"); + lprintf(CTDL_DEBUG, "newer!\n"); return; } - lprintf(9, "older!\n"); + lprintf(CTDL_DEBUG, "older!\n"); /* Existing isn't newer? Then delete the old one(s). */ - CtdlDeleteMessages(CC->quickroom.QRname, msgnum, ""); + CtdlDeleteMessages(CC->room.QRname, msgnum, ""); } /* - * Check to see if any messages already exist which carry the same Extended ID + * Check to see if any messages already exist which carry the same Exclusive ID * as this one. * * If any are found: @@ -1636,24 +1885,23 @@ int ReplicationChecks(struct CtdlMessage *msg) { struct CtdlMessage *template; int abort_this = 0; - lprintf(9, "ReplicationChecks() started\n"); - /* No extended id? Don't do anything. */ + lprintf(CTDL_DEBUG, "ReplicationChecks() started\n"); + /* No exclusive id? Don't do anything. */ if (msg->cm_fields['E'] == NULL) return 0; if (strlen(msg->cm_fields['E']) == 0) return 0; - lprintf(9, "Extended ID: <%s>\n", msg->cm_fields['E']); + lprintf(CTDL_DEBUG, "Exclusive ID: <%s>\n", msg->cm_fields['E']); CtdlAllocUserData(SYM_REPL, sizeof(struct repl)); - strcpy(msg_repl->extended_id, msg->cm_fields['E']); + strcpy(msg_repl->exclusive_id, msg->cm_fields['E']); msg_repl->highest = atol(msg->cm_fields['T']); template = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); memset(template, 0, sizeof(struct CtdlMessage)); - template->cm_fields['E'] = strdoop(msg->cm_fields['E']); + template->cm_fields['E'] = strdup(msg->cm_fields['E']); - CtdlForEachMessage(MSGS_ALL, 0L, (-127), NULL, template, - check_repl, NULL); + CtdlForEachMessage(MSGS_ALL, 0L, NULL, template, check_repl, NULL); - /* If a newer message exists with the same Extended ID, abort + /* If a newer message exists with the same Exclusive ID, abort * this save. */ if (msg_repl->highest > atol(msg->cm_fields['T']) ) { @@ -1661,7 +1909,7 @@ int ReplicationChecks(struct CtdlMessage *msg) { } CtdlFreeMessage(template); - lprintf(9, "ReplicationChecks() returning %d\n", abort_this); + lprintf(CTDL_DEBUG, "ReplicationChecks() returning %d\n", abort_this); return(abort_this); } @@ -1683,7 +1931,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ char recipient[SIZ]; long newmsgid; char *mptr = NULL; - struct usersupp userbuf; + struct ctdluser userbuf; int a, i; struct MetaData smi; FILE *network_fp = NULL; @@ -1693,24 +1941,24 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ struct ser_ret smr; char *hold_R, *hold_D; - lprintf(9, "CtdlSubmitMsg() called\n"); + lprintf(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 * giving it one, right now. */ if (msg->cm_fields['T'] == NULL) { - lprintf(9, "Generating timestamp\n"); - sprintf(aaa, "%ld", (long)time(NULL)); - msg->cm_fields['T'] = strdoop(aaa); + lprintf(CTDL_DEBUG, "Generating timestamp\n"); + snprintf(aaa, sizeof aaa, "%ld", (long)time(NULL)); + msg->cm_fields['T'] = strdup(aaa); } /* If this message has no path, we generate one. */ if (msg->cm_fields['P'] == NULL) { - lprintf(9, "Generating path\n"); + lprintf(CTDL_DEBUG, "Generating path\n"); if (msg->cm_fields['A'] != NULL) { - msg->cm_fields['P'] = strdoop(msg->cm_fields['A']); + msg->cm_fields['P'] = strdup(msg->cm_fields['A']); for (a=0; acm_fields['P']); ++a) { if (isspace(msg->cm_fields['P'][a])) { msg->cm_fields['P'][a] = ' '; @@ -1718,16 +1966,22 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } } else { - msg->cm_fields['P'] = strdoop("unknown"); + msg->cm_fields['P'] = strdup("unknown"); } } - strcpy(force_room, force); + if (force == NULL) { + strcpy(force_room, ""); + } + else { + strcpy(force_room, force); + } /* Learn about what's inside, because it's what's inside that counts */ - lprintf(9, "Learning what's inside\n"); + lprintf(CTDL_DEBUG, "Learning what's inside\n"); if (msg->cm_fields['M'] == NULL) { - lprintf(1, "ERROR: attempt to save message with NULL body\n"); + lprintf(CTDL_ERR, "ERROR: attempt to save message with NULL body\n"); + return(-1); } switch (msg->cm_format_type) { @@ -1760,17 +2014,18 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ } /* Goto the correct room */ - lprintf(9, "Switching rooms\n"); - strcpy(hold_rm, CC->quickroom.QRname); - strcpy(actual_rm, CC->quickroom.QRname); + lprintf(CTDL_DEBUG, "Selected room %s\n", (recps) ? CC->room.QRname : SENTITEMS); + strcpy(hold_rm, CC->room.QRname); + strcpy(actual_rm, CC->room.QRname); if (recps != NULL) { strcpy(actual_rm, SENTITEMS); } /* If the user is a twit, move to the twit room for posting */ - lprintf(9, "Handling twit stuff\n"); + lprintf(CTDL_DEBUG, "Handling twit stuff: %s\n", + (CC->user.axlevel == 2) ? config.c_twitroom : "OK"); if (TWITDETECT) { - if (CC->usersupp.axlevel == 2) { + if (CC->user.axlevel == 2) { strcpy(hold_rm, actual_rm); strcpy(actual_rm, config.c_twitroom); } @@ -1781,44 +2036,44 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ strcpy(actual_rm, force_room); } - lprintf(9, "Possibly relocating\n"); - if (strcasecmp(actual_rm, CC->quickroom.QRname)) { - getroom(&CC->quickroom, actual_rm); + lprintf(CTDL_DEBUG, "Final selection: %s\n", actual_rm); + if (strcasecmp(actual_rm, CC->room.QRname)) { + getroom(&CC->room, actual_rm); } /* * If this message has no O (room) field, generate one. */ if (msg->cm_fields['O'] == NULL) { - msg->cm_fields['O'] = strdoop(CC->quickroom.QRname); + msg->cm_fields['O'] = strdup(CC->room.QRname); } /* Perform "before save" hooks (aborting if any return nonzero) */ - lprintf(9, "Performing before-save hooks\n"); + lprintf(CTDL_DEBUG, "Performing before-save hooks\n"); if (PerformMessageHooks(msg, EVT_BEFORESAVE) > 0) return(-1); - /* If this message has an Extended ID, perform replication checks */ - lprintf(9, "Performing replication checks\n"); + /* If this message has an Exclusive ID, perform replication checks */ + lprintf(CTDL_DEBUG, "Performing replication checks\n"); if (ReplicationChecks(msg) > 0) return(-1); /* Save it to disk */ - lprintf(9, "Saving to disk\n"); - newmsgid = send_message(msg, NULL); + lprintf(CTDL_DEBUG, "Saving to disk\n"); + newmsgid = send_message(msg); if (newmsgid <= 0L) return(-1); /* Write a supplemental message info record. This doesn't have to * be a critical section because nobody else knows about this message * yet. */ - lprintf(9, "Creating MetaData record\n"); + lprintf(CTDL_DEBUG, "Creating MetaData record\n"); memset(&smi, 0, sizeof(struct MetaData)); smi.meta_msgnum = newmsgid; smi.meta_refcount = 0; - safestrncpy(smi.meta_content_type, content_type, 64); + safestrncpy(smi.meta_content_type, content_type, sizeof smi.meta_content_type); PutMetaData(&smi); /* Now figure out where to store the pointers */ - lprintf(9, "Storing pointers\n"); + lprintf(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 @@ -1826,8 +2081,8 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ */ if ((!CC->internal_pgm) || (recps == NULL)) { if (CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0) != 0) { - lprintf(3, "ERROR saving message pointer!\n"); - CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0); + lprintf(CTDL_ERR, "ERROR saving message pointer!\n"); + CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0); } } @@ -1842,15 +2097,15 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ if (recps->num_room > 0) for (i=0; irecp_room, '|'); ++i) { extract(recipient, recps->recp_room, i); - lprintf(9, "Delivering to local room <%s>\n", recipient); + lprintf(CTDL_DEBUG, "Delivering to local room <%s>\n", recipient); CtdlSaveMsgPointerInRoom(recipient, newmsgid, 0); } /* Bump this user's messages posted counter. */ - lprintf(9, "Updating user\n"); - lgetuser(&CC->usersupp, CC->curr_user); - CC->usersupp.posted = CC->usersupp.posted + 1; - lputuser(&CC->usersupp); + lprintf(CTDL_DEBUG, "Updating user\n"); + lgetuser(&CC->user, CC->curr_user); + CC->user.posted = CC->user.posted + 1; + lputuser(&CC->user); /* If this is private, local mail, make a copy in the * recipient's mailbox and bump the reference count. @@ -1859,20 +2114,21 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ if (recps->num_local > 0) for (i=0; irecp_local, '|'); ++i) { extract(recipient, recps->recp_local, i); - lprintf(9, "Delivering private local mail to <%s>\n", + lprintf(CTDL_DEBUG, "Delivering private local mail to <%s>\n", recipient); if (getuser(&userbuf, recipient) == 0) { - MailboxName(actual_rm, &userbuf, MAILROOM); + MailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM); CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0); + BumpNewMailCounter(userbuf.usernum); } else { - lprintf(9, "No user <%s>\n", recipient); - CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0); + lprintf(CTDL_DEBUG, "No user <%s>\n", recipient); + CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0); } } /* Perform "after save" hooks */ - lprintf(9, "Performing after-save hooks\n"); + lprintf(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 @@ -1890,14 +2146,14 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ hold_R = msg->cm_fields['R']; hold_D = msg->cm_fields['D']; - msg->cm_fields['R'] = mallok(SIZ); - msg->cm_fields['D'] = mallok(SIZ); + msg->cm_fields['R'] = malloc(SIZ); + msg->cm_fields['D'] = malloc(SIZ); extract_token(msg->cm_fields['R'], recipient, 0, '@'); extract_token(msg->cm_fields['D'], recipient, 1, '@'); serialize_message(&smr, msg); if (smr.len > 0) { - sprintf(aaa, + snprintf(aaa, sizeof aaa, "./network/spoolin/netmail.%04lx.%04x.%04x", (long) getpid(), CC->cs_pid, ++seqnum); network_fp = fopen(aaa, "wb+"); @@ -1905,19 +2161,19 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ fwrite(smr.ser, smr.len, 1, network_fp); fclose(network_fp); } - phree(smr.ser); + free(smr.ser); } - phree(msg->cm_fields['R']); - phree(msg->cm_fields['D']); + free(msg->cm_fields['R']); + free(msg->cm_fields['D']); msg->cm_fields['R'] = hold_R; msg->cm_fields['D'] = hold_D; } /* Go back to the room we started from */ - lprintf(9, "Returning to original room\n"); - if (strcasecmp(hold_rm, CC->quickroom.QRname)) - getroom(&CC->quickroom, hold_rm); + lprintf(CTDL_DEBUG, "Returning to original room %s\n", hold_rm); + if (strcasecmp(hold_rm, CC->room.QRname)) + getroom(&CC->room, hold_rm); /* For internet mail, generate delivery instructions. * Yes, this is recursive. Deal with it. Infinite recursion does @@ -1926,9 +2182,9 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ */ if (recps != NULL) if (recps->num_internet > 0) { - lprintf(9, "Generating delivery instructions\n"); - instr = mallok(SIZ * 2); - sprintf(instr, + lprintf(CTDL_DEBUG, "Generating delivery instructions\n"); + instr = malloc(SIZ * 2); + snprintf(instr, SIZ * 2, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n" "bounceto|%s@%s\n", SPOOLMIME, newmsgid, (long)time(NULL), @@ -1936,17 +2192,18 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ ); for (i=0; irecp_internet, '|'); ++i) { + size_t tmp = strlen(instr); extract(recipient, recps->recp_internet, i); - sprintf(&instr[strlen(instr)], - "remote|%s|0||\n", recipient); + snprintf(&instr[tmp], SIZ * 2 - tmp, + "remote|%s|0||\n", recipient); } - imsg = mallok(sizeof(struct CtdlMessage)); + imsg = malloc(sizeof(struct CtdlMessage)); memset(imsg, 0, sizeof(struct CtdlMessage)); imsg->cm_magic = CTDLMESSAGE_MAGIC; imsg->cm_anon_type = MES_NORMAL; imsg->cm_format_type = FMT_RFC822; - imsg->cm_fields['A'] = strdoop("Citadel"); + imsg->cm_fields['A'] = strdup("Citadel"); imsg->cm_fields['M'] = instr; CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(imsg); @@ -1960,36 +2217,44 @@ long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ /* * Convenience function for generating small administrative messages. */ -void quickie_message(char *from, char *to, char *room, char *text) +void quickie_message(char *from, char *to, char *room, char *text, + int format_type, char *subject) { struct CtdlMessage *msg; + struct recptypes *recp = NULL; - msg = mallok(sizeof(struct CtdlMessage)); + msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; - msg->cm_format_type = 0; - msg->cm_fields['A'] = strdoop(from); - msg->cm_fields['O'] = strdoop(room); - msg->cm_fields['N'] = strdoop(NODENAME); - if (to != NULL) - msg->cm_fields['R'] = strdoop(to); - msg->cm_fields['M'] = strdoop(text); - - CtdlSubmitMsg(msg, NULL, room); + msg->cm_format_type = format_type; + msg->cm_fields['A'] = strdup(from); + if (room != NULL) msg->cm_fields['O'] = strdup(room); + msg->cm_fields['N'] = strdup(NODENAME); + if (to != NULL) { + msg->cm_fields['R'] = strdup(to); + recp = validate_recipients(to); + } + if (subject != NULL) { + msg->cm_fields['U'] = strdup(subject); + } + msg->cm_fields['M'] = strdup(text); + + CtdlSubmitMsg(msg, recp, room); CtdlFreeMessage(msg); - syslog(LOG_NOTICE, text); + if (recp != NULL) free(recp); } /* - * Back end function used by make_message() and similar functions + * Back end function used by CtdlMakeMessage() and similar functions */ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ size_t maxlen, /* maximum message length */ - char *exist /* if non-null, append to it; + char *exist, /* if non-null, append to it; exist is ALWAYS freed */ + int crlf /* CRLF newlines instead of LF */ ) { char buf[SIZ]; int linelen; @@ -1997,68 +2262,70 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ size_t buffer_len = 0; char *ptr; char *m; + int flushing = 0; + int finished = 0; if (exist == NULL) { - m = mallok(4096); + m = malloc(4096); + m[0] = 0; + buffer_len = 4096; + message_len = 0; } else { - m = reallok(exist, strlen(exist) + 4096); - if (m == NULL) phree(exist); + message_len = strlen(exist); + buffer_len = message_len + 4096; + m = realloc(exist, buffer_len); + if (m == NULL) { + free(exist); + return m; + } } /* flush the input if we have nowhere to store it */ if (m == NULL) { - while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;; - return(NULL); + flushing = 1; } - /* otherwise read it into memory */ - else { - buffer_len = 4096; - m[0] = 0; - message_len = 0; - } /* read in the lines of message text one by one */ - while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) { - - /* strip trailing newline type stuff */ - if (buf[strlen(buf)-1]==10) buf[strlen(buf)-1]=0; - if (buf[strlen(buf)-1]==13) buf[strlen(buf)-1]=0; - - linelen = strlen(buf); - - /* augment the buffer if we have to */ - if ((message_len + linelen + 2) > buffer_len) { - lprintf(9, "realloking\n"); - ptr = reallok(m, (buffer_len * 2) ); - if (ptr == NULL) { /* flush if can't allocate */ - while ( (client_gets(buf)>0) && - strcmp(buf, terminator)) ;; - return(m); - } else { - buffer_len = (buffer_len * 2); - m = ptr; - lprintf(9, "buffer_len is %ld\n", (long)buffer_len); - } + do { + if (client_gets(buf) < 1) finished = 1; + if (!strcmp(buf, terminator)) finished = 1; + if (crlf) { + strcat(buf, "\r\n"); + } + else { + strcat(buf, "\n"); } - /* Add the new line to the buffer. NOTE: this loop must avoid - * using functions like strcat() and strlen() because they - * traverse the entire buffer upon every call, and doing that - * for a multi-megabyte message slows it down beyond usability. - */ - strcpy(&m[message_len], buf); - m[message_len + linelen] = '\n'; - m[message_len + linelen + 1] = 0; - message_len = message_len + linelen + 1; + if ( (!flushing) && (!finished) ) { + /* Measure the line */ + linelen = strlen(buf); + + /* augment the buffer if we have to */ + if ((message_len + linelen) >= buffer_len) { + ptr = realloc(m, (buffer_len * 2) ); + if (ptr == NULL) { /* flush if can't allocate */ + flushing = 1; + } else { + buffer_len = (buffer_len * 2); + m = ptr; + lprintf(CTDL_DEBUG, "buffer_len is now %ld\n", (long)buffer_len); + } + } + + /* Add the new line to the buffer. NOTE: this loop must avoid + * using functions like strcat() and strlen() because they + * traverse the entire buffer upon every call, and doing that + * for a multi-megabyte message slows it down beyond usability. + */ + strcpy(&m[message_len], buf); + message_len += linelen; + } /* if we've hit the max msg length, flush the rest */ - if (message_len >= maxlen) { - while ( (client_gets(buf)>0) - && strcmp(buf, terminator)) ;; - return(m); - } - } + if (message_len >= maxlen) flushing = 1; + + } while (!finished); return(m); } @@ -2067,21 +2334,27 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ /* * Build a binary message to be saved on disk. + * (NOTE: if you supply 'preformatted_text', the buffer you give it + * will become part of the message. This means you are no longer + * responsible for managing that memory -- it will be freed along with + * the rest of the fields when CtdlFreeMessage() is called.) */ -static struct CtdlMessage *make_message( - struct usersupp *author, /* author's usersupp structure */ +struct CtdlMessage *CtdlMakeMessage( + struct ctdluser *author, /* author's user structure */ char *recipient, /* NULL if it's not mail */ char *room, /* room where it's going */ int type, /* see MES_ types in header file */ int format_type, /* variformat, plain text, MIME... */ - char *fake_name /* who we're masquerading as */ + char *fake_name, /* who we're masquerading as */ + char *subject, /* Subject (optional) */ + char *preformatted_text /* ...or NULL to read text from client */ ) { char dest_node[SIZ]; char buf[SIZ]; struct CtdlMessage *msg; - msg = mallok(sizeof(struct CtdlMessage)); + msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = type; @@ -2092,36 +2365,52 @@ static struct CtdlMessage *make_message( striplt(recipient); - sprintf(buf, "cit%ld", author->usernum); /* Path */ - msg->cm_fields['P'] = strdoop(buf); + snprintf(buf, sizeof buf, "cit%ld", author->usernum); /* Path */ + msg->cm_fields['P'] = strdup(buf); - sprintf(buf, "%ld", (long)time(NULL)); /* timestamp */ - msg->cm_fields['T'] = strdoop(buf); + snprintf(buf, sizeof buf, "%ld", (long)time(NULL)); /* timestamp */ + msg->cm_fields['T'] = strdup(buf); if (fake_name[0]) /* author */ - msg->cm_fields['A'] = strdoop(fake_name); + msg->cm_fields['A'] = strdup(fake_name); else - msg->cm_fields['A'] = strdoop(author->fullname); + msg->cm_fields['A'] = strdup(author->fullname); - if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */ - msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]); + if (CC->room.QRflags & QR_MAILBOX) { /* room */ + msg->cm_fields['O'] = strdup(&CC->room.QRname[11]); } else { - msg->cm_fields['O'] = strdoop(CC->quickroom.QRname); + msg->cm_fields['O'] = strdup(CC->room.QRname); } - msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */ - msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */ + msg->cm_fields['N'] = strdup(NODENAME); /* nodename */ + msg->cm_fields['H'] = strdup(HUMANNODE); /* hnodename */ if (recipient[0] != 0) { - msg->cm_fields['R'] = strdoop(recipient); + msg->cm_fields['R'] = strdup(recipient); } if (dest_node[0] != 0) { - msg->cm_fields['D'] = strdoop(dest_node); + msg->cm_fields['D'] = strdup(dest_node); + } + + if ( (author == &CC->user) && (strlen(CC->cs_inet_email) > 0) ) { + msg->cm_fields['F'] = strdup(CC->cs_inet_email); } - msg->cm_fields['M'] = CtdlReadMessageBody("000", - config.c_maxmsglen, NULL); + if (subject != NULL) { + striplt(subject); + if (strlen(subject) > 0) { + msg->cm_fields['U'] = strdup(subject); + } + } + + if (preformatted_text != NULL) { + msg->cm_fields['M'] = preformatted_text; + } + else { + msg->cm_fields['M'] = CtdlReadMessageBody("000", + config.c_maxmsglen, NULL, 0); + } return(msg); } @@ -2132,29 +2421,29 @@ static struct CtdlMessage *make_message( * room. Returns a *CITADEL ERROR CODE* and puts a message in errmsgbuf, or * returns 0 on success. */ -int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) { +int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf, size_t n) { if (!(CC->logged_in)) { - sprintf(errmsgbuf, "Not logged in."); + snprintf(errmsgbuf, n, "Not logged in."); return (ERROR + NOT_LOGGED_IN); } - if ((CC->usersupp.axlevel < 2) - && ((CC->quickroom.QRflags & QR_MAILBOX) == 0)) { - sprintf(errmsgbuf, "Need to be validated to enter " + if ((CC->user.axlevel < 2) + && ((CC->room.QRflags & QR_MAILBOX) == 0)) { + snprintf(errmsgbuf, n, "Need to be validated to enter " "(except in %s> to sysop)", MAILROOM); return (ERROR + HIGHER_ACCESS_REQUIRED); } - if ((CC->usersupp.axlevel < 4) - && (CC->quickroom.QRflags & QR_NETWORK)) { - sprintf(errmsgbuf, "Need net privileges to enter here."); + if ((CC->user.axlevel < 4) + && (CC->room.QRflags & QR_NETWORK)) { + snprintf(errmsgbuf, n, "Need net privileges to enter here."); return (ERROR + HIGHER_ACCESS_REQUIRED); } - if ((CC->usersupp.axlevel < 6) - && (CC->quickroom.QRflags & QR_READONLY)) { - sprintf(errmsgbuf, "Sorry, this is a read-only room."); + if ((CC->user.axlevel < 6) + && (CC->room.QRflags & QR_READONLY)) { + snprintf(errmsgbuf, n, "Sorry, this is a read-only room."); return (ERROR + HIGHER_ACCESS_REQUIRED); } @@ -2163,6 +2452,30 @@ int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) { } +/* + * Check to see if the specified user has Internet mail permission + * (returns nonzero if permission is granted) + */ +int CtdlCheckInternetMailPermission(struct ctdluser *who) { + + /* Do not allow twits to send Internet mail */ + if (who->axlevel <= 2) return(0); + + /* Globally enabled? */ + if (config.c_restrict == 0) return(1); + + /* User flagged ok? */ + if (who->flags & US_INTERNET) return(2); + + /* Aide level access? */ + if (who->axlevel >= 6) return(3); + + /* No mail for you! */ + return(0); +} + + + /* * Validate recipients, count delivery types and errors, and handle aliasing * FIXME check for dupes!!!!! @@ -2170,12 +2483,14 @@ int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) { struct recptypes *validate_recipients(char *recipients) { struct recptypes *ret; char this_recp[SIZ]; + char this_recp_cooked[SIZ]; char append[SIZ]; int num_recps; - int i; + int i, j; int mailtype; int invalid; - struct usersupp tempUS; + struct ctdluser tempUS; + struct ctdlroom tempQR; /* Initialize */ ret = (struct recptypes *) malloc(sizeof(struct recptypes)); @@ -2186,6 +2501,7 @@ struct recptypes *validate_recipients(char *recipients) { ret->num_internet = 0; ret->num_ignet = 0; ret->num_error = 0; + ret->num_room = 0; if (recipients == NULL) { num_recps = 0; @@ -2208,14 +2524,24 @@ struct recptypes *validate_recipients(char *recipients) { if (num_recps > 0) for (i=0; i\n", i, this_recp); + lprintf(CTDL_DEBUG, "Evaluating recipient #%d <%s>\n", i, this_recp); + mailtype = alias(this_recp); + mailtype = alias(this_recp); mailtype = alias(this_recp); + for (j=0; j<=strlen(this_recp); ++j) { + if (this_recp[j]=='_') { + this_recp_cooked[j] = ' '; + } + else { + this_recp_cooked[j] = this_recp[j]; + } + } invalid = 0; switch(mailtype) { case MES_LOCAL: if (!strcasecmp(this_recp, "sysop")) { ++ret->num_room; - strcpy(this_recp, AIDEROOM); + strcpy(this_recp, config.c_aideroom); if (strlen(ret->recp_room) > 0) { strcat(ret->recp_room, "|"); } @@ -2229,17 +2555,45 @@ struct recptypes *validate_recipients(char *recipients) { } strcat(ret->recp_local, this_recp); } + else if (getuser(&tempUS, this_recp_cooked) == 0) { + ++ret->num_local; + strcpy(this_recp, tempUS.fullname); + if (strlen(ret->recp_local) > 0) { + 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; } break; case MES_INTERNET: - ++ret->num_internet; - if (strlen(ret->recp_internet) > 0) { - strcat(ret->recp_internet, "|"); + /* Yes, you're reading this correctly: if the target + * domain points back to the local system or an attached + * Citadel directory, the address is invalid. That's + * because if the address were valid, we would have + * already translated it to a local address by now. + */ + if (IsDirectory(this_recp)) { + ++ret->num_error; + invalid = 1; + } + else { + ++ret->num_internet; + if (strlen(ret->recp_internet) > 0) { + strcat(ret->recp_internet, "|"); + } + strcat(ret->recp_internet, this_recp); } - strcat(ret->recp_internet, this_recp); break; case MES_IGNET: ++ret->num_ignet; @@ -2255,13 +2609,13 @@ struct recptypes *validate_recipients(char *recipients) { } if (invalid) { if (strlen(ret->errormsg) == 0) { - sprintf(append, - "Invalid recipient: %s", - this_recp); + snprintf(append, sizeof append, + "Invalid recipient: %s", + this_recp); } else { - sprintf(append, - ", %s", this_recp); + snprintf(append, sizeof append, + ", %s", this_recp); } if ( (strlen(ret->errormsg) + strlen(append)) < SIZ) { strcat(ret->errormsg, append); @@ -2272,7 +2626,8 @@ struct recptypes *validate_recipients(char *recipients) { strcpy(append, this_recp); } else { - sprintf(append, ", %s", this_recp); + snprintf(append, sizeof append, ", %s", + this_recp); } if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) { strcat(ret->display_recp, append); @@ -2286,6 +2641,13 @@ struct recptypes *validate_recipients(char *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); + return(ret); } @@ -2307,15 +2669,19 @@ void cmd_ent0(char *entargs) char errmsg[SIZ]; int err = 0; struct recptypes *valid = NULL; + char subject[SIZ]; + + unbuffer_output(); post = extract_int(entargs, 0); extract(recp, entargs, 1); anon_flag = extract_int(entargs, 2); format_type = extract_int(entargs, 3); + extract(subject, entargs, 4); /* first check to make sure the request is valid. */ - err = CtdlDoIHavePermissionToPostInThisRoom(errmsg); + err = CtdlDoIHavePermissionToPostInThisRoom(errmsg, sizeof errmsg); if (err) { cprintf("%d %s\n", err, errmsg); return; @@ -2324,22 +2690,28 @@ void cmd_ent0(char *entargs) /* Check some other permission type things. */ if (post == 2) { - if (CC->usersupp.axlevel < 6) { + if (CC->user.axlevel < 6) { cprintf("%d You don't have permission to masquerade.\n", ERROR + HIGHER_ACCESS_REQUIRED); return; } - extract(newusername, entargs, 4); + extract(newusername, entargs, 5); memset(CC->fake_postname, 0, sizeof(CC->fake_postname) ); safestrncpy(CC->fake_postname, newusername, sizeof(CC->fake_postname) ); - cprintf("%d ok\n", OK); + cprintf("%d ok\n", CIT_OK); return; } CC->cs_flags |= CS_POSTING; - if (CC->quickroom.QRflags & QR_MAILBOX) { - if (CC->usersupp.axlevel < 2) { + /* In the Mail> room we have to behave a little differently -- + * make sure the user has specified at least one recipient. Then + * validate the recipient(s). + */ + if ( (CC->room.QRflags & QR_MAILBOX) + && (!strcasecmp(&CC->room.QRname[11], MAILROOM)) ) { + + if (CC->user.axlevel < 2) { strcpy(recp, "sysop"); } @@ -2347,24 +2719,33 @@ void cmd_ent0(char *entargs) if (valid->num_error > 0) { cprintf("%d %s\n", ERROR + NO_SUCH_USER, valid->errormsg); - phree(valid); + free(valid); return; } + if (valid->num_internet > 0) { + if (CtdlCheckInternetMailPermission(&CC->user)==0) { + cprintf("%d You do not have permission " + "to send Internet mail.\n", + ERROR + HIGHER_ACCESS_REQUIRED); + free(valid); + return; + } + } if ( ( (valid->num_internet + valid->num_ignet) > 0) - && (CC->usersupp.axlevel < 4) ) { + && (CC->user.axlevel < 4) ) { cprintf("%d Higher access required for network mail.\n", ERROR + HIGHER_ACCESS_REQUIRED); - phree(valid); + free(valid); return; } if ((RESTRICT_INTERNET == 1) && (valid->num_internet > 0) - && ((CC->usersupp.flags & US_INTERNET) == 0) + && ((CC->user.flags & US_INTERNET) == 0) && (!CC->internal_pgm)) { cprintf("%d You don't have access to Internet mail.\n", ERROR + HIGHER_ACCESS_REQUIRED); - phree(valid); + free(valid); return; } @@ -2372,16 +2753,16 @@ void cmd_ent0(char *entargs) /* Is this a room which has anonymous-only or anonymous-option? */ anonymous = MES_NORMAL; - if (CC->quickroom.QRflags & QR_ANONONLY) { + if (CC->room.QRflags & QR_ANONONLY) { anonymous = MES_ANONONLY; } - if (CC->quickroom.QRflags & QR_ANONOPT) { + if (CC->room.QRflags & QR_ANONOPT) { if (anon_flag == 1) { /* only if the user requested it */ anonymous = MES_ANONOPT; } } - if ((CC->quickroom.QRflags & QR_MAILBOX) == 0) { + if ((CC->room.QRflags & QR_MAILBOX) == 0) { recp[0] = 0; } @@ -2389,9 +2770,9 @@ void cmd_ent0(char *entargs) * success without creating the message. */ if (post == 0) { - cprintf("%d %s\n", OK, + cprintf("%d %s\n", CIT_OK, ((valid != NULL) ? valid->display_recp : "") ); - phree(valid); + free(valid); return; } @@ -2408,15 +2789,16 @@ void cmd_ent0(char *entargs) /* Read in the message from the client. */ cprintf("%d send message\n", SEND_LISTING); - msg = make_message(&CC->usersupp, recp, - CC->quickroom.QRname, anonymous, format_type, masquerade_as); + msg = CtdlMakeMessage(&CC->user, recp, + CC->room.QRname, anonymous, format_type, + masquerade_as, subject, NULL); if (msg != NULL) { CtdlSubmitMsg(msg, valid, ""); CtdlFreeMessage(msg); } CC->fake_postname[0] = '\0'; - phree(valid); + free(valid); return; } @@ -2432,28 +2814,30 @@ int CtdlDeleteMessages(char *room_name, /* which room */ ) { - struct quickroom qrbuf; + struct ctdlroom qrbuf; struct cdbdata *cdbfr; long *msglist = NULL; + long *dellist = NULL; int num_msgs = 0; int i; int num_deleted = 0; int delete_this; struct MetaData smi; - lprintf(9, "CtdlDeleteMessages(%s, %ld, %s)\n", + lprintf(CTDL_DEBUG, "CtdlDeleteMessages(%s, %ld, %s)\n", room_name, dmsgnum, content_type); /* get room record, obtaining a lock... */ if (lgetroom(&qrbuf, room_name) != 0) { - lprintf(7, "CtdlDeleteMessages(): Room <%s> not found\n", + lprintf(CTDL_ERR, "CtdlDeleteMessages(): Room <%s> not found\n", room_name); return (0); /* room not found */ } cdbfr = cdb_fetch(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long)); if (cdbfr != NULL) { - msglist = mallok(cdbfr->len); + msglist = malloc(cdbfr->len); + dellist = malloc(cdbfr->len); memcpy(msglist, cdbfr->ptr, cdbfr->len); num_msgs = cdbfr->len / sizeof(long); cdb_free(cdbfr); @@ -2479,21 +2863,36 @@ int CtdlDeleteMessages(char *room_name, /* which room */ /* Delete message only if all bits are set */ if (delete_this == 0x03) { - AdjRefCount(msglist[i], -1); + dellist[num_deleted++] = msglist[i]; msglist[i] = 0L; - ++num_deleted; } } num_msgs = sort_msglist(msglist, num_msgs); - cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, sizeof(long), - msglist, (num_msgs * sizeof(long))); + cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, (int)sizeof(long), + msglist, (int)(num_msgs * sizeof(long))); qrbuf.QRhighest = msglist[num_msgs - 1]; - phree(msglist); } lputroom(&qrbuf); - lprintf(9, "%d message(s) deleted.\n", num_deleted); + + /* Go through the messages we pulled out of the index, and decrement + * their reference counts by 1. If this is the only room the message + * was in, the reference count will reach zero and the message will + * automatically be deleted from the database. We do this in a + * separate pass because there might be plug-in hooks getting called, + * and we don't want that happening during an S_ROOMS critical + * section. + */ + if (num_deleted) for (i=0; iusersupp, CC->curr_user); - if ((CC->usersupp.axlevel < 6) - && (CC->usersupp.usernum != CC->quickroom.QRroomaide) - && ((CC->quickroom.QRflags & QR_MAILBOX) == 0) + getuser(&CC->user, CC->curr_user); + if ((CC->user.axlevel < 6) + && (CC->user.usernum != CC->room.QRroomaide) + && ((CC->room.QRflags & QR_MAILBOX) == 0) && (!(CC->internal_pgm))) { return(0); } @@ -2531,13 +2930,13 @@ void cmd_dele(char *delstr) } delnum = extract_long(delstr, 0); - num_deleted = CtdlDeleteMessages(CC->quickroom.QRname, delnum, ""); + num_deleted = CtdlDeleteMessages(CC->room.QRname, delnum, ""); if (num_deleted) { - cprintf("%d %d message%s deleted.\n", OK, + cprintf("%d %d message%s deleted.\n", CIT_OK, num_deleted, ((num_deleted != 1) ? "s" : "")); } else { - cprintf("%d Message %ld not found.\n", ERROR, delnum); + cprintf("%d Message %ld not found.\n", ERROR + MESSAGE_NOT_FOUND, delnum); } } @@ -2564,25 +2963,52 @@ void cmd_move(char *args) { long num; char targ[SIZ]; - struct quickroom qtemp; + struct ctdlroom qtemp; int err; int is_copy = 0; + int ra; + int permit = 0; num = extract_long(args, 0); extract(targ, args, 1); targ[ROOMNAMELEN - 1] = 0; is_copy = extract_int(args, 2); - getuser(&CC->usersupp, CC->curr_user); - if ((CC->usersupp.axlevel < 6) - && (CC->usersupp.usernum != CC->quickroom.QRroomaide)) { - cprintf("%d Higher access required.\n", - ERROR + HIGHER_ACCESS_REQUIRED); + if (getroom(&qtemp, targ) != 0) { + cprintf("%d '%s' does not exist.\n", + ERROR + ROOM_NOT_FOUND, targ); return; } - if (getroom(&qtemp, targ) != 0) { - cprintf("%d '%s' does not exist.\n", ERROR, targ); + getuser(&CC->user, CC->curr_user); + ra = CtdlRoomAccess(&qtemp, &CC->user); + + /* Check for permission to perform this operation. + * Remember: "CC->room" is source, "qtemp" is target. + */ + permit = 0; + + /* Aides can move/copy */ + if (CC->user.axlevel >= 6) permit = 1; + + /* Room aides can move/copy */ + if (CC->user.usernum == CC->room.QRroomaide) permit = 1; + + /* Permit move/copy from personal rooms */ + if ((CC->room.QRflags & QR_MAILBOX) + && (qtemp.QRflags & QR_MAILBOX)) permit = 1; + + /* Permit only copy from public to personal room */ + if ( (is_copy) + && (!(CC->room.QRflags & QR_MAILBOX)) + && (qtemp.QRflags & QR_MAILBOX)) permit = 1; + + /* User must have access to target room */ + if (!(ra & UA_KNOWN)) permit = 0; + + if (!permit) { + cprintf("%d Higher access required.\n", + ERROR + HIGHER_ACCESS_REQUIRED); return; } @@ -2597,10 +3023,10 @@ void cmd_move(char *args) * if this is a 'move' rather than a 'copy' operation. */ if (is_copy == 0) { - CtdlDeleteMessages(CC->quickroom.QRname, num, ""); + CtdlDeleteMessages(CC->room.QRname, num, ""); } - cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") ); + cprintf("%d Message %s.\n", CIT_OK, (is_copy ? "copied" : "moved") ); } @@ -2643,12 +3069,12 @@ void PutMetaData(struct MetaData *smibuf) /* Use the negative of the message number for the metadata db index */ TheIndex = (0L - smibuf->meta_msgnum); - lprintf(9, "PutMetaData(%ld) - ref count is %d\n", + lprintf(CTDL_DEBUG, "PutMetaData(%ld) - ref count is %d\n", smibuf->meta_msgnum, smibuf->meta_refcount); cdb_store(CDB_MSGMAIN, - &TheIndex, sizeof(long), - smibuf, sizeof(struct MetaData)); + &TheIndex, (int)sizeof(long), + smibuf, (int)sizeof(struct MetaData)); } @@ -2668,25 +3094,26 @@ void AdjRefCount(long msgnum, int incr) */ begin_critical_section(S_SUPPMSGMAIN); GetMetaData(&smi, msgnum); - lprintf(9, "Ref count for message <%ld> before write is <%d>\n", + lprintf(CTDL_DEBUG, "Ref count for message <%ld> before write is <%d>\n", msgnum, smi.meta_refcount); smi.meta_refcount += incr; PutMetaData(&smi); end_critical_section(S_SUPPMSGMAIN); - lprintf(9, "Ref count for message <%ld> after write is <%d>\n", + lprintf(CTDL_DEBUG, "Ref count for message <%ld> after write is <%d>\n", msgnum, 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(9, "Deleting message <%ld>\n", msgnum); + lprintf(CTDL_DEBUG, "Deleting message <%ld>\n", msgnum); delnum = msgnum; - cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long)); + cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long)); + cdb_delete(CDB_BIGMSGS, &delnum, (int)sizeof(long)); /* We have to delete the metadata record too! */ delnum = (0L - msgnum); - cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long)); + cdb_delete(CDB_MSGMAIN, &delnum, (int)sizeof(long)); } } @@ -2699,92 +3126,108 @@ void AdjRefCount(long msgnum, int incr) 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 */ - struct usersupp *is_mailbox, /* Mailbox room? */ + 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 */ ) { - FILE *fp, *tempfp; - char filename[PATH_MAX]; - char cmdbuf[SIZ]; - char ch; - struct quickroom qrbuf; + FILE *fp; + struct ctdlroom qrbuf; char roomname[ROOMNAMELEN]; struct CtdlMessage *msg; - size_t len; + + char *raw_message = NULL; + char *encoded_message = NULL; + off_t raw_length = 0; if (is_mailbox != NULL) - MailboxName(roomname, is_mailbox, req_room); + MailboxName(roomname, sizeof roomname, is_mailbox, req_room); else safestrncpy(roomname, req_room, sizeof(roomname)); - lprintf(9, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags); + lprintf(CTDL_DEBUG, "CtdlWriteObject() to <%s> (flags=%d)\n", roomname, flags); - strcpy(filename, tmpnam(NULL)); - fp = fopen(filename, "w"); - if (fp == NULL) - return; - tempfp = fopen(tempfilename, "r"); - if (tempfp == NULL) { - fclose(fp); - unlink(filename); + 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); - fprintf(fp, "Content-type: %s\n", content_type); - lprintf(9, "Content-type: %s\n", content_type); + raw_message = malloc((size_t)raw_length + 2); + fread(raw_message, (size_t)raw_length, 1, fp); + fclose(fp); - if (is_binary == 0) { - fprintf(fp, "Content-transfer-encoding: 7bit\n\n"); - while (ch = getc(tempfp), ch > 0) - putc(ch, fp); - fclose(tempfp); - putc(0, fp); - fclose(fp); - } else { - fprintf(fp, "Content-transfer-encoding: base64\n\n"); - fclose(tempfp); - fclose(fp); - sprintf(cmdbuf, "./base64 -e <%s >>%s", - tempfilename, filename); - system(cmdbuf); + if (is_binary) { + encoded_message = malloc((size_t) + (((raw_length * 134) / 100) + 4096 ) ); } + else { + encoded_message = malloc((size_t)(raw_length + 4096)); + } + + sprintf(encoded_message, "Content-type: %s\n", content_type); - lprintf(9, "Allocating\n"); - msg = mallok(sizeof(struct CtdlMessage)); + if (is_binary) { + sprintf(&encoded_message[strlen(encoded_message)], + "Content-transfer-encoding: base64\n\n" + ); + } + else { + sprintf(&encoded_message[strlen(encoded_message)], + "Content-transfer-encoding: 7bit\n\n" + ); + } + + if (is_binary) { + CtdlEncodeBase64( + &encoded_message[strlen(encoded_message)], + raw_message, + (int)raw_length + ); + } + else { + raw_message[raw_length] = 0; + memcpy( + &encoded_message[strlen(encoded_message)], + raw_message, + (int)(raw_length+1) + ); + } + + free(raw_message); + + lprintf(CTDL_DEBUG, "Allocating\n"); + msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; msg->cm_format_type = 4; - msg->cm_fields['A'] = strdoop(CC->usersupp.fullname); - msg->cm_fields['O'] = strdoop(req_room); - msg->cm_fields['N'] = strdoop(config.c_nodename); - msg->cm_fields['H'] = strdoop(config.c_humannode); + msg->cm_fields['A'] = strdup(CC->user.fullname); + msg->cm_fields['O'] = strdup(req_room); + msg->cm_fields['N'] = strdup(config.c_nodename); + msg->cm_fields['H'] = strdup(config.c_humannode); msg->cm_flags = flags; - lprintf(9, "Loading\n"); - fp = fopen(filename, "rb"); - fseek(fp, 0L, SEEK_END); - len = ftell(fp); - rewind(fp); - msg->cm_fields['M'] = mallok(len); - fread(msg->cm_fields['M'], len, 1, fp); - fclose(fp); - unlink(filename); + msg->cm_fields['M'] = encoded_message; /* Create the requested room if we have to. */ if (getroom(&qrbuf, roomname) != 0) { create_room(roomname, ( (is_mailbox != NULL) ? 5 : 3 ), - "", 0, 1); + "", 0, 1, 0, VIEW_BBS); } /* If the caller specified this object as unique, delete all * other objects of this type that are currently in the room. */ if (is_unique) { - lprintf(9, "Deleted %d other msgs of this type\n", + lprintf(CTDL_DEBUG, "Deleted %d other msgs of this type\n", CtdlDeleteMessages(roomname, 0L, content_type)); } /* Now write the data */ @@ -2809,9 +3252,9 @@ char *CtdlGetSysConfig(char *sysconfname) { struct CtdlMessage *msg; char buf[SIZ]; - strcpy(hold_rm, CC->quickroom.QRname); - if (getroom(&CC->quickroom, SYSCONFIGROOM) != 0) { - getroom(&CC->quickroom, hold_rm); + strcpy(hold_rm, CC->room.QRname); + if (getroom(&CC->room, SYSCONFIGROOM) != 0) { + getroom(&CC->room, hold_rm); return NULL; } @@ -2819,7 +3262,7 @@ char *CtdlGetSysConfig(char *sysconfname) { /* We want the last (and probably only) config in this room */ begin_critical_section(S_CONFIG); config_msgnum = (-1L); - CtdlForEachMessage(MSGS_LAST, 1, (-127), sysconfname, NULL, + CtdlForEachMessage(MSGS_LAST, 1, sysconfname, NULL, CtdlGetSysConfigBackend, NULL); msgnum = config_msgnum; end_critical_section(S_CONFIG); @@ -2828,9 +3271,9 @@ char *CtdlGetSysConfig(char *sysconfname) { conf = NULL; } else { - msg = CtdlFetchMessage(msgnum); + msg = CtdlFetchMessage(msgnum, 1); if (msg != NULL) { - conf = strdoop(msg->cm_fields['M']); + conf = strdup(msg->cm_fields['M']); CtdlFreeMessage(msg); } else { @@ -2838,7 +3281,7 @@ char *CtdlGetSysConfig(char *sysconfname) { } } - getroom(&CC->quickroom, hold_rm); + getroom(&CC->room, hold_rm); if (conf != NULL) do { extract_token(buf, conf, 0, '\n'); @@ -2863,3 +3306,50 @@ void CtdlPutSysConfig(char *sysconfname, char *sysconfdata) { CtdlWriteObject(SYSCONFIGROOM, sysconfname, temp, NULL, 0, 1, 0); unlink(temp); } + + +/* + * Determine whether a given Internet address belongs to the current user + */ +int CtdlIsMe(char *addr) { + struct recptypes *recp; + int i; + + recp = validate_recipients(addr); + if (recp == NULL) return(0); + + if (recp->num_local == 0) { + free(recp); + return(0); + } + + for (i=0; inum_local; ++i) { + extract(addr, recp->recp_local, i); + if (!strcasecmp(addr, CC->user.fullname)) { + free(recp); + return(1); + } + } + + free(recp); + return(0); +} + + +/* + * Citadel protocol command to do the same + */ +void cmd_isme(char *argbuf) { + char addr[SIZ]; + + if (CtdlAccessCheck(ac_logged_in)) return; + extract(addr, argbuf, 0); + + if (CtdlIsMe(addr)) { + cprintf("%d %s\n", CIT_OK, addr); + } + else { + cprintf("%d Not you.\n", ERROR + ILLEGAL_VALUE); + } + +}