Added the Cc's to ReplyAll
[citadel.git] / webcit / messages.c
index c190e0ff8df42183da279ca0c88472f6d811e065..63ab14c989aab0c4ef01f221dd4cb56753de38f1 100644 (file)
@@ -1,8 +1,21 @@
 /*
- * $Id$
- *
  * Functions which deal with the fetching and displaying of messages.
  *
+ * Copyright (c) 1996-2011 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.
+ *
+ * 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 "webcit.h"
@@ -33,7 +46,6 @@ int load_message(message_summary *Msg,
                 StrBuf *FoundCharset,
                 StrBuf **Error)
 {
-       wcsession *WCC = WC;
        StrBuf *Buf;
        StrBuf *HdrToken;
        headereval *Hdr;
@@ -43,7 +55,6 @@ int load_message(message_summary *Msg,
        int state=0;
        
        Buf = NewStrBuf();
-       lprintf(9, "MSG4 %ld|%s\n", Msg->msgnum, ChrPtr(Msg->PartNum));
        if (Msg->PartNum != NULL) {
                serv_printf("MSG4 %ld|%s", Msg->msgnum, ChrPtr(Msg->PartNum));
        }
@@ -56,7 +67,7 @@ int load_message(message_summary *Msg,
                *Error = NewStrBuf();
                StrBufAppendPrintf(*Error, "<strong>");
                StrBufAppendPrintf(*Error, _("ERROR:"));
-               StrBufAppendPrintf(*Error, "</strong> %s<br />\n", &buf[4]);
+               StrBufAppendPrintf(*Error, "</strong> %s<br>\n", &buf[4]);
                FreeStrBuf(&Buf);
                return 0;
        }
@@ -74,7 +85,7 @@ int load_message(message_summary *Msg,
                                Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/html"));
                                StrBufAppendPrintf(Msg->MsgBody->Data, "<div><i>");
                                StrBufAppendPrintf(Msg->MsgBody->Data, _("Empty message"));
-                               StrBufAppendPrintf(Msg->MsgBody->Data, "</i><br /><br />\n");
+                               StrBufAppendPrintf(Msg->MsgBody->Data, "</i><br><br>\n");
                                StrBufAppendPrintf(Msg->MsgBody->Data, "</div>\n");
                        }
                        break;
@@ -88,9 +99,6 @@ int load_message(message_summary *Msg,
                        StrBufExtract_token(HdrToken, Buf, 0, '=');
                        StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
                        
-#ifdef TECH_PREVIEW
-                       if (dbg_analyze_msg) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf));
-#endif
                        /* look up one of the examine_* functions to parse the content */
                        if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
                            (vHdr != NULL)) {
@@ -119,9 +127,6 @@ int load_message(message_summary *Msg,
                                StrBufExtract_token(HdrToken, Buf, 0, ':');
                                if (StrLength(HdrToken) > 0) {
                                        StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
-#ifdef TECH_PREVIEW
-                                       if (dbg_analyze_msg) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf));
-#endif
                                        /* the examine*'s know how to do with mime headers too... */
                                        if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
                                            (vHdr != NULL)) {
@@ -155,38 +160,6 @@ int load_message(message_summary *Msg,
        /* now we put the body mimepart we read above into the mimelist */
        Put(Msg->AllAttach, SKEY(Msg->MsgBody->PartNum), Msg->MsgBody, DestroyMime);
        
-       /* Generate a reply-to address */
-       if (StrLength(Msg->Rfca) > 0) {
-               if (Msg->reply_to == NULL)
-                       Msg->reply_to = NewStrBuf();
-               if (StrLength(Msg->from) > 0) {
-                       StrBufPrintf(Msg->reply_to, "%s <%s>", ChrPtr(Msg->from), ChrPtr(Msg->Rfca));
-               }
-               else {
-                       FlushStrBuf(Msg->reply_to);
-                       StrBufAppendBuf(Msg->reply_to, Msg->Rfca, 0);
-               }
-       }
-       else 
-       {
-               if ((StrLength(Msg->OtherNode)>0) && 
-                   (strcasecmp(ChrPtr(Msg->OtherNode), ChrPtr(WCC->serv_info->serv_nodename))) &&
-                   (strcasecmp(ChrPtr(Msg->OtherNode), ChrPtr(WCC->serv_info->serv_humannode)) ))
-               {
-                       if (Msg->reply_to == NULL)
-                               Msg->reply_to = NewStrBuf();
-                       StrBufPrintf(Msg->reply_to, 
-                                    "%s @ %s",
-                                    ChrPtr(Msg->from), 
-                                    ChrPtr(Msg->OtherNode));
-               }
-               else {
-                       if (Msg->reply_to == NULL)
-                               Msg->reply_to = NewStrBuf();
-                       FlushStrBuf(Msg->reply_to);
-                       StrBufAppendBuf(Msg->reply_to, Msg->from, 0);
-               }
-       }
        FreeStrBuf(&Buf);
        FreeStrBuf(&HdrToken);
        return 1;
@@ -479,22 +452,6 @@ void print_message(void) {
        wDumpContent(0);
 }
 
-/* 
- * Mobile browser view of message
- */
-void mobile_message_view(void) 
-{
-       long msgnum = 0L;
-       const StrBuf *Mime;
-  
-       msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
-       output_headers(1, 0, 0, 0, 0, 1);
-       begin_burst();
-       do_template("msgcontrols", NULL);
-       read_message(WC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
-       wDumpContent(0);
-}
-
 /*
  * Display a message's headers
  */
@@ -547,8 +504,10 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu
        Msg = (message_summary*)malloc(sizeof(message_summary));
        memset(Msg, 0, sizeof(message_summary));
        while (len = StrBuf_ServGetln(Buf),
+              (len >= 0) && 
               ((len != 3)  ||
-               strcmp(ChrPtr(Buf), "000")== 0)){
+               strcmp(ChrPtr(Buf), "000")))
+       {
                buf = ChrPtr(Buf);
                ebuf = strchr(ChrPtr(Buf), '=');
                nBuf = ebuf - buf;
@@ -557,7 +516,7 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu
                        StrBufCutLeft(Buf, nBuf + 1);
                        Eval->f(Msg, Buf);
                }
-               else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
+               else syslog(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
        }
        return Msg;
 }
@@ -599,7 +558,10 @@ int load_msg_ptrs(const char *servcmd,
                return (Stat->nummsgs);
        }
        Buf2 = NewStrBuf();
-       while (len = StrBuf_ServGetln(Buf), ((len != 3) || strcmp(ChrPtr(Buf), "000")!= 0))
+       while (len = StrBuf_ServGetln(Buf), 
+              ((len >= 0) &&
+               ((len != 3) || 
+                strcmp(ChrPtr(Buf), "000")!= 0)))
        {
                if (Stat->nummsgs < Stat->maxload) {
                        skipit = 0;
@@ -651,17 +613,40 @@ int load_msg_ptrs(const char *servcmd,
 
 
 
-
-
-void load_seen_flags(void)
+/**
+ * \brief sets FlagToSet for each of ScanMe that appears in MatchMSet
+ * \param ScanMe List of BasicMsgStruct to be searched it MatchSet
+ * \param MatchMSet MSet we want to flag
+ * \param FlagToSet Flag to set on each BasicMsgStruct->Flags if in MSet
+ */
+void SetFlagsFromMSet(HashList *ScanMe, MSet *MatchMSet, int FlagToSet, int Reverse)
 {
-       message_summary *Msg;
        const char *HashKey;
        long HKLen;
        HashPos *at;
        void *vMsg;
+       message_summary *Msg;
+
+       at = GetNewHashPos(ScanMe, 0);
+       while (GetNextHashPos(ScanMe, at, &HKLen, &HashKey, &vMsg)) {
+               /* Are you a new message, or an old message? */
+               Msg = (message_summary*) vMsg;
+               if (Reverse && IsInMSetList(MatchMSet, Msg->msgnum)) {
+                       Msg->Flags = Msg->Flags | FlagToSet;
+               }
+               else if (!Reverse && !IsInMSetList(MatchMSet, Msg->msgnum)) {
+                       Msg->Flags = Msg->Flags | FlagToSet;
+               }
+       }
+       DeleteHashPos(&at);
+}
+
+
+void load_seen_flags(void)
+{
        StrBuf *OldMsg;
        wcsession *WCC = WC;
+       MSet *MatchMSet;
 
        OldMsg = NewStrBuf();
        serv_puts("GTSN");
@@ -673,19 +658,13 @@ void load_seen_flags(void)
                FreeStrBuf(&OldMsg);
                return;
        }
-       at = GetNewHashPos(WCC->summ, 0);
-       while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
-               /* Are you a new message, or an old message? */
-               Msg = (message_summary*) vMsg;
-               if (is_msg_in_mset(ChrPtr(OldMsg), Msg->msgnum)) {
-                       Msg->is_new = 0;
-               }
-               else {
-                       Msg->is_new = 1;
-               }
+
+       if (ParseMSet(&MatchMSet, OldMsg))
+       {
+               SetFlagsFromMSet(WCC->summ, MatchMSet, MSGFLAG_READ, 0);
        }
+       DeleteMSet(&MatchMSet);
        FreeStrBuf(&OldMsg);
-       DeleteHashPos(&at);
 }
 
 extern readloop_struct rlid[];
@@ -736,6 +715,11 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
                return;
        }
 
+       if (WCC->CurRoom.view == VIEW_WIKI) {
+               http_redirect("wiki?page=home");
+               return;
+       }
+
        memset(&Stat, 0, sizeof(SharedMessageStatus));
        Stat.maxload = 10000;
        Stat.lowest_found = (-1);
@@ -811,9 +795,15 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
        WCC->num_displayed = 0;
 
        /* Put some helpful data in vars for mailsummary_json */
-       svputlong("READLOOP:TOTALMSGS", Stat.nummsgs);
-       svputlong("READLOOP:STARTMSG", Stat.startmsg);
-       svputlong("WCVIEW", WCC->CurRoom.view);
+       {
+               StrBuf *Foo;
+               
+               Foo = NewStrBuf ();
+               StrBufPrintf(Foo, "%ld", Stat.nummsgs);
+               PutBstr(HKEY("__READLOOP:TOTALMSGS"), NewStrBufDup(Foo));
+               StrBufPrintf(Foo, "%ld", Stat.startmsg);
+               PutBstr(HKEY("__READLOOP:STARTMSG"), Foo);
+       }
 
        /*
         * iterate over each message. if we need to load an attachment, do it here. 
@@ -827,7 +817,11 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
                while ( GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
                        Msg = (message_summary*) vMsg;          
                        if ((Msg->msgnum >= Stat.startmsg) && (Stat.num_displayed <= Stat.maxmsgs)) {
-                               ViewMsg->LoadMsgFromServer(&Stat, &ViewSpecific, Msg, Msg->is_new, i);
+                               ViewMsg->LoadMsgFromServer(&Stat, 
+                                                          &ViewSpecific, 
+                                                          Msg, 
+                                                          (Msg->Flags & MSGFLAG_READ) != 0, 
+                                                          i);
                        } 
                        i++;
                }
@@ -914,7 +908,7 @@ void post_mime_to_server(void) {
                text_to_server_qp(txtmail);     /* Transmit message in quoted-printable encoding */
                free(txtmail);
 
-               serv_printf("--%s", alt_boundary);
+               serv_printf("\n--%s", alt_boundary);
        }
 
        serv_puts("Content-type: text/html; charset=utf-8");
@@ -982,7 +976,6 @@ void post_message(void)
        char buf[1024];
        StrBuf *encoded_subject = NULL;
        static long dont_post = (-1L);
-       wc_mime_attachment  *att;
        int is_anonymous = 0;
        const StrBuf *display_name = NULL;
        wcsession *WCC = WC;
@@ -1000,96 +993,13 @@ void post_message(void)
                }
        }
 
