Renderers cleanup part 1
[citadel.git] / webcit / messages.c
index 3f8108f0a97c54511b4f97df414caec04346b535..db7638b51a254bf399b463e6def3ee8809a7685b 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Functions which deal with the fetching and displaying of messages.
  *
- * Copyright (c) 1996-2012 by the citadel.org team
+ * Copyright (c) 1996-2018 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, version 3.
@@ -17,8 +17,6 @@
 #include "dav.h"
 #include "calendar.h"
 
-HashList *MsgHeaderHandler = NULL;
-HashList *MsgEvaluators = NULL;
 HashList *MimeRenderHandler = NULL;
 HashList *ReadLoopHandler = NULL;
 int dbg_analyze_msg = 0;
@@ -31,12 +29,6 @@ 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 */
@@ -52,12 +44,11 @@ int load_message(message_summary *Msg,
 {
        StrBuf *Buf;
        StrBuf *HdrToken;
-       headereval *Hdr;
-       void *vHdr;
        char buf[SIZ];
        int Done = 0;
        int state=0;
-       
+       int rc;
+
        Buf = NewStrBuf();
        if (Msg->PartNum != NULL) {
                serv_printf("MSG4 %ld|%s", Msg->msgnum, ChrPtr(Msg->PartNum));
@@ -104,14 +95,11 @@ int load_message(message_summary *Msg,
                        StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
                        
                        /* look up one of the examine_* functions to parse the content */
-                       if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
-                           (vHdr != NULL)) {
-                               Hdr = (headereval*)vHdr;
-                               Hdr->evaluator(Msg, Buf, FoundCharset);
-                               if (Hdr->Type == 1) {
-                                       state++;
-                               }
-                       }/* TODO: 
+                       rc =  EvaluateMsgHdr(SKEY(HdrToken), Msg, Buf, FoundCharset);
+                       if (rc == 1) {
+                               state++;
+                       }
+                       /* TODO: 
                        else LogError(Target, 
                                      __FUNCTION__,  
                                      "don't know how to handle message header[%s]\n", 
@@ -132,11 +120,8 @@ int load_message(message_summary *Msg,
                                if (StrLength(HdrToken) > 0) {
                                        StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
                                        /* the examine*'s know how to do with mime headers too... */
-                                       if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
-                                           (vHdr != NULL)) {
-                                               Hdr = (headereval*)vHdr;
-                                               Hdr->evaluator(Msg, Buf, FoundCharset);
-                                       }
+                                       EvaluateMsgHdr(SKEY(HdrToken), Msg, Buf, FoundCharset);
+                                       
                                        break;
                                }
                        }
@@ -178,7 +163,7 @@ int load_message(message_summary *Msg,
  * printable_view      Nonzero to display a printable view
  * section             Optional for encapsulated message/rfc822 submessage
  */
-int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, const StrBuf *PartNum, const StrBuf **OutMime) 
+int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, const StrBuf *PartNum, const StrBuf **OutMime, WCTemplputParams *TP
 {
        StrBuf *Buf;
        StrBuf *FoundCharset;
@@ -192,6 +177,9 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
        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));
@@ -212,12 +200,11 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
        StrBufTrim(Buf);
        StrBufLowerCase(Buf);
 
-       StackContext(NULL, &SuperTP, Msg, CTX_MAILSUM, 0, NULL);
+       StackContext(TP, &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)) {
-                       WCTemplputParams SubTP;
                        RenderMimeFuncStruct *Render;
                        
                        StackContext(&SuperTP, &SubTP, Msg->MsgBody, CTX_MIME_ATACH, 0, NULL);
@@ -362,9 +349,9 @@ void handle_one_message(void)
        case ePOST:
                Tmpl = sbstr("template");
                if (StrLength(Tmpl) > 0) 
-                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
+                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime, NULL);
                else 
-                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
+                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime, NULL);
                http_transmit_thing(ChrPtr(Mime), 0);
                break;
        case eDELETE:
@@ -421,9 +408,9 @@ void embed_message(void) {
        case ePOST:
                Tmpl = sbstr("template");
                if (StrLength(Tmpl) > 0) 
-                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
+                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime, NULL);
                else 
