From c5e674bcf3177520398f964a9c654777e6ceab53 Mon Sep 17 00:00:00 2001 From: Art Cancro Date: Sun, 30 Dec 2001 21:26:01 +0000 Subject: [PATCH] * CtdlSaveMsg() is now CtdlSubmitMsg() and can accept any combination of local, IGnet, and Internet recipients. Caller must supply a "struct recptypes" structure, which is generated by validate_recipients(), if the message is mail (or NULL for an ordinary post). * BROKEN BUILD ALERT! I haven't rewritten serv_smtp.c and serv_network.c to deliver messages using the new framework yet. Don't use this code! --- citadel/aidepost.c | 11 +- citadel/citadel.c | 16 +- citadel/citadel.h | 4 +- citadel/client_chat.c | 2 +- citadel/docs/citadel-with-berkeley-db.txt | 39 +- citadel/docs/copyright.txt | 4 +- citadel/imap_misc.c | 2 +- citadel/messages.c | 4 +- citadel/messages.h | 2 +- citadel/msgbase.c | 452 ++++++++++++---------- citadel/msgbase.h | 6 +- citadel/serv_chat.c | 2 +- citadel/serv_network.c | 4 +- citadel/serv_smtp.c | 22 +- citadel/serv_vcard.c | 2 +- 15 files changed, 321 insertions(+), 251 deletions(-) diff --git a/citadel/aidepost.c b/citadel/aidepost.c index 37d1a295e..c7a7265c6 100644 --- a/citadel/aidepost.c +++ b/citadel/aidepost.c @@ -30,7 +30,10 @@ #endif -static void make_message(FILE *fp, char *target_room, char *author) +/* + * Simplified function to generate a message in our format + */ +static void ap_make_message(FILE *fp, char *target_room, char *author) { int a; long bb, cc; @@ -94,7 +97,6 @@ int main(int argc, char **argv) } } - snprintf(tempspool, sizeof tempspool, "./network/spoolin/ap.%04x", getpid()); @@ -106,7 +108,7 @@ int main(int argc, char **argv) } /* Generate a message from stdin */ - make_message(tempfp, target_room, author); + ap_make_message(tempfp, target_room, author); /* Copy it to a new temp file in the spool directory */ rewind(tempfp); @@ -116,8 +118,9 @@ int main(int argc, char **argv) perror("cannot open spool file"); exit(errno); } - while (ch = getc(tempfp), (ch >= 0)) + while (ch = getc(tempfp), (ch >= 0)) { putc(ch, spoolfp); + } fclose(tempfp); fclose(spoolfp); diff --git a/citadel/citadel.c b/citadel/citadel.c index 1534b02b9..ce8c2889a 100644 --- a/citadel/citadel.c +++ b/citadel/citadel.c @@ -90,6 +90,7 @@ int timescalled; int posted; unsigned userflags; long usernum = 0L; /* user number */ +time_t lastcall = 0L; /* Date/time of previous login */ char newnow; long highest_msg_read; /* used for bandon room cmd */ long maxmsgnum; /* used for oto */ @@ -232,6 +233,7 @@ void load_user_info(char *params) posted = extract_int(params, 3); userflags = extract_int(params, 4); usernum = extract_long(params, 5); + lastcall = extract_long(params, 6); } @@ -1107,9 +1109,15 @@ PWOK: enable_color = 0; } - printf("%s\nAccess level: %d (%s)\nUser #%ld / Call #%d\n", - fullname, axlevel, axdefs[(int) axlevel], - usernum, timescalled); + printf("%s\nAccess level: %d (%s)\n" + "User #%ld / Login #%d", + fullname, axlevel, axdefs[(int) axlevel], + usernum, timescalled); + if (lastcall > 0L) { + printf(" / Last login: %s\n", + asctime(localtime(&lastcall)) ); + } + printf("\n"); serv_puts("CHEK"); serv_gets(aaa); @@ -1581,7 +1589,7 @@ TERMN8: printf("%s logged out.\n", fullname); remove_march(march->march_name, 0); } if (mcmd == 30) { - printf("\n\nType 'off' to hang up, or next user...\n"); + printf("\n\nType 'off' to disconnect, or next user...\n"); } snprintf(aaa, sizeof aaa, "LOUT"); serv_puts(aaa); diff --git a/citadel/citadel.h b/citadel/citadel.h index 8b1e08602..357df0e21 100644 --- a/citadel/citadel.h +++ b/citadel/citadel.h @@ -208,8 +208,8 @@ struct quickroom { /* Miscellaneous */ #define MES_NORMAL 65 /* Normal message */ -#define MES_ANON 66 /* "****" header */ -#define MES_AN2 67 /* "Anonymous" header */ +#define MES_ANONONLY 66 /* "****" header */ +#define MES_ANONOPT 67 /* "Anonymous" header */ #define MES_ERROR (-1) /* Can't send message due to bad address */ #define MES_LOCAL 0 /* Local message, do no network processing */ diff --git a/citadel/client_chat.c b/citadel/client_chat.c index 49418a5d2..268d47d55 100644 --- a/citadel/client_chat.c +++ b/citadel/client_chat.c @@ -256,7 +256,7 @@ void page_user() printf("%s\n", &buf[4]); return; } - if (make_message(temp, touser, 0, 0, 0) != 0) { + if (client_make_message(temp, touser, 0, 0, 0) != 0) { printf("No message sent.\n"); return; } diff --git a/citadel/docs/citadel-with-berkeley-db.txt b/citadel/docs/citadel-with-berkeley-db.txt index b7fefdd98..e7518a44c 100644 --- a/citadel/docs/citadel-with-berkeley-db.txt +++ b/citadel/docs/citadel-with-berkeley-db.txt @@ -3,7 +3,7 @@ Abstract - [1]Citadel/UX can now use the robust and scalable [2]Berkeley DB from + Citadel/UX can now use the robust and scalable Berkeley DB from Sleepycat Software as its data store, for increased scalability, reliability, and recoverability. @@ -16,16 +16,16 @@ History and introduction concurrent use, and aspirations of becoming a world-class messaging/groupware platform someday, the developers made the decision to switch to an embedded database. The Free Software Foundation's - [3]GDBM product was chosen for its simple API and its free license + GDBM product was chosen for its simple API and its free license (the LGPL). Somewhat less than trouble-free operation from 1998 through 2000, however, proved that GDBM was not the best choice. Heavily utilized systems experienced occasional database corruption, often resulting in repeated crashes of the Citadel server. As a result, we made the - decision to switch to [4]Berkeley DB. + decision to switch to Berkeley DB. - Berkeley DB offers [5]numerous features which help Citadel/UX to meet + Berkeley DB offers numerous features which help Citadel/UX to meet its goals as a high-end messaging platform: * Database sizes can scale to hundreds of terabytes * A transaction-based logging system @@ -37,17 +37,23 @@ History and introduction THE USE OF GDBM IS DEPRECATED AND STRONGLY DISCOURAGED. If you are bringing up a new site you should use Berkeley DB, period. If you are maintaining an existing site using GDBM you should migrate it to Berkeley - DB as soon as possible. + DB as soon as possible, because support for it will be dropped in a future + release. Building Citadel/UX with DB support + + If you are running Citadel/UX on a Red Hat Linux system, you don't have + to build Berekeley DB from source. Simply install the RPM's for "db3" + and "db3-devel" (the latter may be on the second CD) and you're ready to + run Citadel. - Here are the steps required to get Citadel/UX running with Berkeley - DB as its back end data store. + Otherwise, here are the steps required to get Citadel running with + Berkeley DB as its back end data store. 1. First, you must download and build Berkeley DB itself. Citadel has been developed and tested with DB 3.1.17, which can be - downloaded from [6]www.sleepycat.com. Follow the "[7]Building for + downloaded from www.sleepycat.com. Follow the "Building for UNIX" instructions. Make sure that you run the test suite, and perhaps test with some of the sample applications, before moving on. @@ -72,7 +78,7 @@ Migrating an existing GDBM-based Citadel to a DB-based Citadel If you have an existing system, you must export your databases, rebuild Citadel with DB support, and then re-import your databases - into the new system. Please refer to the document "[8]How to use the + into the new system. Please refer to the document "How to use the importer/exporter" for detailed instructions on this. After you export your database, but before you re-import it, you must @@ -92,10 +98,10 @@ Care and feeding of your DB-powered Citadel So do you have to keep these log files around forever? No, but there are some rules you should follow: * Don't remove a log file if it's the only log file there. - * If it's not listed in the output of the db_archive[9] command, it's + * If it's not listed in the output of the db_archive command, it's not safe to remove. * After a successful backup (see below) log files listed by the - db_archive[9] command may be removed to conserve disk space, if + db_archive command may be removed to conserve disk space, if those log files were backed up. You may think that it's going to keep writing to that one log file @@ -110,14 +116,3 @@ Care and feeding of your DB-powered Citadel server will automatically remove them when they're no longer needed. -References - - 1. http://uncensored.citadel.org/citadel - 2. http://www.sleepycat.com/ - 3. http://www.gnu.org/software/gdbm/gdbm.html - 4. http://www.sleepycat.com/ - 5. http://www.sleepycat.com/xactfeatures.html - 6. http://www.sleepycat.com/ - 7. http://www.sleepycat.com/docs/ref/build_unix/intro.html - 8. http://pixel.citadel.org/citadel/docs/export.html - 9. http://www.sleepycat.com/docs/utility/db_archive.html diff --git a/citadel/docs/copyright.txt b/citadel/docs/copyright.txt index caf2d72bc..56c12c58e 100644 --- a/citadel/docs/copyright.txt +++ b/citadel/docs/copyright.txt @@ -11,12 +11,12 @@ Brian Costello (cosmetics, additional commands) Andru Luvisi (troubleshooting and development assistance) Daniel Malament (string compare function for IMAP server) - Stu Mark (IGnet/Open protocol design) + Stu Mark (additional client features, IGnet protocol design) Ben Mehlman (additional client features) Ari Samson (project management) John Walker (author of public domain base64 encoder/decoder) Steve Williams (documentation) - Ethan Young (IGnet/Open protocol design) + Ethan Young (IGnet protocol design) ------------------------------------------------------------------------------ diff --git a/citadel/imap_misc.c b/citadel/imap_misc.c index add72d2fb..75e4b0d33 100644 --- a/citadel/imap_misc.c +++ b/citadel/imap_misc.c @@ -279,7 +279,7 @@ void imap_append(int num_parms, char *parms[]) { else { /* Yes ... go ahead and post! */ if (msg != NULL) { - CtdlSaveMsg(msg, "", "", 0); + CtdlSubmitMsg(msg, NULL, ""); } cprintf("%s OK APPEND completed\r\n", parms[0]); } diff --git a/citadel/messages.c b/citadel/messages.c index 3939839d3..bf43fd8f0 100644 --- a/citadel/messages.c +++ b/citadel/messages.c @@ -599,7 +599,7 @@ void replace_string(char *filename, long int startpos) /* * Function to begin composing a new message */ -int make_message(char *filename, /* temporary file name */ +int client_make_message(char *filename, /* temporary file name */ char *recipient, /* NULL if it's not mail */ int anon_type, /* see MES_ types in header file */ int format_type, @@ -922,7 +922,7 @@ int entmsg(int is_reply, /* nonzero if this was a eply command */ } /* Now compose the message... */ - if (make_message(temp, buf, b, 0, c) != 0) { + if (client_make_message(temp, buf, b, 0, c) != 0) { return (2); } diff --git a/citadel/messages.h b/citadel/messages.h index 2c4f47e43..030e5fa1d 100644 --- a/citadel/messages.h +++ b/citadel/messages.h @@ -6,7 +6,7 @@ void edit_system_message(char *which_message); pid_t ka_wait(int *kstatus); void list_urls(void); void check_message_base(void); -int make_message(char *filename, /* temporary file name */ +int client_make_message(char *filename, /* temporary file name */ char *recipient, /* NULL if it's not mail */ int anon_type, /* see MES_ types in header file */ int format_type, diff --git a/citadel/msgbase.c b/citadel/msgbase.c index a7c6373ac..ba2921be9 100644 --- a/citadel/msgbase.c +++ b/citadel/msgbase.c @@ -26,6 +26,7 @@ # endif #endif + #include #include #include @@ -793,6 +794,7 @@ void CtdlFreeMessage(struct CtdlMessage *msg) { int i; + lprintf(9, "CtdlFreeMessage() called\n"); if (is_valid_message(msg) == 0) return; for (i = 0; i < 256; ++i) @@ -1037,7 +1039,7 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, } /* nhdr=yes means that we're only displaying headers, no body */ - if ((TheMessage->cm_anon_type == MES_ANON) && (mode == MT_CITADEL)) { + if ((TheMessage->cm_anon_type == MES_ANONONLY) && (mode == MT_CITADEL)) { if (do_proto) cprintf("nhdr=yes\n"); } @@ -1049,15 +1051,18 @@ int CtdlOutputPreLoadedMsg(struct CtdlMessage *TheMessage, if (TheMessage->cm_fields['A']) { strcpy(buf, TheMessage->cm_fields['A']); PerformUserHooks(buf, (-1L), EVT_OUTPUTMSG); - if (TheMessage->cm_anon_type == MES_ANON) + if (TheMessage->cm_anon_type == MES_ANONONLY) { strcpy(display_name, "****"); - else if (TheMessage->cm_anon_type == MES_AN2) + } + else if (TheMessage->cm_anon_type == MES_ANONOPT) { strcpy(display_name, "anonymous"); - else + } + else { strcpy(display_name, buf); + } if ((is_room_aide()) - && ((TheMessage->cm_anon_type == MES_ANON) - || (TheMessage->cm_anon_type == MES_AN2))) { + && ((TheMessage->cm_anon_type == MES_ANONONLY) + || (TheMessage->cm_anon_type == MES_ANONOPT))) { sprintf(&display_name[strlen(display_name)], " [%s]", buf); } @@ -1487,7 +1492,7 @@ int CtdlSaveMsgPointerInRoom(char *roomname, long msgid, int flags) { * Message base operation to send a message to the master file * (returns new message number) * - * This is the back end for CtdlSaveMsg() and should not be directly + * This is the back end for CtdlSubmitMsg() and should not be directly * called by server-side modules. * */ @@ -1661,14 +1666,13 @@ int ReplicationChecks(struct CtdlMessage *msg) { /* - * Save a message to disk + * Save a message to disk and submit it into the delivery system. */ -long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ - char *rec, /* Recipient (mail) */ - char *force, /* force a particular room? */ - int supplied_mailtype) /* local or remote type */ -{ - char aaa[100]; +long CtdlSubmitMsg(struct CtdlMessage *msg, /* message to save */ + struct recptypes *recps, /* recipients (if mail) */ + char *force /* force a particular room? */ +) { + char aaa[SIZ]; char hold_rm[ROOMNAMELEN]; char actual_rm[ROOMNAMELEN]; char force_room[ROOMNAMELEN]; @@ -1677,17 +1681,17 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ long newmsgid; char *mptr = NULL; struct usersupp userbuf; - int a; + int a, i; struct MetaData smi; FILE *network_fp = NULL; static int seqnum = 1; - struct CtdlMessage *imsg; + struct CtdlMessage *imsg = NULL; char *instr; - int mailtype; + struct ser_ret smr; + char *hold_R, *hold_D; - lprintf(9, "CtdlSaveMsg() called\n"); + lprintf(9, "CtdlSubmitMsg() called\n"); if (is_valid_message(msg) == 0) return(-1); /* self check */ - mailtype = supplied_mailtype; /* If this message has no timestamp, we take the liberty of * giving it one, right now. @@ -1717,27 +1721,6 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ strcpy(force_room, force); - /* Strip non-printable characters out of the recipient name */ - lprintf(9, "Checking recipient (if present)\n"); - strcpy(recipient, rec); - for (a = 0; a < strlen(recipient); ++a) - if (!isprint(recipient[a])) - strcpy(&recipient[a], &recipient[a + 1]); - - /* Change "user @ xxx" to "user" if xxx is an alias for this host */ - for (a=0; a\n", recipient); - mailtype = MES_LOCAL; - } - } - } - - lprintf(9, "Recipient is <%s>, mailtype is %d\n", recipient, mailtype); - /* Learn about what's inside, because it's what's inside that counts */ lprintf(9, "Learning what's inside\n"); if (msg->cm_fields['M'] == NULL) { @@ -1777,7 +1760,7 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ lprintf(9, "Switching rooms\n"); strcpy(hold_rm, CC->quickroom.QRname); strcpy(actual_rm, CC->quickroom.QRname); - if (strlen(recipient) > 0) { + if (recps != NULL) { strcpy(actual_rm, SENTITEMS); } @@ -1815,25 +1798,9 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ lprintf(9, "Performing replication checks\n"); if (ReplicationChecks(msg) > 0) return(-1); - /* Network mail - send a copy to the network program. */ - if ((strlen(recipient) > 0) && (mailtype == MES_IGNET)) { - lprintf(9, "Sending network spool\n"); - sprintf(aaa, "./network/spoolin/netmail.%04lx.%04x.%04x", - (long) getpid(), CC->cs_pid, ++seqnum); - lprintf(9, "Saving a copy to %s\n", aaa); - network_fp = fopen(aaa, "ab+"); - if (network_fp == NULL) - lprintf(2, "ERROR: %s\n", strerror(errno)); - } - /* Save it to disk */ lprintf(9, "Saving to disk\n"); - newmsgid = send_message(msg, network_fp); - if (network_fp != NULL) { - fclose(network_fp); - /* FIXME start a network run here */ - } - + newmsgid = send_message(msg, NULL); if (newmsgid <= 0L) return(-1); /* Write a supplemental message info record. This doesn't have to @@ -1862,7 +1829,8 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ } /* For internet mail, drop a copy in the outbound queue room */ - if (mailtype == MES_INTERNET) { + if (recps != NULL) + if (recps->num_internet > 0) { CtdlSaveMsgPointerInRoom(SMTP_SPOOLOUT_ROOM, newmsgid, 0); } @@ -1875,16 +1843,18 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ /* If this is private, local mail, make a copy in the * recipient's mailbox and bump the reference count. */ - if ((strlen(recipient) > 0) && (mailtype == MES_LOCAL)) { + if (recps != NULL) + 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", + recipient); if (getuser(&userbuf, recipient) == 0) { - lprintf(9, "Delivering private mail\n"); MailboxName(actual_rm, &userbuf, MAILROOM); CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0); } else { - lprintf(9, "No user <%s>, saving in %s> instead\n", - recipient, AIDEROOM); - CtdlSaveMsgPointerInRoom(AIDEROOM, newmsgid, 0); + lprintf(9, "No user <%s>\n", recipient); } } @@ -1892,24 +1862,71 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ lprintf(9, "Performing after-save hooks\n"); PerformMessageHooks(msg, EVT_AFTERSAVE); - /* */ + /* For IGnet mail, we have to save a new copy into the spooler for + * each recipient, with the R and D fields set to the recipient and + * destination-node. This has two ugly side effects: all other + * recipients end up being unlisted in this recipient's copy of the + * message, and it has to deliver multiple messages to the same + * 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) + for (i=0; irecp_ignet, '|'); ++i) { + extract(recipient, recps->recp_ignet, i); + + hold_R = msg->cm_fields['R']; + hold_D = msg->cm_fields['D']; + msg->cm_fields['R'] = mallok(SIZ); + msg->cm_fields['D'] = mallok(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, + "./network/spoolin/netmail.%04lx.%04x.%04x", + (long) getpid(), CC->cs_pid, ++seqnum); + network_fp = fopen(aaa, "wb+"); + if (network_fp != NULL) { + fwrite(smr.ser, smr.len, 1, network_fp); + fclose(network_fp); + } + phree(smr.ser); + } + + phree(msg->cm_fields['R']); + phree(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); - /* For internet mail, generate delivery instructions - * (Yes, this is recursive! Deal with it!) + /* 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 (mailtype == MES_INTERNET) { + if (recps != NULL) + if (recps->num_internet > 0) { lprintf(9, "Generating delivery instructions\n"); - instr = mallok(2048); + instr = mallok(SIZ * 2); sprintf(instr, "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n" - "bounceto|%s@%s\n" - "remote|%s|0||\n", + "bounceto|%s@%s\n", SPOOLMIME, newmsgid, (long)time(NULL), - msg->cm_fields['A'], msg->cm_fields['N'], - recipient ); + msg->cm_fields['A'], msg->cm_fields['N'] + ); + + for (i=0; irecp_internet, '|'); ++i) { + extract(recipient, recps->recp_internet, i); + sprintf(&instr[strlen(instr)], + "remote|%s|0||\n", recipient); + } imsg = mallok(sizeof(struct CtdlMessage)); memset(imsg, 0, sizeof(struct CtdlMessage)); @@ -1918,7 +1935,7 @@ long CtdlSaveMsg(struct CtdlMessage *msg, /* message to save */ imsg->cm_format_type = FMT_RFC822; imsg->cm_fields['A'] = strdoop("Citadel"); imsg->cm_fields['M'] = instr; - CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL); + CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(imsg); } @@ -1946,7 +1963,7 @@ void quickie_message(char *from, char *to, char *room, char *text) msg->cm_fields['R'] = strdoop(to); msg->cm_fields['M'] = strdoop(text); - CtdlSaveMsg(msg, "", room, MES_LOCAL); + CtdlSubmitMsg(msg, NULL, room); CtdlFreeMessage(msg); syslog(LOG_NOTICE, text); } @@ -1975,10 +1992,15 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ m = reallok(exist, strlen(exist) + 4096); if (m == NULL) phree(exist); } + + /* flush the input if we have nowhere to store it */ if (m == NULL) { while ( (client_gets(buf)>0) && strcmp(buf, terminator) ) ;; return(NULL); - } else { + } + + /* otherwise read it into memory */ + else { buffer_len = 4096; m[0] = 0; message_len = 0; @@ -2007,9 +2029,10 @@ char *CtdlReadMessageBody(char *terminator, /* token signalling EOT */ } } - /* Add the new line to the buffer. We avoid using strcat() - * because that would involve traversing the entire message - * after each line, and this function needs to run fast. + /* 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'; @@ -2038,13 +2061,10 @@ static struct CtdlMessage *make_message( char *recipient, /* NULL if it's not mail */ char *room, /* room where it's going */ int type, /* see MES_ types in header file */ - int net_type, /* see MES_ types in header file */ - int format_type, /* local or remote (see citadel.h) */ - char *fake_name) /* who we're masquerading as */ -{ - - int a; - char dest_node[32]; + int format_type, /* variformat, plain text, MIME... */ + char *fake_name /* who we're masquerading as */ +) { + char dest_node[SIZ]; char buf[SIZ]; struct CtdlMessage *msg; @@ -2057,24 +2077,7 @@ static struct CtdlMessage *make_message( /* Don't confuse the poor folks if it's not routed mail. */ strcpy(dest_node, ""); - /* If net_type is MES_IGNET, split out the destination node. */ - if (net_type == MES_IGNET) { - strcpy(dest_node, NODENAME); - for (a = 0; a < strlen(recipient); ++a) { - if (recipient[a] == '@') { - recipient[a] = 0; - strcpy(dest_node, &recipient[a + 1]); - } - } - } - - /* if net_type is MES_INTERNET, set the dest node to 'internet' */ - if (net_type == MES_INTERNET) { - strcpy(dest_node, "internet"); - } - - while (isspace(recipient[strlen(recipient) - 1])) - recipient[strlen(recipient) - 1] = 0; + striplt(recipient); sprintf(buf, "cit%ld", author->usernum); /* Path */ msg->cm_fields['P'] = strdoop(buf); @@ -2087,24 +2090,26 @@ static struct CtdlMessage *make_message( else msg->cm_fields['A'] = strdoop(author->fullname); - if (CC->quickroom.QRflags & QR_MAILBOX) /* room */ + if (CC->quickroom.QRflags & QR_MAILBOX) { /* room */ msg->cm_fields['O'] = strdoop(&CC->quickroom.QRname[11]); - else + } + else { msg->cm_fields['O'] = strdoop(CC->quickroom.QRname); + } msg->cm_fields['N'] = strdoop(NODENAME); /* nodename */ msg->cm_fields['H'] = strdoop(HUMANNODE); /* hnodename */ - if (recipient[0] != 0) + if (recipient[0] != 0) { msg->cm_fields['R'] = strdoop(recipient); - if (dest_node[0] != 0) + } + if (dest_node[0] != 0) { msg->cm_fields['D'] = strdoop(dest_node); - + } msg->cm_fields['M'] = CtdlReadMessageBody("000", config.c_maxmsglen, NULL); - return(msg); } @@ -2145,16 +2150,18 @@ int CtdlDoIHavePermissionToPostInThisRoom(char *errmsgbuf) { } -#ifdef NOT_YET_FINISHED /* FIXME */ /* * Validate recipients, count delivery types and errors, and handle aliasing + * FIXME check for dupes!!!!! */ struct recptypes *validate_recipients(char *recipients) { struct recptypes *ret; char this_recp[SIZ]; + char append[SIZ]; int num_recps; int i; int mailtype; + int invalid; struct usersupp tempUS; /* Initialize */ @@ -2167,48 +2174,99 @@ struct recptypes *validate_recipients(char *recipients) { ret->num_ignet = 0; ret->num_error = 0; - if (recipients == NULL) return(ret); - if (strlen(recipients) == NULL) return(ret); - - /* Allow either , or ; separators by changing ; to , */ - for (i=0; i 0) for (i=0; i\n", i, this_recp); mailtype = alias(this_recp); + invalid = 0; switch(mailtype) { case MES_LOCAL: - if (getuser(&tempUS, buf) == 0) { + if (getuser(&tempUS, this_recp) == 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 { ++ret->num_error; + invalid = 1; } break; case MES_INTERNET: ++ret->num_internet; + if (strlen(ret->recp_internet) > 0) { + strcat(ret->recp_internet, "|"); + } + strcat(ret->recp_internet, this_recp); break; case MES_IGNET: ++ret->num_ignet; + if (strlen(ret->recp_ignet) > 0) { + strcat(ret->recp_ignet, "|"); + } + strcat(ret->recp_ignet, this_recp); break; case MES_ERROR: ++ret->num_error; + invalid = 1; break; } + if (invalid) { + if (strlen(ret->errormsg) == 0) { + sprintf(append, + "Invalid recipient: %s", + this_recp); + } + else { + sprintf(append, + ", %s", this_recp); + } + if ( (strlen(ret->errormsg) + strlen(append)) < SIZ) { + strcat(ret->errormsg, append); + } + } + else { + if (strlen(ret->display_recp) == 0) { + strcpy(append, this_recp); + } + else { + sprintf(append, ", %s", this_recp); + } + if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) { + strcat(ret->display_recp, append); + } + } + } + if ((ret->num_local + ret->num_internet + + ret->num_ignet + ret->num_error) == 0) { + ++ret->num_error; + strcpy(ret->errormsg, "No recipients specified."); } return(ret); } -#endif /* FIXME */ @@ -2218,141 +2276,139 @@ struct recptypes *validate_recipients(char *recipients) { void cmd_ent0(char *entargs) { int post = 0; - char recipient[SIZ]; + char recp[SIZ]; + char masquerade_as[SIZ]; int anon_flag = 0; int format_type = 0; char newusername[SIZ]; struct CtdlMessage *msg; - int a, b; - int e = 0; + int anonymous = 0; int mtsflag = 0; - struct usersupp tempUS; - char buf[SIZ]; + char errmsg[SIZ]; int err = 0; + struct recptypes *valid = NULL; post = extract_int(entargs, 0); - extract(recipient, entargs, 1); + extract(recp, entargs, 1); anon_flag = extract_int(entargs, 2); format_type = extract_int(entargs, 3); /* first check to make sure the request is valid. */ - err = CtdlDoIHavePermissionToPostInThisRoom(buf); + err = CtdlDoIHavePermissionToPostInThisRoom(errmsg); if (err) { - cprintf("%d %s\n", err, buf); + cprintf("%d %s\n", err, errmsg); return; } /* Check some other permission type things. */ if (post == 2) { - if (CC->usersupp.axlevel < 6) { + if (CC->usersupp.axlevel < 6) { cprintf("%d You don't have permission to masquerade.\n", ERROR + HIGHER_ACCESS_REQUIRED); return; } extract(newusername, entargs, 4); - memset(CC->fake_postname, 0, 32); - strcpy(CC->fake_postname, newusername); - cprintf("%d Ok\n", OK); + memset(CC->fake_postname, 0, sizeof(CC->fake_postname) ); + safestrncpy(CC->fake_postname, newusername, + sizeof(CC->fake_postname) ); + cprintf("%d ok\n", OK); return; } CC->cs_flags |= CS_POSTING; - buf[0] = 0; if (CC->quickroom.QRflags & QR_MAILBOX) { - if (CC->usersupp.axlevel >= 2) { - strcpy(buf, recipient); - } - else { - strcpy(buf, "sysop"); - } - e = alias(buf); /* alias and mail type */ - if ((buf[0] == 0) || (e == MES_ERROR)) { - cprintf("%d Unknown address - cannot send message.\n", - ERROR + NO_SUCH_USER); + if (CC->usersupp.axlevel < 2) { + strcpy(recp, "sysop"); + } + + valid = validate_recipients(recp); + lprintf(9, "validate_recipients(%s)\n", recp); + lprintf(9, " num_local = %d\n", valid->num_local); + lprintf(9, " num_internet = %d\n", valid->num_internet); + lprintf(9, " num_ignet = %d\n", valid->num_ignet); + lprintf(9, " num_error = %d\n", valid->num_error); + lprintf(9, " errormsg = %s\n", valid->errormsg); + lprintf(9, " display_recp = %s\n", valid->display_recp); + + if (valid->num_error > 0) { + cprintf("%d %s\n", + ERROR + NO_SUCH_USER, + valid->errormsg); + phree(valid); return; } - if ((e != MES_LOCAL) && (CC->usersupp.axlevel < 4)) { - cprintf("%d Net privileges required for network mail.\n", + + if ( ( (valid->num_internet + valid->num_ignet) > 0) + && (CC->usersupp.axlevel < 4) ) { + cprintf("%d Higher access required for network mail.\n", ERROR + HIGHER_ACCESS_REQUIRED); + phree(valid); return; } - if ((RESTRICT_INTERNET == 1) && (e == MES_INTERNET) + + if ((RESTRICT_INTERNET == 1) && (valid->num_internet > 0) && ((CC->usersupp.flags & US_INTERNET) == 0) && (!CC->internal_pgm)) { cprintf("%d You don't have access to Internet mail.\n", ERROR + HIGHER_ACCESS_REQUIRED); + phree(valid); return; } - if (!strcasecmp(buf, "sysop")) { + + if (!strcasecmp(recp, "sysop")) { mtsflag = 1; } - else if (e == MES_LOCAL) { /* don't search local file */ - - -/*** We can probably do this now. - if (!strcasecmp(buf, CC->usersupp.fullname)) { - cprintf("%d Can't send mail to yourself!\n", - ERROR + NO_SUCH_USER); - return; - } - *** hi from Stu -*/ + } - /* Check to make sure the user exists; also get the correct - * upper/lower casing of the name. - */ - a = getuser(&tempUS, buf); - if (a != 0) { - cprintf("%d No such user.\n", ERROR + NO_SUCH_USER); - return; - } - strcpy(buf, tempUS.fullname); + /* Is this a room which has anonymous-only or anonymous-option? */ + anonymous = MES_NORMAL; + if (CC->quickroom.QRflags & QR_ANONONLY) { + anonymous = MES_ANONONLY; + } + if (CC->quickroom.QRflags & QR_ANONOPT) { + if (anon_flag == 1) { /* only if the user requested it */ + anonymous = MES_ANONOPT; } } - b = MES_NORMAL; - if (CC->quickroom.QRflags & QR_ANONONLY) - b = MES_ANON; - if (CC->quickroom.QRflags & QR_ANONOPT) { - if (anon_flag == 1) - b = MES_AN2; + if ((CC->quickroom.QRflags & QR_MAILBOX) == 0) { + recp[0] = 0; } - if ((CC->quickroom.QRflags & QR_MAILBOX) == 0) - buf[0] = 0; /* If we're only checking the validity of the request, return * success without creating the message. */ if (post == 0) { - cprintf("%d %s\n", OK, buf); + cprintf("%d %s\n", OK, + ((valid != NULL) ? valid->display_recp : "") ); + phree(valid); return; } - cprintf("%d send message\n", SEND_LISTING); - - /* Read in the message from the client. */ + /* Handle author masquerading */ if (CC->fake_postname[0]) { - msg = make_message(&CC->usersupp, buf, - CC->quickroom.QRname, b, e, format_type, - CC->fake_postname); + strcpy(masquerade_as, CC->fake_postname); } else if (CC->fake_username[0]) { - msg = make_message(&CC->usersupp, buf, - CC->quickroom.QRname, b, e, format_type, - CC->fake_username); + strcpy(masquerade_as, CC->fake_username); } else { - msg = make_message(&CC->usersupp, buf, - CC->quickroom.QRname, b, e, format_type, ""); + strcpy(masquerade_as, ""); } + /* 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); + if (msg != NULL) { - CtdlSaveMsg(msg, buf, (mtsflag ? AIDEROOM : ""), e); + CtdlSubmitMsg(msg, valid, (mtsflag ? AIDEROOM : "") ); CtdlFreeMessage(msg); } CC->fake_postname[0] = '\0'; + phree(valid); return; } @@ -2532,7 +2588,9 @@ void cmd_move(char *args) /* Now delete the message from the source room, * if this is a 'move' rather than a 'copy' operation. */ - if (is_copy == 0) CtdlDeleteMessages(CC->quickroom.QRname, num, ""); + if (is_copy == 0) { + CtdlDeleteMessages(CC->quickroom.QRname, num, ""); + } cprintf("%d Message %s.\n", OK, (is_copy ? "copied" : "moved") ); } @@ -2574,10 +2632,10 @@ void PutMetaData(struct MetaData *smibuf) { long TheIndex; - /* Use the negative of the message number for its supp record index */ + /* Use the negative of the message number for the metadata db index */ TheIndex = (0L - smibuf->meta_msgnum); - lprintf(9, "PuttMetaData(%ld) - ref count is %d\n", + lprintf(9, "PutMetaData(%ld) - ref count is %d\n", smibuf->meta_msgnum, smibuf->meta_refcount); cdb_store(CDB_MSGMAIN, @@ -2617,6 +2675,8 @@ void AdjRefCount(long msgnum, int incr) lprintf(9, "Deleting message <%ld>\n", msgnum); delnum = msgnum; cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long)); + + /* We have to delete the metadata record too! */ delnum = (0L - msgnum); cdb_delete(CDB_MSGMAIN, &delnum, sizeof(long)); } @@ -2720,7 +2780,7 @@ void CtdlWriteObject(char *req_room, /* Room to stuff it in */ CtdlDeleteMessages(roomname, 0L, content_type)); } /* Now write the data */ - CtdlSaveMsg(msg, "", roomname, MES_LOCAL); + CtdlSubmitMsg(msg, NULL, roomname); CtdlFreeMessage(msg); } diff --git a/citadel/msgbase.h b/citadel/msgbase.h index 90dae4256..ea521a392 100644 --- a/citadel/msgbase.h +++ b/citadel/msgbase.h @@ -50,6 +50,10 @@ struct recptypes { int num_ignet; int num_error; char errormsg[SIZ]; + char recp_local[SIZ]; + char recp_internet[SIZ]; + char recp_ignet[SIZ]; + char display_recp[SIZ]; }; @@ -68,7 +72,7 @@ void cmd_msg4 (char *cmdbuf); void cmd_opna (char *cmdbuf); long send_message (struct CtdlMessage *, FILE *); void loadtroom (void); -long CtdlSaveMsg(struct CtdlMessage *, char *, char *, int); +long CtdlSubmitMsg(struct CtdlMessage *, struct recptypes *, char *); void quickie_message (char *, char *, char *, char *); void cmd_ent0 (char *entargs); void cmd_dele (char *delstr); diff --git a/citadel/serv_chat.c b/citadel/serv_chat.c index 0ac6799e3..396455e26 100644 --- a/citadel/serv_chat.c +++ b/citadel/serv_chat.c @@ -585,7 +585,7 @@ int send_express_message(char *lun, char *x_user, char *x_msg) * creating the room if necessary. */ create_room(PAGELOGROOM, 4, "", 0, 1); - msgnum = CtdlSaveMsg(logmsg, "", PAGELOGROOM, MES_LOCAL); + msgnum = CtdlSubmitMsg(logmsg, NULL, PAGELOGROOM); /* Now save a copy in the global log room, if configured */ if (strlen(config.c_logpages) > 0) { diff --git a/citadel/serv_network.c b/citadel/serv_network.c index a66f577c2..86e10c3b7 100644 --- a/citadel/serv_network.c +++ b/citadel/serv_network.c @@ -484,7 +484,7 @@ void network_spool_msg(long msgnum, void *userdata) { imsg->cm_fields['M'] = instr; /* Save delivery instructions in spoolout room */ - CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL); + CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(imsg); } @@ -983,7 +983,7 @@ void network_process_buffer(char *buffer, long size) { /* save the message into a room */ msg->cm_flags = CM_SKIP_HOOKS; - CtdlSaveMsg(msg, "", target_room, 0); + CtdlSubmitMsg(msg, NULL, target_room); CtdlFreeMessage(msg); } diff --git a/citadel/serv_smtp.c b/citadel/serv_smtp.c index 03e336f65..65ef7e75f 100644 --- a/citadel/serv_smtp.c +++ b/citadel/serv_smtp.c @@ -545,10 +545,9 @@ int smtp_message_delivery(struct CtdlMessage *msg) { if (msg->cm_fields['O']==NULL) msg->cm_fields['O'] = strdoop(MAILROOM); /* Save the message in the queue */ - msgid = CtdlSaveMsg(msg, - "", - SMTP_SPOOLOUT_ROOM, - MES_LOCAL); + msgid = CtdlSubmitMsg(msg, + NULL, + SMTP_SPOOLOUT_ROOM); ++successful_saves; instr = mallok(1024); @@ -610,13 +609,13 @@ int smtp_message_delivery(struct CtdlMessage *msg) { imsg->cm_anon_type = MES_NORMAL; imsg->cm_format_type = FMT_RFC822; imsg->cm_fields['M'] = instr; - CtdlSaveMsg(imsg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL); + CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(imsg); } /* If there are no remote spools, delete the message */ else { - phree(instr); /* only needed here, because CtdlSaveMsg() + phree(instr); /* only needed here, because CtdlSubmitMsg() * would free this buffer otherwise */ CtdlDeleteMessages(SMTP_SPOOLOUT_ROOM, msgid, ""); } @@ -1193,22 +1192,23 @@ void smtp_do_bounce(char *instr) { lprintf(7, "No bounce address specified\n"); bounce_msgid = (-1L); } +/* FIXME this won't work else if (mes_type = alias(bounceto), mes_type == MES_ERROR) { lprintf(7, "Invalid bounce address <%s>\n", bounceto); bounce_msgid = (-1L); } else { - bounce_msgid = CtdlSaveMsg(bmsg, + bounce_msgid = CtdlSubmitMsg(bmsg, bounceto, "", mes_type); } + */ TRACE; /* Otherwise, go to the Aide> room */ lprintf(9, "bounce to room?\n"); - if (bounce_msgid < 0L) bounce_msgid = CtdlSaveMsg(bmsg, - "", AIDEROOM, - MES_LOCAL); + if (bounce_msgid < 0L) bounce_msgid = CtdlSubmitMsg(bmsg, + NULL, AIDEROOM); } CtdlFreeMessage(bmsg); @@ -1428,7 +1428,7 @@ void smtp_do_procmsg(long msgnum, void *userdata) { "retry|%ld\n", SPOOLMIME, instr, (long)time(NULL), (long)retry ); phree(instr); - CtdlSaveMsg(msg, "", SMTP_SPOOLOUT_ROOM, MES_LOCAL); + CtdlSubmitMsg(msg, NULL, SMTP_SPOOLOUT_ROOM); CtdlFreeMessage(msg); } diff --git a/citadel/serv_vcard.c b/citadel/serv_vcard.c index 102b2d766..c3b27f092 100644 --- a/citadel/serv_vcard.c +++ b/citadel/serv_vcard.c @@ -451,7 +451,7 @@ void vcard_purge(char *username, long usernum) { msg->cm_fields['S'] = strdoop("CANCEL"); - CtdlSaveMsg(msg, "", ADDRESS_BOOK_ROOM, MES_LOCAL); + CtdlSubmitMsg(msg, NULL, ADDRESS_BOOK_ROOM); CtdlFreeMessage(msg); } -- 2.39.2