-       if (WCC->upload_length > 0) {
-               const char *pch;
-               int n;
-               const char *newn;
-               long newnlen;
-               void *v;
-
-               /* There's an attachment.  Save it to this struct... */
-               lprintf(9, "Client is uploading %d bytes\n", WCC->upload_length);
-               att = malloc(sizeof(wc_mime_attachment));
-               memset(att, 0, sizeof(wc_mime_attachment ));
-               att->length = WCC->upload_length;
-               att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1);
-               att->FileName = NewStrBufDup(WCC->upload_filename);
-               
-               if (WCC->attachments == NULL) {
-                       WCC->attachments = NewHash(1, Flathash);
-               }
-
-               /* And add it to the list. */
-               n = 0;
-               if ((GetCount(WCC->attachments) > 0) && 
-                   GetHashAt(WCC->attachments, 
-                             GetCount(WCC->attachments) -1, 
-                             &newnlen, &newn, &v))
-                   n = *((int*) newn) + 1;
-               Put(WCC->attachments, IKEY(n), att, DestroyMime);
-
-               /*
-                * Mozilla sends a simple filename, which is what we want,
-                * but Satan's Browser sends an entire pathname.  Reduce
-                * the path to just a filename if we need to.
-                */
-               pch = strrchr(ChrPtr(att->FileName), '/');
-               if (pch != NULL) {
-                       StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
-               }
-               pch = strrchr(ChrPtr(att->FileName), '\\');
-               if (pch != NULL) {
-                       StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
-               }
-
-               /*
-                * Transfer control of this memory from the upload struct
-                * to the attachment struct.
-                */
-               att->Data = WCC->upload;
-               WCC->upload = NULL;
-               WCC->upload_length = 0;
-               display_enter();
-               return;
-       }
-
-       if (havebstr("cancel_button")) {
+       if (!strcasecmp(bstr("submit_action"), "cancel")) {
                sprintf(WCC->ImportantMessage, 
                        _("Cancelled.  Message was not posted."));
-       } else if (havebstr("attach_button")) {
-               display_enter();
-               return;
        } else if (lbstr("postseq") == dont_post) {
                sprintf(WCC->ImportantMessage, 
                        _("Automatically cancelled because you have already "
                          "saved this message."));
-       } else if (havebstr("remove_attach_button")) {
-               /* now thats st00pit. need to find it by name. */
-               void *vAtt;
-               StrBuf *WhichAttachment;
-               HashPos *at;
-               long len;
-               const char *key;
-
-               WhichAttachment = NewStrBufDup(sbstr("which_attachment"));
-               StrBufUnescape(WhichAttachment, 0);
-               at = GetNewHashPos(WCC->attachments, 0);
-               do {
-                       GetHashPos(WCC->attachments, at, &len, &key, &vAtt);
-               
-                       att = (wc_mime_attachment*) vAtt;
-                       if ((att != NULL) && 
-                           (strcmp(ChrPtr(WhichAttachment), 
-                                   ChrPtr(att->FileName)   ) == 0))
-                       {
-                               DeleteEntryFromHash(WCC->attachments, at);
-                               break;
-                       }
-               }
-               while (NextHashPos(WCC->attachments, at));
-               FreeStrBuf(&WhichAttachment);
-               display_enter();
-               return;
        } else {
                const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
                StrBuf *Recp = NULL; 
@@ -1102,7 +1012,7 @@ void post_message(void)
                int save_to_drafts;
                long HeaderLen;
 
-               save_to_drafts = havebstr("save_button");
+               save_to_drafts = !strcasecmp(bstr("submit_action"), "drafts");
                Buf = NewStrBuf();
 
                if (save_to_drafts) {
@@ -1112,7 +1022,7 @@ void post_message(void)
                        if (GetServerStatus(Buf, NULL) != 2) {
                                /* You probably don't even have a dumb Drafts folder */
                                StrBufCutLeft(Buf, 4);
-                               lprintf(9, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
+                               syslog(9, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
                                StrBufAppendBufPlain(WCC->ImportantMsg, _("Saved to Drafts failed: "), -1, 0);
                                StrBufAppendBuf(WCC->ImportantMsg, Buf, 0);
                                display_enter();
@@ -1185,7 +1095,7 @@ void post_message(void)
                }
                else 
                {
-                       lprintf(9, "%s\n", ChrPtr(CmdBuf));
+                       syslog(9, "%s\n", ChrPtr(CmdBuf));
                        serv_puts(ChrPtr(CmdBuf));
                        FreeStrBuf(&CmdBuf);
 
@@ -1223,7 +1133,7 @@ void post_message(void)
                        } else {
                                StrBufCutLeft(Buf, 4);
 
-                               lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
+                               syslog(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
                                StrBufAppendBuf(WCC->ImportantMsg, Buf, 0);
                                if (save_to_drafts) gotoroom(WCC->CurRoom.name);
                                display_enter();
@@ -1233,6 +1143,7 @@ void post_message(void)
                                return;
                        }
                }
+               FreeStrBuf(&Recp);
                FreeStrBuf(&Buf);
                FreeStrBuf(&Cc);
                FreeStrBuf(&Bcc);
@@ -1263,6 +1174,106 @@ void post_message(void)
 }
 
 
+/*
+ * Client is uploading an attachment
+ */
+void upload_attachment(void) {
+       wcsession *WCC = WC;
+       const char *pch;
+       int n;
+       const char *newn;
+       long newnlen;
+       void *v;
+       wc_mime_attachment *att;
+
+       syslog(9, "upload_attachment()\n");
+       wc_printf("upload_attachment()<br>\n");
+
+       if (WCC->upload_length <= 0) {
+               syslog(9, "ERROR no attachment was uploaded\n");
+               wc_printf("ERROR no attachment was uploaded<br>\n");
+               return;
+       }
+
+       syslog(9, "Client is uploading %d bytes\n", WCC->upload_length);
+       wc_printf("Client is uploading %d bytes<br>\n", WCC->upload_length);
+       att = malloc(sizeof(wc_mime_attachment));
+       memset(att, 0, sizeof(wc_mime_attachment ));
+       att->length = WCC->upload_length;
+       att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1);
+       att->FileName = NewStrBufDup(WCC->upload_filename);
+       
+       if (WCC->attachments == NULL) {
+               WCC->attachments = NewHash(1, Flathash);
+       }
+
+       /* And add it to the list. */
+       n = 0;
+       if ((GetCount(WCC->attachments) > 0) && 
+           GetHashAt(WCC->attachments, 
+                     GetCount(WCC->attachments) -1, 
+                     &newnlen, &newn, &v))
+           n = *((int*) newn) + 1;
+       Put(WCC->attachments, IKEY(n), att, DestroyMime);
+
+       /*
+        * Mozilla sends a simple filename, which is what we want,
+        * but Satan's Browser sends an entire pathname.  Reduce
+        * the path to just a filename if we need to.
+        */
+       pch = strrchr(ChrPtr(att->FileName), '/');
+       if (pch != NULL) {
+               StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
+       }
+       pch = strrchr(ChrPtr(att->FileName), '\\');
+       if (pch != NULL) {
+               StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
+       }
+
+       /*
+        * Transfer control of this memory from the upload struct
+        * to the attachment struct.
+        */
+       att->Data = WCC->upload;
+       WCC->upload = NULL;
+       WCC->upload_length = 0;
+}
+
+
+/*
+ * Remove an attachment from the message currently being composed.
+ *
+ * Currently we identify the attachment to be removed by its filename.
+ * There is probably a better way to do this.
+ */
+void remove_attachment(void) {
+       wcsession *WCC = WC;
+       wc_mime_attachment *att;
+       void *vAtt;
+       StrBuf *WhichAttachment;
+       HashPos *at;
+       long len;
+       const char *key;
+
+       WhichAttachment = NewStrBufDup(sbstr("which_attachment"));
+       StrBufUnescape(WhichAttachment, 0);
+       at = GetNewHashPos(WCC->attachments, 0);
+       do {
+               GetHashPos(WCC->attachments, at, &len, &key, &vAtt);
+       
+               att = (wc_mime_attachment*) vAtt;
+               if ((att != NULL) && 
+                   (strcmp(ChrPtr(WhichAttachment), 
+                           ChrPtr(att->FileName)   ) == 0))
+               {
+                       DeleteEntryFromHash(WCC->attachments, at);
+                       break;
+               }
+       }
+       while (NextHashPos(WCC->attachments, at));
+       FreeStrBuf(&WhichAttachment);
+       wc_printf("remove_attachment() completed\n");
+}
 
 
 /*
@@ -1278,6 +1289,7 @@ void display_enter(void)
        int recipient_bad = 0;
        int is_anonymous = 0;
        wcsession *WCC = WC;
+       int i = 0;
 
        now = time(NULL);
 
@@ -1336,6 +1348,137 @@ void display_enter(void)
                return;
        }
 
+
+       /*
+        * If the "replying_to" variable is set, it refers to a message
+        * number from which we must extract some header fields...
+        */
+       long replying_to = lbstr("replying_to");
+       if (replying_to > 0) {
+               char wefw[1024] = "";
+               char msgn[256] = "";
+               char from[256] = "";
+               char node[256] = "";
+               char rfca[256] = "";
+               char rcpt[SIZ] = "";
+               char cccc[SIZ] = "";
+               serv_printf("MSG0 %ld|1", replying_to); 
+               serv_getln(buf, sizeof buf);
+               if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
+
+                       if ( (!strncasecmp(buf, "subj=", 5)) && (strlen(buf) > 5) ) {
+                               StrBuf *subj = NewStrBuf();
+                               if (!strcasecmp(bstr("replying_mode"), "forward")) {
+                                       if (strncasecmp(&buf[5], "Fw:", 3)) {
+                                               StrBufAppendBufPlain(subj, HKEY("Fw: "), 0);
+                                       }
+                               }
+                               else {
+                                       if (strncasecmp(&buf[5], "Re:", 3)) {
+                                               StrBufAppendBufPlain(subj, HKEY("Re: "), 0);
+                                       }
+                               }
+                               StrBufAppendBufPlain(subj, &buf[5], -1, 0);
+                               PutBstr(HKEY("subject"), subj);
+                       }
+
+                       else if (!strncasecmp(buf, "wefw=", 5)) {
+                               safestrncpy(wefw, &buf[5], sizeof wefw);
+
+                               /* Trim down excessively long lists of thread references.  We eliminate the
+                                * second one in the list so that the thread root remains intact.
+                                */
+                               int rrtok = num_tokens(wefw, '|');
+                               int rrlen = strlen(wefw);
+                               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
+                                       remove_token(wefw, 1, '|');
+                               }
+                       }
+
+                       else if (!strncasecmp(buf, "msgn=", 5)) {
+                               safestrncpy(msgn, &buf[5], sizeof msgn);
+                       }
+
+                       else if (!strncasecmp(buf, "from=", 5)) {
+                               safestrncpy(from, &buf[5], sizeof from);
+                               for (i=0; i<strlen(from); ++i) {
+                                       if (from[i] == ',') from[i] = ' ';
+                               }
+                       }
+
+                       else if (!strncasecmp(buf, "rcpt=", 5)) {
+                               safestrncpy(rcpt, &buf[5], sizeof rcpt);
+                       }
+
+                       else if (!strncasecmp(buf, "cccc=", 5)) {
+                               safestrncpy(cccc, &buf[5], sizeof cccc);
+                       }
+
+                       else if (!strncasecmp(buf, "node=", 5)) {
+                               safestrncpy(node, &buf[5], sizeof node);
+                       }
+
+                       else if (!strncasecmp(buf, "rfca=", 5)) {
+                               safestrncpy(rfca, &buf[5], sizeof rfca);
+                       }
+
+               }
+
+               if (strlen(wefw) + strlen(msgn) > 0) {
+                       StrBuf *refs = NewStrBuf();
+                       if (!IsEmptyStr(wefw)) {
+                               StrBufAppendBufPlain(refs, wefw, -1, 0);
+                       }
+                       if ( (!IsEmptyStr(wefw)) && (!IsEmptyStr(msgn)) ) {
+                               StrBufAppendBufPlain(refs, HKEY("|"), 0);
+                       }
+                       if (!IsEmptyStr(msgn)) {
+                               StrBufAppendBufPlain(refs, msgn, -1, 0);
+                       }
+                       PutBstr(HKEY("references"), refs);
+               }
+
+               /*
+                * If this is a Reply or a ReplyAll, copy the sender's email into the To: field
+                */
+               if (    (!strcasecmp(bstr("replying_mode"), "reply"))
+                       || (!strcasecmp(bstr("replying_mode"), "replyall"))
+               ) {
+                       StrBuf *to_rcpt = NewStrBuf();
+                       if (!IsEmptyStr(rfca)) {
+                               StrBufAppendPrintf(to_rcpt, "%s <%s>", from, rfca);
+                       }
+                       else {
+                               StrBufAppendPrintf(to_rcpt, "%s", from);
+                               if (    (!IsEmptyStr(node))
+                                       && (strcasecmp(node, ChrPtr(WC->serv_info->serv_nodename)))
+                               ) {
+                                       StrBufAppendPrintf(to_rcpt, " @ %s", node);
+                               }
+                       }
+                       PutBstr(HKEY("recp"), to_rcpt);
+               }
+
+               /*
+                * Only if this is a ReplyAll, copy all recipients into the Cc: field
+                */
+               if (    (!strcasecmp(bstr("replying_mode"), "replyall"))
+               ) {
+                       StrBuf *cc_rcpt = NewStrBuf();
+                       if (!IsEmptyStr(rcpt)) {
+                               StrBufAppendPrintf(cc_rcpt, "%s", rcpt);
+                       }
+                       if ( (!IsEmptyStr(rcpt)) && (!IsEmptyStr(cccc)) ) {
+                               StrBufAppendPrintf(cc_rcpt, ", ");
+                       }
+                       if (!IsEmptyStr(cccc)) {
+                               StrBufAppendPrintf(cc_rcpt, "%s", cccc);
+                       }
+                       PutBstr(HKEY("cc"), cc_rcpt);
+               }
+
+       }
+
        /*
         * Otherwise proceed normally.
         * Do a custom room banner with no navbar...
@@ -1382,12 +1525,14 @@ void display_enter(void)
                        }
                }
                else if (buf[0] != '2') {       /* Any other error means that we cannot continue */
-                       wc_printf("<em>%s</em><br />\n", &buf[4]);      /* TODO -> important message */
+                       wc_printf("<em>%s</em><br>\n", &buf[4]);        /* TODO -> important message */
                        return;
                }
        }
