RSS-Client: add missing locks
[citadel.git] / citadel / msgbase.c
index 2299d9a481fe37226aed7e6891f6fe03a5be8448..f23af01c8d43302e9c8b2f8ec00c1c3f4e5287c2 100644 (file)
@@ -1,21 +1,15 @@
 /*
  * Implements the message store.
  *
- * Copyright (c) 1987-2011 by the citadel.org team
+ * Copyright (c) 1987-2012 by the citadel.org team
  *
  * This program is open source software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
+ * it under the terms of the GNU General Public License version 3.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  */
 
 #include "sysdep.h"
@@ -74,6 +68,8 @@ struct addresses_to_be_filed *atbf = NULL;
 /* This temp file holds the queue of operations for AdjRefCount() */
 static FILE *arcfp = NULL;
 
+void AdjRefCountList(long *msgnum, long nmsg, int incr);
+
 /*
  * These are the four-character field headers we use when outputting
  * messages in Citadel format (as opposed to RFC822 format).
@@ -620,9 +616,23 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
        }
 
        /* Learn about the user and room in question */
+       if (server_shutting_down) {
+               if (need_to_free_re) regfree(&re);
+               return -1;
+       }
        CtdlGetUser(&CC->user, CC->curr_user);
+
+       if (server_shutting_down) {
+               if (need_to_free_re) regfree(&re);
+               return -1;
+       }
        CtdlGetRelationship(&vbuf, &CC->user, &CC->room);
 