-                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
+                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime, NULL);
                http_transmit_thing(ChrPtr(Mime), 0);
                break;
        case eDELETE:
@@ -460,7 +447,7 @@ void print_message(void) {
 
        begin_burst();
 
-       read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL, &Mime);
+       read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL, &Mime, NULL);
 
        wDumpContent(0);
 }
@@ -481,7 +468,7 @@ void display_headers(void) {
                PACKAGE_STRING);
        begin_burst();
 
-       serv_printf("MSG2 %ld|3", msgnum);
+       serv_printf("MSG2 %ld|1", msgnum);
        serv_getln(buf, sizeof buf);
        if (buf[0] == '1') {
                while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
@@ -493,50 +480,6 @@ 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
@@ -545,8 +488,13 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu
  */
 int load_msg_ptrs(const char *servcmd,
                  const char *filter,
+                 StrBuf *FoundCharset,
                  SharedMessageStatus *Stat, 
-                 load_msg_ptrs_detailheaders LH)
+                 void **ViewSpecific,
+                 load_msg_ptrs_detailheaders LH,
+                 StrBuf *FetchMessageList,
+                 eMessageField *MessageFieldList,
+                 long HeaderCount)
 {
         wcsession *WCC = WC;
        message_summary *Msg;
@@ -578,6 +526,10 @@ int load_msg_ptrs(const char *servcmd,
                         serv_puts("000");
                        break;
                }
+               else if (FetchMessageList != NULL) {
+                       serv_putbuf(FetchMessageList);
+                       break;
+               }
                /* fall back to empty filter in case of we were fooled... */
                serv_puts("");
                serv_puts("000");
@@ -600,21 +552,30 @@ int load_msg_ptrs(const char *servcmd,
 
                        Msg->msgnum = StrBufExtractNext_long(Buf, &Ptr, '|');
                        Msg->date = StrBufExtractNext_long(Buf, &Ptr, '|');
-
-                       if (Stat->nummsgs == 0) {
-                               if (Msg->msgnum < Stat->lowest_found) {
-                                       Stat->lowest_found = Msg->msgnum;
-                               }
-                               if (Msg->msgnum > Stat->highest_found) {
-                                       Stat->highest_found = Msg->msgnum;
+                       if (MessageFieldList != NULL) {
+                               long i;
+                               for (i = 0; i < HeaderCount; i++) {
+                                       StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|');
+                                       if (StrLength(Buf2) > 0) {
+                                               EvaluateMsgHdrEnum(MessageFieldList[i], Msg, Buf2, FoundCharset);
+                                       }
                                }
                        }
+                       else {
+                               if (Stat->nummsgs == 0) {
+                                       if (Msg->msgnum < Stat->lowest_found) {
+                                               Stat->lowest_found = Msg->msgnum;
+                                       }
+                                       if (Msg->msgnum > Stat->highest_found) {
+                                               Stat->highest_found = Msg->msgnum;
+                                       }
+                               }
 
-                       if ((Msg->msgnum == 0) && (StrLength(Buf) < 32)) {
-                               free(Msg);
-                               continue;
+                               if ((Msg->msgnum == 0) && (StrLength(Buf) < 32)) {
+                                       free(Msg);
+                                       continue;
+                               }
                        }
-
                        /* 
                         * as citserver probably gives us messages in forward date sorting
                         * nummsgs should be the same order as the message date.
@@ -625,7 +586,7 @@ int load_msg_ptrs(const char *servcmd,
                                        skipit = 1;
                        }
                        if ((!skipit) && (LH != NULL)) {
-                               if (!LH(Buf, &Ptr, Msg, Buf2)){
+                               if (!LH(Buf, &Ptr, Msg, Buf2, ViewSpecific)){
                                        free(Msg);
                                        continue;
                                }                                       
@@ -648,10 +609,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;
@@ -662,17 +624,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;
@@ -685,15 +651,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[];
@@ -709,6 +676,9 @@ typedef struct _RoomRenderer{
        RenderView_or_Tail_func RenderView_or_Tail;
        View_Cleanup_func ViewCleanup;
        load_msg_ptrs_detailheaders LHParse;
+       long HeaderCount;
+       StrBuf *FetchMessageList;
+       eMessageField *MessageFieldList;
 } RoomRenderer;
 
 
@@ -732,7 +702,7 @@ 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;
@@ -790,9 +760,21 @@ void readloop(long oper, eCustomRoomRenderer ForceRenderer)
        }
        if (!IsEmptyStr(cmd)) {
                const char *p = NULL;
+               StrBuf *FoundCharset = NULL;
                if (!IsEmptyStr(filter))
                        p = filter;
-               Stat.nummsgs = load_msg_ptrs(cmd, p, &Stat, ViewMsg->LHParse);
+               if (ViewMsg->HeaderCount > 0) {
+                       FoundCharset = NewStrBuf();
+               }
+               Stat.nummsgs = load_msg_ptrs(cmd, p,
+                                            FoundCharset,
+                                            &Stat,
+                                            &ViewSpecific,
+                                            ViewMsg->LHParse,
+                                            ViewMsg->FetchMessageList,
+                                            ViewMsg->MessageFieldList,
+                                            ViewMsg->HeaderCount);
+               FreeStrBuf(&FoundCharset);
        }
 
        if (Stat.sortit) {
@@ -812,7 +794,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...
@@ -830,9 +812,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. */
        }
 
        /*
@@ -928,6 +914,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");
@@ -937,9 +924,11 @@ 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);
        }
 
@@ -947,7 +936,7 @@ void post_mime_to_server(void) {
        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 */
+       text_to_server_qp(sbstr("msgtext"));    /* Transmit message in quoted-printable encoding */
        serv_puts("</body></html>\r\n");
 
        if (include_text_alt) {
@@ -1036,7 +1025,7 @@ void post_message(void)
                StrBuf *Recp = NULL; 
                StrBuf *Cc = NULL;
                StrBuf *Bcc = NULL;
-               char *wikipage = NULL;
+               StrBuf *wikipage = NULL;
                const StrBuf *my_email_addr = NULL;
                StrBuf *CmdBuf = NULL;
                StrBuf *references = NULL;
@@ -1052,7 +1041,7 @@ void post_message(void)
                        StrBuf_ServGetln(Buf);
                        if (GetServerStatusMsg(Buf, NULL, 1, 2) != 2) {
                                /* You probably don't even have a dumb Drafts folder */
-                               syslog(9, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf) + 4);
+                               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);
@@ -1091,7 +1080,7 @@ void post_message(void)
                FreeStrBuf(&EmailAddress);
                FreeStrBuf(&EncBuf);
 
-               wikipage = strdup(bstr("page"));
+               wikipage = NewStrBufDup(sbstr("page"));
                str_wiki_index(wikipage);
                my_email_addr = sbstr("my_email_addr");
                
@@ -1099,7 +1088,7 @@ void post_message(void)
                        StrLength(encoded_subject) +
                        StrLength(Cc) +
                        StrLength(Bcc) + 
-                       strlen(wikipage) +
+                       StrLength(wikipage) +
                        StrLength(my_email_addr) + 
                        StrLength(references);
                CmdBuf = NewStrBufPlain(NULL, sizeof (CMD) + HeaderLen);
@@ -1111,7 +1100,7 @@ void post_message(void)
                             ChrPtr(display_name),
                             saving_to_drafts?"":ChrPtr(Cc),
                             saving_to_drafts?"":ChrPtr(Bcc),
-                            wikipage,
+                            ChrPtr(wikipage),
                             ChrPtr(my_email_addr),
                             ChrPtr(references));
                FreeStrBuf(&references);
@@ -1126,7 +1115,7 @@ void post_message(void)
                }
                else 
                {
-                       syslog(9, "%s\n", ChrPtr(CmdBuf));
+                       syslog(LOG_DEBUG, "%s\n", ChrPtr(CmdBuf));
                        serv_puts(ChrPtr(CmdBuf));
                        FreeStrBuf(&CmdBuf);
 
@@ -1163,7 +1152,7 @@ void post_message(void)
                                }
                                dont_post = lbstr("postseq");
                        } else {
-                               syslog(9, "%s:%d: server post error: %s", __FILE__, __LINE__, ChrPtr(Buf) + 4);
+                               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);
@@ -1217,24 +1206,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);
        }