-       svputlong("RCPTREQUIRED", recipient_required);
-       svputlong("SUBJREQUIRED", recipient_required || subject_required);
+       if (recipient_required)
+               PutBstr(HKEY("__RCPTREQUIRED"), NewStrBufPlain(HKEY("1")));
+       if (recipient_required || subject_required)
+               PutBstr(HKEY("__SUBJREQUIRED"), NewStrBufPlain(HKEY("1")));
 
        begin_burst();
        output_headers(1, 0, 0, 0, 1, 0);
@@ -1467,7 +1612,7 @@ void confirm_move_msg(void)
        wc_printf("<CENTER>");
 
        wc_printf(_("Move this message to:"));
-       wc_printf("<br />\n");
+       wc_printf("<br>\n");
 
        wc_printf("<form METHOD=\"POST\" action=\"move_msg\">\n");
        wc_printf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
@@ -1485,7 +1630,7 @@ void confirm_move_msg(void)
                }
        }
        wc_printf("</SELECT>\n");
-       wc_printf("<br />\n");
+       wc_printf("<br>\n");
 
        wc_printf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
        wc_printf("&nbsp;");
@@ -1655,8 +1800,6 @@ void MimeLoadData(wc_mime_attachment *Mime)
 }
 
 
-
-
 void view_mimepart(void) {
        mimepart(0);
 }