+       if (server_shutting_down) {
+               if (need_to_free_re) regfree(&re);
+               return -1;
+       }
+
        /* Load the message list */
        cdbfr = cdb_fetch(CDB_MSGLISTS, &CC->room.QRnumber, sizeof(long));
        if (cdbfr == NULL) {
@@ -655,6 +665,11 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
                         * enough to only do the read if the caller has
                         * specified something that will need it.
                         */
+                       if (server_shutting_down) {
+                               if (need_to_free_re) regfree(&re);
+                               free(msglist);
+                               return -1;
+                       }
                        GetMetaData(&smi, msglist[a]);
 
                        /* if (strcasecmp(smi.meta_content_type, content_type)) { old non-regex way */
@@ -672,6 +687,11 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
        if (num_msgs > 0) {
                if (compare != NULL) {
                        for (a = 0; a < num_msgs; ++a) {
+                               if (server_shutting_down) {
+                                       if (need_to_free_re) regfree(&re);
+                                       free(msglist);
+                                       return -1;
+                               }
                                msg = CtdlFetchMessage(msglist[a], 1);
                                if (msg != NULL) {
                                        if (CtdlMsgCmp(msg, compare)) {
@@ -734,6 +754,11 @@ int CtdlForEachMessage(int mode, long ref, char *search_string,
         */
        if (num_msgs > 0)
                for (a = 0; a < num_msgs; ++a) {
+                       if (server_shutting_down) {
+                               if (need_to_free_re) regfree(&re);
+                               free(msglist);
+                               return num_processed;
+                       }
                        thismsg = msglist[a];
                        if (mode == MSGS_ALL) {
                                is_seen = 0;
@@ -1084,26 +1109,37 @@ void mime_download(char *name, char *filename, char *partnum, char *disp,
                   char *encoding, char *cbid, void *cbuserdata)
 {
        int rv = 0;
+       CitContext *CCC = MyContext();
 
        /* Silently go away if there's already a download open. */
-       if (CC->download_fp != NULL)
+       if (CCC->download_fp != NULL)
                return;
 
        if (
-               (!IsEmptyStr(partnum) && (!strcasecmp(CC->download_desired_section, partnum)))
-       ||      (!IsEmptyStr(cbid) && (!strcasecmp(CC->download_desired_section, cbid)))
+               (!IsEmptyStr(partnum) && (!strcasecmp(CCC->download_desired_section, partnum)))
+       ||      (!IsEmptyStr(cbid) && (!strcasecmp(CCC->download_desired_section, cbid)))
        ) {
-               CC->download_fp = tmpfile();
-               if (CC->download_fp == NULL)
+               CCC->download_fp = tmpfile();
+               if (CCC->download_fp == NULL) {
+                       syslog(LOG_EMERG, "mime_download(): Couldn't write: %s\n",
+                              strerror(errno));
+                       cprintf("%d cannot open temporary file: %s\n",
+                               ERROR + INTERNAL_ERROR, strerror(errno));
                        return;
+               }
        
                rv = fwrite(content, length, 1, CC->download_fp);
-               if (rv == -1) {
+               if (rv <= 0) {
                        syslog(LOG_EMERG, "mime_download(): Couldn't write: %s\n",
                               strerror(errno));
+                       cprintf("%d unable to write tempfile.\n",
+                               ERROR + TOO_BIG);
+                       fclose(CCC->download_fp);
+                       CCC->download_fp = NULL;
+                       return;
                }
-               fflush(CC->download_fp);
-               rewind(CC->download_fp);
+               fflush(CCC->download_fp);
+               rewind(CCC->download_fp);
        
                OpenCmdResult(filename, cbtype);
        }
@@ -1239,26 +1275,28 @@ int is_valid_message(struct CtdlMessage *msg) {
        return 1;
 }
 
+void CtdlFreeMessageContents(struct CtdlMessage *msg)
+{
+       int i;
+
+       for (i = 0; i < 256; ++i)
+               if (msg->cm_fields[i] != NULL) {
+                       free(msg->cm_fields[i]);
+               }
 
+       msg->cm_magic = 0;      /* just in case */
+}
 /*
  * 'Destructor' for struct CtdlMessage
  */
 void CtdlFreeMessage(struct CtdlMessage *msg)
 {
-       int i;
-
        if (is_valid_message(msg) == 0) 
        {
                if (msg != NULL) free (msg);
                return;
        }
-
-       for (i = 0; i < 256; ++i)
-               if (msg->cm_fields[i] != NULL) {
-                       free(msg->cm_fields[i]);
-               }
-
-       msg->cm_magic = 0;      /* just in case */
+       CtdlFreeMessageContents(msg);
        free(msg);
 }
 
@@ -1354,7 +1392,7 @@ void fixed_output(char *name, char *filename, char *partnum, char *disp,
                ptr = html_to_ascii(content, length, 80, 0);
                wlen = strlen(ptr);
                client_write(ptr, wlen);
-               if (ptr[wlen-1] != '\n') {
+               if ((wlen > 0) && (ptr[wlen-1] != '\n')) {
                        cprintf("\n");
                }
                free(ptr);
@@ -2756,9 +2794,7 @@ int CtdlSaveMsgPointersInRoom(char *roomname, long newmsgidlist[], int num_newms
 
        /* Bump the reference count for all messages which were merged */
        if (!suppress_refcount_adj) {
-               for (i=0; i<num_msgs_to_be_merged; ++i) {
-                       AdjRefCount(msgs_to_be_merged[i], +1);
-               }
+               AdjRefCountList(msgs_to_be_merged, num_msgs_to_be_merged, +1);
        }
 
        /* Free up memory... */
@@ -3020,7 +3056,6 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
        int qualified_for_journaling = 0;
        CitContext *CCC = MyContext();
        char bounce_to[1024] = "";
-       size_t tmp = 0;
        int rv = 0;
 
        syslog(LOG_DEBUG, "CtdlSubmitMsg() called\n");
@@ -3199,7 +3234,7 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                for (i=0; i<num_tokens(recps->recp_room, '|'); ++i) {
                        extract_token(recipient, recps->recp_room, i,
                                      '|', sizeof recipient);
-                       syslog(LOG_DEBUG, "Delivering to room <%s>\n", recipient);
+                       syslog(LOG_DEBUG, "Delivering to room <%s>\n", recipient);///// xxxx
                        CtdlSaveMsgPointerInRoom(recipient, newmsgid, 0, msg);
                }
 
@@ -3230,8 +3265,6 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,       /* message to save */
                        syslog(LOG_DEBUG, "Delivering private local mail to <%s>\n",
                               recipient);
                        if (CtdlGetUser(&userbuf, recipient) == 0) {
-                               // Add a flag so the Funambol module knows its mail
-                               msg->cm_fields['W'] = strdup(recipient);
                                CtdlMailboxName(actual_rm, sizeof actual_rm, &userbuf, MAILROOM);
                                CtdlSaveMsgPointerInRoom(actual_rm, newmsgid, 0, msg);
                                CtdlBumpNewMailCounter(userbuf.usernum);
@@ -3253,24 +3286,29 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
                                        imsg->cm_magic = CTDLMESSAGE_MAGIC;
                                        imsg->cm_anon_type = MES_NORMAL;
                                        imsg->cm_format_type = FMT_RFC822;
+                                       imsg->cm_fields['U'] = strdup("QMSG");
                                        imsg->cm_fields['A'] = strdup("Citadel");
                                        imsg->cm_fields['J'] = strdup("do not journal");
                                        imsg->cm_fields['M'] = instr;   /* imsg owns this memory now */
-                                       imsg->cm_fields['W'] = strdup(recipient);
+                                       imsg->cm_fields['2'] = strdup(recipient);
                                        CtdlSubmitMsg(imsg, NULL, FNBL_QUEUE_ROOM, 0);
                                        CtdlFreeMessage(imsg);
                                }
                        }
                        else {
                                syslog(LOG_DEBUG, "No user <%s>\n", recipient);
-                               CtdlSaveMsgPointerInRoom(config.c_aideroom,
-                                                        newmsgid, 0, msg);
+                               CtdlSaveMsgPointerInRoom(config.c_aideroom, newmsgid, 0, msg);
                        }
                }
 
        /* Perform "after save" hooks */
        syslog(LOG_DEBUG, "Performing after-save hooks\n");
+       if (msg->cm_fields['3'] != NULL) free(msg->cm_fields['3']);
+       msg->cm_fields['3'] = malloc(20);
+       snprintf(msg->cm_fields['3'], 20, "%ld", newmsgid);
        PerformMessageHooks(msg, EVT_AFTERSAVE);
+       free(msg->cm_fields['3']);
+       msg->cm_fields['3'] = NULL;
 
        /* 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
@@ -3327,29 +3365,41 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,     /* message to save */
         * contain a recipient.
         */
        if ((recps != NULL) && (recps->num_internet > 0)) {
+               StrBuf *SpoolMsg = NewStrBuf();
+               long nTokens;
+
                syslog(LOG_DEBUG, "Generating delivery instructions\n");
-               instr_alloc = 1024;
-               instr = malloc(instr_alloc);
-               snprintf(instr, instr_alloc,
-                        "Content-type: %s\n\nmsgid|%ld\nsubmitted|%ld\n"
-                        "bounceto|%s\n",
-                        SPOOLMIME, newmsgid, (long)time(NULL),
-                        bounce_to
-                       );
 
-               if (recps->envelope_from != NULL) {
-                       tmp = strlen(instr);
-                       snprintf(&instr[tmp], instr_alloc-tmp, "envelope_from|%s\n", recps->envelope_from);
-               }
+               StrBufPrintf(SpoolMsg,
+                            "Content-type: "SPOOLMIME"\n"
+                            "\n"
+                            "msgid|%ld\n"
+                            "submitted|%ld\n"
+                            "bounceto|%s\n",
+                            newmsgid,
+                            (long)time(NULL),
+                            bounce_to);
 
-               for (i=0; i<num_tokens(recps->recp_internet, '|'); ++i) {
-                       tmp = strlen(instr);
-                       extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient);
-                       if ((tmp + strlen(recipient) + 32) > instr_alloc) {
-                               instr_alloc = instr_alloc * 2;
-                               instr = realloc(instr, instr_alloc);
+               if (recps->envelope_from != NULL) {
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("envelope_from|"), 0);
+                       StrBufAppendBufPlain(SpoolMsg, recps->envelope_from, -1, 0);
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
+               }
+               if (recps->sending_room != NULL) {
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("source_room|"), 0);
+                       StrBufAppendBufPlain(SpoolMsg, recps->sending_room, -1, 0);
+                       StrBufAppendBufPlain(SpoolMsg, HKEY("\n"), 0);
+               }
+
+               nTokens = num_tokens(recps->recp_internet, '|');
+               for (i = 0; i < nTokens; i++) {
+                       long len;
+                       len = extract_token(recipient, recps->recp_internet, i, '|', sizeof recipient);
+                       if (len > 0) {
+                               StrBufAppendBufPlain(SpoolMsg, HKEY("remote|"), 0);
+                               StrBufAppendBufPlain(SpoolMsg, recipient, len, 0);
+                               StrBufAppendBufPlain(SpoolMsg, HKEY("|0||\n"), 0);
                        }
-                       snprintf(&instr[tmp], instr_alloc - tmp, "remote|%s|0||\n", recipient);
                }
 
                imsg = malloc(sizeof(struct CtdlMessage));
@@ -3357,9 +3407,10 @@ long CtdlSubmitMsg(struct CtdlMessage *msg,      /* message to save */
                imsg->cm_magic = CTDLMESSAGE_MAGIC;
                imsg->cm_anon_type = MES_NORMAL;
                imsg->cm_format_type = FMT_RFC822;
+               imsg->cm_fields['U'] = strdup("QMSG");
                imsg->cm_fields['A'] = strdup("Citadel");
                imsg->cm_fields['J'] = strdup("do not journal");
-               imsg->cm_fields['M'] = instr;   /* imsg owns this memory now */
+               imsg->cm_fields['M'] = SmashStrBuf(&SpoolMsg);  /* imsg owns this memory now */
                CtdlSubmitMsg(imsg, NULL, SMTP_SPOOLOUT_ROOM, QP_EADDR);
                CtdlFreeMessage(imsg);
        }
@@ -3858,10 +3909,10 @@ struct CtdlMessage *CtdlMakeMessage(
                msg->cm_fields['E'] = strdup(supplied_euid);
        }
 
-       if (references != NULL) {
-               if (!IsEmptyStr(references)) {
-                       msg->cm_fields['W'] = strdup(references);
-               }
+       if ((references != NULL) && (!IsEmptyStr(references))) {
+               if (msg->cm_fields['W'] != NULL)
+                       free(msg->cm_fields['W']);
+               msg->cm_fields['W'] = strdup(references);
        }
 
        if (preformatted_text != NULL) {
@@ -3991,9 +4042,11 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                                      int Flags) {
        struct recptypes *ret;
        char *recipients = NULL;
+       char *org_recp;
        char this_recp[256];
        char this_recp_cooked[256];
        char append[SIZ];
+       long len;
        int num_recps = 0;
        int i, j;
        int mailtype;
@@ -4023,19 +4076,22 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
         * actually need, but it's healthier for the heap than doing lots of tiny
         * realloc() calls instead.
         */
-
-       ret->errormsg = malloc(strlen(recipients) + 1024);
-       ret->recp_local = malloc(strlen(recipients) + 1024);
-       ret->recp_internet = malloc(strlen(recipients) + 1024);
-       ret->recp_ignet = malloc(strlen(recipients) + 1024);
-       ret->recp_room = malloc(strlen(recipients) + 1024);
-       ret->display_recp = malloc(strlen(recipients) + 1024);
+       len = strlen(recipients) + 1024;
+       ret->errormsg = malloc(len);
+       ret->recp_local = malloc(len);
+       ret->recp_internet = malloc(len);
+       ret->recp_ignet = malloc(len);
+       ret->recp_room = malloc(len);
+       ret->display_recp = malloc(len);
+       ret->recp_orgroom = malloc(len);
+       org_recp = malloc(len);
 
        ret->errormsg[0] = 0;
        ret->recp_local[0] = 0;
        ret->recp_internet[0] = 0;
        ret->recp_ignet[0] = 0;
        ret->recp_room[0] = 0;
+       ret->recp_orgroom[0] = 0;
        ret->display_recp[0] = 0;
 
        ret->recptypes_magic = RECPTYPES_MAGIC;
@@ -4050,7 +4106,6 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
        /* Now start extracting recipients... */
 
        while (!IsEmptyStr(recipients)) {
-
                for (i=0; i<=strlen(recipients); ++i) {
                        if (recipients[i] == '\"') in_quotes = 1 - in_quotes;
                        if ( ( (recipients[i] == ',') && (!in_quotes) ) || (recipients[i] == 0) ) {
@@ -4071,6 +4126,8 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                        break;
                syslog(LOG_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp);
                ++num_recps;
+
+               strcpy(org_recp, this_recp);
                mailtype = alias(this_recp);
                mailtype = alias(this_recp);
                mailtype = alias(this_recp);
@@ -4122,6 +4179,12 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                                                strcat(ret->recp_room, "|");
                                        }
                                        strcat(ret->recp_room, &this_recp_cooked[5]);
+
+                                       if (!IsEmptyStr(ret->recp_orgroom)) {
+                                               strcat(ret->recp_orgroom, "|");
+                                       }
+                                       strcat(ret->recp_orgroom, org_recp);
+
                                }
                                        
                                /* Restore room in case something needs it */
@@ -4206,6 +4269,7 @@ struct recptypes *validate_recipients(const char *supplied_recipients,
                        }
                }
        }
+       free(org_recp);
 
        if ((ret->num_local + ret->num_internet + ret->num_ignet +
             ret->num_room + ret->num_error) == 0) {
@@ -4244,9 +4308,11 @@ void free_recipients(struct recptypes *valid) {
        if (valid->recp_internet != NULL)       free(valid->recp_internet);
        if (valid->recp_ignet != NULL)          free(valid->recp_ignet);
        if (valid->recp_room != NULL)           free(valid->recp_room);
+       if (valid->recp_orgroom != NULL)        free(valid->recp_orgroom);
        if (valid->display_recp != NULL)        free(valid->display_recp);
        if (valid->bounce_to != NULL)           free(valid->bounce_to);
        if (valid->envelope_from != NULL)       free(valid->envelope_from);
+       if (valid->sending_room != NULL)        free(valid->sending_room);
        free(valid);
 }
 
@@ -4257,6 +4323,7 @@ void free_recipients(struct recptypes *valid) {
  */
 void cmd_ent0(char *entargs)
 {
+       struct CitContext *CCC = CC;
        int post = 0;
        char recp[SIZ];
        char cc[SIZ];
@@ -4277,6 +4344,7 @@ void cmd_ent0(char *entargs)
        char subject[SIZ];
        int subject_required = 0;
        int do_confirm = 0;
+       int verbose_reply = 0;
        long msgnum;
        int i, j;
        char buf[256];
@@ -4295,6 +4363,7 @@ void cmd_ent0(char *entargs)
        do_confirm = extract_int(entargs, 6);
        extract_token(cc, entargs, 7, '|', sizeof cc);
        extract_token(bcc, entargs, 8, '|', sizeof bcc);
+       verbose_reply = extract_int(entargs, 9);
        switch(CC->room.QRdefaultview) {
        case VIEW_NOTES:
        case VIEW_WIKI:
@@ -4328,11 +4397,11 @@ void cmd_ent0(char *entargs)
        /* Check some other permission type things. */
 
        if (IsEmptyStr(newusername)) {
-               strcpy(newusername, CC->user.fullname);
+               strcpy(newusername, CCC->user.fullname);
        }
-       if (  (CC->user.axlevel < AxAideU)
-             && (strcasecmp(newusername, CC->user.fullname))
-             && (strcasecmp(newusername, CC->cs_inet_fn))
+       if (  (CCC->user.axlevel < AxAideU)
+             && (strcasecmp(newusername, CCC->user.fullname))
+             && (strcasecmp(newusername, CCC->cs_inet_fn))
                ) {     
                cprintf("%d You don't have permission to author messages as '%s'.\n",
                        ERROR + HIGHER_ACCESS_REQUIRED,
@@ -4347,13 +4416,13 @@ void cmd_ent0(char *entargs)
        }
 
        if (!IsEmptyStr(newuseremail)) {
-               if (!strcasecmp(newuseremail, CC->cs_inet_email)) {
+               if (!strcasecmp(newuseremail, CCC->cs_inet_email)) {
                        newuseremail_ok = 1;
                }
-               else if (!IsEmptyStr(CC->cs_inet_other_emails)) {
-                       j = num_tokens(CC->cs_inet_other_emails, '|');
+               else if (!IsEmptyStr(CCC->cs_inet_other_emails)) {
+                       j = num_tokens(CCC->cs_inet_other_emails, '|');
                        for (i=0; i<j; ++i) {
-                               extract_token(buf, CC->cs_inet_other_emails, i, '|', sizeof buf);
+                               extract_token(buf, CCC->cs_inet_other_emails, i, '|', sizeof buf);
                                if (!strcasecmp(newuseremail, buf)) {
                                        newuseremail_ok = 1;
                                }
@@ -4369,7 +4438,7 @@ void cmd_ent0(char *entargs)
                return;
        }
 
-       CC->cs_flags |= CS_POSTING;
+       CCC->cs_flags |= CS_POSTING;
 
        /* In mailbox rooms we have to behave a little differently --
         * make sure the user has specified at least one recipient.  Then
@@ -4378,10 +4447,10 @@ void cmd_ent0(char *entargs)
         * is the DRAFTS room which does not require recipients
         */
 
-       if ( (  ( (CC->room.QRflags & QR_MAILBOX) && (!strcasecmp(&CC->room.QRname[11], MAILROOM)) )
-               || ( (CC->room.QRflags & QR_MAILBOX) && (CC->curr_view == VIEW_MAILBOX) )
-                    ) && (strcasecmp(&CC->room.QRname[11], USERDRAFTROOM)) !=0 ) {
-               if (CC->user.axlevel < AxProbU) {
+       if ( (  ( (CCC->room.QRflags & QR_MAILBOX) && (!strcasecmp(&CCC->room.QRname[11], MAILROOM)) )
+               || ( (CCC->room.QRflags & QR_MAILBOX) && (CCC->curr_view == VIEW_MAILBOX) )
+                    ) && (strcasecmp(&CCC->room.QRname[11], USERDRAFTROOM)) !=0 ) {
+               if (CCC->user.axlevel < AxProbU) {
                        strcpy(recp, "sysop");
                        strcpy(cc, "");
                        strcpy(bcc, "");
@@ -4421,7 +4490,7 @@ void cmd_ent0(char *entargs)
                }
 
                if (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0) {
-                       if (CtdlCheckInternetMailPermission(&CC->user)==0) {
+                       if (CtdlCheckInternetMailPermission(&CCC->user)==0) {
                                cprintf("%d You do not have permission "
                                        "to send Internet mail.\n",
                                        ERROR + HIGHER_ACCESS_REQUIRED);
@@ -4433,7 +4502,7 @@ void cmd_ent0(char *entargs)
                }
 
                if ( ( (valid_to->num_internet + valid_to->num_ignet + valid_cc->num_internet + valid_cc->num_ignet + valid_bcc->num_internet + valid_bcc->num_ignet) > 0)
-                    && (CC->user.axlevel < AxNetU) ) {
+                    && (CCC->user.axlevel < AxNetU) ) {
                        cprintf("%d Higher access required for network mail.\n",
                                ERROR + HIGHER_ACCESS_REQUIRED);
                        free_recipients(valid_to);
@@ -4444,8 +4513,8 @@ void cmd_ent0(char *entargs)
        
                if ((RESTRICT_INTERNET == 1)
                    && (valid_to->num_internet + valid_cc->num_internet + valid_bcc->num_internet > 0)
-                   && ((CC->user.flags & US_INTERNET) == 0)
-                   && (!CC->internal_pgm)) {
+                   && ((CCC->user.flags & US_INTERNET) == 0)
+                   && (!CCC->internal_pgm)) {
                        cprintf("%d You don't have access to Internet mail.\n",
                                ERROR + HIGHER_ACCESS_REQUIRED);
                        free_recipients(valid_to);
@@ -4458,16 +4527,16 @@ void cmd_ent0(char *entargs)
 
        /* Is this a room which has anonymous-only or anonymous-option? */
        anonymous = MES_NORMAL;
-       if (CC->room.QRflags & QR_ANONONLY) {
+       if (CCC->room.QRflags & QR_ANONONLY) {
                anonymous = MES_ANONONLY;
        }
-       if (CC->room.QRflags & QR_ANONOPT) {
+       if (CCC->room.QRflags & QR_ANONOPT) {
                if (anon_flag == 1) {   /* only if the user requested it */
                        anonymous = MES_ANONOPT;
                }
        }
 
-       if ((CC->room.QRflags & QR_MAILBOX) == 0) {
+       if ((CCC->room.QRflags & QR_MAILBOX) == 0) {
                recp[0] = 0;
        }
 
@@ -4475,7 +4544,7 @@ void cmd_ent0(char *entargs)
         * strongly recommended in this room, if either the SUBJECTREQ flag
         * is set, or if there is one or more Internet email recipients.
         */
-       if (CC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1;
+       if (CCC->room.QRflags2 & QR2_SUBJECTREQ) subject_required = 1;
        if ((valid_to)  && (valid_to->num_internet > 0))        subject_required = 1;
        if ((valid_cc)  && (valid_cc->num_internet > 0))        subject_required = 1;
        if ((valid_bcc) && (valid_bcc->num_internet > 0))       subject_required = 1;
@@ -4505,8 +4574,8 @@ void cmd_ent0(char *entargs)
                cprintf("%d send message\n", SEND_LISTING);
        }
 
-       msg = CtdlMakeMessage(&CC->user, recp, cc,
-                             CC->room.QRname, anonymous, format_type,
+       msg = CtdlMakeMessage(&CCC->user, recp, cc,
+                             CCC->room.QRname, anonymous, format_type,
                              newusername, newuseremail, subject,
                              ((!IsEmptyStr(supplied_euid)) ? supplied_euid : NULL),
                              NULL, references);
@@ -4536,16 +4605,35 @@ void cmd_ent0(char *entargs)
        }
        free(all_recps);
 
+       if ((valid != NULL) && (valid->num_room == 1))
+       {
+               /* posting into an ML room? set the envelope from 
+                * to the actual mail address so others get a valid
+                * reply-to-header.
+                */
+               msg->cm_fields['V'] = strdup(valid->recp_orgroom);
+       }
+
        if (msg != NULL) {
                msgnum = CtdlSubmitMsg(msg, valid, "", QP_EADDR);
+               if (verbose_reply)
+               {
+                       if (StrLength(CCC->StatusMessage)>0)
+                       {
+                               StrBufAppendBufPlain(CCC->StatusMessage, HKEY("\n000\n"), 0);
+                               cputbuf(CCC->StatusMessage);
+                       }
+                       else
+                               client_write(HKEY("\n000\n"));
+               }
 
                if (do_confirm) {
                        cprintf("%ld\n", msgnum);
                        if (msgnum >= 0L) {
-                               cprintf("Message accepted.\n");
+                               client_write(HKEY("Message accepted.\n"));
                        }
                        else {
-                               cprintf("Internal error.\n");
+                               client_write(HKEY("Internal error.\n"));
                        }
                        if (msg->cm_fields['E'] != NULL) {
                                cprintf("%s\n", msg->cm_fields['E']);
@@ -4612,32 +4700,51 @@ int CtdlDeleteMessages(char *room_name,         /* which room */
                cdb_free(cdbfr);
        }
        if (num_msgs > 0) {
-               for (i = 0; i < num_msgs; ++i) {
+               int have_contenttype = !IsEmptyStr(content_type);
+               int have_delmsgs = (num_dmsgnums == 0) || (dmsgnums == NULL);
+               int have_more_del = 1;
+
+               num_msgs = sort_msglist(msglist, num_msgs);
+               if (num_dmsgnums > 1)
+                       num_dmsgnums = sort_msglist(dmsgnums, num_dmsgnums);
+/*
+               {
+                       StrBuf *dbg = NewStrBuf();
+                       for (i = 0; i < num_dmsgnums; i++)
+                               StrBufAppendPrintf(dbg, ", %ld", dmsgnums[i]);
+                       syslog(LOG_DEBUG, "Deleting before: %s", ChrPtr(dbg));
+                       FreeStrBuf(&dbg);
+               }
+*/
+               i = 0; j = 0;
+               while ((i < num_msgs) && (have_more_del)) {
                        delete_this = 0x00;
 
+
                        /* Set/clear a bit for each criterion */
 
                        /* 0 messages in the list or a null list means that we are
                         * interested in deleting any messages which meet the other criteria.
                         */
-                       if ((num_dmsgnums == 0) || (dmsgnums == NULL)) {
+                       if (have_delmsgs) {
                                delete_this |= 0x01;
                        }
                        else {
-                               for (j=0; j<num_dmsgnums; ++j) {
-                                       if (msglist[i] == dmsgnums[j]) {
-                                               delete_this |= 0x01;
-                                       }
+                               while ((i < num_msgs) && (msglist[i] < dmsgnums[j])) i++;
+                               if (msglist[i] == dmsgnums[j]) {
+                                       delete_this |= 0x01;
                                }
+                               j++;
+                               have_more_del = (j < num_dmsgnums);
                        }
 
-                       if (IsEmptyStr(content_type)) {
-                               delete_this |= 0x02;
-                       } else {
+                       if (have_contenttype) {
                                GetMetaData(&smi, msglist[i]);
                                if (regexec(&re, smi.meta_content_type, 1, &pm, 0) == 0) {
                                        delete_this |= 0x02;
                                }
+                       } else {
+                               delete_this |= 0x02;
                        }
 
                        /* Delete message only if all bits are set */
@@ -4645,8 +4752,17 @@ int CtdlDeleteMessages(char *room_name,          /* which room */
                                dellist[num_deleted++] = msglist[i];
                                msglist[i] = 0L;
                        }
+                       i++;
                }
-
+/*
+               {
+                       StrBuf *dbg = NewStrBuf();
+                       for (i = 0; i < num_deleted; i++)
+                               StrBufAppendPrintf(dbg, ", %ld", dellist[i]);
+                       syslog(LOG_DEBUG, "Deleting: %s", ChrPtr(dbg));
+                       FreeStrBuf(&dbg);
+               }
+*/
                num_msgs = sort_msglist(msglist, num_msgs);
                cdb_store(CDB_MSGLISTS, &qrbuf.QRnumber, (int)sizeof(long),
                          msglist, (int)(num_msgs * sizeof(long)));
@@ -4666,11 +4782,12 @@ int CtdlDeleteMessages(char *room_name,         /* which room */
         * and we don't want that happening during an S_ROOMS critical
         * section.
         */
-       if (num_deleted) for (i=0; i<num_deleted; ++i) {
+       if (num_deleted) {
+               for (i=0; i<num_deleted; ++i) {
                        PerformDeleteHooks(qrbuf.QRname, dellist[i]);
-                       AdjRefCount(dellist[i], -1);
                }
-
+               AdjRefCountList(dellist, num_deleted, -1);
+       }
        /* Now free the memory we used, and go away. */
        if (msglist != NULL) free(msglist);
        if (dellist != NULL) free(dellist);
@@ -4908,13 +5025,13 @@ void AdjRefCount(long msgnum, int incr)
        struct arcq new_arcq;
        int rv = 0;
 
-       syslog(LOG_DEBUG, "AdjRefCount() msg %ld ref count delta %+d\n",
-              msgnum, incr
-               );
+       syslog(LOG_DEBUG, "AdjRefCount() msg %ld ref count delta %+d\n", msgnum, incr);
 
        begin_critical_section(S_SUPPMSGMAIN);
        if (arcfp == NULL) {
                arcfp = fopen(file_arcq, "ab+");
+               chown(file_arcq, CTDLUID, (-1));
+               chmod(file_arcq, 0600);
        }
        end_critical_section(S_SUPPMSGMAIN);
 
@@ -4951,6 +5068,57 @@ void AdjRefCount(long msgnum, int incr)
        return;
 }
 
+void AdjRefCountList(long *msgnum, long nmsg, int incr)
+{
+       long i, the_size, offset;
+       struct arcq *new_arcq;
+       int rv = 0;
+
+       syslog(LOG_DEBUG, "AdjRefCountList() msg %ld ref count delta %+d\n", nmsg, incr);
+
+       begin_critical_section(S_SUPPMSGMAIN);
+       if (arcfp == NULL) {
+               arcfp = fopen(file_arcq, "ab+");
+               chown(file_arcq, CTDLUID, (-1));
+               chmod(file_arcq, 0600);
+       }
+       end_critical_section(S_SUPPMSGMAIN);
+
+       /*
+        * If we can't open the queue, perform the operation synchronously.
+        */
+       if (arcfp == NULL) {
+               for (i = 0; i < nmsg; i++)
+                       TDAP_AdjRefCount(msgnum[i], incr);
+               return;
+       }
+
+       the_size = sizeof(struct arcq) * nmsg;
+       new_arcq = malloc(the_size);
+       for (i = 0; i < nmsg; i++) {
+               new_arcq[i].arcq_msgnum = msgnum[i];
+               new_arcq[i].arcq_delta = incr;
+       }
+       rv = 0;
+       offset = 0;
+       while ((rv >= 0) && (offset < the_size))
+       {
+               rv = fwrite(new_arcq + offset, 1, the_size - offset, arcfp);
+               if (rv == -1) {
+                       syslog(LOG_EMERG, "Couldn't write Refcount Queue File %s: %s\n",
+                              file_arcq,
+                              strerror(errno));
+               }
+               else {
+                       offset += rv;
+               }
+       }
+       free(new_arcq);
+       fflush(arcfp);
+
+       return;
+}
+
 
 /*
  * TDAP_ProcessAdjRefCountQueue()