Finalize vcard editing:
[citadel.git] / webcit / messages.c
index 94968338888761f68e8033e224acd73fa5e1b687..70cd85ab80c794c3e8fb68bde5073ff20a8371a1 100644 (file)
@@ -1,29 +1,23 @@
 /*
  * Functions which deal with the fetching and displaying of messages.
  *
- * Copyright (c) 1996-2011 by the citadel.org team
+ * Copyright (c) 1996-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.
+ * modify 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 "webcit.h"
 #include "webserver.h"
-#include "groupdav.h"
+#include "dav.h"
+#include "calendar.h"
 
 HashList *MsgHeaderHandler = NULL;
-HashList *MsgEvaluators = NULL;
 HashList *MimeRenderHandler = NULL;
 HashList *ReadLoopHandler = NULL;
 int dbg_analyze_msg = 0;
@@ -36,17 +30,19 @@ void jsonMessageListHdr(void);
 
 void display_enter(void);
 
-typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf);
-
-typedef struct _MsgPartEvaluatorStruct {
-       MsgPartEvaluatorFunc f;
-} MsgPartEvaluatorStruct;
-
+void fixview()
+{
+       /* workaround for json listview; its not useable directly */
+       if (WC->CurRoom.view == VIEW_JSON_LIST) {
+               StrBuf *View = NewStrBuf();
+               StrBufPrintf(View, "%d", VIEW_MAILBOX);
+               putbstr("view", View);;
+       }
+}
 int load_message(message_summary *Msg, 
                 StrBuf *FoundCharset,
                 StrBuf **Error)
 {
-       wcsession *WCC = WC;
        StrBuf *Buf;
        StrBuf *HdrToken;
        headereval *Hdr;
@@ -161,38 +157,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;
@@ -217,9 +181,13 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
        void *vHdr;
        long len;
        const char *Key;
+       WCTemplputParams SuperTP;
        WCTemplputParams SubTP;
        StrBuf *Error = NULL;
 
+       memset(&SuperTP, 0, sizeof(WCTemplputParams));
+       memset(&SubTP, 0, sizeof(WCTemplputParams));
+
        Buf = NewStrBuf();
        FoundCharset = NewStrBuf();
        Msg = (message_summary *)malloc(sizeof(message_summary));
@@ -240,37 +208,46 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
        StrBufTrim(Buf);
        StrBufLowerCase(Buf);
 
-       /* Locate a renderer capable of converting this MIME part into HTML */
-       if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) &&
-           (vHdr != NULL)) {
-               RenderMimeFuncStruct *Render;
-               Render = (RenderMimeFuncStruct*)vHdr;
-               Render->f(Msg->MsgBody, NULL, FoundCharset);
-       }
-
-       if (StrLength(Msg->reply_references)> 0) {
-               /* 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(ChrPtr(Msg->reply_references), '|');
-               int rrlen = StrLength(Msg->reply_references);
-               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
-                       StrBufRemove_token(Msg->reply_references, 1, '|');
+       StackContext(NULL, &SuperTP, Msg, CTX_MAILSUM, 0, NULL);
+       {
+               /* Locate a renderer capable of converting this MIME part into HTML */
+               if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) &&
+                   (vHdr != NULL)) {
+                       RenderMimeFuncStruct *Render;
+                       
+                       StackContext(&SuperTP, &SubTP, Msg->MsgBody, CTX_MIME_ATACH, 0, NULL);
+                       {
+                               Render = (RenderMimeFuncStruct*)vHdr;
+                               Render->f(Target, &SubTP, FoundCharset);
+                       }
+                       UnStackContext(&SubTP);
+               }
+               
+               if (StrLength(Msg->reply_references)> 0) {
+                       /* 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(ChrPtr(Msg->reply_references), '|');
+                       int rrlen = StrLength(Msg->reply_references);
+                       if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
+                               StrBufRemove_token(Msg->reply_references, 1, '|');
+                       }
                }
-       }
 
-       /* now check if we need to translate some mimeparts, and remove the duplicate */
-       it = GetNewHashPos(Msg->AllAttach, 0);
-       while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
-              (vMime != NULL)) {
-               wc_mime_attachment *Mime = (wc_mime_attachment*) vMime;
-               evaluate_mime_part(Msg, Mime);
+               /* now check if we need to translate some mimeparts, and remove the duplicate */
+               it = GetNewHashPos(Msg->AllAttach, 0);
+               while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
+                      (vMime != NULL)) {
+                       StackContext(&SuperTP, &SubTP, vMime, CTX_MIME_ATACH, 0, NULL);
+                       {
+                               evaluate_mime_part(Target, &SubTP);
+                       }
+                       UnStackContext(&SubTP);
+               }
+               DeleteHashPos(&it);
+               *OutMime = DoTemplate(tmpl, tmpllen, Target, &SuperTP);
        }
-       DeleteHashPos(&it);
-       memset(&SubTP, 0, sizeof(WCTemplputParams));
-       SubTP.Filter.ContextType = CTX_MAILSUM;
-       SubTP.Context = Msg;
-       *OutMime = DoTemplate(tmpl, tmpllen, Target, &SubTP);
+       UnStackContext(&SuperTP);
 
        DestroyMessageSummary(Msg);
        FreeStrBuf(&FoundCharset);
@@ -279,7 +256,7 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
 }
 
 
-void
+long
 HttpStatus(long CitadelStatus)
 {
        long httpstatus = 502;
@@ -344,7 +321,7 @@ HttpStatus(long CitadelStatus)
                break;
        }
 
-
+       return httpstatus;
 }
 
 /*
@@ -353,7 +330,7 @@ HttpStatus(long CitadelStatus)
  */
 void handle_one_message(void) 
 {
-       long CitStatus;
+       long CitStatus = ERROR + NOT_HERE;
        int CopyMessage = 0;
        const StrBuf *Destination;
        void *vLine;
@@ -394,9 +371,7 @@ void handle_one_message(void)
                        serv_printf("MOVE %ld|_TRASH_|0", msgnum);
                }
                StrBuf_ServGetln(CmdBuf);
-               FlushStrBuf(WCC->ImportantMsg);
-               StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
-               GetServerStatus(CmdBuf, &CitStatus);
+               GetServerStatusMsg(CmdBuf, &CitStatus, 1, 0);
                HttpStatus(CitStatus);
                break;
        case eCOPY:
@@ -407,8 +382,7 @@ void handle_one_message(void)
                        Destination = (StrBuf*) vLine;
                        serv_printf("MOVE %ld|%s|%d", msgnum, ChrPtr(Destination), CopyMessage);
                        StrBuf_ServGetln(CmdBuf);
-                       FlushStrBuf(WCC->ImportantMsg);
-                       StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
+                       GetServerStatusMsg(CmdBuf, NULL, 1, 0);
                        GetServerStatus(CmdBuf, &CitStatus);
                        HttpStatus(CitStatus);
                }