@@ -1691,6 +1834,13 @@ void download_postpart(void) {
        FreeStrBuf(&partnum);
 }
 
+
+
+void show_num_attachments(void) {
+       wc_printf("%d", GetCount(WC->attachments));
+}
+
+
 void h_readnew(void) { readloop(readnew, eUseDefault);}
 void h_readold(void) { readloop(readold, eUseDefault);}
 void h_readfwd(void) { readloop(readfwd, eUseDefault);}
@@ -1770,12 +1920,12 @@ InitModule_MSG
                           NULL);
        RegisterPreference("mailbox",_("Mailbox view mode"), PRF_STRING, NULL);
 
-       WebcitAddUrlHandler(HKEY("readnew"), "", 0, h_readnew, NEED_URL);
-       WebcitAddUrlHandler(HKEY("readold"), "", 0, h_readold, NEED_URL);
-       WebcitAddUrlHandler(HKEY("readfwd"), "", 0, h_readfwd, NEED_URL);
+       WebcitAddUrlHandler(HKEY("readnew"), "", 0, h_readnew, ANONYMOUS|NEED_URL);
+       WebcitAddUrlHandler(HKEY("readold"), "", 0, h_readold, ANONYMOUS|NEED_URL);
+       WebcitAddUrlHandler(HKEY("readfwd"), "", 0, h_readfwd, ANONYMOUS|NEED_URL);
        WebcitAddUrlHandler(HKEY("headers"), "", 0, h_headers, NEED_URL);
