Back to index

webcit  8.12-dfsg
messages.c
Go to the documentation of this file.
00001 /*
00002  * Functions which deal with the fetching and displaying of messages.
00003  *
00004  * Copyright (c) 1996-2012 by the citadel.org team
00005  *
00006  * This program is open source software.  You can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License, version 3.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "webcit.h"
00016 #include "webserver.h"
00017 #include "dav.h"
00018 #include "calendar.h"
00019 
00020 HashList *MsgHeaderHandler = NULL;
00021 HashList *MsgEvaluators = NULL;
00022 HashList *MimeRenderHandler = NULL;
00023 HashList *ReadLoopHandler = NULL;
00024 int dbg_analyze_msg = 0;
00025 
00026 #define SUBJ_COL_WIDTH_PCT         50     /* Mailbox view column width */
00027 #define SENDER_COL_WIDTH_PCT              30     /* Mailbox view column width */
00028 #define DATE_PLUS_BUTTONS_WIDTH_PCT       20     /* Mailbox view column width */
00029 
00030 void jsonMessageListHdr(void);
00031 
00032 void display_enter(void);
00033 
00034 typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf);
00035 
00036 typedef struct _MsgPartEvaluatorStruct {
00037        MsgPartEvaluatorFunc f;
00038 } MsgPartEvaluatorStruct;
00039 
00040 void fixview()
00041 {
00042        /* workaround for json listview; its not useable directly */
00043        if (WC->CurRoom.view == VIEW_JSON_LIST) {
00044               StrBuf *View = NewStrBuf();
00045               StrBufPrintf(View, "%d", VIEW_MAILBOX);
00046               putbstr("view", View);;
00047        }
00048 }
00049 int load_message(message_summary *Msg, 
00050                StrBuf *FoundCharset,
00051                StrBuf **Error)
00052 {
00053        StrBuf *Buf;
00054        StrBuf *HdrToken;
00055        headereval *Hdr;
00056        void *vHdr;
00057        char buf[SIZ];
00058        int Done = 0;
00059        int state=0;
00060        
00061        Buf = NewStrBuf();
00062        if (Msg->PartNum != NULL) {
00063               serv_printf("MSG4 %ld|%s", Msg->msgnum, ChrPtr(Msg->PartNum));
00064        }
00065        else {
00066               serv_printf("MSG4 %ld", Msg->msgnum);
00067        }
00068 
00069        StrBuf_ServGetln(Buf);
00070        if (GetServerStatus(Buf, NULL) != 1) {
00071               *Error = NewStrBuf();
00072               StrBufAppendPrintf(*Error, "<strong>");
00073               StrBufAppendPrintf(*Error, _("ERROR:"));
00074               StrBufAppendPrintf(*Error, "</strong> %s<br>\n", &buf[4]);
00075               FreeStrBuf(&Buf);
00076               return 0;
00077        }
00078 
00079        /* begin everythingamundo table */
00080        HdrToken = NewStrBuf();
00081        while (!Done && StrBuf_ServGetln(Buf)>=0) {
00082               if ( (StrLength(Buf)==3) && 
00083                   !strcmp(ChrPtr(Buf), "000")) 
00084               {
00085                      Done = 1;
00086                      if (state < 2) {
00087                             if (Msg->MsgBody->Data == NULL)
00088                                    Msg->MsgBody->Data = NewStrBuf();
00089                             Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/html"));
00090                             StrBufAppendPrintf(Msg->MsgBody->Data, "<div><i>");
00091                             StrBufAppendPrintf(Msg->MsgBody->Data, _("Empty message"));
00092                             StrBufAppendPrintf(Msg->MsgBody->Data, "</i><br><br>\n");
00093                             StrBufAppendPrintf(Msg->MsgBody->Data, "</div>\n");
00094                      }
00095                      break;
00096               }
00097               switch (state) {
00098               case 0:/* Citadel Message Headers */
00099                      if (StrLength(Buf) == 0) {
00100                             state ++;
00101                             break;
00102                      }
00103                      StrBufExtract_token(HdrToken, Buf, 0, '=');
00104                      StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
00105                      
00106                      /* look up one of the examine_* functions to parse the content */
00107                      if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
00108                          (vHdr != NULL)) {
00109                             Hdr = (headereval*)vHdr;
00110                             Hdr->evaluator(Msg, Buf, FoundCharset);
00111                             if (Hdr->Type == 1) {
00112                                    state++;
00113                             }
00114                      }/* TODO: 
00115                      else LogError(Target, 
00116                                   __FUNCTION__,  
00117                                   "don't know how to handle message header[%s]\n", 
00118                                   ChrPtr(HdrToken));
00119                       */
00120                      break;
00121               case 1:/* Message Mime Header */
00122                      if (StrLength(Buf) == 0) {
00123                             state++;
00124                             if (Msg->MsgBody->ContentType == NULL)
00125                                    /* end of header or no header? */
00126                                    Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/plain"));
00127                              /* usual end of mime header */
00128                      }
00129                      else
00130                      {
00131                             StrBufExtract_token(HdrToken, Buf, 0, ':');
00132                             if (StrLength(HdrToken) > 0) {
00133                                    StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
00134                                    /* the examine*'s know how to do with mime headers too... */
00135                                    if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
00136                                        (vHdr != NULL)) {
00137                                           Hdr = (headereval*)vHdr;
00138                                           Hdr->evaluator(Msg, Buf, FoundCharset);
00139                                    }
00140                                    break;
00141                             }
00142                      }
00143               case 2: /* Message Body */
00144                      
00145                      if (Msg->MsgBody->size_known > 0) {
00146                             StrBuf_ServGetBLOBBuffered(Msg->MsgBody->Data, Msg->MsgBody->length);
00147                             state ++;
00148                             /*/ todo: check next line, if not 000, append following lines */
00149                      }
00150                      else if (1){
00151                             if (StrLength(Msg->MsgBody->Data) > 0)
00152                                    StrBufAppendBufPlain(Msg->MsgBody->Data, "\n", 1, 0);
00153                             StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0);
00154                      }
00155                      break;
00156               case 3:
00157                      StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0);
00158                      break;
00159               }
00160        }
00161 
00162        if (Msg->AllAttach == NULL)
00163               Msg->AllAttach = NewHash(1,NULL);
00164        /* now we put the body mimepart we read above into the mimelist */
00165        Put(Msg->AllAttach, SKEY(Msg->MsgBody->PartNum), Msg->MsgBody, DestroyMime);
00166        
00167        FreeStrBuf(&Buf);
00168        FreeStrBuf(&HdrToken);
00169        return 1;
00170 }
00171 
00172 
00173 
00174 /*
00175  * I wanna SEE that message!
00176  *
00177  * msgnum            Message number to display
00178  * printable_view    Nonzero to display a printable view
00179  * section           Optional for encapsulated message/rfc822 submessage
00180  */
00181 int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, const StrBuf *PartNum, const StrBuf **OutMime) 
00182 {
00183        StrBuf *Buf;
00184        StrBuf *FoundCharset;
00185        HashPos  *it;
00186        void *vMime;
00187        message_summary *Msg = NULL;
00188        void *vHdr;
00189        long len;
00190        const char *Key;
00191        WCTemplputParams SubTP;
00192        StrBuf *Error = NULL;
00193 
00194        Buf = NewStrBuf();
00195        FoundCharset = NewStrBuf();
00196        Msg = (message_summary *)malloc(sizeof(message_summary));
00197        memset(Msg, 0, sizeof(message_summary));
00198        Msg->msgnum = msgnum;
00199        Msg->PartNum = PartNum;
00200        Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
00201        memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
00202        Msg->MsgBody->msgnum = msgnum;
00203 
00204        if (!load_message(Msg, FoundCharset, &Error)) {
00205               StrBufAppendBuf(Target, Error, 0);
00206               FreeStrBuf(&Error);
00207        }
00208 
00209        /* Extract just the content-type (omit attributes such as "charset") */
00210        StrBufExtract_token(Buf, Msg->MsgBody->ContentType, 0, ';');
00211        StrBufTrim(Buf);
00212        StrBufLowerCase(Buf);
00213 
00214        /* Locate a renderer capable of converting this MIME part into HTML */
00215        if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) &&
00216            (vHdr != NULL)) {
00217               RenderMimeFuncStruct *Render;
00218               Render = (RenderMimeFuncStruct*)vHdr;
00219               Render->f(Msg->MsgBody, NULL, FoundCharset);
00220        }
00221 
00222        if (StrLength(Msg->reply_references)> 0) {
00223               /* Trim down excessively long lists of thread references.  We eliminate the
00224                * second one in the list so that the thread root remains intact.
00225                */
00226               int rrtok = num_tokens(ChrPtr(Msg->reply_references), '|');
00227               int rrlen = StrLength(Msg->reply_references);
00228               if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
00229                      StrBufRemove_token(Msg->reply_references, 1, '|');
00230               }
00231        }
00232 
00233        /* now check if we need to translate some mimeparts, and remove the duplicate */
00234        it = GetNewHashPos(Msg->AllAttach, 0);
00235        while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
00236               (vMime != NULL)) {
00237               wc_mime_attachment *Mime = (wc_mime_attachment*) vMime;
00238               evaluate_mime_part(Msg, Mime);
00239        }
00240        DeleteHashPos(&it);
00241        memset(&SubTP, 0, sizeof(WCTemplputParams));
00242        SubTP.Filter.ContextType = CTX_MAILSUM;
00243        SubTP.Context = Msg;
00244        *OutMime = DoTemplate(tmpl, tmpllen, Target, &SubTP);
00245 
00246        DestroyMessageSummary(Msg);
00247        FreeStrBuf(&FoundCharset);
00248        FreeStrBuf(&Buf);
00249        return 1;
00250 }
00251 
00252 
00253 long
00254 HttpStatus(long CitadelStatus)
00255 {
00256        long httpstatus = 502;
00257        
00258        switch (MAJORCODE(CitadelStatus))
00259        {
00260        case LISTING_FOLLOWS:
00261        case CIT_OK:
00262               httpstatus = 201;
00263               break;
00264        case ERROR:
00265               switch (MINORCODE(CitadelStatus))
00266               {
00267               case INTERNAL_ERROR:
00268                      httpstatus = 403;
00269                      break;
00270                      
00271               case TOO_BIG:
00272               case ILLEGAL_VALUE:
00273               case HIGHER_ACCESS_REQUIRED:
00274               case MAX_SESSIONS_EXCEEDED:
00275               case RESOURCE_BUSY:
00276               case RESOURCE_NOT_OPEN:
00277               case NOT_HERE:
00278               case INVALID_FLOOR_OPERATION:
00279               case FILE_NOT_FOUND:
00280               case ROOM_NOT_FOUND:
00281                      httpstatus = 409;
00282                      break;
00283 
00284               case MESSAGE_NOT_FOUND:
00285               case ALREADY_EXISTS:
00286                      httpstatus = 403;
00287                      break;
00288 
00289               case NO_SUCH_SYSTEM:
00290                      httpstatus = 502;
00291                      break;
00292 
00293               default:
00294               case CMD_NOT_SUPPORTED:
00295               case PASSWORD_REQUIRED:
00296               case ALREADY_LOGGED_IN:
00297               case USERNAME_REQUIRED:
00298               case NOT_LOGGED_IN:
00299               case SERVER_SHUTTING_DOWN:
00300               case NO_SUCH_USER:
00301               case ASYNC_GEXP:
00302                      httpstatus = 502;
00303                      break;
00304               }
00305               break;
00306 
00307        default:
00308        case BINARY_FOLLOWS:
00309        case SEND_BINARY:
00310        case START_CHAT_MODE:
00311        case ASYNC_MSG:
00312        case MORE_DATA:
00313        case SEND_LISTING:
00314               httpstatus = 502; /* aeh... whut? */
00315               break;
00316        }
00317 
00318        return httpstatus;
00319 }
00320 
00321 /*
00322  * Unadorned HTML output of an individual message, suitable
00323  * for placing in a hidden iframe, for printing, or whatever
00324  */
00325 void handle_one_message(void) 
00326 {
00327        long CitStatus;
00328        int CopyMessage = 0;
00329        const StrBuf *Destination;
00330        void *vLine;
00331        const StrBuf *Mime;
00332        long msgnum = 0L;
00333        wcsession *WCC = WC;
00334        const StrBuf *Tmpl;
00335        StrBuf *CmdBuf = NULL;
00336        const char *pMsg;
00337 
00338 
00339        pMsg = strchr(ChrPtr(WCC->Hdr->HR.ReqLine), '/');
00340        if (pMsg == NULL) {
00341               HttpStatus(CitStatus);
00342               return;
00343        }
00344 
00345        msgnum = atol(pMsg + 1);
00346        StrBufCutAt(WCC->Hdr->HR.ReqLine, 0, pMsg);
00347        gotoroom(WCC->Hdr->HR.ReqLine);
00348        switch (WCC->Hdr->HR.eReqType)
00349        {
00350        case eGET:
00351        case ePOST:
00352               Tmpl = sbstr("template");
00353               if (StrLength(Tmpl) > 0) 
00354                      read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
00355               else 
00356                      read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
00357               http_transmit_thing(ChrPtr(Mime), 0);
00358               break;
00359        case eDELETE:
00360               CmdBuf = NewStrBuf ();
00361               if ((WCC->CurRoom.RAFlags & UA_ISTRASH) != 0) {  /* Delete from Trash is a real delete */
00362                      serv_printf("DELE %ld", msgnum);   
00363               }
00364               else {               /* Otherwise move it to Trash */
00365                      serv_printf("MOVE %ld|_TRASH_|0", msgnum);
00366               }
00367               StrBuf_ServGetln(CmdBuf);
00368               GetServerStatusMsg(CmdBuf, &CitStatus, 1, 0);
00369               HttpStatus(CitStatus);
00370               break;
00371        case eCOPY:
00372               CopyMessage = 1;
00373        case eMOVE:
00374               if (GetHash(WCC->Hdr->HTTPHeaders, HKEY("DESTINATION"), &vLine) &&
00375                   (vLine!=NULL)) {
00376                      Destination = (StrBuf*) vLine;
00377                      serv_printf("MOVE %ld|%s|%d", msgnum, ChrPtr(Destination), CopyMessage);
00378                      StrBuf_ServGetln(CmdBuf);
00379                      GetServerStatusMsg(CmdBuf, NULL, 1, 0);
00380                      GetServerStatus(CmdBuf, &CitStatus);
00381                      HttpStatus(CitStatus);
00382               }
00383               else
00384                      HttpStatus(500);
00385               break;
00386        default:
00387               break;
00388 
00389        }
00390 }
00391 
00392 
00393 /*
00394  * Unadorned HTML output of an individual message, suitable
00395  * for placing in a hidden iframe, for printing, or whatever
00396  */
00397 void embed_message(void) {
00398        const StrBuf *Mime;
00399        long msgnum = 0L;
00400        wcsession *WCC = WC;
00401        const StrBuf *Tmpl;
00402        StrBuf *CmdBuf = NULL;
00403 
00404        msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
00405        if (msgnum <= 0) return;
00406 
00407        switch (WCC->Hdr->HR.eReqType)
00408        {
00409        case eGET:
00410        case ePOST:
00411               Tmpl = sbstr("template");
00412               if (StrLength(Tmpl) > 0) 
00413                      read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL, &Mime);
00414               else 
00415                      read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL, &Mime);
00416               http_transmit_thing(ChrPtr(Mime), 0);
00417               break;
00418        case eDELETE:
00419               CmdBuf = NewStrBuf ();
00420               if ((WCC->CurRoom.RAFlags & UA_ISTRASH) != 0) {  /* Delete from Trash is a real delete */
00421                      serv_printf("DELE %ld", msgnum);   
00422               }
00423               else {               /* Otherwise move it to Trash */
00424                      serv_printf("MOVE %ld|_TRASH_|0", msgnum);
00425               }
00426               StrBuf_ServGetln(CmdBuf);
00427               GetServerStatusMsg(CmdBuf, NULL, 1, 0);
00428               break;
00429        default:
00430               break;
00431 
00432        }
00433 }
00434 
00435 
00436 /*
00437  * Printable view of a message
00438  */
00439 void print_message(void) {
00440        long msgnum = 0L;
00441        const StrBuf *Mime;
00442 
00443        msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
00444        output_headers(0, 0, 0, 0, 0, 0);
00445 
00446        hprintf("Content-type: text/html\r\n"
00447               "Server: " PACKAGE_STRING "\r\n"
00448               "Connection: close\r\n");
00449 
00450        begin_burst();
00451 
00452        read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL, &Mime);
00453 
00454        wDumpContent(0);
00455 }
00456 
00457 /*
00458  * Display a message's headers
00459  */
00460 void display_headers(void) {
00461        long msgnum = 0L;
00462        char buf[1024];
00463 
00464        msgnum = StrBufExtract_long(WC->Hdr->HR.ReqLine, 0, '/');
00465        output_headers(0, 0, 0, 0, 0, 0);
00466 
00467        hprintf("Content-type: text/plain\r\n"
00468               "Server: %s\r\n"
00469               "Connection: close\r\n",
00470               PACKAGE_STRING);
00471        begin_burst();
00472 
00473        serv_printf("MSG2 %ld|3", msgnum);
00474        serv_getln(buf, sizeof buf);
00475        if (buf[0] == '1') {
00476               while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
00477                      wc_printf("%s\n", buf);
00478               }
00479        }
00480 
00481        wDumpContent(0);
00482 }
00483 
00484 
00485 message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) 
00486 {
00487        void                 *vEval;
00488        MsgPartEvaluatorStruct  *Eval;
00489        message_summary      *Msg;
00490        StrBuf *Buf;
00491        const char *buf;
00492        const char *ebuf;
00493        int nBuf;
00494        long len;
00495        
00496        Buf = NewStrBuf();
00497 
00498        serv_printf("MSG0 %ld|1", MsgNum); /* ask for headers only */
00499        
00500        StrBuf_ServGetln(Buf);
00501        if (GetServerStatus(Buf, NULL) == 1) {
00502               FreeStrBuf(&Buf);
00503               return NULL;
00504        }
00505 
00506        Msg = (message_summary*)malloc(sizeof(message_summary));
00507        memset(Msg, 0, sizeof(message_summary));
00508        while (len = StrBuf_ServGetln(Buf),
00509               (len >= 0) && 
00510               ((len != 3)  ||
00511               strcmp(ChrPtr(Buf), "000")))
00512        {
00513               buf = ChrPtr(Buf);
00514               ebuf = strchr(ChrPtr(Buf), '=');
00515               nBuf = ebuf - buf;
00516               if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) {
00517                      Eval = (MsgPartEvaluatorStruct*) vEval;
00518                      StrBufCutLeft(Buf, nBuf + 1);
00519                      Eval->f(Msg, Buf);
00520               }
00521               else syslog(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
00522        }
00523        return Msg;
00524 }
00525 
00526 
00527 
00528 
00529 
00530 /*
00531  * load message pointers from the server for a "read messages" operation
00532  *
00533  * servcmd:          the citadel command to send to the citserver
00534  */
00535 int load_msg_ptrs(const char *servcmd,
00536                 const char *filter,
00537                 SharedMessageStatus *Stat, 
00538                 load_msg_ptrs_detailheaders LH)
00539 {
00540         wcsession *WCC = WC;
00541        message_summary *Msg;
00542        StrBuf *Buf, *Buf2;
00543        long len;
00544        int n;
00545        int skipit;
00546        const char *Ptr = NULL;
00547        int StatMajor;
00548 
00549        Stat->lowest_found = LONG_MAX;
00550        Stat->highest_found = LONG_MIN;
00551 
00552        if (WCC->summ != NULL) {
00553               DeleteHash(&WCC->summ);
00554        }
00555        WCC->summ = NewHash(1, Flathash);
00556        
00557        Buf = NewStrBuf();
00558        serv_puts(servcmd);
00559        StrBuf_ServGetln(Buf);
00560        StatMajor = GetServerStatus(Buf, NULL);
00561        switch (StatMajor) {
00562        case 1:
00563               break;
00564        case 8:
00565               if (filter != NULL) {
00566                      serv_puts(filter);
00567                         serv_puts("000");
00568                      break;
00569               }
00570               /* fall back to empty filter in case of we were fooled... */
00571               serv_puts("");
00572               serv_puts("000");
00573               break;
00574        default:
00575               FreeStrBuf(&Buf);
00576               return (Stat->nummsgs);
00577        }
00578        Buf2 = NewStrBuf();
00579        while (len = StrBuf_ServGetln(Buf), 
00580               ((len >= 0) &&
00581               ((len != 3) || 
00582                strcmp(ChrPtr(Buf), "000")!= 0)))
00583        {
00584               if (Stat->nummsgs < Stat->maxload) {
00585                      skipit = 0;
00586                      Ptr = NULL;
00587                      Msg = (message_summary*)malloc(sizeof(message_summary));
00588                      memset(Msg, 0, sizeof(message_summary));
00589 
00590                      Msg->msgnum = StrBufExtractNext_long(Buf, &Ptr, '|');
00591                      Msg->date = StrBufExtractNext_long(Buf, &Ptr, '|');
00592 
00593                      if (Stat->nummsgs == 0) {
00594                             if (Msg->msgnum < Stat->lowest_found) {
00595                                    Stat->lowest_found = Msg->msgnum;
00596                             }
00597                             if (Msg->msgnum > Stat->highest_found) {
00598                                    Stat->highest_found = Msg->msgnum;
00599                             }
00600                      }
00601 
00602                      if ((Msg->msgnum == 0) && (StrLength(Buf) < 32)) {
00603                             free(Msg);
00604                             continue;
00605                      }
00606 
00607                      /* 
00608                       * as citserver probably gives us messages in forward date sorting
00609                       * nummsgs should be the same order as the message date.
00610                       */
00611                      if (Msg->date == 0) {
00612                             Msg->date = Stat->nummsgs;
00613                             if (StrLength(Buf) < 32) 
00614                                    skipit = 1;
00615                      }
00616                      if ((!skipit) && (LH != NULL)) {
00617                             if (!LH(Buf, &Ptr, Msg, Buf2)){
00618                                    free(Msg);
00619                                    continue;
00620                             }                                  
00621                      }
00622                      n = Msg->msgnum;
00623                      Put(WCC->summ, (const char *)&n, sizeof(n), Msg, DestroyMessageSummary);
00624               }
00625               Stat->nummsgs++;
00626        }
00627        FreeStrBuf(&Buf2);
00628        FreeStrBuf(&Buf);
00629        return (Stat->nummsgs);
00630 }
00631 
00632 
00633 
00640 void SetFlagsFromMSet(HashList *ScanMe, MSet *MatchMSet, int FlagToSet, int Reverse)
00641 {
00642        const char *HashKey;
00643        long HKLen;
00644        HashPos *at;
00645        void *vMsg;
00646        message_summary *Msg;
00647 
00648        at = GetNewHashPos(ScanMe, 0);
00649        while (GetNextHashPos(ScanMe, at, &HKLen, &HashKey, &vMsg)) {
00650               /* Are you a new message, or an old message? */
00651               Msg = (message_summary*) vMsg;
00652               if (Reverse && IsInMSetList(MatchMSet, Msg->msgnum)) {
00653                      Msg->Flags = Msg->Flags | FlagToSet;
00654               }
00655               else if (!Reverse && !IsInMSetList(MatchMSet, Msg->msgnum)) {
00656                      Msg->Flags = Msg->Flags | FlagToSet;
00657               }
00658        }
00659        DeleteHashPos(&at);
00660 }
00661 
00662 
00663 void load_seen_flags(void)
00664 {
00665        StrBuf *OldMsg;
00666        wcsession *WCC = WC;
00667        MSet *MatchMSet;
00668 
00669        OldMsg = NewStrBuf();
00670        serv_puts("GTSN");
00671        StrBuf_ServGetln(OldMsg);
00672        if (GetServerStatus(OldMsg, NULL) == 2) {
00673               StrBufCutLeft(OldMsg, 4);
00674        }
00675        else {
00676               FreeStrBuf(&OldMsg);
00677               return;
00678        }
00679 
00680        if (ParseMSet(&MatchMSet, OldMsg))
00681        {
00682               SetFlagsFromMSet(WCC->summ, MatchMSet, MSGFLAG_READ, 0);
00683        }
00684        DeleteMSet(&MatchMSet);
00685        FreeStrBuf(&OldMsg);
00686 }
00687 
00688 extern readloop_struct rlid[];
00689 
00690 typedef struct _RoomRenderer{
00691        int RoomType;
00692 
00693        GetParamsGetServerCall_func GetParamsGetServerCall;
00694        
00695        PrintViewHeader_func PrintPageHeader;
00696        PrintViewHeader_func PrintViewHeader;
00697        LoadMsgFromServer_func LoadMsgFromServer;
00698        RenderView_or_Tail_func RenderView_or_Tail;
00699        View_Cleanup_func ViewCleanup;
00700        load_msg_ptrs_detailheaders LHParse;
00701 } RoomRenderer;
00702 
00703 
00704 /*
00705  * command loop for reading messages
00706  *
00707  * Set oper to "readnew" or "readold" or "readfwd" or "headers" or "readgt" or "readlt" or "do_search"
00708  */
00709 void readloop(long oper, eCustomRoomRenderer ForceRenderer)
00710 {
00711        RoomRenderer *ViewMsg;
00712        void *vViewMsg;
00713        void *vMsg;
00714        message_summary *Msg;
00715        char cmd[256] = "";
00716        char filter[256] = "";
00717        int i, r;
00718        wcsession *WCC = WC;
00719        HashPos *at;
00720        const char *HashKey;
00721        long HKLen;
00722        WCTemplputParams SubTP;
00723        SharedMessageStatus Stat;
00724        void *ViewSpecific;
00725 
00726        if (havebstr("is_summary") && (1 == (ibstr("is_summary")))) {
00727               WCC->CurRoom.view = VIEW_MAILBOX;
00728        }
00729 
00730        if (havebstr("view")) {
00731               WCC->CurRoom.view = ibstr("view");
00732        }
00733 
00734        memset(&Stat, 0, sizeof(SharedMessageStatus));
00735        Stat.maxload = 10000;
00736        Stat.lowest_found = (-1);
00737        Stat.highest_found = (-1);
00738        if (ForceRenderer == eUseDefault)
00739               GetHash(ReadLoopHandler, IKEY(WCC->CurRoom.view), &vViewMsg);
00740        else 
00741               GetHash(ReadLoopHandler, IKEY(ForceRenderer), &vViewMsg);
00742        if (vViewMsg == NULL) {
00743               WCC->CurRoom.view = VIEW_BBS;
00744               GetHash(ReadLoopHandler, IKEY(WCC->CurRoom.view), &vViewMsg);
00745        }
00746        if (vViewMsg == NULL) {
00747               return;                     /* TODO: print message */
00748        }
00749 
00750        ViewMsg = (RoomRenderer*) vViewMsg;
00751        if (ViewMsg->PrintPageHeader == NULL)
00752               output_headers(1, 1, 1, 0, 0, 0);
00753        else 
00754               ViewMsg->PrintPageHeader(&Stat, ViewSpecific);
00755 
00756        if (ViewMsg->GetParamsGetServerCall != NULL) {
00757               r = ViewMsg->GetParamsGetServerCall(
00758                      &Stat,
00759                      &ViewSpecific,
00760                      oper,
00761                      cmd, sizeof(cmd),
00762                      filter, sizeof(filter)
00763               );
00764        } else {
00765               r = 0;
00766        }
00767 
00768        switch(r)
00769        {
00770        case 400:
00771        case 404:
00772 
00773               return;
00774        case 300: /* the callback hook should do the work for us here, since he knows what to do. */
00775               return;
00776        case 200:
00777        default:
00778               break;
00779        }
00780        if (!IsEmptyStr(cmd)) {
00781               const char *p = NULL;
00782               if (!IsEmptyStr(filter))
00783                      p = filter;
00784               Stat.nummsgs = load_msg_ptrs(cmd, p, &Stat, ViewMsg->LHParse);
00785        }
00786 
00787        if (Stat.sortit) {
00788               CompareFunc SortIt;
00789               memset(&SubTP, 0, sizeof(WCTemplputParams));
00790               SubTP.Filter.ContextType = CTX_MAILSUM;
00791               SubTP.Context = NULL;
00792               SortIt =  RetrieveSort(&SubTP, NULL, 0,
00793                                    HKEY("date"), Stat.defaultsortorder);
00794               if (SortIt != NULL)
00795                      SortByPayload(WCC->summ, SortIt);
00796        }
00797        if (Stat.startmsg < 0) {
00798               Stat.startmsg =  0;
00799        }
00800 
00801        if (Stat.load_seen) load_seen_flags();
00802        
00803         /*
00804         * Print any inforation above the message list...
00805         */
00806        if (ViewMsg->PrintViewHeader != NULL)
00807               ViewMsg->PrintViewHeader(&Stat, &ViewSpecific);
00808 
00809        WCC->startmsg =  Stat.startmsg;
00810        WCC->maxmsgs = Stat.maxmsgs;
00811        WCC->num_displayed = 0;
00812 
00813        /* Put some helpful data in vars for mailsummary_json */
00814        {
00815               StrBuf *Foo;
00816               
00817               Foo = NewStrBuf ();
00818               StrBufPrintf(Foo, "%ld", Stat.nummsgs);
00819               PutBstr(HKEY("__READLOOP:TOTALMSGS"), NewStrBufDup(Foo));
00820               StrBufPrintf(Foo, "%ld", Stat.startmsg);
00821               PutBstr(HKEY("__READLOOP:STARTMSG"), Foo);
00822        }
00823 
00824        /*
00825         * iterate over each message. if we need to load an attachment, do it here. 
00826         */
00827 
00828        if ((ViewMsg->LoadMsgFromServer != NULL) && 
00829            (!IsEmptyStr(cmd)))
00830        {
00831               at = GetNewHashPos(WCC->summ, 0);
00832               Stat.num_displayed = i = 0;
00833               while (       GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
00834                      Msg = (message_summary*) vMsg;            
00835                      if ((Msg->msgnum >= Stat.startmsg) && (Stat.num_displayed <= Stat.maxmsgs)) {
00836                             ViewMsg->LoadMsgFromServer(&Stat, 
00837                                                     &ViewSpecific, 
00838                                                     Msg, 
00839                                                     (Msg->Flags & MSGFLAG_READ) != 0, 
00840                                                     i);
00841                      } 
00842                      i++;
00843               }
00844               DeleteHashPos(&at);
00845        }
00846 
00847        /*
00848         * Done iterating the message list. now tasks we want to do after.
00849         */
00850        if (ViewMsg->RenderView_or_Tail != NULL)
00851               ViewMsg->RenderView_or_Tail(&Stat, &ViewSpecific, oper);
00852 
00853        if (ViewMsg->ViewCleanup != NULL)
00854               ViewMsg->ViewCleanup(&ViewSpecific);
00855 
00856        WCC->startmsg = 0;
00857        WCC->maxmsgs = 0;
00858        if (WCC->summ != NULL) {
00859               DeleteHash(&WCC->summ);
00860        }
00861 }
00862 
00863 
00864 /*
00865  * Back end for post_message()
00866  * ... this is where the actual message gets transmitted to the server.
00867  */
00868 void post_mime_to_server(void) {
00869        wcsession *WCC = WC;
00870        char top_boundary[SIZ];
00871        char alt_boundary[SIZ];
00872        int is_multipart = 0;
00873        static int seq = 0;
00874        wc_mime_attachment *att;
00875        char *encoded;
00876        size_t encoded_length;
00877        size_t encoded_strlen;
00878        char *txtmail = NULL;
00879        int include_text_alt = 0;   /* Set to nonzero to include multipart/alternative text/plain */
00880 
00881        sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x",
00882               ChrPtr(WCC->serv_info->serv_fqdn),
00883               getpid(),
00884               ++seq
00885        );
00886        sprintf(alt_boundary, "Citadel--Multipart--%s--%04x--%04x",
00887               ChrPtr(WCC->serv_info->serv_fqdn),
00888               getpid(),
00889               ++seq
00890        );
00891 
00892        /* RFC2045 requires this, and some clients look for it... */
00893        serv_puts("MIME-Version: 1.0");
00894        serv_puts("X-Mailer: " PACKAGE_STRING);
00895 
00896        /* If there are attachments, we have to do multipart/mixed */
00897        if (GetCount(WCC->attachments) > 0) {
00898               is_multipart = 1;
00899        }
00900 
00901        /* Only do multipart/alternative for mailboxes.  BBS and Wiki rooms don't need it. */
00902        if (WC->CurRoom.view == VIEW_MAILBOX) {
00903               include_text_alt = 1;
00904        }
00905 
00906        if (is_multipart) {
00907               /* Remember, serv_printf() appends an extra newline */
00908               serv_printf("Content-type: multipart/mixed; boundary=\"%s\"\n", top_boundary);
00909               serv_printf("This is a multipart message in MIME format.\n");
00910               serv_printf("--%s", top_boundary);
00911        }
00912 
00913        /* Remember, serv_printf() appends an extra newline */
00914        if (include_text_alt) {
00915               serv_printf("Content-type: multipart/alternative; "
00916                      "boundary=\"%s\"\n", alt_boundary);
00917               serv_printf("This is a multipart message in MIME format.\n");
00918               serv_printf("--%s", alt_boundary);
00919 
00920               serv_puts("Content-type: text/plain; charset=utf-8");
00921               serv_puts("Content-Transfer-Encoding: quoted-printable");
00922               serv_puts("");
00923               txtmail = html_to_ascii(bstr("msgtext"), 0, 80, 0);
00924               text_to_server_qp(txtmail);     /* Transmit message in quoted-printable encoding */
00925               free(txtmail);
00926 
00927               serv_printf("\n--%s", alt_boundary);
00928        }
00929 
00930        serv_puts("Content-type: text/html; charset=utf-8");
00931        serv_puts("Content-Transfer-Encoding: quoted-printable");
00932        serv_puts("");
00933        serv_puts("<html><body>\r\n");
00934        text_to_server_qp(bstr("msgtext"));       /* Transmit message in quoted-printable encoding */
00935        serv_puts("</body></html>\r\n");
00936 
00937        if (include_text_alt) {
00938               serv_printf("--%s--", alt_boundary);
00939        }
00940        
00941        if (is_multipart) {
00942               long len;
00943               const char *Key; 
00944               void *vAtt;
00945               HashPos  *it;
00946 
00947               /* Add in the attachments */
00948               it = GetNewHashPos(WCC->attachments, 0);
00949               while (GetNextHashPos(WCC->attachments, it, &len, &Key, &vAtt)) {
00950                      att = (wc_mime_attachment *)vAtt;
00951                      if (att->length == 0)
00952                             continue;
00953                      encoded_length = ((att->length * 150) / 100);
00954                      encoded = malloc(encoded_length);
00955                      if (encoded == NULL) break;
00956                      encoded_strlen = CtdlEncodeBase64(encoded, ChrPtr(att->Data), StrLength(att->Data), 1);
00957 
00958                      serv_printf("--%s", top_boundary);
00959                      serv_printf("Content-type: %s", ChrPtr(att->ContentType));
00960                      serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att->FileName));
00961                      serv_puts("Content-transfer-encoding: base64");
00962                      serv_puts("");
00963                      serv_write(encoded, encoded_strlen);
00964                      serv_puts("");
00965                      serv_puts("");
00966                      free(encoded);
00967               }
00968               serv_printf("--%s--", top_boundary);
00969               DeleteHashPos(&it);
00970        }
00971 
00972        serv_puts("000");
00973 }
00974 
00975 
00976 /*
00977  * Post message (or don't post message)
00978  *
00979  * Note regarding the "dont_post" variable:
00980  * A random value (actually, it's just a timestamp) is inserted as a hidden
00981  * field called "postseq" when the display_enter page is generated.  This
00982  * value is checked when posting, using the static variable dont_post.  If a
00983  * user attempts to post twice using the same dont_post value, the message is
00984  * discarded.  This prevents the accidental double-saving of the same message
00985  * if the user happens to click the browser "back" button.
00986  */
00987 void post_message(void)
00988 {
00989        StrBuf *UserName;
00990        StrBuf *EmailAddress;
00991        StrBuf *EncBuf;
00992        char buf[1024];
00993        StrBuf *encoded_subject = NULL;
00994        static long dont_post = (-1L);
00995        int is_anonymous = 0;
00996        const StrBuf *display_name = NULL;
00997        wcsession *WCC = WC;
00998        StrBuf *Buf;
00999        
01000        if (havebstr("force_room")) {
01001               gotoroom(sbstr("force_room"));
01002        }
01003 
01004        if (havebstr("display_name")) {
01005               display_name = sbstr("display_name");
01006               if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
01007                      display_name = NULL;
01008                      is_anonymous = 1;
01009               }
01010        }
01011 
01012        if (!strcasecmp(bstr("submit_action"), "cancel")) {
01013               AppendImportantMessage(_("Cancelled.  Message was not posted."), -1);
01014        } else if (lbstr("postseq") == dont_post) {
01015               AppendImportantMessage(
01016                      _("Automatically cancelled because you have already "
01017                        "saved this message."), -1);
01018        } else {
01019               const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
01020               StrBuf *Recp = NULL; 
01021               StrBuf *Cc = NULL;
01022               StrBuf *Bcc = NULL;
01023               char *wikipage = NULL;
01024               const StrBuf *my_email_addr = NULL;
01025               StrBuf *CmdBuf = NULL;
01026               StrBuf *references = NULL;
01027               int saving_to_drafts = 0;
01028               long HeaderLen = 0;
01029 
01030               saving_to_drafts = !strcasecmp(bstr("submit_action"), "draft");
01031               Buf = NewStrBuf();
01032 
01033               if (saving_to_drafts) {
01034                       /* temporarily change to the drafts room */
01035                       serv_puts("GOTO _DRAFTS_");
01036                      StrBuf_ServGetln(Buf);
01037                      if (GetServerStatusMsg(Buf, NULL, 1, 2) != 2) {
01038                             /* You probably don't even have a dumb Drafts folder */
01039                             syslog(9, "%s:%d: server save to drafts error: %s\n", __FILE__, __LINE__, ChrPtr(Buf) + 4);
01040                             AppendImportantMessage(_("Saved to Drafts failed: "), -1);
01041                             display_enter();
01042                             FreeStrBuf(&Buf);
01043                             return;
01044                      }
01045               }
01046 
01047               if (havebstr("references"))
01048               {
01049                      const StrBuf *ref = sbstr("references");
01050                      references = NewStrBufDup(ref);
01051                      if (*ChrPtr(references) == '|') {  /* remove leading '|' if present */
01052                             StrBufCutLeft(references, 1);
01053                      }
01054                      StrBufReplaceChars(references, '|', '!');
01055               }
01056               if (havebstr("subject")) {
01057                      const StrBuf *Subj;
01058                      /*
01059                       * make enough room for the encoded string; 
01060                       * plus the QP header 
01061                       */
01062                      Subj = sbstr("subject");
01063                      
01064                      StrBufRFC2047encode(&encoded_subject, Subj);
01065               }
01066               UserName = NewStrBuf();
01067               EmailAddress = NewStrBuf();
01068               EncBuf = NewStrBuf();
01069 
01070               Recp = StrBufSanitizeEmailRecipientVector(sbstr("recp"), UserName, EmailAddress, EncBuf);
01071               Cc = StrBufSanitizeEmailRecipientVector(sbstr("cc"), UserName, EmailAddress, EncBuf);
01072               Bcc = StrBufSanitizeEmailRecipientVector(sbstr("bcc"), UserName, EmailAddress, EncBuf);
01073 
01074               FreeStrBuf(&UserName);
01075               FreeStrBuf(&EmailAddress);
01076               FreeStrBuf(&EncBuf);
01077 
01078               wikipage = strdup(bstr("page"));
01079               str_wiki_index(wikipage);
01080               my_email_addr = sbstr("my_email_addr");
01081               
01082               HeaderLen = StrLength(Recp) + 
01083                      StrLength(encoded_subject) +
01084                      StrLength(Cc) +
01085                      StrLength(Bcc) + 
01086                      strlen(wikipage) +
01087                      StrLength(my_email_addr) + 
01088                      StrLength(references);
01089               CmdBuf = NewStrBufPlain(NULL, sizeof (CMD) + HeaderLen);
01090               StrBufPrintf(CmdBuf, 
01091                           CMD,
01092                           saving_to_drafts?"":ChrPtr(Recp),
01093                           is_anonymous,
01094                           ChrPtr(encoded_subject),
01095                           ChrPtr(display_name),
01096                           saving_to_drafts?"":ChrPtr(Cc),
01097                           saving_to_drafts?"":ChrPtr(Bcc),
01098                           wikipage,
01099                           ChrPtr(my_email_addr),
01100                           ChrPtr(references));
01101               FreeStrBuf(&references);
01102               FreeStrBuf(&encoded_subject);
01103               free(wikipage);
01104 
01105               if ((HeaderLen + StrLength(sbstr("msgtext")) < 10) && 
01106                   (GetCount(WCC->attachments) == 0)){
01107                      AppendImportantMessage(_("Refusing to post empty message.\n"), -1);
01108                      FreeStrBuf(&CmdBuf);
01109                             
01110               }
01111               else 
01112               {
01113                      syslog(9, "%s\n", ChrPtr(CmdBuf));
01114                      serv_puts(ChrPtr(CmdBuf));
01115                      FreeStrBuf(&CmdBuf);
01116 
01117                      StrBuf_ServGetln(Buf);
01118                      if (GetServerStatus(Buf, NULL) == 4) {
01119                             if (saving_to_drafts) {
01120                                    if (  (havebstr("recp"))
01121                                          || (havebstr("cc"  ))
01122                                          || (havebstr("bcc" )) ) {
01123                                           /* save recipient headers or room to post to */
01124                                           serv_printf("To: %s", ChrPtr(Recp));
01125                                           serv_printf("Cc: %s", ChrPtr(Cc));
01126                                           serv_printf("Bcc: %s", ChrPtr(Bcc));
01127                                    } else {
01128                                           serv_printf("X-Citadel-Room: %s", ChrPtr(WC->CurRoom.name));
01129                                    }
01130                             }
01131                             post_mime_to_server();
01132                             if (saving_to_drafts) {
01133                                    AppendImportantMessage(_("Message has been saved to Drafts.\n"), -1);
01134                                    gotoroom(WCC->CurRoom.name);
01135                                    fixview();
01136                                    readloop(readnew, eUseDefault);
01137                                    FreeStrBuf(&Buf);
01138                                    return;
01139                             } else if (  (havebstr("recp"))
01140                                         || (havebstr("cc"  ))
01141                                         || (havebstr("bcc" ))
01142                                    ) {
01143                                    AppendImportantMessage(_("Message has been sent.\n"), -1);
01144                             }
01145                             else {
01146                                    AppendImportantMessage(_("Message has been posted.\n"), -1);
01147                             }
01148                             dont_post = lbstr("postseq");
01149                      } else {
01150                             syslog(9, "%s:%d: server post error: %s", __FILE__, __LINE__, ChrPtr(Buf) + 4);
01151                             AppendImportantMessage(ChrPtr(Buf) + 4, StrLength(Buf) - 4);
01152                             display_enter();
01153                             if (saving_to_drafts) gotoroom(WCC->CurRoom.name);
01154                             FreeStrBuf(&Recp);
01155                             FreeStrBuf(&Buf);
01156                             FreeStrBuf(&Cc);
01157                             FreeStrBuf(&Bcc);
01158                             return;
01159                      }
01160               }
01161               FreeStrBuf(&Recp);
01162               FreeStrBuf(&Buf);
01163               FreeStrBuf(&Cc);
01164               FreeStrBuf(&Bcc);
01165        }
01166 
01167        DeleteHash(&WCC->attachments);
01168 
01169        /*
01170         *  We may have been supplied with instructions regarding the location
01171         *  to which we must return after posting.  If found, go there.
01172         */
01173        if (havebstr("return_to")) {
01174               http_redirect(bstr("return_to"));
01175        }
01176        /*
01177         *  If we were editing a page in a wiki room, go to that page now.
01178         */
01179        else if (havebstr("page")) {
01180               snprintf(buf, sizeof buf, "wiki?page=%s", bstr("page"));
01181               http_redirect(buf);
01182        }
01183        /*
01184         *  Otherwise, just go to the "read messages" loop.
01185         */
01186        else {
01187               fixview();
01188               readloop(readnew, eUseDefault);
01189        }
01190 }
01191 
01192 
01193 /*
01194  * Client is uploading an attachment
01195  */
01196 void upload_attachment(void) {
01197        wcsession *WCC = WC;
01198        const char *pch;
01199        int n;
01200        const char *newn;
01201        long newnlen;
01202        void *v;
01203        wc_mime_attachment *att;
01204 
01205        syslog(9, "upload_attachment()\n");
01206        wc_printf("upload_attachment()<br>\n");
01207 
01208        if (WCC->upload_length <= 0) {
01209               syslog(9, "ERROR no attachment was uploaded\n");
01210               wc_printf("ERROR no attachment was uploaded<br>\n");
01211               return;
01212        }
01213 
01214        syslog(9, "Client is uploading %d bytes\n", WCC->upload_length);
01215        wc_printf("Client is uploading %d bytes<br>\n", WCC->upload_length);
01216        att = malloc(sizeof(wc_mime_attachment));
01217        memset(att, 0, sizeof(wc_mime_attachment ));
01218        att->length = WCC->upload_length;
01219        att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1);
01220        att->FileName = NewStrBufDup(WCC->upload_filename);
01221        
01222        if (WCC->attachments == NULL) {
01223               WCC->attachments = NewHash(1, Flathash);
01224        }
01225 
01226        /* And add it to the list. */
01227        n = 0;
01228        if ((GetCount(WCC->attachments) > 0) && 
01229            GetHashAt(WCC->attachments, 
01230                     GetCount(WCC->attachments) -1, 
01231                     &newnlen, &newn, &v))
01232            n = *((int*) newn) + 1;
01233        Put(WCC->attachments, IKEY(n), att, DestroyMime);
01234 
01235        /*
01236         * Mozilla sends a simple filename, which is what we want,
01237         * but Satan's Browser sends an entire pathname.  Reduce
01238         * the path to just a filename if we need to.
01239         */
01240        pch = strrchr(ChrPtr(att->FileName), '/');
01241        if (pch != NULL) {
01242               StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
01243        }
01244        pch = strrchr(ChrPtr(att->FileName), '\\');
01245        if (pch != NULL) {
01246               StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1);
01247        }
01248 
01249        /*
01250         * Transfer control of this memory from the upload struct
01251         * to the attachment struct.
01252         */
01253        att->Data = WCC->upload;
01254        WCC->upload = NULL;
01255        WCC->upload_length = 0;
01256 }
01257 
01258 
01259 /*
01260  * Remove an attachment from the message currently being composed.
01261  *
01262  * Currently we identify the attachment to be removed by its filename.
01263  * There is probably a better way to do this.
01264  */
01265 void remove_attachment(void) {
01266        wcsession *WCC = WC;
01267        wc_mime_attachment *att;
01268        void *vAtt;
01269        StrBuf *WhichAttachment;
01270        HashPos *at;
01271        long len;
01272        const char *key;
01273 
01274        WhichAttachment = NewStrBufDup(sbstr("which_attachment"));
01275        StrBufUnescape(WhichAttachment, 0);
01276        at = GetNewHashPos(WCC->attachments, 0);
01277        do {
01278               GetHashPos(WCC->attachments, at, &len, &key, &vAtt);
01279        
01280               att = (wc_mime_attachment*) vAtt;
01281               if ((att != NULL) && 
01282                   (strcmp(ChrPtr(WhichAttachment), 
01283                          ChrPtr(att->FileName)   ) == 0))
01284               {
01285                      DeleteEntryFromHash(WCC->attachments, at);
01286                      break;
01287               }
01288        }
01289        while (NextHashPos(WCC->attachments, at));
01290        FreeStrBuf(&WhichAttachment);
01291        wc_printf("remove_attachment() completed\n");
01292 }
01293 
01294 
01295 long FourHash(const char *key, long length) 
01296 {
01297         int i;
01298         long ret = 0;
01299         const unsigned char *ptr = (const unsigned char*)key;
01300 
01301         for (i = 0; i < 4; i++, ptr ++) 
01302                 ret = (ret << 8) | 
01303                         ( ((*ptr >= 'a') &&
01304                            (*ptr <= 'z'))? 
01305                           *ptr - 'a' + 'A': 
01306                           *ptr);
01307 
01308         return ret;
01309 }
01310 
01311 long l_subj;
01312 long l_wefw;
01313 long l_msgn;
01314 long l_from;
01315 long l_rcpt;
01316 long l_cccc;
01317 long l_node;
01318 long l_rfca;
01319 
01320 /*
01321  * display the message entry screen
01322  */
01323 void display_enter(void)
01324 {
01325        StrBuf *Line;
01326        long Result;
01327        int rc;
01328        const StrBuf *display_name = NULL;
01329        int recipient_required = 0;
01330        int subject_required = 0;
01331        int is_anonymous = 0;
01332        wcsession *WCC = WC;
01333        int i = 0;
01334        long replying_to;
01335 
01336        if (havebstr("force_room")) {
01337               gotoroom(sbstr("force_room"));
01338        }
01339 
01340        display_name = sbstr("display_name");
01341        if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
01342               display_name = NULL;
01343               is_anonymous = 1;
01344        }
01345 
01346        /*
01347         * First, do we have permission to enter messages in this room at all?
01348         */
01349        Line = NewStrBuf();
01350        serv_puts("ENT0 0");
01351        StrBuf_ServGetln(Line);
01352        rc = GetServerStatusMsg(Line, &Result, 0, 2);
01353 
01354        if (Result == 570) {        /* 570 means that we need a recipient here */
01355               recipient_required = 1;
01356        }
01357        else if (rc != 2) {         /* Any other error means that we cannot continue */
01358               rc = GetServerStatusMsg(Line, &Result, 0, 2);
01359               fixview();
01360               readloop(readnew, eUseDefault);
01361               FreeStrBuf(&Line);
01362               return;
01363        }
01364 
01365        /* Is the server strongly recommending that the user enter a message subject? */
01366        if (StrLength(Line) > 4) {
01367               subject_required = extract_int(ChrPtr(Line) + 4, 1);
01368        }
01369 
01370        /*
01371         * Are we perhaps in an address book view?  If so, then an "enter
01372         * message" command really means "add new entry."
01373         */
01374        if (WCC->CurRoom.defview == VIEW_ADDRESSBOOK) {
01375               do_edit_vcard(-1, "", NULL, NULL, "",  ChrPtr(WCC->CurRoom.name));
01376               FreeStrBuf(&Line);
01377               return;
01378        }
01379 
01380        /*
01381         * Are we perhaps in a calendar room?  If so, then an "enter
01382         * message" command really means "add new calendar item."
01383         */
01384        if (WCC->CurRoom.defview == VIEW_CALENDAR) {
01385               display_edit_event();
01386               FreeStrBuf(&Line);
01387               return;
01388        }
01389 
01390        /*
01391         * Are we perhaps in a tasks view?  If so, then an "enter
01392         * message" command really means "add new task."
01393         */
01394        if (WCC->CurRoom.defview == VIEW_TASKS) {
01395               display_edit_task();
01396               FreeStrBuf(&Line);
01397               return;
01398        }
01399 
01400 
01401        /*
01402         * If the "replying_to" variable is set, it refers to a message
01403         * number from which we must extract some header fields...
01404         */
01405        replying_to = lbstr("replying_to");
01406        if (replying_to > 0) {
01407               long len;
01408               StrBuf *wefw = NULL;
01409               StrBuf *msgn = NULL;
01410               StrBuf *from = NULL;
01411               StrBuf *node = NULL;
01412               StrBuf *rfca = NULL;
01413               StrBuf *rcpt = NULL;
01414               StrBuf *cccc = NULL;
01415               serv_printf("MSG0 %ld|1", replying_to);   
01416 
01417               StrBuf_ServGetln(Line);
01418               if (GetServerStatusMsg(Line, NULL, 0, 0) == 1)
01419                      while (len = StrBuf_ServGetln(Line),
01420                             (len >= 0) && 
01421                             ((len != 3)  ||
01422                             strcmp(ChrPtr(Line), "000")))
01423                      {
01424                             long which = 0;
01425                             if ((StrLength(Line) > 4) && 
01426                                 (ChrPtr(Line)[4] == '='))
01427                                    which = FourHash(ChrPtr(Line), 4);
01428 
01429                             if (which == l_subj)
01430                             {
01431                                    StrBuf *subj = NewStrBuf();
01432                                    StrBuf *FlatSubject;
01433 
01434                                    if (!strcasecmp(bstr("replying_mode"), "forward")) {
01435                                           if (strncasecmp(ChrPtr(Line) + 5, "Fw:", 3)) {
01436                                                  StrBufAppendBufPlain(subj, HKEY("Fw: "), 0);
01437                                           }
01438                                    }
01439                                    else {
01440                                           if (strncasecmp(ChrPtr(Line) + 5, "Re:", 3)) {
01441                                                  StrBufAppendBufPlain(subj, HKEY("Re: "), 0);
01442                                           }
01443                                    }
01444                                    StrBufAppendBufPlain(subj, 
01445                                                       ChrPtr(Line) + 5, 
01446                                                       StrLength(Line) - 5, 0);
01447                                    FlatSubject = NewStrBufPlain(NULL, StrLength(subj));
01448                                    StrBuf_RFC822_to_Utf8(FlatSubject, subj, NULL, NULL);
01449 
01450                                    PutBstr(HKEY("subject"), FlatSubject);
01451                             }
01452 
01453                             else if (which == l_wefw)
01454                             {
01455                                    int rrtok;
01456                                    int rrlen;
01457 
01458                                    wefw = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01459                                    
01460                                    /* Trim down excessively long lists of thread references.  We eliminate the
01461                                     * second one in the list so that the thread root remains intact.
01462                                     */
01463                                    rrtok = num_tokens(ChrPtr(wefw), '|');
01464                                    rrlen = StrLength(wefw);
01465                                    if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
01466                                           StrBufRemove_token(wefw, 1, '|');
01467                                    }
01468                             }
01469 
01470                             else if (which == l_msgn) {
01471                                    msgn = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01472                             }
01473 
01474                             else if (which == l_from) {
01475                                    StrBuf *FlatFrom;
01476                                    from = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01477                                    FlatFrom = NewStrBufPlain(NULL, StrLength(from));
01478                                    StrBuf_RFC822_to_Utf8(FlatFrom, from, NULL, NULL);
01479                                    FreeStrBuf(&from);
01480                                    from = FlatFrom;
01481                                    for (i=0; i<StrLength(from); ++i) {
01482                                           if (ChrPtr(from)[i] == ',')
01483                                                  StrBufPeek(from, NULL, i, ' ');
01484                                    }
01485                             }
01486                             
01487                             else if (which == l_rcpt) {
01488                                    rcpt = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01489                             }
01490                             
01491                             else if (which == l_cccc) {
01492                                    cccc = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01493                             }
01494                             
01495                             else if (which == l_node) {
01496                                    node = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01497                             }
01498                             
01499                             else if (which == l_rfca) {
01500                                    StrBuf *FlatRFCA;
01501                                    rfca = NewStrBufPlain(ChrPtr(Line) + 5, StrLength(Line) - 5);
01502                                    FlatRFCA = NewStrBufPlain(NULL, StrLength(rfca));
01503                                    StrBuf_RFC822_to_Utf8(FlatRFCA, rfca, NULL, NULL);
01504                                    FreeStrBuf(&rfca);
01505                                    rfca = FlatRFCA;
01506                             }
01507                      }
01508 
01509 
01510               if (StrLength(wefw) + StrLength(msgn) > 0) {
01511                      StrBuf *refs = NewStrBuf();
01512                      if (StrLength(wefw) > 0) {
01513                             StrBufAppendBuf(refs, wefw, 0);
01514                      }
01515                      if ( (StrLength(wefw) > 0) && 
01516                           (StrLength(msgn) > 0) ) 
01517                      {
01518                             StrBufAppendBufPlain(refs, HKEY("|"), 0);
01519                      }
01520                      if (StrLength(msgn) > 0) {
01521                             StrBufAppendBuf(refs, msgn, 0);
01522                      }
01523                      PutBstr(HKEY("references"), refs);
01524               }
01525 
01526               /*
01527                * If this is a Reply or a ReplyAll, copy the sender's email into the To: field
01528                */
01529               if (   (!strcasecmp(bstr("replying_mode"), "reply"))
01530                      || (!strcasecmp(bstr("replying_mode"), "replyall"))
01531               ) {
01532                      StrBuf *to_rcpt;
01533                      if (StrLength(rfca) > 0) {
01534                             to_rcpt = NewStrBuf();
01535                             StrBufAppendBuf(to_rcpt, from, 0);
01536                             StrBufAppendBufPlain(to_rcpt, HKEY(" <"), 0);
01537                             StrBufAppendBuf(to_rcpt, rfca, 0);
01538                             StrBufAppendBufPlain(to_rcpt, HKEY(">"), 0);
01539                      }
01540                      else {
01541                             to_rcpt =  from;
01542                             from = NULL;
01543                             if (   (StrLength(node) > 0)
01544                                    && (strcasecmp(ChrPtr(node), ChrPtr(WC->serv_info->serv_nodename)))
01545                             ) {
01546                                    StrBufAppendBufPlain(to_rcpt, HKEY(" @ "), 0);
01547                                    StrBufAppendBuf(to_rcpt, node, 0);
01548                             }
01549                      }
01550                      PutBstr(HKEY("recp"), to_rcpt);
01551               }
01552 
01553               /*
01554                * Only if this is a ReplyAll, copy all recipients into the Cc: field
01555                */
01556               if (   (!strcasecmp(bstr("replying_mode"), "replyall"))
01557               ) {
01558                      StrBuf *cc_rcpt = rcpt;
01559                      rcpt = NULL;
01560                      if (StrLength(cccc) > 0) {
01561                             if (cc_rcpt != NULL)  {
01562                                    StrBufAppendPrintf(cc_rcpt, ", ");
01563                                    StrBufAppendBuf(cc_rcpt, cccc, 0);
01564                             } else {
01565                                    cc_rcpt = cccc;
01566                                    cccc = NULL;
01567                             }
01568                      }
01569                      if (cc_rcpt != NULL)
01570                             PutBstr(HKEY("cc"), cc_rcpt);
01571               }
01572               FreeStrBuf(&wefw);
01573               FreeStrBuf(&msgn);
01574               FreeStrBuf(&from);
01575               FreeStrBuf(&node);
01576               FreeStrBuf(&rfca);
01577               FreeStrBuf(&rcpt);
01578               FreeStrBuf(&cccc);
01579        }
01580        FreeStrBuf(&Line);
01581        /*
01582         * Otherwise proceed normally.
01583         * Do a custom room banner with no navbar...
01584         */
01585 
01586        if (recipient_required) {
01587               const StrBuf *Recp = NULL; 
01588               const StrBuf *Cc = NULL;
01589               const StrBuf *Bcc = NULL;
01590               char *wikipage = NULL;
01591               StrBuf *CmdBuf = NULL;
01592               const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s";
01593               
01594               Recp = sbstr("recp");
01595               Cc = sbstr("cc");
01596               Bcc = sbstr("bcc");
01597               wikipage = strdup(bstr("page"));
01598               str_wiki_index(wikipage);
01599               
01600               CmdBuf = NewStrBufPlain(NULL, 
01601                                    sizeof (CMD) + 
01602                                    StrLength(Recp) + 
01603                                    StrLength(display_name) +
01604                                    StrLength(Cc) +
01605                                    StrLength(Bcc) + 
01606                                    strlen(wikipage));
01607 
01608               StrBufPrintf(CmdBuf, 
01609                           CMD,
01610                           ChrPtr(Recp), 
01611                           is_anonymous,
01612                           ChrPtr(display_name),
01613                           ChrPtr(Cc), 
01614                           ChrPtr(Bcc), 
01615                           wikipage
01616               );
01617               serv_puts(ChrPtr(CmdBuf));
01618               StrBuf_ServGetln(CmdBuf);
01619               free(wikipage);
01620 
01621               rc = GetServerStatusMsg(CmdBuf, &Result, 0, 0);
01622 
01623               if (   (Result == 570)             /* invalid or missing recipient(s) */
01624                      || (Result == 550)   /* higher access required to send Internet mail */
01625               ) {
01626                      /* These errors will have been displayed and are excusable */
01627               }
01628               else if (rc != 2) {  /* Any other error means that we cannot continue */
01629                      AppendImportantMessage(ChrPtr(CmdBuf) + 4, StrLength(CmdBuf) - 4);
01630                      FreeStrBuf(&CmdBuf);
01631                      fixview();
01632                      readloop(readnew, eUseDefault);
01633                      return;
01634               }
01635               FreeStrBuf(&CmdBuf);
01636        }
01637        if (recipient_required)
01638               PutBstr(HKEY("__RCPTREQUIRED"), NewStrBufPlain(HKEY("1")));
01639        if (recipient_required || subject_required)
01640               PutBstr(HKEY("__SUBJREQUIRED"), NewStrBufPlain(HKEY("1")));
01641 
01642        begin_burst();
01643        output_headers(1, 0, 0, 0, 1, 0);
01644        DoTemplate(HKEY("edit_message"), NULL, &NoCtx);
01645        end_burst();
01646 
01647        return;
01648 }
01649 
01650 /*
01651  * delete a message
01652  */
01653 void delete_msg(void)
01654 {
01655        long msgid;
01656        StrBuf *Line;
01657        
01658        msgid = lbstr("msgid");
01659        Line = NewStrBuf();
01660        if ((WC->CurRoom.RAFlags & UA_ISTRASH) != 0) {   /* Delete from Trash is a real delete */
01661               serv_printf("DELE %ld", msgid);    
01662        }
01663        else {               /* Otherwise move it to Trash */
01664               serv_printf("MOVE %ld|_TRASH_|0", msgid);
01665        }
01666 
01667        StrBuf_ServGetln(Line);
01668        GetServerStatusMsg(Line, NULL, 1, 0);
01669 
01670        fixview();
01671 
01672        readloop(readnew, eUseDefault);
01673 }
01674 
01675 
01676 /*
01677  * move a message to another room
01678  */
01679 void move_msg(void)
01680 {
01681        long msgid;
01682 
01683        msgid = lbstr("msgid");
01684 
01685        if (havebstr("move_button")) {
01686               StrBuf *Line;
01687               serv_printf("MOVE %ld|%s", msgid, bstr("target_room"));
01688               Line = NewStrBuf();
01689               StrBuf_ServGetln(Line);
01690               GetServerStatusMsg(Line, NULL, 1, 0);
01691               FreeStrBuf(&Line);
01692        } else {
01693               AppendImportantMessage(_("The message was not moved."), -1);
01694        }
01695 
01696        fixview();
01697        readloop(readnew, eUseDefault);
01698 }
01699 
01700 
01701 
01702 /*
01703  * Generic function to output an arbitrary MIME attachment from
01704  * message being composed
01705  *
01706  * partnum           The MIME part to be output
01707  * filename          Fake filename to give
01708  * force_download    Nonzero to force set the Content-Type: header to "application/octet-stream"
01709  */
01710 void postpart(StrBuf *partnum, StrBuf *filename, int force_download)
01711 {
01712        void *vPart;
01713        StrBuf *content_type;
01714        wc_mime_attachment *part;
01715        int i;
01716 
01717        i = StrToi(partnum);
01718        if (GetHash(WC->attachments, IKEY(i), &vPart) &&
01719            (vPart != NULL)) {
01720               part = (wc_mime_attachment*) vPart;
01721               if (force_download) {
01722                      content_type = NewStrBufPlain(HKEY("application/octet-stream"));
01723               }
01724               else {
01725                      content_type = NewStrBufDup(part->ContentType);
01726               }
01727               StrBufAppendBuf(WC->WBuf, part->Data, 0);
01728               http_transmit_thing(ChrPtr(content_type), 0);
01729        } else {
01730               hprintf("HTTP/1.1 404 %s\n", ChrPtr(partnum));
01731               output_headers(0, 0, 0, 0, 0, 0);
01732               hprintf("Content-Type: text/plain\r\n");
01733               begin_burst();
01734               wc_printf(_("An error occurred while retrieving this part: %s/%s\n"), 
01735                      ChrPtr(partnum), ChrPtr(filename));
01736               end_burst();
01737        }
01738        FreeStrBuf(&content_type);
01739 }
01740 
01741 
01742 /*
01743  * Generic function to output an arbitrary MIME part from an arbitrary
01744  * message number on the server.
01745  *
01746  * msgnum            Number of the item on the citadel server
01747  * partnum           The MIME part to be output
01748  * force_download    Nonzero to force set the Content-Type: header to "application/octet-stream"
01749  */
01750 void mimepart(int force_download)
01751 {
01752        long msgnum;
01753        long ErrorDetail;
01754        StrBuf *att;
01755        wcsession *WCC = WC;
01756        StrBuf *Buf;
01757        off_t bytes;
01758        StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream"));
01759        const char *CT;
01760 
01761        att = Buf = NewStrBuf();
01762        msgnum = StrBufExtract_long(WCC->Hdr->HR.ReqLine, 0, '/');
01763        StrBufExtract_token(att, WCC->Hdr->HR.ReqLine, 1, '/');
01764 
01765        serv_printf("OPNA %ld|%s", msgnum, ChrPtr(att));
01766        StrBuf_ServGetln(Buf);
01767        if (GetServerStatus(Buf, &ErrorDetail) == 2) {
01768               StrBufCutLeft(Buf, 4);
01769               bytes = StrBufExtract_long(Buf, 0, '|');
01770               if (!force_download) {
01771                      StrBufExtract_token(ContentType, Buf, 3, '|');
01772               }
01773 
01774               serv_read_binary(WCC->WBuf, bytes, Buf);
01775               serv_puts("CLOS");
01776               StrBuf_ServGetln(Buf);
01777               CT = ChrPtr(ContentType);
01778 
01779               if (!force_download) {
01780                      if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
01781                             StrBufExtract_token(Buf, WCC->Hdr->HR.ReqLine, 2, '/');
01782                             CT = GuessMimeByFilename(SKEY(Buf));
01783                      }
01784                      if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) {
01785                             CT = GuessMimeType(SKEY(WCC->WBuf));
01786                      }
01787               }
01788               http_transmit_thing(CT, 0);
01789        } else {
01790               StrBufCutLeft(Buf, 4);
01791               switch (ErrorDetail) {
01792               default:
01793               case ERROR + MESSAGE_NOT_FOUND:
01794                      hprintf("HTTP/1.1 404 %s\n", ChrPtr(Buf));
01795                      break;
01796               case ERROR + NOT_LOGGED_IN:
01797                      hprintf("HTTP/1.1 401 %s\n", ChrPtr(Buf));
01798                      break;
01799 
01800               case ERROR + HIGHER_ACCESS_REQUIRED:
01801                      hprintf("HTTP/1.1 403 %s\n", ChrPtr(Buf));
01802                      break;
01803               case ERROR + INTERNAL_ERROR:
01804               case ERROR + TOO_BIG:
01805                      hprintf("HTTP/1.1 500 %s\n", ChrPtr(Buf));
01806                      break;
01807               }
01808               output_headers(0, 0, 0, 0, 0, 0);
01809               hprintf("Content-Type: text/plain\r\n");
01810               begin_burst();
01811               wc_printf(_("An error occurred while retrieving this part: %s\n"), 
01812                      ChrPtr(Buf));
01813               end_burst();
01814        }
01815        FreeStrBuf(&ContentType);
01816        FreeStrBuf(&Buf);
01817 }
01818 
01819 
01820 /*
01821  * Read any MIME part of a message, from the server, into memory.
01822  */
01823 StrBuf *load_mimepart(long msgnum, char *partnum)
01824 {
01825        off_t bytes;
01826        StrBuf *Buf;
01827        
01828        Buf = NewStrBuf();
01829        serv_printf("DLAT %ld|%s", msgnum, partnum);
01830        StrBuf_ServGetln(Buf);
01831        if (GetServerStatus(Buf, NULL) == 6) {
01832               StrBufCutLeft(Buf, 4);
01833               bytes = StrBufExtract_long(Buf, 0, '|');
01834               FreeStrBuf(&Buf);
01835               Buf = NewStrBuf();
01836               StrBuf_ServGetBLOBBuffered(Buf, bytes);
01837               return(Buf);
01838        }
01839        else {
01840               FreeStrBuf(&Buf);
01841               return(NULL);
01842        }
01843 }
01844 
01845 /*
01846  * Read any MIME part of a message, from the server, into memory.
01847  */
01848 void MimeLoadData(wc_mime_attachment *Mime)
01849 {
01850        StrBuf *Buf;
01851        const char *Ptr;
01852        off_t bytes;
01853        /* TODO: is there a chance the content type is different from the one we know? */
01854 
01855        serv_printf("DLAT %ld|%s", Mime->msgnum, ChrPtr(Mime->PartNum));
01856        Buf = NewStrBuf();
01857        StrBuf_ServGetln(Buf);
01858        if (GetServerStatus(Buf, NULL) == 6) {
01859               Ptr = &(ChrPtr(Buf)[4]);
01860               bytes = StrBufExtractNext_long(Buf, &Ptr, '|');
01861               StrBufSkip_NTokenS(Buf, &Ptr, '|', 3);  /* filename, cbtype, mimetype */
01862               if (Mime->Charset == NULL) Mime->Charset = NewStrBuf();
01863               StrBufExtract_NextToken(Mime->Charset, Buf, &Ptr, '|');
01864               
01865               if (Mime->Data == NULL)
01866                      Mime->Data = NewStrBufPlain(NULL, bytes);
01867               StrBuf_ServGetBLOBBuffered(Mime->Data, bytes);
01868        }
01869        else {
01870               FlushStrBuf(Mime->Data);
01871               /* TODO XImportant message */
01872        }
01873        FreeStrBuf(&Buf);
01874 }
01875 
01876 
01877 void view_mimepart(void) {
01878        mimepart(0);
01879 }
01880 
01881 void download_mimepart(void) {
01882        mimepart(1);
01883 }
01884 
01885 void view_postpart(void) {
01886        StrBuf *filename = NewStrBuf();
01887        StrBuf *partnum = NewStrBuf();
01888 
01889        StrBufExtract_token(partnum, WC->Hdr->HR.ReqLine, 0, '/');
01890        StrBufExtract_token(filename, WC->Hdr->HR.ReqLine, 1, '/');
01891 
01892        postpart(partnum, filename, 0);
01893 
01894        FreeStrBuf(&filename);
01895        FreeStrBuf(&partnum);
01896 }
01897 
01898 void download_postpart(void) {
01899        StrBuf *filename = NewStrBuf();
01900        StrBuf *partnum = NewStrBuf();
01901 
01902        StrBufExtract_token(partnum, WC->Hdr->HR.ReqLine, 0, '/');
01903        StrBufExtract_token(filename, WC->Hdr->HR.ReqLine, 1, '/');
01904 
01905        postpart(partnum, filename, 1);
01906 
01907        FreeStrBuf(&filename);
01908        FreeStrBuf(&partnum);
01909 }
01910 
01911 
01912 
01913 void show_num_attachments(void) {
01914        wc_printf("%d", GetCount(WC->attachments));
01915 }
01916 
01917 
01918 void h_readnew(void) { readloop(readnew, eUseDefault);}
01919 void h_readold(void) { readloop(readold, eUseDefault);}
01920 void h_readfwd(void) { readloop(readfwd, eUseDefault);}
01921 void h_headers(void) { readloop(headers, eUseDefault);}
01922 void h_do_search(void) { readloop(do_search, eUseDefault);}
01923 void h_readgt(void) { readloop(readgt, eUseDefault);}
01924 void h_readlt(void) { readloop(readlt, eUseDefault);}
01925 
01926 
01927 
01928 /* Output message list in JSON format */
01929 void jsonMessageList(void) {
01930        StrBuf *View = NewStrBuf();
01931        const StrBuf *room = sbstr("room");
01932        long oper = (havebstr("query")) ? do_search : readnew;
01933        StrBufPrintf(View, "%d", VIEW_JSON_LIST);
01934        putbstr("view", View);; 
01935        gotoroom(room);
01936        readloop(oper, eUseDefault);
01937 }
01938 
01939 void RegisterReadLoopHandlerset(
01940        int RoomType,
01941        GetParamsGetServerCall_func GetParamsGetServerCall,
01942        PrintViewHeader_func PrintPageHeader,
01943        PrintViewHeader_func PrintViewHeader,
01944        load_msg_ptrs_detailheaders LH,
01945        LoadMsgFromServer_func LoadMsgFromServer,
01946        RenderView_or_Tail_func RenderView_or_Tail,
01947        View_Cleanup_func ViewCleanup
01948        )
01949 {
01950        RoomRenderer *Handler;
01951 
01952        Handler = (RoomRenderer*) malloc(sizeof(RoomRenderer));
01953 
01954        Handler->RoomType = RoomType;
01955        Handler->GetParamsGetServerCall = GetParamsGetServerCall;
01956        Handler->PrintPageHeader = PrintPageHeader;
01957        Handler->PrintViewHeader = PrintViewHeader;
01958        Handler->LoadMsgFromServer = LoadMsgFromServer;
01959        Handler->RenderView_or_Tail = RenderView_or_Tail;
01960        Handler->ViewCleanup = ViewCleanup;
01961        Handler->LHParse = LH;
01962 
01963        Put(ReadLoopHandler, IKEY(RoomType), Handler, NULL);
01964 }
01965 
01966 void 
01967 InitModule_MSG
01968 (void)
01969 {
01970        RegisterPreference("use_sig",
01971                         _("Attach signature to email messages?"), 
01972                         PRF_YESNO, 
01973                         NULL);
01974        RegisterPreference("signature", _("Use this signature:"), PRF_QP_STRING, NULL);
01975        RegisterPreference("default_header_charset", 
01976                         _("Default character set for email headers:"), 
01977                         PRF_STRING, 
01978                         NULL);
01979        RegisterPreference("defaultfrom", _("Preferred email address"), PRF_STRING, NULL);
01980        RegisterPreference("defaultname", 
01981                         _("Preferred display name for email messages"), 
01982                         PRF_STRING, 
01983                         NULL);
01984        RegisterPreference("defaulthandle", 
01985                         _("Preferred display name for bulletin board posts"), 
01986                         PRF_STRING, 
01987                         NULL);
01988        RegisterPreference("mailbox",_("Mailbox view mode"), PRF_STRING, NULL);
01989 
01990        WebcitAddUrlHandler(HKEY("readnew"), "", 0, h_readnew, ANONYMOUS|NEED_URL);
01991        WebcitAddUrlHandler(HKEY("readold"), "", 0, h_readold, ANONYMOUS|NEED_URL);
01992        WebcitAddUrlHandler(HKEY("readfwd"), "", 0, h_readfwd, ANONYMOUS|NEED_URL);
01993        WebcitAddUrlHandler(HKEY("headers"), "", 0, h_headers, NEED_URL);
01994        WebcitAddUrlHandler(HKEY("readgt"), "", 0, h_readgt, ANONYMOUS|NEED_URL);
01995        WebcitAddUrlHandler(HKEY("readlt"), "", 0, h_readlt, ANONYMOUS|NEED_URL);
01996        WebcitAddUrlHandler(HKEY("do_search"), "", 0, h_do_search, 0);
01997        WebcitAddUrlHandler(HKEY("display_enter"), "", 0, display_enter, 0);
01998        WebcitAddUrlHandler(HKEY("post"), "", 0, post_message, PROHIBIT_STARTPAGE);
01999        WebcitAddUrlHandler(HKEY("move_msg"), "", 0, move_msg, PROHIBIT_STARTPAGE);
02000        WebcitAddUrlHandler(HKEY("delete_msg"), "", 0, delete_msg, PROHIBIT_STARTPAGE);
02001        WebcitAddUrlHandler(HKEY("msg"), "", 0, embed_message, NEED_URL);
02002        WebcitAddUrlHandler(HKEY("message"), "", 0, handle_one_message, NEED_URL|XHTTP_COMMANDS|COOKIEUNNEEDED|FORCE_SESSIONCLOSE);
02003        WebcitAddUrlHandler(HKEY("printmsg"), "", 0, print_message, NEED_URL);
02004        WebcitAddUrlHandler(HKEY("msgheaders"), "", 0, display_headers, NEED_URL);
02005 
02006        WebcitAddUrlHandler(HKEY("mimepart"), "", 0, view_mimepart, NEED_URL);
02007        WebcitAddUrlHandler(HKEY("mimepart_download"), "", 0, download_mimepart, NEED_URL);
02008        WebcitAddUrlHandler(HKEY("postpart"), "", 0, view_postpart, NEED_URL|PROHIBIT_STARTPAGE);
02009        WebcitAddUrlHandler(HKEY("postpart_download"), "", 0, download_postpart, NEED_URL|PROHIBIT_STARTPAGE);
02010        WebcitAddUrlHandler(HKEY("upload_attachment"), "", 0, upload_attachment, AJAX);
02011        WebcitAddUrlHandler(HKEY("remove_attachment"), "", 0, remove_attachment, AJAX);
02012        WebcitAddUrlHandler(HKEY("show_num_attachments"), "", 0, show_num_attachments, AJAX);
02013 
02014        /* json */
02015        WebcitAddUrlHandler(HKEY("roommsgs"), "", 0, jsonMessageList,0);
02016 
02017        l_subj = FourHash("subj", 4);
02018        l_wefw = FourHash("wefw", 4);
02019        l_msgn = FourHash("msgn", 4);
02020        l_from = FourHash("from", 4);
02021        l_rcpt = FourHash("rcpt", 4);
02022        l_cccc = FourHash("cccc", 4);
02023        l_node = FourHash("node", 4);
02024        l_rfca = FourHash("rfca", 4);
02025 
02026        return ;
02027 }
02028 
02029 void
02030 SessionDetachModule_MSG
02031 (wcsession *sess)
02032 {
02033        DeleteHash(&sess->summ);
02034 }