@@ -434,6 +408,8 @@ void embed_message(void) {
        StrBuf *CmdBuf = NULL;
 
        msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
+       if (msgnum <= 0) return;
+
        switch (WCC->Hdr->HR.eReqType)
        {
        case eGET:
@@ -454,8 +430,7 @@ void embed_message(void) {
                        serv_printf("MOVE %ld|_TRASH_|0", msgnum);
                }
                StrBuf_ServGetln(CmdBuf);
-               FlushStrBuf(WCC->ImportantMsg);
-               StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
+               GetServerStatusMsg(CmdBuf, NULL, 1, 0);
                break;
        default:
                break;
@@ -513,57 +488,14 @@ void display_headers(void) {
 }
 
 
-message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) 
-{
-       void                 *vEval;
-       MsgPartEvaluatorStruct  *Eval;
-       message_summary      *Msg;
-       StrBuf *Buf;
-       const char *buf;
-       const char *ebuf;
-       int nBuf;
-       long len;
-       
-       Buf = NewStrBuf();
-
-       serv_printf("MSG0 %ld|1", MsgNum);      /* ask for headers only */
-       
-       StrBuf_ServGetln(Buf);
-       if (GetServerStatus(Buf, NULL) == 1) {
-               FreeStrBuf(&Buf);
-               return NULL;
-       }
-
-       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")))
-       {
-               buf = ChrPtr(Buf);
-               ebuf = strchr(ChrPtr(Buf), '=');
-               nBuf = ebuf - buf;
-               if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) {
-                       Eval = (MsgPartEvaluatorStruct*) vEval;
-                       StrBufCutLeft(Buf, nBuf + 1);
-                       Eval->f(Msg, Buf);
-               }
-               else syslog(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
-       }
-       return Msg;
-}
-
-
-
-
 
 /*
  * load message pointers from the server for a "read messages" operation
  *
  * servcmd:            the citadel command to send to the citserver
  */
-int load_msg_ptrs(const char *servcmd, 
+int load_msg_ptrs(const char *servcmd,
+                 const char *filter,
                  SharedMessageStatus *Stat, 
                  load_msg_ptrs_detailheaders LH)
 {
@@ -574,6 +506,7 @@ int load_msg_ptrs(const char *servcmd,
        int n;
        int skipit;
        const char *Ptr = NULL;
+       int StatMajor;
 
        Stat->lowest_found = LONG_MAX;
        Stat->highest_found = LONG_MIN;
@@ -586,7 +519,21 @@ int load_msg_ptrs(const char *servcmd,
        Buf = NewStrBuf();
        serv_puts(servcmd);
        StrBuf_ServGetln(Buf);
-       if (GetServerStatus(Buf, NULL) != 1) {
+       StatMajor = GetServerStatus(Buf, NULL);
+       switch (StatMajor) {
+       case 1:
+               break;
+       case 8:
+               if (filter != NULL) {
+                       serv_puts(filter);
+                        serv_puts("000");
+                       break;
+               }
+               /* fall back to empty filter in case of we were fooled... */
+               serv_puts("");
+               serv_puts("000");
+               break;
+       default:
                FreeStrBuf(&Buf);
                return (Stat->nummsgs);
        }
@@ -652,10 +599,11 @@ int load_msg_ptrs(const char *servcmd,
  * \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)
+long SetFlagsFromMSet(HashList *ScanMe, MSet *MatchMSet, int FlagToSet, int Reverse)
 {
        const char *HashKey;
        long HKLen;
+       long count = 0;
        HashPos *at;
        void *vMsg;
        message_summary *Msg;
@@ -666,17 +614,21 @@ void SetFlagsFromMSet(HashList *ScanMe, MSet *MatchMSet, int FlagToSet, int Reve
                Msg = (message_summary*) vMsg;
                if (Reverse && IsInMSetList(MatchMSet, Msg->msgnum)) {
                        Msg->Flags = Msg->Flags | FlagToSet;
+                       count++;
                }
                else if (!Reverse && !IsInMSetList(MatchMSet, Msg->msgnum)) {
                        Msg->Flags = Msg->Flags | FlagToSet;
+                       count++;
                }
        }
        DeleteHashPos(&at);
+       return count;
 }
 
 
-void load_seen_flags(void)
+long load_seen_flags(void)
 {
+       long count = 0;
        StrBuf *OldMsg;
        wcsession *WCC = WC;
        MSet *MatchMSet;
@@ -689,15 +641,16 @@ void load_seen_flags(void)
        }
        else {
                FreeStrBuf(&OldMsg);
-               return;
+               return 0;
        }
 
        if (ParseMSet(&MatchMSet, OldMsg))
        {
-               SetFlagsFromMSet(WCC->summ, MatchMSet, MSGFLAG_READ, 0);
+               count = SetFlagsFromMSet(WCC->summ, MatchMSet, MSGFLAG_READ, 0);
        }
        DeleteMSet(&MatchMSet);
        FreeStrBuf(&OldMsg);
+       return count;
 }
 
 extern readloop_struct rlid[];
@@ -706,6 +659,8 @@ typedef struct _RoomRenderer{
        int RoomType;
 
        GetParamsGetServerCall_func GetParamsGetServerCall;
+       
+       PrintViewHeader_func PrintPageHeader;
        PrintViewHeader_func PrintViewHeader;
        LoadMsgFromServer_func LoadMsgFromServer;
        RenderView_or_Tail_func RenderView_or_Tail;
@@ -726,6 +681,7 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
        void *vMsg;
        message_summary *Msg;
        char cmd[256] = "";
+       char filter[256] = "";
        int i, r;
        wcsession *WCC = WC;
        HashPos *at;
@@ -733,24 +689,14 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
        long HKLen;
        WCTemplputParams SubTP;
        SharedMessageStatus Stat;
-       void *ViewSpecific;
+       void *ViewSpecific = NULL;
 
        if (havebstr("is_summary") && (1 == (ibstr("is_summary")))) {
                WCC->CurRoom.view = VIEW_MAILBOX;
        }
 
-       if (havebstr("is_ajax") && (1 == (ibstr("is_ajax")))) {
-               WCC->is_ajax = 1;
-       }
-
-       if ((oper == do_search) && (WCC->CurRoom.view == VIEW_WIKI)) {
-               display_wiki_pagelist();
-               return;
-       }
-
-       if (WCC->CurRoom.view == VIEW_WIKI) {
-               http_redirect("wiki?page=home");
-               return;
+       if (havebstr("view")) {
+               WCC->CurRoom.view = ibstr("view");
        }
 
        memset(&Stat, 0, sizeof(SharedMessageStatus));
@@ -766,26 +712,27 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
                GetHash(ReadLoopHandler, IKEY(WCC->CurRoom.view), &vViewMsg);
        }
        if (vViewMsg == NULL) {
-               return;                 // TODO: print message
+               return;                 /* TODO: print message */
        }
 
        ViewMsg = (RoomRenderer*) vViewMsg;
-       if (!WCC->is_ajax) {
+       if (ViewMsg->PrintPageHeader == NULL)
                output_headers(1, 1, 1, 0, 0, 0);
-       } else if (WCC->CurRoom.view == VIEW_MAILBOX) {
-               jsonMessageListHdr();
-       }
+       else 
+               ViewMsg->PrintPageHeader(&Stat, ViewSpecific);
 
        if (ViewMsg->GetParamsGetServerCall != NULL) {
                r = ViewMsg->GetParamsGetServerCall(
                       &Stat,
                       &ViewSpecific,
                       oper,
-                      cmd, sizeof(cmd)
+                      cmd, sizeof(cmd),
+                      filter, sizeof(filter)
                );
        } else {
                r = 0;
        }
+
        switch(r)
        {
        case 400:
@@ -798,16 +745,23 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
        default:
                break;
        }
-       if (!IsEmptyStr(cmd))
-               Stat.nummsgs = load_msg_ptrs(cmd, &Stat, ViewMsg->LHParse);
+       if (!IsEmptyStr(cmd)) {
+               const char *p = NULL;
+               if (!IsEmptyStr(filter))
+                       p = filter;
+               Stat.nummsgs = load_msg_ptrs(cmd, p, &Stat, ViewMsg->LHParse);
+       }
 
        if (Stat.sortit) {
                CompareFunc SortIt;
-               memset(&SubTP, 0, sizeof(WCTemplputParams));
-               SubTP.Filter.ContextType = CTX_MAILSUM;
-               SubTP.Context = NULL;
-               SortIt =  RetrieveSort(&SubTP, NULL, 0,
-                                      HKEY("date"), Stat.defaultsortorder);
+               StackContext(NULL, &SubTP, NULL, CTX_MAILSUM, 0, NULL);
+               {
+                       SortIt =  RetrieveSort(&SubTP,
+                                              NULL, 0,
+                                              HKEY("date"),
+                                              Stat.defaultsortorder);
+               }
+               UnStackContext(&SubTP);
                if (SortIt != NULL)
                        SortByPayload(WCC->summ, SortIt);
        }
@@ -815,7 +769,7 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
                Stat.startmsg =  0;
        }
 
-       if (Stat.load_seen) load_seen_flags();
+       if (Stat.load_seen) Stat.numNewmsgs = load_seen_flags();
        
         /*
         * Print any inforation above the message list...
@@ -833,9 +787,13 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
                
                Foo = NewStrBuf ();
                StrBufPrintf(Foo, "%ld", Stat.nummsgs);
-               PutBstr(HKEY("__READLOOP:TOTALMSGS"), NewStrBufDup(Foo));
+               PutBstr(HKEY("__READLOOP:TOTALMSGS"), NewStrBufDup(Foo)); // keep Foo!
+
+               StrBufPrintf(Foo, "%ld", Stat.numNewmsgs);
+               PutBstr(HKEY("__READLOOP:NEWMSGS"), NewStrBufDup(Foo)); // keep Foo!
+
                StrBufPrintf(Foo, "%ld", Stat.startmsg);
-               PutBstr(HKEY("__READLOOP:STARTMSG"), Foo);
+               PutBstr(HKEY("__READLOOP:STARTMSG"), Foo); // store Foo elsewhere, descope it here.
        }
 
        /*
@@ -916,7 +874,9 @@ void post_mime_to_server(void) {
        }
 
        /* Only do multipart/alternative for mailboxes.  BBS and Wiki rooms don't need it. */
-       if (WC->CurRoom.view == VIEW_MAILBOX) {
+       if ((WCC->CurRoom.view == VIEW_MAILBOX) ||
+           (WCC->CurRoom.view == VIEW_JSON_LIST))
+       {
                include_text_alt = 1;
        }
 
@@ -929,6 +889,7 @@ void post_mime_to_server(void) {
 
        /* Remember, serv_printf() appends an extra newline */
        if (include_text_alt) {
+               StrBuf *Buf;
                serv_printf("Content-type: multipart/alternative; "
                        "boundary=\"%s\"\n", alt_boundary);
                serv_printf("This is a multipart message in MIME format.\n");
@@ -938,18 +899,30 @@ void post_mime_to_server(void) {
                serv_puts("Content-Transfer-Encoding: quoted-printable");
                serv_puts("");
                txtmail = html_to_ascii(bstr("msgtext"), 0, 80, 0);
-               text_to_server_qp(txtmail);     /* Transmit message in quoted-printable encoding */
+               Buf = NewStrBufPlain(txtmail, -1);
                free(txtmail);
 
+               text_to_server_qp(Buf);     /* Transmit message in quoted-printable encoding */
+               FreeStrBuf(&Buf);
                serv_printf("\n--%s", alt_boundary);
        }
 
-       serv_puts("Content-type: text/html; charset=utf-8");
-       serv_puts("Content-Transfer-Encoding: quoted-printable");
-       serv_puts("");
-       serv_puts("<html><body>\r\n");
-       text_to_server_qp(bstr("msgtext"));     /* Transmit message in quoted-printable encoding */
-       serv_puts("</body></html>\r\n");
+       if (havebstr("markdown"))
+       {
+               serv_puts("Content-type: text/x-markdown; charset=utf-8");
+               serv_puts("Content-Transfer-Encoding: quoted-printable");
+               serv_puts("");
+               text_to_server_qp(sbstr("msgtext"));    /* Transmit message in quoted-printable encoding */
+       }
+       else
+       {
+               serv_puts("Content-type: text/html; charset=utf-8");
+               serv_puts("Content-Transfer-Encoding: quoted-printable");
+               serv_puts("");
+               serv_puts("<html><body>\r\n");
+               text_to_server_qp(sbstr("msgtext"));    /* Transmit message in quoted-printable encoding */
+               serv_puts("</body></html>\r\n");
+       }
 
        if (include_text_alt) {
                serv_printf("--%s--", alt_boundary);
@@ -1027,37 +1000,34 @@ void post_message(void)
        }
 
        if (!strcasecmp(bstr("submit_action"), "cancel")) {
-               sprintf(WCC->ImportantMessage, 
-                       _("Cancelled.  Message was not posted."));
+               AppendImportantMessage(_("Cancelled.  Message was not posted."), -1);
        } else if (lbstr("postseq") == dont_post) {
-               sprintf(WCC->ImportantMessage, 
+               AppendImportantMessage(
                        _("Automatically cancelled because you have already "
-                         "saved this message."));
+                         "saved this message."), -1);
        } else {
                const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
                StrBuf *Recp = NULL; 
                StrBuf *Cc = NULL;
                StrBuf *Bcc = NULL;
-               const StrBuf *Wikipage = NULL;
+               char *wikipage = NULL;
                const StrBuf *my_email_addr = NULL;
                StrBuf *CmdBuf = NULL;
                StrBuf *references = NULL;
-               int save_to_drafts;
-               long HeaderLen;
+               int saving_to_drafts = 0;
+               long HeaderLen = 0;
 
-               save_to_drafts = !strcasecmp(bstr("submit_action"), "drafts");
+               saving_to_drafts = !strcasecmp(bstr("submit_action"), "draft");
                Buf = NewStrBuf();
 
-               if (save_to_drafts) {
+               if (saving_to_drafts) {
                        /* temporarily change to the drafts room */
                        serv_puts("GOTO _DRAFTS_");
                        StrBuf_ServGetln(Buf);
-                       if (GetServerStatus(Buf, NULL) != 2) {
+                       if (GetServerStatusMsg(Buf, NULL, 1, 2) != 2) {
                                /* You probably don't even have a dumb Drafts folder */
-                               StrBufCutLeft(Buf, 4);
-                               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);
+                               syslog(LOG_DEBUG, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf) + 4);
+                               AppendImportantMessage(_("Saved to Drafts failed: "), -1);
                                display_enter();
                                FreeStrBuf(&Buf);
                                return;
@@ -1095,46 +1065,48 @@ void post_message(void)
                FreeStrBuf(&EmailAddress);
                FreeStrBuf(&EncBuf);
 
-               Wikipage = sbstr("page");
+               wikipage = strdup(bstr("page"));
+               str_wiki_index(wikipage);
                my_email_addr = sbstr("my_email_addr");
                
                HeaderLen = StrLength(Recp) + 
                        StrLength(encoded_subject) +
                        StrLength(Cc) +
                        StrLength(Bcc) + 
-                       StrLength(Wikipage) +
+                       strlen(wikipage) +
                        StrLength(my_email_addr) + 
                        StrLength(references);
                CmdBuf = NewStrBufPlain(NULL, sizeof (CMD) + HeaderLen);
                StrBufPrintf(CmdBuf, 
                             CMD,
-                            save_to_drafts?"":ChrPtr(Recp),
+                            saving_to_drafts?"":ChrPtr(Recp),
                             is_anonymous,
                             ChrPtr(encoded_subject),
                             ChrPtr(display_name),
-                            save_to_drafts?"":ChrPtr(Cc),
-                            save_to_drafts?"":ChrPtr(Bcc),
-                            ChrPtr(Wikipage),
+                            saving_to_drafts?"":ChrPtr(Cc),
+                            saving_to_drafts?"":ChrPtr(Bcc),
+                            wikipage,
                             ChrPtr(my_email_addr),
                             ChrPtr(references));
                FreeStrBuf(&references);
                FreeStrBuf(&encoded_subject);
+               free(wikipage);
 
                if ((HeaderLen + StrLength(sbstr("msgtext")) < 10) && 
                    (GetCount(WCC->attachments) == 0)){
-                       StrBufAppendBufPlain(WCC->ImportantMsg, _("Refusing to post empty message.\n"), -1, 0);
+                       AppendImportantMessage(_("Refusing to post empty message.\n"), -1);
                        FreeStrBuf(&CmdBuf);
                                
                }
                else 
                {
-                       syslog(9, "%s\n", ChrPtr(CmdBuf));
+                       syslog(LOG_DEBUG, "%s\n", ChrPtr(CmdBuf));
                        serv_puts(ChrPtr(CmdBuf));
                        FreeStrBuf(&CmdBuf);
 
                        StrBuf_ServGetln(Buf);
                        if (GetServerStatus(Buf, NULL) == 4) {
-                               if (save_to_drafts) {
+                               if (saving_to_drafts) {
                                        if (  (havebstr("recp"))
                                              || (havebstr("cc"  ))
                                              || (havebstr("bcc" )) ) {
@@ -1143,33 +1115,33 @@ void post_message(void)
                                                serv_printf("Cc: %s", ChrPtr(Cc));
                                                serv_printf("Bcc: %s", ChrPtr(Bcc));
                                        } else {
-                                               serv_printf("X-Citadel-Room: %s", ChrPtr(WC->CurRoom.name));
+                                               serv_printf("X-Citadel-Room: %s", ChrPtr(WCC->CurRoom.name));
                                        }
                                }
                                post_mime_to_server();
-                               if (save_to_drafts) {
-                                       StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been saved to Drafts.\n"), -1, 0);
+                               if (saving_to_drafts) {
+                                       AppendImportantMessage(_("Message has been saved to Drafts.\n"), -1);
                                        gotoroom(WCC->CurRoom.name);
-                                       display_enter();
+                                       fixview();
+                                       readloop(readnew, eUseDefault);
                                        FreeStrBuf(&Buf);
                                        return;
                                } else if (  (havebstr("recp"))
                                             || (havebstr("cc"  ))
                                             || (havebstr("bcc" ))
                                        ) {
-                                       StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been sent.\n"), -1, 0);
+                                       AppendImportantMessage(_("Message has been sent.\n"), -1);
                                }
                                else {
-                                       StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been posted.\n"), -1, 0);
+                                       AppendImportantMessage(_("Message has been posted.\n"), -1);
                                }
                                dont_post = lbstr("postseq");
                        } else {
-                               StrBufCutLeft(Buf, 4);
-
-                               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);
+                               syslog(LOG_DEBUG, "%s:%d: server post error: %s", __FILE__, __LINE__, ChrPtr(Buf) + 4);
+                               AppendImportantMessage(ChrPtr(Buf) + 4, StrLength(Buf) - 4);
                                display_enter();
+                               if (saving_to_drafts) gotoroom(WCC->CurRoom.name);
+                               FreeStrBuf(&Recp);
                                FreeStrBuf(&Buf);
                                FreeStrBuf(&Cc);
                                FreeStrBuf(&Bcc);
@@ -1202,6 +1174,7 @@ void post_message(void)
         *  Otherwise, just go to the "read messages" loop.
         */
        else {
+               fixview();
                readloop(readnew, eUseDefault);
        }
 }
@@ -1218,24 +1191,40 @@ void upload_attachment(void) {
        long newnlen;
        void *v;
        wc_mime_attachment *att;
+       const StrBuf *Tmpl = sbstr("template");
+       const StrBuf *MimeType = NULL;
+       const StrBuf *UID;
 
-       syslog(9, "upload_attachment()\n");
-       wc_printf("upload_attachment()<br>\n");
+       begin_burst();
+       syslog(LOG_DEBUG, "upload_attachment()\n");
+       if (!Tmpl) 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");
+               syslog(LOG_DEBUG, "ERROR no attachment was uploaded\n");
+               if (Tmpl)
+               {
+                       putlbstr("UPLOAD_ERROR", 1);
+                       MimeType = DoTemplate(SKEY(Tmpl), NULL, &NoCtx);
+               }
+               else      wc_printf("ERROR no attachment was uploaded<br>\n");
+               http_transmit_thing(ChrPtr(MimeType), 0);
+
                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));
+       syslog(LOG_DEBUG, "Client is uploading %d bytes\n", WCC->upload_length);
+       if (Tmpl) putlbstr("UPLOAD_LENGTH", WCC->upload_length);
+       else wc_printf("Client is uploading %d bytes<br>\n", WCC->upload_length);
+
+       att = (wc_mime_attachment*)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);
-       
+       UID = sbstr("qquuid");
+       if (UID)
+               att->PartNum = NewStrBufDup(UID);
+
        if (WCC->attachments == NULL) {
                WCC->attachments = NewHash(1, Flathash);
        }
@@ -1270,6 +1259,9 @@ void upload_attachment(void) {
        att->Data = WCC->upload;
        WCC->upload = NULL;
        WCC->upload_length = 0;
+       
+       if (Tmpl) MimeType = DoTemplate(SKEY(Tmpl), NULL, &NoCtx);
+       http_transmit_thing(ChrPtr(MimeType), 0);
 }
 
 
@@ -1286,44 +1278,93 @@ void remove_attachment(void) {
        StrBuf *WhichAttachment;
        HashPos *at;
        long len;
+       int found=0;
        const char *key;
 
        WhichAttachment = NewStrBufDup(sbstr("which_attachment"));
+       if (ChrPtr(WhichAttachment)[0] == '/')
+               StrBufCutLeft(WhichAttachment, 1);
        StrBufUnescape(WhichAttachment, 0);
        at = GetNewHashPos(WCC->attachments, 0);
        do {
+               vAtt = NULL;
                GetHashPos(WCC->attachments, at, &len, &key, &vAtt);
-       
+
                att = (wc_mime_attachment*) vAtt;
-               if ((att != NULL) && 
-                   (strcmp(ChrPtr(WhichAttachment), 
-                           ChrPtr(att->FileName)   ) == 0))
+               if ((att != NULL) &&
+                   (
+                           !strcmp(ChrPtr(WhichAttachment), ChrPtr(att->FileName)) ||
+                   ((att->PartNum != NULL) &&
+                    !strcmp(ChrPtr(WhichAttachment), ChrPtr(att->PartNum)))
+                           ))
                {
                        DeleteEntryFromHash(WCC->attachments, at);
+                       found=1;
                        break;
                }
        }
        while (NextHashPos(WCC->attachments, at));
+
        FreeStrBuf(&WhichAttachment);
-       wc_printf("remove_attachment() completed\n");
+       wc_printf("remove_attachment(%d) completed\n", found);
 }
 
 
+long FourHash(const char *key, long length) 
+{
+        int i;
+        long ret = 0;
+        const unsigned char *ptr = (const unsigned char*)key;
+
+        for (i = 0; i < 4; i++, ptr ++) 
+                ret = (ret << 8) | 
+                        ( ((*ptr >= 'a') &&
+                           (*ptr <= 'z'))? 
+                          *ptr - 'a' + 'A': 
+                          *ptr);
+
+        return ret;
+}
+
+long l_subj;
+long l_wefw;
+long l_msgn;
+long l_from;
+long l_rcpt;
+long l_cccc;
+long l_replyto;
+long l_node;
+long l_rfca;
+long l_nvto;
+
+const char *ReplyToModeStrings [3] = {
+       "reply",
+       "replyall",
+       "forward"
+};
+typedef enum _eReplyToNodes {
+       eReply,
+       eReplyAll,
+       eForward
+}eReplyToNodes;
+       
 /*
  * display the message entry screen
  */
 void display_enter(void)
 {
-       char buf[SIZ];
-       long now;
+       const char *ReplyingModeStr;
+       eReplyToNodes ReplyMode = eReply;
+       StrBuf *Line;
+       long Result;
+       int rc;
        const StrBuf *display_name = NULL;
        int recipient_required = 0;
        int subject_required = 0;
-       int recipient_bad = 0;
        int is_anonymous = 0;
        wcsession *WCC = WC;
-
-       now = time(NULL);
+       int i = 0;
+       long replying_to;
 
        if (havebstr("force_room")) {
                gotoroom(sbstr("force_room"));
@@ -1335,22 +1376,28 @@ void display_enter(void)
                is_anonymous = 1;
        }
 
-       /* First test to see whether this is a room that requires recipients to be entered */
+       /*
+        * First, do we have permission to enter messages in this room at all?
+        */
+       Line = NewStrBuf();
        serv_puts("ENT0 0");
-       serv_getln(buf, sizeof buf);
+       StrBuf_ServGetln(Line);
+       rc = GetServerStatusMsg(Line, &Result, 0, 2);
 
-       if (!strncmp(buf, "570", 3)) {          /* 570 means that we need a recipient here */
+       if (Result == 570) {            /* 570 means that we need a recipient here */
                recipient_required = 1;
        }
-       else if (buf[0] != '2') {               /* Any other error means that we cannot continue */
-               sprintf(WCC->ImportantMessage, "%s", &buf[4]);
+       else if (rc != 2) {             /* Any other error means that we cannot continue */
+               rc = GetServerStatusMsg(Line, &Result, 0, 2);
+               fixview();
                readloop(readnew, eUseDefault);
+               FreeStrBuf(&Line);
                return;
        }
 
        /* Is the server strongly recommending that the user enter a message subject? */
-       if ((buf[3] != '\0') && (buf[4] != '\0')) {
-               subject_required = extract_int(&buf[4], 1);
+       if (StrLength(Line) > 4) {
+               subject_required = extract_int(ChrPtr(Line) + 4, 1);
        }
 
        /*
@@ -1359,6 +1406,7 @@ void display_enter(void)
         */
        if (WCC->CurRoom.defview == VIEW_ADDRESSBOOK) {
                do_edit_vcard(-1, "", NULL, NULL, "",  ChrPtr(WCC->CurRoom.name));
+               FreeStrBuf(&Line);
                return;
        }
 
@@ -1368,6 +1416,7 @@ void display_enter(void)
         */
        if (WCC->CurRoom.defview == VIEW_CALENDAR) {
                display_edit_event();
+               FreeStrBuf(&Line);
                return;
        }
 
@@ -1377,9 +1426,212 @@ void display_enter(void)
         */
        if (WCC->CurRoom.defview == VIEW_TASKS) {
                display_edit_task();
+               FreeStrBuf(&Line);
                return;
        }
 
+
+       ReplyingModeStr = bstr("replying_mode");
+       if (ReplyingModeStr != NULL) for (i = 0; i < 3; i++) {
+                       if (strcmp(ReplyingModeStr, ReplyToModeStrings[i]) == 0) {
+                               ReplyMode = (eReplyToNodes) i;
+                               break;
+                       }
+               }
+               
+
+       /*
+        * If the "replying_to" variable is set, it refers to a message
+        * number from which we must extract some header fields...
+        */
+       replying_to = lbstr("replying_to");
+       if (replying_to > 0) {
+               long len;
+               StrBuf *wefw = NULL;
+               StrBuf *msgn = NULL;
+               StrBuf *from = NULL;
+               StrBuf *node = NULL;
+               StrBuf *rfca = NULL;
+               StrBuf *rcpt = NULL;
+               StrBuf *cccc = NULL;
+               StrBuf *replyto = NULL;
+               StrBuf *nvto = NULL;
+               serv_printf("MSG0 %ld|1", replying_to); 
+
+               StrBuf_ServGetln(Line);
+               if (GetServerStatusMsg(Line, NULL, 0, 0) == 1)
+                       while (len = StrBuf_ServGetln(Line),
+                              (len >= 0) && 
+                              ((len != 3)  ||
+                               strcmp(ChrPtr(Line), "000")))
+                       {
+                               long which = 0;
+                               if ((StrLength(Line) > 4) && 
+                                   (ChrPtr(Line)[4] == '='))
+                                       which = FourHash(ChrPtr(Line), 4);
+
+                               if (which == l_subj)
+                               {
+                                       StrBuf *subj = NewStrBuf();
+                                       StrBuf *FlatSubject;
+
+                                       if (ReplyMode == eForward) {
+                                               if (strncasecmp(ChrPtr(Line) + 5, "Fw:", 3)) {
+                                                       StrBufAppendBufPlain(subj, HKEY("Fw: "), 0);
+                                               }
+                                       }
+                                       else {
+                                               if (strncasecmp(ChrPtr(Line) + 5, "Re:", 3)) {
+                                                       StrBufAppendBufPlain(subj, HKEY("Re: "), 0);
+                                               }
+                                       }
+                                       StrBufAppendBufPlain(subj, 
+                                                            ChrPtr(Line) + 5, 
+                                                            StrLength(Line) - 5, 0);
+                                       FlatSubject = NewStrBufPlain(NULL, StrLength(subj));
+                                       StrBuf_RFC822_to_Utf8(FlatSubject, subj, NULL, NULL);
+
+                                       PutBstr(HKEY("subject"), FlatSubject);
+                               }
+
+                               else if (which == l_wefw)
+                               {
+                                       int rrtok;
+                                       int rrlen;
+
+                                       wefw = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                       
+                                       /* Trim down excessively long lists of thread references.  We eliminate the
+                                        * second one in the list so that the thread root remains intact.
+                                        */
+                                       rrtok = num_tokens(ChrPtr(wefw), '|');
+                                       rrlen = StrLength(wefw);
+                                       if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
+                                               StrBufRemove_token(wefw, 1, '|');
+                                       }
+                               }
+
+                               else if (which == l_msgn) {
+                                       msgn = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }
+
+                               else if (which == l_from) {
+                                       StrBuf *FlatFrom;
+                                       from = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                       FlatFrom = NewStrBufPlain(NULL, StrLength(from));
+                                       StrBuf_RFC822_to_Utf8(FlatFrom, from, NULL, NULL);
+                                       FreeStrBuf(&from);
+                                       from = FlatFrom;
+                                       for (i=0; i<StrLength(from); ++i) {
+                                               if (ChrPtr(from)[i] == ',')
+                                                       StrBufPeek(from, NULL, i, ' ');
+                                       }
+                               }
+                               
+                               else if (which == l_rcpt) {
+                                       rcpt = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }
+                               
+                               else if (which == l_cccc) {
+                                       cccc = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }
+                               
+                               else if (which == l_node) {
+                                       node = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }
+                               else if (which == l_replyto) {
+                                       replyto = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                               }
+                               else if (which == l_rfca) {
+                                       StrBuf *FlatRFCA;
+                                       rfca = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                       FlatRFCA = NewStrBufPlain(NULL, StrLength(rfca));
+                                       StrBuf_RFC822_to_Utf8(FlatRFCA, rfca, NULL, NULL);
+                                       FreeStrBuf(&rfca);
+                                       rfca = FlatRFCA;
+                               }
+                               else if (which == l_nvto) {
+                                       nvto = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                       putbstr("nvto", nvto);
+                               }
+                       }
+
+
+               if (StrLength(wefw) + StrLength(msgn) > 0) {
+                       StrBuf *refs = NewStrBuf();
+                       if (StrLength(wefw) > 0) {
+                               StrBufAppendBuf(refs, wefw, 0);
+                       }
+                       if ( (StrLength(wefw) > 0) && 
+                            (StrLength(msgn) > 0) ) 
+                       {
+                               StrBufAppendBufPlain(refs, HKEY("|"), 0);
+                       }
+                       if (StrLength(msgn) > 0) {
+                               StrBufAppendBuf(refs, msgn, 0);
+                       }
+                       PutBstr(HKEY("references"), refs);
+               }
+
+               /*
+                * If this is a Reply or a ReplyAll, copy the sender's email into the To: field
+                */
+               if ((ReplyMode == eReply) || (ReplyMode == eReplyAll))
+               {
+                       StrBuf *to_rcpt;
+                       if ((StrLength(replyto) > 0) && (ReplyMode == eReplyAll)) {
+                               to_rcpt = NewStrBuf();
+                               StrBufAppendBuf(to_rcpt, replyto, 0);
+                       }
+                       else if (StrLength(rfca) > 0) {
+                               to_rcpt = NewStrBuf();
+                               StrBufAppendBuf(to_rcpt, from, 0);
+                               StrBufAppendBufPlain(to_rcpt, HKEY(" <"), 0);
+                               StrBufAppendBuf(to_rcpt, rfca, 0);
+                               StrBufAppendBufPlain(to_rcpt, HKEY(">"), 0);
+                       }
+                       else {
+                               to_rcpt =  from;
+                               from = NULL;
+                               if (    (StrLength(node) > 0)
+                                       && (strcasecmp(ChrPtr(node), ChrPtr(WCC->serv_info->serv_nodename)))
+                               ) {
+                                       StrBufAppendBufPlain(to_rcpt, HKEY(" @ "), 0);
+                                       StrBufAppendBuf(to_rcpt, node, 0);
+                               }
+                       }
+                       PutBstr(HKEY("recp"), to_rcpt);
+               }
+
+               /*
+                * Only if this is a ReplyAll, copy all recipients into the Cc: field
+                */
+               if (ReplyMode == eReplyAll)
+               {
+                       StrBuf *cc_rcpt = rcpt;
+                       rcpt = NULL;
+                       if ((StrLength(cccc) > 0) && (StrLength(replyto) == 0))
+                       {
+                               if (cc_rcpt != NULL)  {
+                                       StrBufAppendPrintf(cc_rcpt, ", ");
+                                       StrBufAppendBuf(cc_rcpt, cccc, 0);
+                               } else {
+                                       cc_rcpt = cccc;
+                                       cccc = NULL;
+                               }
+                       }
+                       if (cc_rcpt != NULL)
+                               PutBstr(HKEY("cc"), cc_rcpt);
+               }
+               FreeStrBuf(&wefw);
+               FreeStrBuf(&msgn);
+               FreeStrBuf(&from);
+               FreeStrBuf(&node);
+               FreeStrBuf(&rfca);
+               FreeStrBuf(&rcpt);
+               FreeStrBuf(&cccc);
+       }
+       FreeStrBuf(&Line);
        /*
         * Otherwise proceed normally.
         * Do a custom room banner with no navbar...
@@ -1389,14 +1641,15 @@ void display_enter(void)
                const StrBuf *Recp = NULL; 
                const StrBuf *Cc = NULL;
                const StrBuf *Bcc = NULL;
-               const StrBuf *Wikipage = NULL;
+               char *wikipage = NULL;
                StrBuf *CmdBuf = NULL;
                const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s";
                
                Recp = sbstr("recp");
                Cc = sbstr("cc");
                Bcc = sbstr("bcc");
-               Wikipage = sbstr("page");
+               wikipage = strdup(bstr("page"));
+               str_wiki_index(wikipage);
                
                CmdBuf = NewStrBufPlain(NULL, 
                                        sizeof (CMD) + 
@@ -1404,7 +1657,7 @@ void display_enter(void)
                                        StrLength(display_name) +
                                        StrLength(Cc) +
                                        StrLength(Bcc) + 
-                                       StrLength(Wikipage));
+                                       strlen(wikipage));
 
                StrBufPrintf(CmdBuf, 
                             CMD,
@@ -1413,22 +1666,27 @@ void display_enter(void)
                             ChrPtr(display_name),
                             ChrPtr(Cc), 
                             ChrPtr(Bcc), 
-                            ChrPtr(Wikipage));
+                            wikipage
+               );
                serv_puts(ChrPtr(CmdBuf));
-               serv_getln(buf, sizeof buf);
-               FreeStrBuf(&CmdBuf);
+               StrBuf_ServGetln(CmdBuf);
+               free(wikipage);
 
-               if (!strncmp(buf, "570", 3)) {  /* 570 means we have an invalid recipient listed */
-                       if (havebstr("recp") && 
-                           havebstr("cc"  ) && 
-                           havebstr("bcc" )) {
-                               recipient_bad = 1;
-                       }
+               rc = GetServerStatusMsg(CmdBuf, &Result, 0, 0);
+
+               if (    (Result == 570)         /* invalid or missing recipient(s) */
+                       || (Result == 550)      /* higher access required to send Internet mail */
+               ) {
+                       /* These errors will have been displayed and are excusable */
                }
-               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 */
+               else if (rc != 2) {     /* Any other error means that we cannot continue */
+                       AppendImportantMessage(ChrPtr(CmdBuf) + 4, StrLength(CmdBuf) - 4);
+                       FreeStrBuf(&CmdBuf);
+                       fixview();
+                       readloop(readnew, eUseDefault);
                        return;
                }
+               FreeStrBuf(&CmdBuf);
        }
        if (recipient_required)
                PutBstr(HKEY("__RCPTREQUIRED"), NewStrBufPlain(HKEY("1")));
@@ -1437,7 +1695,10 @@ void display_enter(void)
 
        begin_burst();
        output_headers(1, 0, 0, 0, 1, 0);
-       DoTemplate(HKEY("edit_message"), NULL, &NoCtx);
+       if (WCC->CurRoom.defview == VIEW_WIKIMD) 
+               DoTemplate(HKEY("edit_markdown_epic"), NULL, &NoCtx);
+       else
+               DoTemplate(HKEY("edit_message"), NULL, &NoCtx);
        end_burst();
 
        return;
@@ -1449,10 +1710,10 @@ void display_enter(void)
 void delete_msg(void)
 {
        long msgid;
-       char buf[SIZ];
-
+       StrBuf *Line;
+       
        msgid = lbstr("msgid");
-
+       Line = NewStrBuf();
        if ((WC->CurRoom.RAFlags & UA_ISTRASH) != 0) {  /* Delete from Trash is a real delete */
                serv_printf("DELE %ld", msgid); 
        }
@@ -1460,8 +1721,11 @@ void delete_msg(void)
                serv_printf("MOVE %ld|_TRASH_|0", msgid);
        }
 
-       serv_getln(buf, sizeof buf);
-       sprintf(WC->ImportantMessage, "%s", &buf[4]);
+       StrBuf_ServGetln(Line);
+       GetServerStatusMsg(Line, NULL, 1, 0);
+
+       fixview();
+
        readloop(readnew, eUseDefault);
 }
 
@@ -1472,76 +1736,25 @@ void delete_msg(void)
 void move_msg(void)
 {
        long msgid;
-       char buf[SIZ];
 
        msgid = lbstr("msgid");
 
        if (havebstr("move_button")) {
-               sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
-               serv_puts(buf);
-               serv_getln(buf, sizeof buf);
-               sprintf(WC->ImportantMessage, "%s", &buf[4]);
+               StrBuf *Line;
+               serv_printf("MOVE %ld|%s", msgid, bstr("target_room"));
+               Line = NewStrBuf();
+               StrBuf_ServGetln(Line);
+               GetServerStatusMsg(Line, NULL, 1, 0);
+               FreeStrBuf(&Line);
        } else {
-               sprintf(WC->ImportantMessage, (_("The message was not moved.")));
+               AppendImportantMessage(_("The message was not moved."), -1);
        }
 
+       fixview();
        readloop(readnew, eUseDefault);
 }
 
 
-/*
- * Confirm move of a message
- */
-void confirm_move_msg(void)
-{
-       long msgid;
-       char buf[SIZ];
-       char targ[SIZ];
-
-       msgid = lbstr("msgid");
-
-
-       output_headers(1, 1, 2, 0, 0, 0);
-       wc_printf("<div id=\"banner\">\n");
-       wc_printf("<h1>");
-       wc_printf(_("Confirm move of message"));
-       wc_printf("</h1>");
-       wc_printf("</div>\n");
-
-       wc_printf("<div id=\"content\" class=\"service\">\n");
-
-       wc_printf("<CENTER>");
-
-       wc_printf(_("Move this message to:"));
-       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);
-       wc_printf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
-
-       wc_printf("<SELECT NAME=\"target_room\" SIZE=5>\n");
-       serv_puts("LKRA");
-       serv_getln(buf, sizeof buf);
-       if (buf[0] == '1') {
-               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
-                       extract_token(targ, buf, 0, '|', sizeof targ);
-                       wc_printf("<OPTION>");
-                       escputs(targ);
-                       wc_printf("\n");
-               }
-       }
-       wc_printf("</SELECT>\n");
-       wc_printf("<br>\n");
-
-       wc_printf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
-       wc_printf("&nbsp;");
-       wc_printf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
-       wc_printf("</form></CENTER>\n");
-
-       wc_printf("</CENTER>\n");
-       wDumpContent(1);
-}
-
 
 /*
  * Generic function to output an arbitrary MIME attachment from
@@ -1593,7 +1806,9 @@ void postpart(StrBuf *partnum, StrBuf *filename, int force_download)
  */
 void mimepart(int force_download)
 {
+       int detect_mime = 0;
        long msgnum;
+       long ErrorDetail;
        StrBuf *att;
        wcsession *WCC = WC;
        StrBuf *Buf;
@@ -1607,32 +1822,60 @@ void mimepart(int force_download)
 
        serv_printf("OPNA %ld|%s", msgnum, ChrPtr(att));
        StrBuf_ServGetln(Buf);
-       if (GetServerStatus(Buf, NULL) == 2) {
+       if (GetServerStatus(Buf, &ErrorDetail) == 2) {
                StrBufCutLeft(Buf, 4);
                bytes = StrBufExtract_long(Buf, 0, '|');
-               if (!force_download) {
-                       StrBufExtract_token(ContentType, Buf, 3, '|');
+               StrBufExtract_token(ContentType, Buf, 3, '|');
+               CheckGZipCompressionAllowed (SKEY(ContentType));
+               if (force_download)
+               {
+                       FlushStrBuf(ContentType);
+                       detect_mime = 0;
                }
-
-               serv_read_binary(WCC->WBuf, bytes, Buf);
-               serv_puts("CLOS");
-               StrBuf_ServGetln(Buf);
-               CT = ChrPtr(ContentType);
-
-               if (!force_download) {
-                       if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
+               else
+               {
+                       if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream"))
+                       {
                                StrBufExtract_token(Buf, WCC->Hdr->HR.ReqLine, 2, '/');
                                CT = GuessMimeByFilename(SKEY(Buf));
+                               StrBufPlain(ContentType, CT, -1);
                        }
-                       if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
-                               CT = GuessMimeType(SKEY(WCC->WBuf));
+                       if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream"))
+                       {
+                               detect_mime = 1;
                        }
                }
-               http_transmit_thing(CT, 0);
+               serv_read_binary_to_http(ContentType, bytes, 0, detect_mime);
+
+               serv_read_binary(WCC->WBuf, bytes, Buf);
+               serv_puts("CLOS");
+               StrBuf_ServGetln(Buf);
+               CT = ChrPtr(ContentType);
        } else {
                StrBufCutLeft(Buf, 4);
-               hprintf("HTTP/1.1 404 %s\n", ChrPtr(Buf));
-               output_headers(0, 0, 0, 0, 0, 0);
+               switch (ErrorDetail) {
+               default:
+               case ERROR + MESSAGE_NOT_FOUND:
+                       hprintf("HTTP/1.1 404 %s\n", ChrPtr(Buf));
+                       break;
+               case ERROR + NOT_LOGGED_IN:
+                       hprintf("HTTP/1.1 401 %s\n", ChrPtr(Buf));
+                       break;
+
+               case ERROR + HIGHER_ACCESS_REQUIRED:
+                       hprintf("HTTP/1.1 403 %s\n", ChrPtr(Buf));
+                       break;
+               case ERROR + INTERNAL_ERROR:
+               case ERROR + TOO_BIG:
+                       hprintf("HTTP/1.1 500 %s\n", ChrPtr(Buf));
+                       break;
+               }
+
+               hprintf("Pragma: no-cache\r\n"
+                       "Cache-Control: no-store\r\n"
+                       "Expires: -1\r\n"
+               );
+
                hprintf("Content-Type: text/plain\r\n");
                begin_burst();
                wc_printf(_("An error occurred while retrieving this part: %s\n"), 
@@ -1750,31 +1993,23 @@ void h_do_search(void) { readloop(do_search, eUseDefault);}
 void h_readgt(void) { readloop(readgt, eUseDefault);}
 void h_readlt(void) { readloop(readlt, eUseDefault);}
 
-void jsonMessageListHdr(void) 
-{
-       /* TODO: make a generic function */
-       hprintf("HTTP/1.1 200 OK\r\n");
-       hprintf("Content-type: application/json; charset=utf-8\r\n");
-       hprintf("Server: %s / %s\r\n", PACKAGE_STRING, ChrPtr(WC->serv_info->serv_software));
-       hprintf("Connection: close\r\n");
-       hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
-       begin_burst();
-}
 
 
 /* Output message list in JSON format */
 void jsonMessageList(void) {
+       StrBuf *View = NewStrBuf();
        const StrBuf *room = sbstr("room");
        long oper = (havebstr("query")) ? do_search : readnew;
-       WC->is_ajax = 1; 
+       StrBufPrintf(View, "%d", VIEW_JSON_LIST);
+       putbstr("view", View);; 
        gotoroom(room);
        readloop(oper, eUseDefault);
-       WC->is_ajax = 0;
 }
 
 void RegisterReadLoopHandlerset(
        int RoomType,
        GetParamsGetServerCall_func GetParamsGetServerCall,
+       PrintViewHeader_func PrintPageHeader,
        PrintViewHeader_func PrintViewHeader,
        load_msg_ptrs_detailheaders LH,
        LoadMsgFromServer_func LoadMsgFromServer,
@@ -1788,6 +2023,7 @@ void RegisterReadLoopHandlerset(
 
        Handler->RoomType = RoomType;
        Handler->GetParamsGetServerCall = GetParamsGetServerCall;
+       Handler->PrintPageHeader = PrintPageHeader;
        Handler->PrintViewHeader = PrintViewHeader;
        Handler->LoadMsgFromServer = LoadMsgFromServer;
        Handler->RenderView_or_Tail = RenderView_or_Tail;
@@ -1832,7 +2068,6 @@ InitModule_MSG
        WebcitAddUrlHandler(HKEY("post"), "", 0, post_message, PROHIBIT_STARTPAGE);
        WebcitAddUrlHandler(HKEY("move_msg"), "", 0, move_msg, PROHIBIT_STARTPAGE);
        WebcitAddUrlHandler(HKEY("delete_msg"), "", 0, delete_msg, PROHIBIT_STARTPAGE);
-       WebcitAddUrlHandler(HKEY("confirm_move_msg"), "", 0, confirm_move_msg, PROHIBIT_STARTPAGE);
        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);
@@ -1848,6 +2083,18 @@ InitModule_MSG
 
        /* json */
        WebcitAddUrlHandler(HKEY("roommsgs"), "", 0, jsonMessageList,0);
+
+       l_subj = FourHash("subj", 4);
+       l_wefw = FourHash("wefw", 4);
+       l_msgn = FourHash("msgn", 4);
+       l_from = FourHash("from", 4);
+       l_rcpt = FourHash("rcpt", 4);
+       l_cccc = FourHash("cccc", 4);
+       l_replyto = FourHash("rep2", 4);
+       l_node = FourHash("node", 4);
+       l_rfca = FourHash("rfca", 4);
+       l_nvto = FourHash("nvto", 4);
+
        return ;
 }