* migrate more of the upload handling to strbuf
[citadel.git] / webcit / messages.c
index eb3f945709baba14f40e41896effd1f9f76313a9..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) {
@@ -276,13 +271,10 @@ int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, co
 }
 
 
-
 void
 HttpStatus(long CitadelStatus)
 {
        long httpstatus = 502;
-       long MajorStat = MAJORCODE(CitadelStatus);
-       long MinorStat = MINORCODE(CitadelStatus);
        
        switch (MAJORCODE(CitadelStatus))
        {
@@ -350,8 +342,6 @@ HttpStatus(long CitadelStatus)
 /*
  * 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 handle_one_message(void) 
 {
@@ -422,11 +412,11 @@ void handle_one_message(void)
 
        }
 }
+
+
 /*
  * 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;
@@ -468,8 +458,6 @@ void embed_message(void) {
 
 /*
  * 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;
@@ -491,8 +479,6 @@ void print_message(void) {
 
 /* 
  * 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) 
 {
@@ -507,10 +493,8 @@ void mobile_message_view(void)
        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;
@@ -625,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.
@@ -638,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)
@@ -653,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, '|');
@@ -670,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);
@@ -719,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;
@@ -760,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.
                 */
@@ -995,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) {
@@ -1028,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)) {
@@ -1085,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.
         */
@@ -1101,7 +1087,7 @@ void readloop(long oper)
                                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;
@@ -1134,7 +1120,7 @@ DONE:
                if (is_singlecard)
                        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:
@@ -1278,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"));
@@ -1296,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.
@@ -1325,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;
        }
@@ -1356,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);
                        }
@@ -1394,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 {
@@ -1457,8 +1488,8 @@ void post_message(void)
 
 
 
-/**
- * \brief display the message entry screen
+/*
+ * display the message entry screen
  */
 void display_enter(void)
 {
@@ -1483,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;
@@ -1566,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;
                }
        }
@@ -1589,8 +1620,8 @@ void display_enter(void)
        return;
 }
 
-/**
- * \brief delete a message
+/*
+ * delete a message
  */
 void delete_msg(void)
 {
@@ -1599,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)
 {
@@ -1636,9 +1666,6 @@ void move_msg(void)
 }
 
 
-
-
-
 /*
  * Confirm move of a message
  */
@@ -1740,18 +1767,19 @@ void postpart(StrBuf *partnum, StrBuf *filename, int force_download)
  */
 void mimepart(int force_download)
 {
-       long msgnum, att;
+       long msgnum;
+       StrBuf *att;
        wcsession *WCC = WC;
        StrBuf *Buf;
        off_t bytes;
        StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream"));
        const char *CT;
 
+       att = Buf = NewStrBuf();
        msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
-       att = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 1, '/');
+       StrBufExtract_token(att, WCC->Hdr->HR.ReqLine, 1, '/');
 
-       Buf = NewStrBuf();
-       serv_printf("OPNA %ld|%ld", msgnum, att);
+       serv_printf("OPNA %ld|%s", msgnum, ChrPtr(att));
        StrBuf_ServGetln(Buf);
        if (GetServerStatus(Buf, NULL) == 2) {
                StrBufCutLeft(Buf, 4);
@@ -1821,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);
@@ -1885,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