* migrate more of the upload handling to strbuf
[citadel.git] / webcit / messages.c
index c6d1c6e75602ef741d3949d1318c5914b825dcdf..47f6f41c5eafb2e90c4efa992a7b57d1637f5268 100644 (file)
@@ -22,18 +22,11 @@ void jsonMessageListHdr(void);
 
 void display_enter(void);
 
-/*----------------------------------------------------------------------------*/
-
-
 typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf);
 
 typedef struct _MsgPartEvaluatorStruct {
        MsgPartEvaluatorFunc f;
-}MsgPartEvaluatorStruct;
-
-
-/*----------------------------------------------------------------------------*/
-
+} MsgPartEvaluatorStruct;
 
 int load_message(message_summary *Msg, 
                 StrBuf *FoundCharset,
@@ -49,11 +42,13 @@ int load_message(message_summary *Msg,
        int state=0;
        
        Buf = NewStrBuf();
-       lprintf(1, "-------------------MSG4 %ld|%s--------------\n", Msg->msgnum, ChrPtr(Msg->PartNum));
-       if (Msg->PartNum != NULL)
+       lprintf(9, "MSG4 %ld|%s\n", Msg->msgnum, ChrPtr(Msg->PartNum));
+       if (Msg->PartNum != NULL) {
                serv_printf("MSG4 %ld|%s", Msg->msgnum, ChrPtr(Msg->PartNum));
-       else
+       }
+       else {
                serv_printf("MSG4 %ld", Msg->msgnum);
+       }
 
        StrBuf_ServGetln(Buf);
        if (GetServerStatus(Buf, NULL) != 1) {
@@ -206,7 +201,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) 
+int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, const StrBuf *PartNum, const StrBuf **OutMime
 {
        StrBuf *Buf;
        StrBuf *FoundCharset;
@@ -267,7 +262,7 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
        memset(&SubTP, 0, sizeof(WCTemplputParams));
        SubTP.Filter.ContextType = CTX_MAILSUM;
        SubTP.Context = Msg;
-       DoTemplate(tmpl, tmpllen, Target, &SubTP);
+       *OutMime = DoTemplate(tmpl, tmpllen, Target, &SubTP);
 
        DestroyMessageSummary(Msg);
        FreeStrBuf(&FoundCharset);
@@ -276,38 +271,199 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
 }
 
 
+void
+HttpStatus(long CitadelStatus)
+{
+       long httpstatus = 502;
+       
+       switch (MAJORCODE(CitadelStatus))
+       {
+       case LISTING_FOLLOWS:
+       case CIT_OK:
+               httpstatus = 201;
+               break;
+       case ERROR:
+               switch (MINORCODE(CitadelStatus))
+               {
+               case INTERNAL_ERROR:
+                       httpstatus = 403;
+                       break;
+                       
+               case TOO_BIG:
+               case ILLEGAL_VALUE:
+               case HIGHER_ACCESS_REQUIRED:
+               case MAX_SESSIONS_EXCEEDED:
+               case RESOURCE_BUSY:
+               case RESOURCE_NOT_OPEN:
+               case NOT_HERE:
+               case INVALID_FLOOR_OPERATION:
+               case FILE_NOT_FOUND:
+               case ROOM_NOT_FOUND:
+                       httpstatus = 409;
+                       break;
 
+               case MESSAGE_NOT_FOUND:
+               case ALREADY_EXISTS:
+                       httpstatus = 403;
+                       break;
+
+               case NO_SUCH_SYSTEM:
+                       httpstatus = 502;
+                       break;
+
+               default:
+               case CMD_NOT_SUPPORTED:
+               case PASSWORD_REQUIRED:
+               case ALREADY_LOGGED_IN:
+               case USERNAME_REQUIRED:
+               case NOT_LOGGED_IN:
+               case SERVER_SHUTTING_DOWN:
+               case NO_SUCH_USER:
+               case ASYNC_GEXP:
+                       httpstatus = 502;
+                       break;
+               }
+               break;
+
+       default:
+       case BINARY_FOLLOWS:
+       case SEND_BINARY:
+       case START_CHAT_MODE:
+       case ASYNC_MSG:
+       case MORE_DATA:
+       case SEND_LISTING:
+               httpstatus = 502; /* aeh... whut? */
+               break;
+       }
 
 
+}
+
+/*
+ * Unadorned HTML output of an individual message, suitable
+ * for placing in a hidden iframe, for printing, or whatever
+ */
+void handle_one_message(void) 
+{
+       long CitStatus;
+       int CopyMessage = 0;
+       const StrBuf *Destination;
+       void *vLine;
+       const StrBuf *Mime;
+       long msgnum = 0L;
+       wcsession *WCC = WC;
+       const StrBuf *Tmpl;
+       StrBuf *CmdBuf = NULL;
+       const char *pMsg;
+
+
+       pMsg = strchr(ChrPtr(WCC->Hdr->HR.ReqLine), '/');
+       if (pMsg == NULL) {
+               HttpStatus(CitStatus);
+               return;
+       }
+
+       msgnum = atol(pMsg + 1);
+       StrBufCutAt(WCC->Hdr->HR.ReqLine, 0, pMsg);
+       gotoroom(WCC->Hdr->HR.ReqLine);
+       switch (WCC->Hdr->HR.eReqType)
+       {
+       case eGET:
+       case ePOST:
+               Tmpl = sbstr("template");
+               if (StrLength(Tmpl) > 0) 
+                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
+               else 
+                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
+               http_transmit_thing(ChrPtr(Mime), 0);
+               break;
+       case eDELETE:
+               CmdBuf = NewStrBuf ();
+               if (WCC->wc_is_trash) { /** Delete from Trash is a real delete */
+                       serv_printf("DELE %ld", msgnum);        
+               }
+               else {                  /** Otherwise move it to Trash */
+                       serv_printf("MOVE %ld|_TRASH_|0", msgnum);
+               }
+               StrBuf_ServGetln(CmdBuf);
+               FlushStrBuf(WCC->ImportantMsg);
+               StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
+               GetServerStatus(CmdBuf, &CitStatus);
+               HttpStatus(CitStatus);
+               break;
+       case eCOPY:
+               CopyMessage = 1;
+       case eMOVE:
+               if (GetHash(WCC->Hdr->HTTPHeaders, HKEY("DESTINATION"), &vLine) &&
+                   (vLine!=NULL)) {
+                       Destination = (StrBuf*) vLine;
+                       serv_printf("MOVE %ld|%s|%d", msgnum, ChrPtr(Destination), CopyMessage);
+                       StrBuf_ServGetln(CmdBuf);
+                       FlushStrBuf(WCC->ImportantMsg);
+                       StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
+                       GetServerStatus(CmdBuf, &CitStatus);
+                       HttpStatus(CitStatus);
+               }
+               else
+                       HttpStatus(500);
+               break;
+       default:
+               break;
+
+       }
+}
+
 
 /*
  * Unadorned HTML output of an individual message, suitable
  * for placing in a hidden iframe, for printing, or whatever
- *
- * msgnum_as_string == Message number, as a string instead of as a long int
  */
 void embed_message(void) {
+       const StrBuf *Mime;
        long msgnum = 0L;
        wcsession *WCC = WC;
-       const StrBuf *Tmpl = sbstr("template");
+       const StrBuf *Tmpl;
+       StrBuf *CmdBuf = NULL;
 
-       msgnum = StrTol(WCC->UrlFragment2);
-       if (StrLength(Tmpl) > 0) 
-               read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL);
-       else 
-               read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL);
+       msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
+       switch (WCC->Hdr->HR.eReqType)
+       {
+       case eGET:
+       case ePOST:
+               Tmpl = sbstr("template");
+               if (StrLength(Tmpl) > 0) 
+                       read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
+               else 
+                       read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
+               http_transmit_thing(ChrPtr(Mime), 0);
+               break;
+       case eDELETE:
+               CmdBuf = NewStrBuf ();
+               if (WCC->wc_is_trash) { /** Delete from Trash is a real delete */
+                       serv_printf("DELE %ld", msgnum);        
+               }
+               else {                  /** Otherwise move it to Trash */
+                       serv_printf("MOVE %ld|_TRASH_|0", msgnum);
+               }
+               StrBuf_ServGetln(CmdBuf);
+               FlushStrBuf(WCC->ImportantMsg);
+               StrBufAppendBuf(WCC->ImportantMsg, CmdBuf, 4);
+               break;
+       default:
+               break;
+
+       }
 }
 
 
 /*
  * Printable view of a message
- *
- * msgnum_as_string == Message number, as a string instead of as a long int
  */
 void print_message(void) {
        long msgnum = 0L;
+       const StrBuf *Mime;
 
-       msgnum = StrTol(WC->UrlFragment2);
+       msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
        output_headers(0, 0, 0, 0, 0, 0);
 
        hprintf("Content-type: text/html\r\n"
@@ -316,36 +472,35 @@ void print_message(void) {
 
        begin_burst();
 
-       read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL);
+       read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL, &Mime);
 
        wDumpContent(0);
 }
 
 /* 
  * Mobile browser view of message
- *
- * @param msg_num_as_string Message number as a string instead of as a long int 
  */