@@ -1269,6 +1274,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);
 }
 
 
@@ -1285,58 +1293,41 @@ 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");
-}
 
-
-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;
+       FreeStrBuf(&WhichAttachment);
+       wc_printf("remove_attachment(%d) completed\n", found);
 }
 
-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;
 
 const char *ReplyToModeStrings [3] = {
        "reply",
-       "replyalle",
+       "replyall",
        "forward"
 };
 typedef enum _eReplyToNodes {
@@ -1452,6 +1443,7 @@ void display_enter(void)
                StrBuf *rcpt = NULL;
                StrBuf *cccc = NULL;
                StrBuf *replyto = NULL;
+               StrBuf *nvto = NULL;
                serv_printf("MSG0 %ld|1", replying_to); 
 
                StrBuf_ServGetln(Line);
@@ -1461,91 +1453,118 @@ void display_enter(void)
                               ((len != 3)  ||
                                strcmp(ChrPtr(Line), "000")))
                        {
-                               long which = 0;
+                               eMessageField which;
                                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);
+                                   (ChrPtr(Line)[4] == '=') &&
+                                   GetFieldFromMnemonic(&which, ChrPtr(Line)))
+                                       switch (which) {
+                                       case eMsgSubject: {
+                                               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);
+                                               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);
+                                               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);
-                               }
+                                               PutBstr(HKEY("subject"), FlatSubject);
+                                       }
+                                               break;
 