-       WebcitAddUrlHandler(HKEY("readgt"), "", 0, h_readgt, NEED_URL);
-       WebcitAddUrlHandler(HKEY("readlt"), "", 0, h_readlt, NEED_URL);
+       WebcitAddUrlHandler(HKEY("readgt"), "", 0, h_readgt, ANONYMOUS|NEED_URL);
+       WebcitAddUrlHandler(HKEY("readlt"), "", 0, h_readlt, ANONYMOUS|NEED_URL);
        WebcitAddUrlHandler(HKEY("do_search"), "", 0, h_do_search, 0);
        WebcitAddUrlHandler(HKEY("display_enter"), "", 0, display_enter, 0);
        WebcitAddUrlHandler(HKEY("post"), "", 0, post_message, PROHIBIT_STARTPAGE);
@@ -1785,13 +1935,15 @@ InitModule_MSG
        WebcitAddUrlHandler(HKEY("msg"), "", 0, embed_message, NEED_URL);
        WebcitAddUrlHandler(HKEY("message"), "", 0, handle_one_message, NEED_URL|XHTTP_COMMANDS|COOKIEUNNEEDED|FORCE_SESSIONCLOSE);
        WebcitAddUrlHandler(HKEY("printmsg"), "", 0, print_message, NEED_URL);
-       WebcitAddUrlHandler(HKEY("mobilemsg"), "", 0, mobile_message_view, NEED_URL);
        WebcitAddUrlHandler(HKEY("msgheaders"), "", 0, display_headers, NEED_URL);
 
        WebcitAddUrlHandler(HKEY("mimepart"), "", 0, view_mimepart, NEED_URL);
        WebcitAddUrlHandler(HKEY("mimepart_download"), "", 0, download_mimepart, NEED_URL);
        WebcitAddUrlHandler(HKEY("postpart"), "", 0, view_postpart, NEED_URL|PROHIBIT_STARTPAGE);
        WebcitAddUrlHandler(HKEY("postpart_download"), "", 0, download_postpart, NEED_URL|PROHIBIT_STARTPAGE);
+       WebcitAddUrlHandler(HKEY("upload_attachment"), "", 0, upload_attachment, AJAX);
+       WebcitAddUrlHandler(HKEY("remove_attachment"), "", 0, remove_attachment, AJAX);
+       WebcitAddUrlHandler(HKEY("show_num_attachments"), "", 0, show_num_attachments, AJAX);
 
        /* json */
        WebcitAddUrlHandler(HKEY("roommsgs"), "", 0, jsonMessageList,0);