-void mobile_message_view(void) {
-  long msgnum = 0L;
-  msgnum = StrTol(WC->UrlFragment2);
-  output_headers(1, 0, 0, 0, 0, 1);
-  begin_burst();
-  do_template("msgcontrols", NULL);
-  read_message(WC->WBuf, HKEY("view_message"), msgnum, NULL);
-  wDumpContent(0);
+void mobile_message_view(void) 
+{
+       long msgnum = 0L;
+       const StrBuf *Mime;
+  
+       msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
+       output_headers(1, 0, 0, 0, 0, 1);
+       begin_burst();
+       do_template("msgcontrols", NULL);
+       read_message(WC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
+       wDumpContent(0);
 }
 
-/**
- * \brief Display a message's headers
- *
- * \param msgnum_as_string Message number, as a string instead of as a long int
+/*
+ * Display a message's headers
  */
 void display_headers(void) {
        long msgnum = 0L;
        char buf[1024];
 
-       msgnum = StrTol(WC->UrlFragment2);
+       msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
        output_headers(0, 0, 0, 0, 0, 0);
 
        hprintf("Content-type: text/plain\r\n"
@@ -426,7 +581,7 @@ int load_msg_ptrs(const char *servcmd, int with_headers)
        long len;
        int n;
        int skipit;
-       const char *Ptr;
+       const char *Ptr = NULL;
 
        if (WCC->summ != NULL) {
                DeleteHash(&WCC->summ);
@@ -454,6 +609,11 @@ int load_msg_ptrs(const char *servcmd, int with_headers)
 
                        Msg->msgnum = StrBufExtractNext_long(Buf, &Ptr, '|');
                        Msg->date = StrBufExtractNext_long(Buf, &Ptr, '|');
+
+                       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.
@@ -467,11 +627,11 @@ int load_msg_ptrs(const char *servcmd, int with_headers)
                                Msg->from = NewStrBufPlain(NULL, StrLength(Buf));
                                StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|');
                                if (StrLength(Buf2) != 0) {
-                                       /** Handle senders with RFC2047 encoding */
+                                       /* Handle senders with RFC2047 encoding */
                                        StrBuf_RFC822_to_Utf8(Msg->from, Buf2, WCC->DefaultCharset, FoundCharset);
                                }
                        
-                               /** Nodename */
+                               /* node name */
                                StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|');
                                if ((StrLength(Buf2) !=0 ) &&
                                    ( ((WCC->room_flags & QR_NETWORK)
@@ -482,9 +642,9 @@ int load_msg_ptrs(const char *servcmd, int with_headers)
                                        StrBufAppendBuf(Msg->from, Buf2, 0);
                                }
 
-                               /** Not used:
-                                   StrBufExtract_token(Msg->inetaddr, Buf, 4, '|');
-                               */
+                               /* Internet address (not used)
+                                *      StrBufExtract_token(Msg->inetaddr, Buf, 4, '|');
+                                */
                                StrBufSkip_NTokenS(Buf, &Ptr, '|', 1);
                                Msg->subj = NewStrBufPlain(NULL, StrLength(Buf));
                                StrBufExtract_NextToken(Buf2,  Buf, &Ptr, '|');
@@ -499,7 +659,6 @@ int load_msg_ptrs(const char *servcmd, int with_headers)
                                        }
                                }
 
-
                                if ((StrLength(Msg->from) > 25) && 
                                    (StrBuf_Utf8StrLen(Msg->from) > 25)) {
                                        StrBuf_Utf8StrCut(Msg->from, 23);
@@ -548,7 +707,7 @@ long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMes
        memset(&SubTP, 0, sizeof(WCTemplputParams));
        SubTP.Filter.ContextType = CTX_LONGVECTOR;
        SubTP.Context = &vector;
-       TmpBuf = NewStrBuf();
+       TmpBuf = NewStrBufPlain(NULL, SIZ);
        At = GetNewHashPos(WCC->summ, nMessages);
        nItems = GetCount(WCC->summ);
        ret = nMessages;
@@ -589,7 +748,7 @@ long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMes
                }
                done = !GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg);
                
-               /**
+               /*
                 * Bump these because although we're thinking in zero base, the user
                 * is a drooling idiot and is thinking in one base.
                 */
@@ -693,14 +852,15 @@ void readloop(long oper)
        int defaultsortorder = 0;
        WCTemplputParams SubTP;
        char *ab_name;
+       const StrBuf *Mime;
 
        if (havebstr("is_summary") && (1 == (ibstr("is_summary"))))
                WCC->wc_view = VIEW_MAILBOX;
 
        if (!WCC->is_ajax) {
-       output_headers(1, 1, 1, 0, 0, 0);
+               output_headers(1, 1, 1, 0, 0, 0);
        } else if (WCC->wc_view == VIEW_MAILBOX) {
-         jsonMessageListHdr();
+               jsonMessageListHdr();
        }
 
        switch (WCC->wc_view) {
@@ -823,13 +983,13 @@ void readloop(long oper)
 
        if (load_seen) load_seen_flags();
        
-        /**
-        * If we're to print s.th. above the message list...
+        /*
+        * Print any inforation above the message list...
         */
        switch (WCC->wc_view) {
        case VIEW_BBS:
-               BBViewToolBar = NewStrBuf();
-               MessageDropdown = NewStrBuf();
+               BBViewToolBar = NewStrBufPlain(NULL, SIZ);
+               MessageDropdown = NewStrBufPlain(NULL, SIZ);
 
                maxmsgs = DrawMessageDropdown(MessageDropdown, maxmsgs, startmsg, num_displayed);
                if (num_displayed < 0) {
@@ -856,13 +1016,11 @@ void readloop(long oper)
        svputlong("READLOOP:TOTALMSGS", nummsgs);
        svputlong("READLOOP:STARTMSG", startmsg);
        svputlong("WCVIEW", WCC->wc_view);
+
        /*
         * iterate over each message. if we need to load an attachment, do it here. 
         */
        if (WCC->wc_view == VIEW_MAILBOX) goto NO_MSG_LOOP;
-       /*
-        * iterate over each message. if we need to load an attachment, do it here. 
-        */
        at = GetNewHashPos(WCC->summ, 0);
        num_displayed = i = 0;
        while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
@@ -913,7 +1071,7 @@ void readloop(long oper)
        }
        DeleteHashPos(&at);
 
- NO_MSG_LOOP:  
+NO_MSG_LOOP:
        /*
         * Done iterating the message list. now tasks we want to do after.
         */
@@ -926,10 +1084,10 @@ void readloop(long oper)
                        /** if we do a split bbview in the future, begin messages div here */
                        
                        for (a=0; a<num_displayed; ++a) {
-                               read_message(WCC->WBuf, HKEY("view_message"), displayed_msgs[a], NULL);
+                               read_message(WCC->WBuf, HKEY("view_message"), displayed_msgs[a], NULL, &Mime);
                        }
                        
-                       /** if we do a split bbview in the future, end messages div here */
+                       /* if we do a split bbview in the future, end messages div here */
                        
                        free(displayed_msgs);
                        displayed_msgs = NULL;
@@ -960,9 +1118,9 @@ DONE:
                break;
        case VIEW_ADDRESSBOOK:
                if (is_singlecard)
-                       read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL);
+                       read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL, &Mime);
                else
-                       do_addrbook_view(addrbook, num_ab);     /** Render the address book */
+                       do_addrbook_view(addrbook, num_ab);     /* Render the address book */
                break;
        case VIEW_MAILBOX: 
        case VIEW_BBS:
@@ -1106,6 +1264,7 @@ void post_message(void)
        int is_anonymous = 0;
        const StrBuf *display_name = NULL;
        wcsession *WCC = WC;
+       StrBuf *Buf;
        
        if (havebstr("force_room")) {
                gotoroom(sbstr("force_room"));
@@ -1124,22 +1283,23 @@ void post_message(void)
                int n;
                char N[64];
 
-               lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length);
-               /** There's an attachment.  Save it to this struct... */
+               /* There's an attachment.  Save it to this struct... */
+               lprintf(9, "Client is uploading %d bytes\n", WCC->upload_length);
                att = malloc(sizeof(wc_mime_attachment));
                memset(att, 0, sizeof(wc_mime_attachment ));
                att->length = WCC->upload_length;
                att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1);
                att->FileName = NewStrBufPlain(WCC->upload_filename, -1);
                
-               
-               if (WCC->attachments == NULL)
+               if (WCC->attachments == NULL) {
                        WCC->attachments = NewHash(1, NULL);
+               }
+
                /* And add it to the list. */
                n = snprintf(N, sizeof N, "%d", GetCount(WCC->attachments) + 1);
                Put(WCC->attachments, N, n, att, DestroyMime);
 
-               /**
+               /*
                 * Mozilla sends a simple filename, which is what we want,
                 * but Satan's Browser sends an entire pathname.  Reduce
                 * the path to just a filename if we need to.
@@ -1153,14 +1313,13 @@ void post_message(void)
                        StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
                }
 
-               /**
+               /*
                 * Transfer control of this memory from the upload struct
                 * to the attachment struct.
                 */
-               att->Data = NewStrBufPlain(WCC->upload, WCC->upload_length);
-               free(WCC->upload);
-               WCC->upload_length = 0;
+               att->Data = WCC->upload;
                WCC->upload = NULL;
+               WCC->upload_length = 0;
                display_enter();
                return;
        }
@@ -1184,11 +1343,31 @@ void post_message(void)
                const StrBuf *my_email_addr = NULL;
                StrBuf *CmdBuf = NULL;
                StrBuf *references = NULL;
+               int save_to_drafts;
+
+               save_to_drafts = havebstr("save_button");
+               Buf = NewStrBuf();
+
+               if (save_to_drafts) {
+                       /* temporarily change to the drafts room */
+                       serv_puts("GOTO _DRAFTS_");
+                       StrBuf_ServGetln(Buf);
+                       if (GetServerStatus(Buf, NULL) != 2) {
+                               /* You probably don't even have a dumb Drafts folder */
+                               StrBufCutLeft(Buf, 4);
+                               lprintf(9, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
+                               StrBufAppendBufPlain(WCC->ImportantMsg, _("Saved to Drafts failed: "), -1, 0);
+                               StrBufAppendBuf(WCC->ImportantMsg, Buf, 0);
+                               display_enter();
+                               FreeStrBuf(&Buf);
+                               return;
+                       }
+               }
 
                if (havebstr("references"))
                {
                        const StrBuf *ref = sbstr("references");
-                       references = NewStrBufPlain(ChrPtr(ref), StrLength(ref));
+                       references = NewStrBufDup(ref);
                        if (*ChrPtr(references) == '|') {       /* remove leading '|' if present */
                                StrBufCutLeft(references, 1);
                        }
@@ -1222,59 +1401,83 @@ void post_message(void)
 
                StrBufPrintf(CmdBuf, 
                             CMD,
-                            ChrPtr(Recp),
+                            save_to_drafts?"":ChrPtr(Recp),
                             is_anonymous,
                             ChrPtr(encoded_subject),
                             ChrPtr(display_name),
-                            ChrPtr(Cc),
-                            ChrPtr(Bcc),
+                            save_to_drafts?"":ChrPtr(Cc),
+                            save_to_drafts?"":ChrPtr(Bcc),
                             ChrPtr(Wikipage),
                             ChrPtr(my_email_addr),
                             ChrPtr(references));
                FreeStrBuf(&references);
+               FreeStrBuf(&encoded_subject);
 
                lprintf(9, "%s\n", ChrPtr(CmdBuf));
                serv_puts(ChrPtr(CmdBuf));
-               serv_getln(buf, sizeof buf);
                FreeStrBuf(&CmdBuf);
-               FreeStrBuf(&encoded_subject);
-               if (buf[0] == '4') {
+
+               StrBuf_ServGetln(Buf);
+               if (GetServerStatus(Buf, NULL) == 4) {
+                       if (save_to_drafts) {
+                               if (  (havebstr("recp"))
+                                   || (havebstr("cc"  ))
+                                   || (havebstr("bcc" )) ) {
+                                       /* save recipient headers or room to post to */
+                                       serv_printf("To: %s", ChrPtr(Recp));
+                                       serv_printf("Cc: %s", ChrPtr(Cc));
+                                       serv_printf("Bcc: %s", ChrPtr(Bcc));
+                               } else {
+                                       serv_printf("X-Citadel-Room: %s", ChrPtr(WC->wc_roomname));
+                               }
+                       }
                        post_mime_to_server();
-                       if (  (havebstr("recp"))
+                       if (save_to_drafts) {
+                               StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been saved to Drafts.\n"), -1, 0);
+                               gotoroom(WCC->wc_roomname);
+                               display_enter();
+                               FreeStrBuf(&Buf);
+                               return;
+                       } else if (  (havebstr("recp"))
                           || (havebstr("cc"  ))
                           || (havebstr("bcc" ))
                        ) {
-                               sprintf(WCC->ImportantMessage, _("Message has been sent.\n"));
+                               StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been sent.\n"), -1, 0);
                        }
                        else {
-                               sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
+                               StrBufAppendBufPlain(WCC->ImportantMsg, _("Message has been posted.\n"), -1, 0);
                        }
                        dont_post = lbstr("postseq");
                } else {
-                       lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, buf);
-                       sprintf(WC->ImportantMessage, "%s", &buf[4]);
+                       StrBufCutLeft(Buf, 4);
+
+                       lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, ChrPtr(Buf));
+                       StrBufAppendBuf(WCC->ImportantMsg, Buf, 0);
+                       if (save_to_drafts) gotoroom(WCC->wc_roomname);
                        display_enter();
+                       FreeStrBuf(&Buf);
                        return;
                }
+               FreeStrBuf(&Buf);
        }
 
        DeleteHash(&WCC->attachments);
 
-       /**
+       /*
         *  We may have been supplied with instructions regarding the location
         *  to which we must return after posting.  If found, go there.
         */
        if (havebstr("return_to")) {
                http_redirect(bstr("return_to"));
        }
-       /**
+       /*
         *  If we were editing a page in a wiki room, go to that page now.
         */
        else if (havebstr("wikipage")) {
                snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
                http_redirect(buf);
        }
-       /**
+       /*
         *  Otherwise, just go to the "read messages" loop.
         */
        else {
@@ -1285,8 +1488,8 @@ void post_message(void)
 
 
 
-/**
- * \brief display the message entry screen
+/*
+ * display the message entry screen
  */
 void display_enter(void)
 {
@@ -1311,14 +1514,14 @@ void display_enter(void)
                is_anonymous = 1;
        }
 
-       /** First test to see whether this is a room that requires recipients to be entered */
+       /* First test to see whether this is a room that requires recipients to be entered */
        serv_puts("ENT0 0");
        serv_getln(buf, sizeof buf);
 
-       if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
+       if (!strncmp(buf, "570", 3)) {          /* 570 means that we need a recipient here */
                recipient_required = 1;
        }
-       else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
+       else if (buf[0] != '2') {               /* Any other error means that we cannot continue */
                sprintf(WCC->ImportantMessage, "%s", &buf[4]);
                readloop(readnew);
                return;
@@ -1394,15 +1597,15 @@ void display_enter(void)
                serv_getln(buf, sizeof buf);
                FreeStrBuf(&CmdBuf);
 
-               if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
+               if (!strncmp(buf, "570", 3)) {  /* 570 means we have an invalid recipient listed */
                        if (havebstr("recp") && 
                            havebstr("cc"  ) && 
                            havebstr("bcc" )) {
                                recipient_bad = 1;
                        }
                }
-               else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
-                       wprintf("<em>%s</em><br />\n", &buf[4]);/*TODO -> important message */
+               else if (buf[0] != '2') {       /* Any other error means that we cannot continue */
+                       wprintf("<em>%s</em><br />\n", &buf[4]);        /* TODO -> important message */
                        return;
                }
        }
@@ -1417,8 +1620,8 @@ void display_enter(void)
        return;
 }
 
-/**
- * \brief delete a message
+/*
+ * delete a message
  */
 void delete_msg(void)
 {
@@ -1427,22 +1630,21 @@ void delete_msg(void)
 
        msgid = lbstr("msgid");
 
-       if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
+       if (WC->wc_is_trash) {  /* Delete from Trash is a real delete */
                serv_printf("DELE %ld", msgid); 
        }
-       else {                  /** Otherwise move it to Trash */
+       else {                  /* Otherwise move it to Trash */
                serv_printf("MOVE %ld|_TRASH_|0", msgid);
        }
 
        serv_getln(buf, sizeof buf);
        sprintf(WC->ImportantMessage, "%s", &buf[4]);
-
        readloop(readnew);
 }
 
 
-/**
- * \brief move a message to another folder
+/*
+ * move a message to another room
  */
 void move_msg(void)
 {
@@ -1464,9 +1666,6 @@ void move_msg(void)
 }
 
 
-
-
-
 /*
  * Confirm move of a message
  */
@@ -1568,14 +1767,19 @@ void postpart(StrBuf *partnum, StrBuf *filename, int force_download)
  */
 void mimepart(int force_download)
 {
+       long msgnum;
+       StrBuf *att;
        wcsession *WCC = WC;
        StrBuf *Buf;
        off_t bytes;
        StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream"));
        const char *CT;
 
-       Buf = NewStrBuf();
-       serv_printf("OPNA %s|%s", ChrPtr(WCC->UrlFragment2), ChrPtr(WCC->UrlFragment3));
+       att = Buf = NewStrBuf();
+       msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
+       StrBufExtract_token(att, WCC->Hdr->HR.ReqLine, 1, '/');
+
+       serv_printf("OPNA %ld|%s", msgnum, ChrPtr(att));
        StrBuf_ServGetln(Buf);
        if (GetServerStatus(Buf, NULL) == 2) {
                StrBufCutLeft(Buf, 4);
@@ -1591,7 +1795,8 @@ void mimepart(int force_download)
 
                if (!force_download) {
                        if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
-                               CT = GuessMimeByFilename(SKEY(WCC->UrlFragment4));
+                               StrBufExtract_token(Buf, WCC->Hdr->HR.ReqLine, 2, '/');
+                               CT = GuessMimeByFilename(SKEY(Buf));
                        }
                        if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
                                CT = GuessMimeType(SKEY(WCC->WBuf));
@@ -1626,7 +1831,8 @@ StrBuf *load_mimepart(long msgnum, char *partnum)
        if (GetServerStatus(Buf, NULL) == 6) {
                StrBufCutLeft(Buf, 4);
                bytes = StrBufExtract_long(Buf, 0, '|');
-
+               FreeStrBuf(&Buf);
+               Buf = NewStrBuf();
                StrBuf_ServGetBLOBBuffered(Buf, bytes);
                return(Buf);
        }
@@ -1643,7 +1849,7 @@ void MimeLoadData(wc_mime_attachment *Mime)
 {
        StrBuf *Buf;
        off_t bytes;
-/* TODO: is there a chance the contenttype is different  to the one we know?    */
+       /* TODO: is there a chance the content type is different from the one we know? */
        serv_printf("DLAT %ld|%s", Mime->msgnum, ChrPtr(Mime->PartNum));
        Buf = NewStrBuf();
        StrBuf_ServGetln(Buf);
@@ -1673,15 +1879,29 @@ void download_mimepart(void) {
 }
 
 void view_postpart(void) {
-       postpart(WC->UrlFragment2,
-                WC->UrlFragment3,
-                0);
+       StrBuf *filename = NewStrBuf();
+       StrBuf *partnum = NewStrBuf();
+
+       StrBufExtract_token(partnum, WC->Hdr->HR.ReqLine, 0, '/');
+       StrBufExtract_token(filename, WC->Hdr->HR.ReqLine, 1, '/');
+
+       postpart(partnum, filename, 0);
+
+       FreeStrBuf(&filename);
+       FreeStrBuf(&partnum);
 }
 
 void download_postpart(void) {
-       postpart(WC->UrlFragment2,
-                WC->UrlFragment3,
-                1);
+       StrBuf *filename = NewStrBuf();
+       StrBuf *partnum = NewStrBuf();
+
+       StrBufExtract_token(partnum, WC->Hdr->HR.ReqLine, 0, '/');
+       StrBufExtract_token(filename, WC->Hdr->HR.ReqLine, 1, '/');
+
+       postpart(partnum, filename, 1);
+
+       FreeStrBuf(&filename);
+       FreeStrBuf(&partnum);
 }
 
 void h_readnew(void) { readloop(readnew);}
@@ -1693,28 +1913,30 @@ void h_do_search(void) { readloop(do_search);}
 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();
+       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();
 }
+
 /* Spit out the new summary view. This is basically a static page, so clients can cache the layout, all the dirty work is javascript :) */
 void new_summary_view(void) {
-  begin_burst();
-  DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
-  DoTemplate(HKEY("trailing"),NULL,&NoCtx);
-  end_burst();
+       begin_burst();
+       DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
+       DoTemplate(HKEY("trailing"),NULL,&NoCtx);
+       end_burst();
 }
-/** Output message list in JSON-format */
+
+/* Output message list in JSON format */
 void jsonMessageList(void) {
-  const StrBuf *room = sbstr("room");
-  long oper = (havebstr("query")) ? do_search : readnew;
-  WC->is_ajax = 1; 
-  gotoroom(room);
-  readloop(oper);
-  WC->is_ajax = 0;
+       const StrBuf *room = sbstr("room");
+       long oper = (havebstr("query")) ? do_search : readnew;
+       WC->is_ajax = 1; 
+       gotoroom(room);
+       readloop(oper);
+       WC->is_ajax = 0;
 }
 
 void 
@@ -1751,7 +1973,8 @@ InitModule_MSG
        WebcitAddUrlHandler(HKEY("move_msg"), move_msg, 0);
        WebcitAddUrlHandler(HKEY("delete_msg"), delete_msg, 0);
        WebcitAddUrlHandler(HKEY("confirm_move_msg"), confirm_move_msg, 0);
-       WebcitAddUrlHandler(HKEY("msg"), embed_message, NEED_URL|AJAX);
+       WebcitAddUrlHandler(HKEY("msg"), embed_message, NEED_URL);
+       WebcitAddUrlHandler(HKEY("message"), handle_one_message, NEED_URL|XHTTP_COMMANDS|COOKIEUNNEEDED|FORCE_SESSIONCLOSE);
        WebcitAddUrlHandler(HKEY("printmsg"), print_message, NEED_URL);
        WebcitAddUrlHandler(HKEY("mobilemsg"), mobile_message_view, NEED_URL);
        WebcitAddUrlHandler(HKEY("msgheaders"), display_headers, NEED_URL);
@@ -1765,3 +1988,10 @@ InitModule_MSG
        WebcitAddUrlHandler(HKEY("roommsgs"), jsonMessageList,0);
        return ;
 }
+
+void
+SessionDetachModule_MSG
+(wcsession *sess)
+{
+       DeleteHash(&sess->summ);
+}