-                               else if (which == l_wefw)
-                               {
-                                       int rrtok;
-                                       int rrlen;
+                                       case eWeferences:
+                                       {
+                                               int rrtok;
+                                               int rrlen;
 
-                                       wefw = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               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, '|');
+                                               /* 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, '|');
+                                               }
+                                               break;
                                        }
-                               }
 
-                               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, ' ');
+                                       case emessageId:
+                                               msgn = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               break;
+
+                                       case eAuthor: {
+                                               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, ' ');
+                                               }
+                                               break;
                                        }
-                               }
                                
-                               else if (which == l_rcpt) {
-                                       rcpt = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
-                               }
+                                       case eRecipient:
+                                               rcpt = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               break;
+                       
                                
-                               else if (which == l_cccc) {
-                                       cccc = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
-                               }
+                                       case eCarbonCopY:
+                                               cccc = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               break;
+
                                
-                               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;
-                               }
+                                       case eNodeName:
+                                               node = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               break;
+                                       case eReplyTo:
+                                               replyto = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               break;
+                                       case erFc822Addr: {
+                                               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;
+                                               break;
+                                       }
+                                       case eenVelopeTo:
+                                               nvto = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
+                                               putbstr("nvto", nvto);
+                                               break;
+                                       case eXclusivID:
+                                       case eHumanNode:
+                                       case eJournal:
+                                       case eListID:
+                                       case eMesageText:
+                                       case eOriginalRoom:
+                                       case eMessagePath:
+                                       case eSpecialField:
+                                       case eTimestamp:
+                                       case eHeaderOnly:
+                                       case eFormatType:
+                                       case eMessagePart:
+                                       case eSubFolder:
+                                       case ePevious:
+                                       case eLastHeader:
+                                               break;
+
+                                       }
                        }
 
 
@@ -1633,14 +1652,14 @@ void display_enter(void)
                const StrBuf *Recp = NULL; 
                const StrBuf *Cc = NULL;
                const StrBuf *Bcc = NULL;
-               char *wikipage = NULL;
+               StrBuf *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 = strdup(bstr("page"));
+               wikipage = NewStrBufDup(sbstr("page"));
                str_wiki_index(wikipage);
                
                CmdBuf = NewStrBufPlain(NULL, 
@@ -1649,7 +1668,7 @@ void display_enter(void)
                                        StrLength(display_name) +
                                        StrLength(Cc) +
                                        StrLength(Bcc) + 
-                                       strlen(wikipage));
+                                       StrLength(wikipage));
 
                StrBufPrintf(CmdBuf, 
                             CMD,
@@ -1658,7 +1677,7 @@ void display_enter(void)
                             ChrPtr(display_name),
                             ChrPtr(Cc), 
                             ChrPtr(Bcc), 
-                            wikipage
+                            ChrPtr(wikipage)
                );
                serv_puts(ChrPtr(CmdBuf));
                StrBuf_ServGetln(CmdBuf);
@@ -1795,6 +1814,7 @@ void postpart(StrBuf *partnum, StrBuf *filename, int force_download)
  */
 void mimepart(int force_download)
 {
+       int detect_mime = 0;
        long msgnum;
        long ErrorDetail;
        StrBuf *att;
@@ -1813,25 +1833,32 @@ void mimepart(int force_download)
        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);
                switch (ErrorDetail) {
@@ -1851,7 +1878,12 @@ void mimepart(int force_download)
                        hprintf("HTTP/1.1 500 %s\n", ChrPtr(Buf));
                        break;
                }
-               output_headers(0, 0, 0, 0, 0, 0);
+
+               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"), 
@@ -1982,6 +2014,15 @@ void jsonMessageList(void) {
        readloop(oper, eUseDefault);
 }
 
+void FreeReadLoopHandlerSet(void *v) {
+       RoomRenderer *Handler = (RoomRenderer *) v;
+       FreeStrBuf(&Handler->FetchMessageList);
+       if (Handler->MessageFieldList != NULL) {
+               free(Handler->MessageFieldList);
+       }
+       free(Handler);
+}
+
 void RegisterReadLoopHandlerset(
        int RoomType,
        GetParamsGetServerCall_func GetParamsGetServerCall,
@@ -1990,9 +2031,12 @@ void RegisterReadLoopHandlerset(
        load_msg_ptrs_detailheaders LH,
        LoadMsgFromServer_func LoadMsgFromServer,
        RenderView_or_Tail_func RenderView_or_Tail,
-       View_Cleanup_func ViewCleanup
+       View_Cleanup_func ViewCleanup,
+       const char **browseListFields
        )
 {
+       long count = 0;
+       long i = 0;
        RoomRenderer *Handler;
 
        Handler = (RoomRenderer*) malloc(sizeof(RoomRenderer));
@@ -2006,7 +2050,30 @@ void RegisterReadLoopHandlerset(
        Handler->ViewCleanup = ViewCleanup;
        Handler->LHParse = LH;
 
-       Put(ReadLoopHandler, IKEY(RoomType), Handler, NULL);
+       if (browseListFields != NULL) {
+               while (browseListFields[count] != NULL) {
+                       count ++;
+               }
+               Handler->HeaderCount = count;
+               Handler->MessageFieldList = (eMessageField*) malloc(sizeof(eMessageField) * count);
+               Handler->FetchMessageList = NewStrBufPlain(NULL, 5 * count + 4 + 5);
+               StrBufPlain(Handler->FetchMessageList, HKEY("time\n"));
+               for (i = 0; i < count; i++) {
+                       if (!GetFieldFromMnemonic(&Handler->MessageFieldList[i], browseListFields[i])) {
+                               fprintf(stderr, "Unknown message header: %s\n", browseListFields[i]);
+                               exit(1);
+                       }
+                       StrBufAppendBufPlain(Handler->FetchMessageList, browseListFields[i], 4, 0);
+                       StrBufAppendBufPlain(Handler->FetchMessageList, HKEY("\n"), 0);
+               }
+               StrBufAppendBufPlain(Handler->FetchMessageList, HKEY("000"), 0);
+       }
+       else {
+               Handler->FetchMessageList = NULL;
+               Handler->MessageFieldList = NULL;
+       }
+
+       Put(ReadLoopHandler, IKEY(RoomType), Handler, FreeReadLoopHandlerSet);
 }
 
 void 
@@ -2018,7 +2085,7 @@ InitModule_MSG
                           PRF_YESNO, 
                           NULL);
        RegisterPreference("signature", _("Use this signature:"), PRF_QP_STRING, NULL);
-       RegisterPreference("default_header_charset", 
+       RegisterPreference("default_header_charset",
                           _("Default character set for email headers:"), 
                           PRF_STRING, 
                           NULL);
@@ -2059,18 +2126,6 @@ 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);
-
-       return ;
 }
 
 void