X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fmessages.c;h=dde57cc4a2f2761d53818c81cea621958ef895cb;hb=27e6a22f021cffe2cee1a41accbf9d2f78b20dab;hp=171373b4a48e17c33636bb25ea553300362b2a43;hpb=8a38099b09f3080925ce7c4a62a016d6884f31bf;p=citadel.git diff --git a/webcit/messages.c b/webcit/messages.c index 171373b4a..dde57cc4a 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -12,275 +12,77 @@ HashList *MsgHeaderHandler = NULL; HashList *MsgEvaluators = NULL; HashList *MimeRenderHandler = NULL; +int dbg_analyze_msg = 0; #define SUBJ_COL_WIDTH_PCT 50 /**< Mailbox view column width */ #define SENDER_COL_WIDTH_PCT 30 /**< Mailbox view column width */ #define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /**< Mailbox view column width */ - +void jsonMessageListHdr(void); void display_enter(void); -int longcmp_r(const void *s1, const void *s2); -int summcmp_subj(const void *s1, const void *s2); -int summcmp_rsubj(const void *s1, const void *s2); -int summcmp_sender(const void *s1, const void *s2); -int summcmp_rsender(const void *s1, const void *s2); -int summcmp_date(const void *s1, const void *s2); -int summcmp_rdate(const void *s1, const void *s2); /*----------------------------------------------------------------------------*/ typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf); - - -const char* SortIcons[3] = { - "static/up_pointer.gif", - "static/down_pointer.gif", - "static/sort_none.gif" -}; - -enum {/// SortByEnum - eDate, - eRDate, - eSubject, - eRSubject, - eSender, - eRSender, - eReverse, - eUnSet -}; - -/* SortEnum to plain string representation */ -static const char* SortByStrings[] = { - "date", - "rdate", - "subject", - "rsubject", - "sender", - "rsender", - "reverse", - "unset" -}; - -/* SortEnum to sort-Function Table */ -const CompareFunc SortFuncs[eUnSet] = { - summcmp_date, - summcmp_rdate, - summcmp_subj, - summcmp_rsubj, - summcmp_sender, - summcmp_rsender, - summcmp_rdate -}; - -/* given a SortEnum, which icon should we choose? */ -const int SortDateToIcon[eUnSet] = { eUp, eDown, eNone, eNone, eNone, eNone, eNone}; -const int SortSubjectToIcon[eUnSet] = { eNone, eNone, eUp, eDown, eNone, eNone, eNone}; -const int SortSenderToIcon[eUnSet] = { eNone, eNone, eNone, eNone, eUp, eDown, eNone}; - -/* given a SortEnum, which would be the "opposite" search option? */ -const int DateInvertSortString[eUnSet] = { eRDate, eDate, eDate, eDate, eDate, eDate, eDate}; -const int SubjectInvertSortString[eUnSet] = { eSubject, eSubject, eRSubject, eUnSet, eSubject, eSubject, eSubject}; -const int SenderInvertSortString[eUnSet] = { eSender, eSender, eSender, eSender, eRSender, eUnSet, eSender}; - +typedef struct _MsgPartEvaluatorStruct { + MsgPartEvaluatorFunc f; +}MsgPartEvaluatorStruct; /*----------------------------------------------------------------------------*/ -/* - * Translates sortoption String to its SortEnum representation - * returns the enum matching the string; defaults to RDate - */ -//SortByEnum -int StrToESort (const StrBuf *sortby) -{////todoo: hash - int result = eDate; - - if (!IsEmptyStr(ChrPtr(sortby))) while (result < eUnSet){ - if (!strcasecmp(ChrPtr(sortby), - SortByStrings[result])) - return result; - result ++; - } - return eRDate; -} - - -typedef int (*QSortFunction) (const void*, const void*); - -/* - * qsort() compatible function to compare two longs in descending order. - */ -int longcmp_r(const void *s1, const void *s2) { - long l1; - long l2; - - l1 = *(long *)GetSearchPayload(s1); - l2 = *(long *)GetSearchPayload(s2); - - if (l1 > l2) return(-1); - if (l1 < l2) return(+1); - return(0); -} - -/* - * qsort() compatible function to compare two longs in descending order. - */ -int qlongcmp_r(const void *s1, const void *s2) { - long l1 = (long) s1; - long l2 = (long) s2; - - if (l1 > l2) return(-1); - if (l1 < l2) return(+1); - return(0); -} - - -/* - * qsort() compatible function to compare two message summary structs by ascending subject. - */ -int summcmp_subj(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - return strcasecmp(ChrPtr(summ1->subj), ChrPtr(summ2->subj)); -} - -/* - * qsort() compatible function to compare two message summary structs by descending subject. - */ -int summcmp_rsubj(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - return strcasecmp(ChrPtr(summ2->subj), ChrPtr(summ1->subj)); -} - -/* - * qsort() compatible function to compare two message summary structs by ascending sender. - */ -int summcmp_sender(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - return strcasecmp(ChrPtr(summ1->from), ChrPtr(summ2->from)); -} - -/* - * qsort() compatible function to compare two message summary structs by descending sender. - */ -int summcmp_rsender(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - return strcasecmp(ChrPtr(summ2->from), ChrPtr(summ1->from)); -} - -/* - * qsort() compatible function to compare two message summary structs by ascending date. - */ -int summcmp_date(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - if (summ1->date < summ2->date) return -1; - else if (summ1->date > summ2->date) return +1; - else return 0; -} - -/* - * qsort() compatible function to compare two message summary structs by descending date. - */ -int summcmp_rdate(const void *s1, const void *s2) { - message_summary *summ1; - message_summary *summ2; - - summ1 = (message_summary *)GetSearchPayload(s1); - summ2 = (message_summary *)GetSearchPayload(s2); - - if (summ1->date < summ2->date) return +1; - else if (summ1->date > summ2->date) return -1; - else return 0; -} - - - - - - -/* - * I wanna SEE that message! - * - * msgnum Message number to display - * printable_view Nonzero to display a printable view - * section Optional for encapsulated message/rfc822 submessage - */ -void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, int printable_view, const StrBuf *section) { +int load_message(message_summary *Msg, + StrBuf *FoundCharset, + StrBuf **Error) +{ + wcsession *WCC = WC; StrBuf *Buf; - StrBuf *Token; - StrBuf *FoundCharset; - message_summary *Msg = NULL; + StrBuf *HdrToken; headereval *Hdr; void *vHdr; char buf[SIZ]; -// char mime_submessages[256] = ""; - char reply_references[1024] = ""; int Done = 0; int state=0; - Buf = NewStrBuf(); - lprintf(1, "-------------------MSG4 %ld|%s--------------\n", msgnum, ChrPtr(section)); - serv_printf("MSG4 %ld|%s", msgnum, ChrPtr(section)); + lprintf(1, "-------------------MSG4 %ld|%s--------------\n", Msg->msgnum, ChrPtr(Msg->PartNum)); + if (Msg->PartNum != NULL) + serv_printf("MSG4 %ld|%s", Msg->msgnum, ChrPtr(Msg->PartNum)); + else + serv_printf("MSG4 %ld", Msg->msgnum); + StrBuf_ServGetln(Buf); if (GetServerStatus(Buf, NULL) != 1) { - StrBufAppendPrintf(Target, ""); - StrBufAppendPrintf(Target, _("ERROR:")); - StrBufAppendPrintf(Target, " %s
\n", &buf[4]); + *Error = NewStrBuf(); + StrBufAppendPrintf(*Error, ""); + StrBufAppendPrintf(*Error, _("ERROR:")); + StrBufAppendPrintf(*Error, " %s
\n", &buf[4]); FreeStrBuf(&Buf); - return; + return 0; } - svputlong("MsgPrintable", printable_view); - /** begin everythingamundo table */ - - Token = NewStrBuf(); - Msg = (message_summary *)malloc(sizeof(message_summary)); - memset(Msg, 0, sizeof(message_summary)); - Msg->msgnum = msgnum; - FoundCharset = NewStrBuf(); + /** begin everythingamundo table */ + HdrToken = NewStrBuf(); while ((StrBuf_ServGetln(Buf)>=0) && !Done) { if ( (StrLength(Buf)==3) && !strcmp(ChrPtr(Buf), "000")) { Done = 1; if (state < 2) { - StrBufAppendPrintf(Target, ""); - StrBufAppendPrintf(Target, _("unexpected end of message")); - StrBufAppendPrintf(Target, " (1)

\n"); - StrBufAppendPrintf(Target, "\n"); - FreeStrBuf(&Buf); - FreeStrBuf(&Token); - DestroyMessageSummary(Msg); - FreeStrBuf(&FoundCharset); - return; - } - else { - break; + lprintf(1, _("unexpected end of message")); + if (Msg->MsgBody->Data == NULL) + Msg->MsgBody->Data = NewStrBuf(); + Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/html")); + StrBufAppendPrintf(Msg->MsgBody->Data, "
"); + StrBufAppendPrintf(Msg->MsgBody->Data, _("unexpected end of message")); + StrBufAppendPrintf(Msg->MsgBody->Data, " (1)

\n"); + StrBufAppendPrintf(Msg->MsgBody->Data, "
\n"); } + break; } switch (state) { case 0:/* Citadel Message Headers */ @@ -288,35 +90,45 @@ void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, i state ++; break; } - StrBufExtract_token(Token, Buf, 0, '='); - StrBufCutLeft(Buf, StrLength(Token) + 1); + StrBufExtract_token(HdrToken, Buf, 0, '='); + StrBufCutLeft(Buf, StrLength(HdrToken) + 1); - lprintf(1, ":: [%s] = [%s]\n", ChrPtr(Token), ChrPtr(Buf)); - if (GetHash(MsgHeaderHandler, SKEY(Token), &vHdr) && +#ifdef TECH_PREVIEW + if (dbg_analyze_msg) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf)); +#endif + /* look up one of the examine_* functions to parse the content */ + if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) && (vHdr != NULL)) { Hdr = (headereval*)vHdr; Hdr->evaluator(Msg, Buf, FoundCharset); if (Hdr->Type == 1) { state++; } - } - else lprintf(1, "don't know how to handle message header[%s]\n", ChrPtr(Token)); + }/* TODO: + else LogError(Target, + __FUNCTION__, + "don't know how to handle message header[%s]\n", + ChrPtr(HdrToken)); + */ break; case 1:/* Message Mime Header */ if (StrLength(Buf) == 0) { state++; - if (Msg->MsgBody.ContentType == NULL) + if (Msg->MsgBody->ContentType == NULL) /* end of header or no header? */ - Msg->MsgBody.ContentType = NewStrBufPlain(HKEY("text/plain")); + Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/plain")); /* usual end of mime header */ } else { - StrBufExtract_token(Token, Buf, 0, ':'); - if (StrLength(Token) > 0) { - StrBufCutLeft(Buf, StrLength(Token) + 1); - lprintf(1, ":: [%s] = [%s]\n", ChrPtr(Token), ChrPtr(Buf)); - if (GetHash(MsgHeaderHandler, SKEY(Token), &vHdr) && + StrBufExtract_token(HdrToken, Buf, 0, ':'); + if (StrLength(HdrToken) > 0) { + StrBufCutLeft(Buf, StrLength(HdrToken) + 1); +#ifdef TECH_PREVIEW + if (dbg_analyze_msg) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf)); +#endif + /* the examine*'s know how to do with mime headers too... */ + if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) && (vHdr != NULL)) { Hdr = (headereval*)vHdr; Hdr->evaluator(Msg, Buf, FoundCharset); @@ -326,45 +138,28 @@ void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, i } case 2: /* Message Body */ - if (Msg->MsgBody.size_known > 0) { - StrBuf_ServGetBLOB(Msg->MsgBody.Data, Msg->MsgBody.length); + if (Msg->MsgBody->size_known > 0) { + StrBuf_ServGetBLOBBuffered(Msg->MsgBody->Data, Msg->MsgBody->length); state ++; - /// todo: check next line, if not 000, append following lines + /*/ todo: check next line, if not 000, append following lines */ } else if (1){ - if (StrLength(Msg->MsgBody.Data) > 0) - StrBufAppendBufPlain(Msg->MsgBody.Data, "\n", 1, 0); - StrBufAppendBuf(Msg->MsgBody.Data, Buf, 0); + if (StrLength(Msg->MsgBody->Data) > 0) + StrBufAppendBufPlain(Msg->MsgBody->Data, "\n", 1, 0); + StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0); } break; case 3: - StrBufAppendBuf(Msg->MsgBody.Data, Buf, 0); + StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0); break; } } - - /* strip the bare contenttype, so we ommit charset etc. */ - StrBufExtract_token(Buf, Msg->MsgBody.ContentType, 0, ';'); - StrBufTrim(Buf); - if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) && - (vHdr != NULL)) { - RenderMimeFunc Render; - Render = (RenderMimeFunc)vHdr; - Render(&Msg->MsgBody, NULL, FoundCharset); - } - - - if (StrLength(Msg->reply_references)> 0) { - /* Trim down excessively long lists of thread references. We eliminate the - * second one in the list so that the thread root remains intact. - */ - int rrtok = num_tokens(ChrPtr(Msg->reply_references), '|'); - int rrlen = StrLength(Msg->reply_references); - if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) { - remove_token(reply_references, 1, '|');////todo - } - } + if (Msg->AllAttach == NULL) + Msg->AllAttach = NewHash(1,NULL); + /* now we put the body mimepart we read above into the mimelist */ + Put(Msg->AllAttach, SKEY(Msg->MsgBody->PartNum), Msg->MsgBody, DestroyMime); + /* Generate a reply-to address */ if (StrLength(Msg->Rfca) > 0) { if (Msg->reply_to == NULL) @@ -380,8 +175,8 @@ void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, i else { if ((StrLength(Msg->OtherNode)>0) && - (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_nodename)) && - (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_humannode)) ) + (strcasecmp(ChrPtr(Msg->OtherNode), ChrPtr(WCC->serv_info->serv_nodename))) && + (strcasecmp(ChrPtr(Msg->OtherNode), ChrPtr(WCC->serv_info->serv_humannode)) )) { if (Msg->reply_to == NULL) Msg->reply_to = NewStrBuf(); @@ -397,16 +192,94 @@ void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, i StrBufAppendBuf(Msg->reply_to, Msg->from, 0); } } - DoTemplate(tmpl, tmpllen, Target, Msg, CTX_MAILSUM); + FreeStrBuf(&Buf); + FreeStrBuf(&HdrToken); + return 1; +} + + + +/* + * I wanna SEE that message! + * + * msgnum Message number to display + * printable_view Nonzero to display a printable view + * section Optional for encapsulated message/rfc822 submessage + */ +int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, const StrBuf *PartNum) +{ + StrBuf *Buf; + StrBuf *FoundCharset; + HashPos *it; + void *vMime; + message_summary *Msg = NULL; + void *vHdr; + long len; + const char *Key; + WCTemplputParams SubTP; + StrBuf *Error = NULL; + + Buf = NewStrBuf(); + FoundCharset = NewStrBuf(); + Msg = (message_summary *)malloc(sizeof(message_summary)); + memset(Msg, 0, sizeof(message_summary)); + Msg->msgnum = msgnum; + Msg->PartNum = PartNum; + Msg->MsgBody = (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment)); + memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment)); + Msg->MsgBody->msgnum = msgnum; + + if (!load_message(Msg, FoundCharset, &Error)) { + StrBufAppendBuf(Target, Error, 0); + FreeStrBuf(&Error); + } + + /* strip the bare contenttype, so we ommit charset etc. */ + StrBufExtract_token(Buf, Msg->MsgBody->ContentType, 0, ';'); + StrBufTrim(Buf); + /* look up the renderer, that will convert this mimeitem into the htmlized form */ + if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) && + (vHdr != NULL)) { + RenderMimeFuncStruct *Render; + Render = (RenderMimeFuncStruct*)vHdr; + Render->f(Msg->MsgBody, NULL, FoundCharset); + } + + if (StrLength(Msg->reply_references)> 0) { + /* Trim down excessively long lists of thread references. We eliminate the + * second one in the list so that the thread root remains intact. + */ + int rrtok = num_tokens(ChrPtr(Msg->reply_references), '|'); + int rrlen = StrLength(Msg->reply_references); + if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) { + StrBufRemove_token(Msg->reply_references, 1, '|'); + } + } + + /* now check if we need to translate some mimeparts, and remove the duplicate */ + it = GetNewHashPos(Msg->AllAttach, 0); + while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && + (vMime != NULL)) { + wc_mime_attachment *Mime = (wc_mime_attachment*) vMime; + evaluate_mime_part(Msg, Mime); + } + DeleteHashPos(&it); + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_MAILSUM; + SubTP.Context = Msg; + DoTemplate(tmpl, tmpllen, Target, &SubTP); DestroyMessageSummary(Msg); FreeStrBuf(&FoundCharset); - FreeStrBuf(&Token); FreeStrBuf(&Buf); + return 1; } + + + /* * Unadorned HTML output of an individual message, suitable * for placing in a hidden iframe, for printing, or whatever @@ -415,13 +288,14 @@ void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, i */ void embed_message(void) { long msgnum = 0L; + wcsession *WCC = WC; const StrBuf *Tmpl = sbstr("template"); - msgnum = StrTol(WC->UrlFragment1); + msgnum = StrTol(WCC->UrlFragment2); if (StrLength(Tmpl) > 0) - read_message(WC->WBuf, SKEY(Tmpl), msgnum, 0, NULL); + read_message(WCC->WBuf, SKEY(Tmpl), msgnum, NULL); else - read_message(WC->WBuf, HKEY("view_message"), msgnum, 0, NULL); + read_message(WCC->WBuf, HKEY("view_message"), msgnum, NULL); } @@ -433,7 +307,7 @@ void embed_message(void) { void print_message(void) { long msgnum = 0L; - msgnum = StrTol(WC->UrlFragment1); + msgnum = StrTol(WC->UrlFragment2); output_headers(0, 0, 0, 0, 0, 0); hprintf("Content-type: text/html\r\n" @@ -442,7 +316,7 @@ void print_message(void) { begin_burst(); - read_message(WC->WBuf, HKEY("view_message_print"), msgnum, 1, NULL); + read_message(WC->WBuf, HKEY("view_message_print"), msgnum, NULL); wDumpContent(0); } @@ -454,11 +328,11 @@ void print_message(void) { */ void mobile_message_view(void) { long msgnum = 0L; - msgnum = StrTol(WC->UrlFragment1); + msgnum = StrTol(WC->UrlFragment2); output_headers(1, 0, 0, 0, 0, 1); begin_burst(); do_template("msgcontrols", NULL); - read_message(WC->WBuf, HKEY("view_message"), msgnum,1, NULL); + read_message(WC->WBuf, HKEY("view_message"), msgnum, NULL); wDumpContent(0); } @@ -471,7 +345,7 @@ void display_headers(void) { long msgnum = 0L; char buf[1024]; - msgnum = StrTol(WC->UrlFragment1); + msgnum = StrTol(WC->UrlFragment2); output_headers(0, 0, 0, 0, 0, 0); hprintf("Content-type: text/plain\r\n" @@ -492,341 +366,10 @@ void display_headers(void) { } - -/** - * \brief Read message in simple, JavaScript-embeddable form for 'forward' - * or 'reply quoted' operations. - * - * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks - * in this function. Doing so would throw a JavaScript error in the - * 'supplied text' argument to the editor. - * - * \param msgnum Message number of the message we want to quote - * \param forward_attachments Nonzero if we want attachments to be forwarded - */ -void pullquote_message(long msgnum, int forward_attachments, int include_headers) { - struct wcsession *WCC = WC; - char buf[SIZ]; - char mime_partnum[256]; - char mime_filename[256]; - char mime_content_type[256]; - char mime_charset[256]; - char mime_disposition[256]; - int mime_length; - char *attachments = NULL; - char *ptr = NULL; - int num_attachments = 0; - wc_attachment *att; - char m_subject[1024]; - char from[256]; - char node[256]; - char rfca[256]; - char to[256]; - char reply_to[512]; - char now[256]; - int format_type = 0; - int nhdr = 0; - int bq = 0; - int i = 0; -#ifdef HAVE_ICONV - iconv_t ic = (iconv_t)(-1) ; - char *ibuf; /**< Buffer of characters to be converted */ - char *obuf; /**< Buffer for converted characters */ - size_t ibuflen; /**< Length of input buffer */ - size_t obuflen; /**< Length of output buffer */ - char *osav; /**< Saved pointer to output buffer */ -#endif - - strcpy(from, ""); - strcpy(node, ""); - strcpy(rfca, ""); - strcpy(reply_to, ""); - strcpy(mime_content_type, "text/plain"); - strcpy(mime_charset, "us-ascii"); - - serv_printf("MSG4 %ld", msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf(_("ERROR:")); - wprintf("%s
", &buf[4]); - return; - } - - strcpy(m_subject, ""); - - while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) { - if (!strcmp(buf, "000")) { - wprintf("%s (3)", _("unexpected end of message")); - return; - } - if (include_headers) { - if (!strncasecmp(buf, "nhdr=yes", 8)) - nhdr = 1; - if (nhdr == 1) - buf[0] = '_'; - if (!strncasecmp(buf, "type=", 5)) - format_type = atoi(&buf[5]); - if (!strncasecmp(buf, "from=", 5)) { - strcpy(from, &buf[5]); - wprintf(_("from ")); - utf8ify_rfc822_string(from); - msgescputs(from); - } - if (!strncasecmp(buf, "subj=", 5)) { - strcpy(m_subject, &buf[5]); - } - if ((!strncasecmp(buf, "hnod=", 5)) - && (strcasecmp(&buf[5], serv_info.serv_humannode))) { - wprintf("(%s) ", &buf[5]); - } - if ((!strncasecmp(buf, "room=", 5)) - && (strcasecmp(&buf[5], WC->wc_roomname)) - && (!IsEmptyStr(&buf[5])) ) { - wprintf(_("in ")); - wprintf("%s> ", &buf[5]); - } - if (!strncasecmp(buf, "rfca=", 5)) { - strcpy(rfca, &buf[5]); - wprintf("<"); - msgescputs(rfca); - wprintf("> "); - } - if (!strncasecmp(buf, "node=", 5)) { - strcpy(node, &buf[5]); - if ( ((WC->room_flags & QR_NETWORK) - || ((strcasecmp(&buf[5], serv_info.serv_nodename) - && (strcasecmp(&buf[5], serv_info.serv_fqdn))))) - && (IsEmptyStr(rfca)) - ) { - wprintf("@%s ", &buf[5]); - } - } - if (!strncasecmp(buf, "rcpt=", 5)) { - wprintf(_("to ")); - strcpy(to, &buf[5]); - utf8ify_rfc822_string(to); - wprintf("%s ", to); - } - if (!strncasecmp(buf, "time=", 5)) { - webcit_fmt_date(now, atol(&buf[5]), 0); - wprintf("%s ", now); - } - } - - /** - * Save attachment info for later. We can't start downloading them - * yet because we're in the middle of a server transaction. - */ - if (!strncasecmp(buf, "part=", 5)) { - ptr = malloc( (strlen(buf) + ((attachments != NULL) ? strlen(attachments) : 0)) ) ; - if (ptr != NULL) { - ++num_attachments; - sprintf(ptr, "%s%s\n", - ((attachments != NULL) ? attachments : ""), - &buf[5] - ); - free(attachments); - attachments = ptr; - lprintf(9, "attachments=<%s>\n", attachments); - } - } - - } - - if (include_headers) { - wprintf("
"); - - utf8ify_rfc822_string(m_subject); - if (!IsEmptyStr(m_subject)) { - wprintf(_("Subject:")); - wprintf(" "); - msgescputs(m_subject); - wprintf("
"); - } - - /** - * Begin body - */ - wprintf("
"); - } - - /** - * Learn the content type - */ - strcpy(mime_content_type, "text/plain"); - while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) { - if (!strcmp(buf, "000")) { - wprintf("%s (4)", _("unexpected end of message")); - goto ENDBODY; - } - if (!strncasecmp(buf, "Content-type: ", 14)) { - int len; - safestrncpy(mime_content_type, &buf[14], - sizeof(mime_content_type)); - for (i=0; i 0) && (buf[len-1] == '\n')) buf[--len] = 0; - if ((len > 0) && (buf[len-1] == '\r')) buf[--len] = 0; - -#ifdef HAVE_ICONV - if (ic != (iconv_t)(-1) ) { - ibuf = buf; - ibuflen = len; - obuflen = SIZ; - obuf = (char *) malloc(obuflen); - osav = obuf; - iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); - osav[SIZ-obuflen] = 0; - safestrncpy(buf, osav, sizeof buf); - free(osav); - } -#endif - - len = strlen(buf); - while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1]))) - buf[--len] = 0; - if ((bq == 0) && - ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { - wprintf("
"); - bq = 1; - } else if ((bq == 1) && - (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) { - wprintf("
"); - bq = 0; - } - wprintf(""); - url(buf, sizeof(buf)); - msgescputs1(buf); - wprintf("
"); - } - wprintf("
"); - } - - /** HTML just gets escaped and stuffed back into the editor */ - else if (!strcasecmp(mime_content_type, "text/html")) { - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - strcat(buf, "\n"); - msgescputs(buf); - } - }//// TODO: charset? utf8? - - /** Unknown weirdness ... don't know how to handle this content type */ - else { - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { } - } - -ENDBODY: - /** end of body handler */ - - /* - * If there were attachments, we have to download them and insert them - * into the attachment chain for the forwarded message we are composing. - */ - if ( (forward_attachments) && (num_attachments) ) { - for (i=0; ilength = mime_length; - att->content_type = NewStrBufPlain(mime_content_type, -1); - att->filename = NewStrBufPlain(mime_filename, -1); - att->data = load_mimepart(msgnum, mime_partnum); - - 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, free_attachment); - } - - } - } - -#ifdef HAVE_ICONV - if (ic != (iconv_t)(-1) ) { - iconv_close(ic); - } -#endif - - if (attachments != NULL) { - free(attachments); - } -} - - - - message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) { void *vEval; - MsgPartEvaluatorFunc Eval; + MsgPartEvaluatorStruct *Eval; message_summary *Msg; StrBuf *Buf; const char *buf; @@ -853,9 +396,9 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu ebuf = strchr(ChrPtr(Buf), '='); nBuf = ebuf - buf; if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) { - Eval = (MsgPartEvaluatorFunc) vEval; + Eval = (MsgPartEvaluatorStruct*) vEval; StrBufCutLeft(Buf, nBuf + 1); - Eval(Msg, Buf); + Eval->f(Msg, Buf); } else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf)); } @@ -863,138 +406,6 @@ message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSu } -/** - * \brief display the adressbook overview - * \param msgnum the citadel message number - * \param alpha what???? - */ -void display_addressbook(long msgnum, char alpha) { - //char buf[SIZ]; - /* char mime_partnum[SIZ]; */ -/* char mime_filename[SIZ]; */ -/* char mime_content_type[SIZ]; */ - ///char mime_disposition[SIZ]; - //int mime_length; - char vcard_partnum[SIZ]; - char *vcard_source = NULL; - message_summary summ;////TODO: this will leak - - memset(&summ, 0, sizeof(summ)); - ///safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj); -///Load Message headers -// Msg = - if (!IsEmptyStr(vcard_partnum)) { - vcard_source = load_mimepart(msgnum, vcard_partnum); - if (vcard_source != NULL) { - - /** Display the summary line */ - display_vcard(WC->WBuf, vcard_source, alpha, 0, NULL,msgnum); - - /** If it's my vCard I can edit it */ - if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) - || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) - || (WC->wc_view == VIEW_ADDRESSBOOK) - ) { - wprintf("", - msgnum, vcard_partnum); - wprintf("[%s]", _("edit")); - } - - free(vcard_source); - } - } - -} - - - -/** - * \brief If it's an old "Firstname Lastname" style record, try to convert it. - * \param namebuf name to analyze, reverse if nescessary - */ -void lastfirst_firstlast(char *namebuf) { - char firstname[SIZ]; - char lastname[SIZ]; - int i; - - if (namebuf == NULL) return; - if (strchr(namebuf, ';') != NULL) return; - - i = num_tokens(namebuf, ' '); - if (i < 2) return; - - extract_token(lastname, namebuf, i-1, ' ', sizeof lastname); - remove_token(namebuf, i-1, ' '); - strcpy(firstname, namebuf); - sprintf(namebuf, "%s; %s", lastname, firstname); -} - -/** - * \brief fetch what??? name - * \param msgnum the citadel message number - * \param namebuf where to put the name in??? - */ -void fetch_ab_name(message_summary *Msg, char *namebuf) { - char buf[SIZ]; - char mime_partnum[SIZ]; - char mime_filename[SIZ]; - char mime_content_type[SIZ]; - char mime_disposition[SIZ]; - int mime_length; - char vcard_partnum[SIZ]; - char *vcard_source = NULL; - int i, len; - message_summary summ;/// TODO this will lak - - if (namebuf == NULL) return; - strcpy(namebuf, ""); - - memset(&summ, 0, sizeof(summ)); - //////safestrncpy(summ.subj, "(no subject)", sizeof summ.subj); - - sprintf(buf, "MSG0 %ld|0", Msg->msgnum); /** unfortunately we need the mime info now */ - serv_puts(buf); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') return; - - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (!strncasecmp(buf, "part=", 5)) { - extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename); - extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum); - extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition); - extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); - mime_length = extract_int(&buf[5], 5); - - if ( (!strcasecmp(mime_content_type, "text/x-vcard")) - || (!strcasecmp(mime_content_type, "text/vcard")) ) { - strcpy(vcard_partnum, mime_partnum); - } - - } - } - - if (!IsEmptyStr(vcard_partnum)) { - vcard_source = load_mimepart(Msg->msgnum, vcard_partnum); - if (vcard_source != NULL) { - - /* Grab the name off the card */ - display_vcard(WC->WBuf, vcard_source, 0, 0, namebuf, Msg->msgnum); - - free(vcard_source); - } - } - - lastfirst_firstlast(namebuf); - striplt(namebuf); - len = strlen(namebuf); - for (i=0; isumm != NULL) { - if (WCC->summ != NULL) - DeleteHash(&WCC->summ); + DeleteHash(&WCC->summ); } WCC->summ = NewHash(1, Flathash); - nummsgs = 0; - maxload = 1000;/// TODO + maxload = 10000; Buf = NewStrBuf(); serv_puts(servcmd); @@ -1039,61 +441,73 @@ int load_msg_ptrs(char *servcmd, int with_headers) FreeStrBuf(&Buf); return (nummsgs); } -// TODO if (with_headers) { //// TODO: Have Attachments? Buf2 = NewStrBuf(); while (len = StrBuf_ServGetln(Buf), ((len != 3) || strcmp(ChrPtr(Buf), "000")!= 0)) { if (nummsgs < maxload) { + skipit = 0; + Ptr = NULL; Msg = (message_summary*)malloc(sizeof(message_summary)); memset(Msg, 0, sizeof(message_summary)); - Msg->msgnum = StrBufExtract_long(Buf, 0, '|'); - Msg->date = StrBufExtract_long(Buf, 1, '|'); - - Msg->from = NewStrBufPlain(NULL, StrLength(Buf)); - StrBufExtract_token(Buf2, Buf, 2, '|'); - if (StrLength(Buf2) != 0) { - /** Handle senders with RFC2047 encoding */ - StrBuf_RFC822_to_Utf8(Msg->from, Buf2, WCC->DefaultCharset, FoundCharset); + Msg->msgnum = StrBufExtractNext_long(Buf, &Ptr, '|'); + Msg->date = StrBufExtractNext_long(Buf, &Ptr, '|'); + /* + * as citserver probably gives us messages in forward date sorting + * nummsgs should be the same order as the message date. + */ + if (Msg->date == 0) { + Msg->date = nummsgs; + if (StrLength(Buf) < 32) + skipit = 1; } + if (!skipit) { + Msg->from = NewStrBufPlain(NULL, StrLength(Buf)); + StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|'); + if (StrLength(Buf2) != 0) { + /** Handle senders with RFC2047 encoding */ + StrBuf_RFC822_to_Utf8(Msg->from, Buf2, WCC->DefaultCharset, FoundCharset); + } - /** Nodename */ - StrBufExtract_token(Buf2, Buf, 3, '|'); - if ((StrLength(Buf2) !=0 ) && - ( ((WCC->room_flags & QR_NETWORK) - || ((strcasecmp(ChrPtr(Buf2), serv_info.serv_nodename) - && (strcasecmp(ChrPtr(Buf2), serv_info.serv_fqdn))))))) - { - StrBufAppendBufPlain(Msg->from, HKEY(" @ "), 0); - StrBufAppendBuf(Msg->from, Buf2, 0); - } - - /** Not used: - StrBufExtract_token(Msg->inetaddr, Buf, 4, '|'); - */ + /** Nodename */ + StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|'); + if ((StrLength(Buf2) !=0 ) && + ( ((WCC->room_flags & QR_NETWORK) + || ((strcasecmp(ChrPtr(Buf2), ChrPtr(WCC->serv_info->serv_nodename)) + && (strcasecmp(ChrPtr(Buf2), ChrPtr(WCC->serv_info->serv_fqdn)))))))) + { + StrBufAppendBufPlain(Msg->from, HKEY(" @ "), 0); + StrBufAppendBuf(Msg->from, Buf2, 0); + } - Msg->subj = NewStrBufPlain(NULL, StrLength(Buf)); - StrBufExtract_token(Buf2, Buf, 5, '|'); - if (StrLength(Buf2) == 0) - StrBufAppendBufPlain(Msg->subj, _("(no subj)"), 0, -1); - else { - StrBuf_RFC822_to_Utf8(Msg->subj, Buf2, WCC->DefaultCharset, FoundCharset); - if ((StrLength(Msg->subj) > 75) && - (StrBuf_Utf8StrLen(Msg->subj) > 75)) { - StrBuf_Utf8StrCut(Msg->subj, 72); - StrBufAppendBufPlain(Msg->subj, HKEY("..."), 0); + /** Not used: + StrBufExtract_token(Msg->inetaddr, Buf, 4, '|'); + */ + StrBufSkip_NTokenS(Buf, &Ptr, '|', 1); + Msg->subj = NewStrBufPlain(NULL, StrLength(Buf)); + StrBufExtract_NextToken(Buf2, Buf, &Ptr, '|'); + if (StrLength(Buf2) == 0) + StrBufAppendBufPlain(Msg->subj, _("(no subject)"), -1,0); + else { + StrBuf_RFC822_to_Utf8(Msg->subj, Buf2, WCC->DefaultCharset, FoundCharset); + if ((StrLength(Msg->subj) > 75) && + (StrBuf_Utf8StrLen(Msg->subj) > 75)) { + StrBuf_Utf8StrCut(Msg->subj, 72); + StrBufAppendBufPlain(Msg->subj, HKEY("..."), 0); + } } - } - if ((StrLength(Msg->from) > 25) && - (StrBuf_Utf8StrLen(Msg->from) > 25)) { - StrBuf_Utf8StrCut(Msg->from, 23); - StrBufAppendBufPlain(Msg->from, HKEY("..."), 0); + if ((StrLength(Msg->from) > 25) && + (StrBuf_Utf8StrLen(Msg->from) > 25)) { + StrBuf_Utf8StrCut(Msg->from, 23); + StrBufAppendBufPlain(Msg->from, HKEY("..."), 0); + } } - Put(WCC->summ, (const char*)&Msg->msgnum, sizeof(Msg->msgnum), Msg, DestroyMessageSummary); + n = Msg->msgnum; + Put(WCC->summ, (const char *)&n, sizeof(n), Msg, DestroyMessageSummary); } nummsgs++; } @@ -1103,564 +517,472 @@ int load_msg_ptrs(char *servcmd, int with_headers) } +inline message_summary* GetMessagePtrAt(int n, HashList *Summ) +{ + const char *Key; + long HKLen; + void *vMsg; + + if (Summ == NULL) + return NULL; + GetHashAt(Summ, n, &HKLen, &Key, &vMsg); + return (message_summary*) vMsg; +} +long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages) +{ + StrBuf *TmpBuf; + wcsession *WCC = WC; + void *vMsg; + int lo, hi; + long ret; + long hklen; + const char *key; + int done = 0; + int nItems; + HashPos *At; + long vector[16]; + WCTemplputParams SubTP; + + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_LONGVECTOR; + SubTP.Context = &vector; + TmpBuf = NewStrBuf(); + At = GetNewHashPos(WCC->summ, nMessages); + nItems = GetCount(WCC->summ); + ret = nMessages; + vector[0] = 7; + vector[2] = 1; + vector[1] = startmsg; + vector[3] = 0; + + while (!done) { + vector[3] = abs(nMessages); + lo = GetHashPosCounter(At); + if (nMessages > 0) { + if (lo + nMessages >= nItems) { + hi = nItems - 1; + vector[3] = nItems - lo; + if (startmsg == lo) + ret = vector[3]; + } + else { + hi = lo + nMessages - 1; + } + } else { + if (lo + nMessages < -1) { + hi = 0; + } + else { + if ((lo % abs(nMessages)) != 0) { + int offset = (lo % abs(nMessages) * + (nMessages / abs(nMessages))); + hi = lo + offset; + vector[3] = abs(offset); + if (startmsg == lo) + ret = offset; + } + else + hi = lo + nMessages; + } + } + 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. + */ + vector[4] = lo + 1; + vector[5] = hi + 1; + vector[6] = lo; + FlushStrBuf(TmpBuf); + dbg_print_longvector(vector); + DoTemplate(HKEY("select_messageindex"), TmpBuf, &SubTP); + StrBufAppendBuf(Selector, TmpBuf, 0); + } + vector[6] = 0; + FlushStrBuf(TmpBuf); + if (maxmsgs == 9999999) { + vector[1] = 1; + ret = maxmsgs; + } + else + vector[1] = 0; + vector[2] = 0; + dbg_print_longvector(vector); + DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &SubTP); + StrBufAppendBuf(Selector, TmpBuf, 0); + FreeStrBuf(&TmpBuf); + DeleteHashPos(&At); + return ret; +} +void load_seen_flags(void) +{ + message_summary *Msg; + const char *HashKey; + long HKLen; + HashPos *at; + void *vMsg; + StrBuf *OldMsg; + wcsession *WCC = WC; + + OldMsg = NewStrBuf(); + serv_puts("GTSN"); + StrBuf_ServGetln(OldMsg); + if (GetServerStatus(OldMsg, NULL) == 2) { + StrBufCutLeft(OldMsg, 4); + } + else { + FreeStrBuf(&OldMsg); + return; + } + at = GetNewHashPos(WCC->summ, 0); + while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { + /** Are you a new message, or an old message? */ + Msg = (message_summary*) vMsg; + if (is_msg_in_mset(ChrPtr(OldMsg), Msg->msgnum)) { + Msg->is_new = 0; + } + else { + Msg->is_new = 1; + } + } + FreeStrBuf(&OldMsg); + DeleteHashPos(&at); +} + +extern readloop_struct rlid[]; /* * command loop for reading messages * * Set oper to "readnew" or "readold" or "readfwd" or "headers" */ -void readloop(char *oper) +void readloop(long oper) { + StrBuf *MessageDropdown = NULL; + StrBuf *BBViewToolBar = NULL; void *vMsg; message_summary *Msg; char cmd[256] = ""; char buf[SIZ]; - char old_msgs[SIZ]; int a = 0; - int b = 0; + int with_headers = 0; int nummsgs; - long startmsg; - int maxmsgs; + long startmsg = 0; + int maxmsgs = 0; long *displayed_msgs = NULL; int num_displayed = 0; - int is_summary = 0; - int is_addressbook = 0; int is_singlecard = 0; - int is_calendar = 0; struct calview calv; - int is_tasks = 0; - int is_notes = 0; - int is_bbview = 0; - int lo, hi; + int i; int lowest_displayed = (-1); int highest_displayed = 0; addrbookent *addrbook = NULL; int num_ab = 0; - const StrBuf *sortby = NULL; - //SortByEnum - int SortBy = eRDate; - const StrBuf *sortpref_value; int bbs_reverse = 0; - struct wcsession *WCC = WC; /* This is done to make it run faster; WC is a function */ + wcsession *WCC = WC; HashPos *at; const char *HashKey; long HKLen; + int care_for_empty_list = 0; + int load_seen = 0; + int sortit = 0; + int defaultsortorder = 0; + WCTemplputParams SubTP; + char *ab_name; - if (WCC->wc_view == VIEW_WIKI) { - sprintf(buf, "wiki?room=%s&page=home", WCC->wc_roomname); - http_redirect(buf); - return; - } - - startmsg = lbstr("startmsg"); - maxmsgs = ibstr("maxmsgs"); - is_summary = (ibstr("is_summary") && !WCC->is_mobile); - if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; - - sortpref_value = get_room_pref("sort"); - - sortby = sbstr("sortby"); - if ( (!IsEmptyStr(ChrPtr(sortby))) && - (strcasecmp(ChrPtr(sortby), ChrPtr(sortpref_value)) != 0)) { - set_room_pref("sort", NewStrBufDup(sortby), 1); - sortpref_value = NULL; - sortpref_value = sortby; - } - - SortBy = StrToESort(sortpref_value); - /* message board sort */ - if (SortBy == eReverse) { - bbs_reverse = 1; - } - else { - bbs_reverse = 0; - } + if (havebstr("is_summary") && (1 == (ibstr("is_summary")))) + WCC->wc_view = VIEW_MAILBOX; + if (!WCC->is_ajax) { output_headers(1, 1, 1, 0, 0, 0); - - /* - * When in summary mode, always show ALL messages instead of just - * new or old. Otherwise, show what the user asked for. - */ - if (!strcmp(oper, "readnew")) { - strcpy(cmd, "MSGS NEW"); - } - else if (!strcmp(oper, "readold")) { - strcpy(cmd, "MSGS OLD"); - } - else if (!strcmp(oper, "do_search")) { - snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); - } - else { - strcpy(cmd, "MSGS ALL"); - } - - if ((WCC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1) && !WCC->is_mobile) { - is_summary = 1; - if (!strcmp(oper, "do_search")) { - snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); - } - else { - strcpy(cmd, "MSGS ALL"); - } - } - - if ((WCC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) { - is_addressbook = 1; - if (!strcmp(oper, "do_search")) { - snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); - } - else { - strcpy(cmd, "MSGS ALL"); - } - maxmsgs = 9999999; - } - - if (is_summary) { /**< fetch header summary */ - snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", - (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"), - (!strcmp(oper, "do_search") ? bstr("query") : "") - ); - startmsg = 1; - maxmsgs = 9999999; - } - if (WCC->is_mobile) { - maxmsgs = 20; - snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", - (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"), - (!strcmp(oper, "do_search") ? bstr("query") : "") - ); - SortBy = eRDate; - } - - /* - * Are we doing a summary view? If so, we need to know old messages - * and new messages, so we can do that pretty boldface thing for the - * new messages. - */ - strcpy(old_msgs, ""); - if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){ - serv_puts("GTSN"); - serv_getln(buf, sizeof buf); - if (buf[0] == '2') { - strcpy(old_msgs, &buf[4]); - } + } else if (WCC->wc_view == VIEW_MAILBOX) { + jsonMessageListHdr(); } - is_singlecard = ibstr("is_singlecard"); - - if (WCC->wc_default_view == VIEW_CALENDAR) { /**< calendar */ - is_calendar = 1; - strcpy(cmd, "MSGS ALL|||1"); + switch (WCC->wc_view) { + case VIEW_WIKI: + sprintf(buf, "wiki?room=%s&page=home", ChrPtr(WCC->wc_roomname)); + http_redirect(buf); + return; + case VIEW_CALBRIEF: + case VIEW_CALENDAR: + load_seen = 1; + strcpy(cmd, "MSGS ALL"); maxmsgs = 32767; parse_calendar_view_request(&calv); - } - if (WCC->wc_default_view == VIEW_TASKS) { /**< tasks */ - is_tasks = 1; + break; + case VIEW_TASKS: strcpy(cmd, "MSGS ALL"); maxmsgs = 32767; - } - if (WCC->wc_default_view == VIEW_NOTES) { /**< notes */ - is_notes = 1; + break; + case VIEW_NOTES: strcpy(cmd, "MSGS ALL"); maxmsgs = 32767; - } - - if (is_notes) { wprintf("
\n"); + break; + case VIEW_ADDRESSBOOK: + is_singlecard = ibstr("is_singlecard"); + if (is_singlecard != 1) { + if (oper == do_search) { + snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); + } + else { + strcpy(cmd, "MSGS ALL"); + } + maxmsgs = 9999999; + break; + } + break; + case VIEW_MAILBOX: + if (!WCC->is_ajax) { + new_summary_view(); + return; + } else { + defaultsortorder = 2; + sortit = 1; + load_seen = 1; + care_for_empty_list = 0; + with_headers = 1; + /* Generally using maxmsgs|startmsg is not required + in mailbox view, but we have a 'safemode' for clients + (*cough* Exploder) that simply can't handle too many */ + if (havebstr("maxmsgs")) maxmsgs = ibstr("maxmsgs"); + else maxmsgs = 9999999; + if (havebstr("startmsg")) startmsg = lbstr("startmsg"); + snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", + (oper == do_search) ? "SEARCH" : "ALL", + (oper == do_search) ? bstr("query") : "" + ); + } + break; + case VIEW_BBS: + default: + defaultsortorder = 1; + startmsg = -1; + sortit = 1; + care_for_empty_list = 1; + + rlid[oper].cmd(cmd, sizeof(cmd)); + SetAccessCommand(oper); + + if (havebstr("maxmsgs")) + maxmsgs = ibstr("maxmsgs"); + if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; + + if (havebstr("startmsg")) { + startmsg = lbstr("startmsg"); + } + } - nummsgs = load_msg_ptrs(cmd, (is_summary || WCC->is_mobile)); + nummsgs = load_msg_ptrs(cmd, with_headers); if (nummsgs == 0) { - - if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) { - wprintf("

"); - if (!strcmp(oper, "readnew")) { + if (care_for_empty_list) { + wprintf("

"); + switch (oper) { + case readnew: wprintf(_("No new messages.")); - } else if (!strcmp(oper, "readold")) { + break; + case readold: wprintf(_("No old messages.")); - } else { + break; + default: wprintf(_("No messages here.")); } - wprintf("
\n"); + wprintf("

\n"); + goto DONE; } - goto DONE; } - if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){ - void *vMsg; - message_summary *Msg; - - at = GetNewHashPos(); - while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { - /** Are you a new message, or an old message? */ - Msg = (message_summary*) vMsg; - if (is_summary) { - if (is_msg_in_mset(old_msgs, Msg->msgnum)) { - Msg->is_new = 0; - } - else { - Msg->is_new = 1; - } + if (sortit) { + CompareFunc SortIt; + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_NONE; + SubTP.Context = NULL; + SortIt = RetrieveSort(&SubTP, NULL, 0, + HKEY("date"), defaultsortorder); + if (SortIt != NULL) + SortByPayload(WCC->summ, SortIt); + if (WCC->wc_view == VIEW_BBS) { + if (lbstr("SortOrder") == 2) { + bbs_reverse = 1; + num_displayed = -DEFAULT_MAXMSGS; + } + else { + bbs_reverse = 0; + num_displayed = DEFAULT_MAXMSGS; } } - DeleteHashPos(&at); - } - - if (startmsg == 0L) { - if (bbs_reverse) { - startmsg = WCC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; - } - else { - startmsg = WCC->msgarr[0]; - } - } - - if (is_summary || WCC->is_mobile) { - SortByPayload(WCC->summ, SortFuncs[SortBy]); - } - - if (is_summary) { - - wprintf("\n" - ); - - /** note that Date and Delete are now in the same column */ - wprintf("
" - "
" - "" - "" - ); - wprintf("\n" - "\n" - "" - "\n" - , - SUBJ_COL_WIDTH_PCT, - _("Subject"), - SortByStrings[SubjectInvertSortString[SortBy]], - SortIcons[SortSubjectToIcon[SortBy]], - SENDER_COL_WIDTH_PCT, - _("Sender"), - SortByStrings[SenderInvertSortString[SortBy]], - SortIcons[SortSenderToIcon[SortBy]], - DATE_PLUS_BUTTONS_WIDTH_PCT, - _("Date"), - SortByStrings[DateInvertSortString[SortBy]], - SortIcons[SortDateToIcon[SortBy]], - _("Delete") - ); - wprintf("
%s %s %s \n" - " " - "" - "
\n"); - wprintf("
" - - "
\n" - "" - ); - } else if (WCC->is_mobile) { - wprintf("
"); } + if (startmsg < 0) startmsg = (bbs_reverse) ? nummsgs - 1 : 0; - - /** - * Set the "is_bbview" variable if it appears that we are looking at - * a classic bulletin board view. + if (load_seen) load_seen_flags(); + + /** + * If we're to print s.th. above the message list... */ - if ((!is_tasks) && (!is_calendar) && (!is_addressbook) - && (!is_notes) && (!is_singlecard) && (!is_summary)) { - is_bbview = 1; - } + switch (WCC->wc_view) { + case VIEW_BBS: + BBViewToolBar = NewStrBuf(); + MessageDropdown = NewStrBuf(); + + maxmsgs = DrawMessageDropdown(MessageDropdown, maxmsgs, startmsg, num_displayed); + if (num_displayed < 0) { + startmsg += maxmsgs; + if (num_displayed != maxmsgs) + maxmsgs = abs(maxmsgs) + 1; + else + maxmsgs = abs(maxmsgs); - /** - * If we're not currently looking at ALL requested - * messages, then display the selector bar - */ - if (is_bbview) { - /** begin bbview scroller */ - wprintf("
\n

"); - wprintf(_("Reading #"));//// TODO this isn't used, should it? : , lowest_displayed, highest_displayed); - - wprintf(" "); - wprintf(_("of %d messages."), nummsgs); - - /** forward/reverse */ - wprintf(""); - wprintf(_("oldest to newest")); - wprintf("    "); - - wprintf(""); - wprintf(_("newest to oldest")); - wprintf("\n"); - - wprintf("

\n"); - /** end bbview scroller */ - } - - at = GetNewHashPos(); + memset(&SubTP, 0, sizeof(WCTemplputParams)); + SubTP.Filter.ContextType = CTX_STRBUF; + SubTP.Context = MessageDropdown; + DoTemplate(HKEY("msg_listselector_top"), BBViewToolBar, &SubTP); + StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0); + FlushStrBuf(BBViewToolBar); + break; + } + WCC->startmsg = startmsg; + WCC->maxmsgs = maxmsgs; + WCC->num_displayed = 0; + + /* Put some helpful data in vars for mailsummary_json */ + 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)) { Msg = (message_summary*) vMsg; - if ((Msg->msgnum >= startmsg) && (num_displayed < maxmsgs)) { - - /** Display the message */ - if (is_summary) { - DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM); - } - else if (is_addressbook) { - fetch_ab_name(Msg, buf); + if ((Msg->msgnum >= startmsg) && (num_displayed <= maxmsgs)) { + switch (WCC->wc_view) { + case VIEW_WIKI: + break; + case VIEW_CALBRIEF: /* load the mime attachments for special tasks... */ + case VIEW_CALENDAR: + load_calendar_item(Msg, Msg->is_new, &calv); + break; + case VIEW_TASKS: + display_task(Msg, Msg->is_new); + break; + case VIEW_NOTES: + display_note(Msg, Msg->is_new); + break; + case VIEW_ADDRESSBOOK: + ab_name = NULL; + fetch_ab_name(Msg, &ab_name); + if (ab_name == NULL) + break; ++num_ab; addrbook = realloc(addrbook, (sizeof(addrbookent) * num_ab) ); - safestrncpy(addrbook[num_ab-1].ab_name, buf, + safestrncpy(addrbook[num_ab-1].ab_name, ab_name, sizeof(addrbook[num_ab-1].ab_name)); addrbook[num_ab-1].ab_msgnum = Msg->msgnum; - } - else if (is_calendar) { - load_calendar_item(Msg, Msg->is_new, &calv); - } - else if (is_tasks) { - display_task(Msg, Msg->is_new); - } - else if (is_notes) { - display_note(Msg, Msg->is_new); - } else if (WCC->is_mobile) { - DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM); - } - else { + free(ab_name); + break; + case VIEW_BBS: /* Tag the mails we want to show in bbview... */ + default: if (displayed_msgs == NULL) { displayed_msgs = malloc(sizeof(long) * - (maxmsgsmsgnum; - } - - if (lowest_displayed < 0) lowest_displayed = a; - highest_displayed = a; + if ((i >= startmsg) && (i < startmsg + maxmsgs)) { + displayed_msgs[num_displayed] = Msg->msgnum; + if (lowest_displayed < 0) lowest_displayed = a; + highest_displayed = a; - ++num_displayed; - } + num_displayed++; + } + } + } + i++; } DeleteHashPos(&at); - /** Output loop */ - if (displayed_msgs != NULL) { - if (bbs_reverse) { - qsort(displayed_msgs, num_displayed, sizeof(long), qlongcmp_r); - } - - /** if we do a split bbview in the future, begin messages div here */ - - for (a=0; aWBuf, HKEY("view_message"), displayed_msgs[a], 0, NULL); - } - - /** if we do a split bbview in the future, end messages div here */ - - free(displayed_msgs); - displayed_msgs = NULL; - } - - if (is_summary) { - wprintf("
" - "
\n"); /**< end of 'fix_scrollbar_bug' div */ - wprintf("
"); /**< end of 'message_list' div */ - - /** Here's the grab-it-to-resize-the-message-list widget */ - wprintf("
" - "

" - "
\n" - ); - - wprintf("
"); /**< The preview pane will initially be empty */ - } else if (WCC->is_mobile) { - wprintf("
"); - } - - /** - * Bump these because although we're thinking in zero base, the user - * is a drooling idiot and is thinking in one base. - */ - ++lowest_displayed; - ++highest_displayed; - - /** - * If we're not currently looking at ALL requested - * messages, then display the selector bar + NO_MSG_LOOP: + /* + * Done iterating the message list. now tasks we want to do after. */ - if (is_bbview) { - /** begin bbview scroller */ - wprintf("
\n

"); - wprintf(_("Reading #")); /// TODO: this isn't used: , lowest_displayed, highest_displayed); - - wprintf(" "); - wprintf(_("of %d messages."), nummsgs); - - /** forward/reverse */ - wprintf(""); - wprintf(_("oldest to newest")); - wprintf("    "); - wprintf(""); - wprintf(_("newest to oldest")); - wprintf("\n"); - - wprintf("

\n"); - /** end bbview scroller */ + FreeStrBuf(&BBViewToolBar); + FreeStrBuf(&MessageDropdown); } + DONE: - if (is_tasks) { - do_tasks_view(); /** Render the task list */ - } - - if (is_calendar) { + switch (WCC->wc_view) { + case VIEW_WIKI: + break; + case VIEW_CALBRIEF: + case VIEW_CALENDAR: render_calendar_view(&calv); + break; + case VIEW_TASKS: + do_tasks_view(); /** Render the task list */ + break; + case VIEW_NOTES: + break; + case VIEW_ADDRESSBOOK: + if (is_singlecard) + read_message(WC->WBuf, HKEY("view_message"), lbstr("startmsg"), NULL); + else + do_addrbook_view(addrbook, num_ab); /** Render the address book */ + break; + case VIEW_MAILBOX: + case VIEW_BBS: + default: + break; } - - if (is_addressbook) { - do_addrbook_view(addrbook, num_ab); /** Render the address book */ - } - /** Note: wDumpContent() will output one additional tag. */ - wprintf("\n"); /** end of 'content' div */ - wDumpContent(1); - - /** free the summary */ + if (WCC->wc_view != VIEW_MAILBOX) { + /* We ought to move this out into template */ + wDumpContent(1); + } else { + end_burst(); + } + WCC->startmsg = 0; + WCC->maxmsgs = 0; if (WCC->summ != NULL) { DeleteHash(&WCC->summ); } if (addrbook != NULL) free(addrbook); + FreeStrBuf(&BBViewToolBar); } @@ -1669,24 +991,24 @@ DONE: * ... this is where the actual message gets transmitted to the server. */ void post_mime_to_server(void) { - struct wcsession *WCC = WC; + wcsession *WCC = WC; char top_boundary[SIZ]; char alt_boundary[SIZ]; int is_multipart = 0; static int seq = 0; - wc_attachment *att; + wc_mime_attachment *att; char *encoded; size_t encoded_length; size_t encoded_strlen; char *txtmail = NULL; sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x", - serv_info.serv_fqdn, + ChrPtr(WCC->serv_info->serv_fqdn), getpid(), ++seq ); sprintf(alt_boundary, "Citadel--Multipart--%s--%04x--%04x", - serv_info.serv_fqdn, + ChrPtr(WCC->serv_info->serv_fqdn), getpid(), ++seq ); @@ -1738,17 +1060,17 @@ void post_mime_to_server(void) { HashPos *it; /* Add in the attachments */ - it = GetNewHashPos(); + it = GetNewHashPos(WCC->attachments, 0); while (GetNextHashPos(WCC->attachments, it, &len, &Key, &vAtt)) { - att = (wc_attachment*)vAtt; + att = (wc_mime_attachment *)vAtt; encoded_length = ((att->length * 150) / 100); encoded = malloc(encoded_length); if (encoded == NULL) break; - encoded_strlen = CtdlEncodeBase64(encoded, att->data, att->length, 1); + encoded_strlen = CtdlEncodeBase64(encoded, ChrPtr(att->Data), StrLength(att->Data), 1); serv_printf("--%s", top_boundary); - serv_printf("Content-type: %s", ChrPtr(att->content_type)); - serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att->filename)); + serv_printf("Content-type: %s", ChrPtr(att->ContentType)); + serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att->FileName)); serv_puts("Content-transfer-encoding: base64"); serv_puts(""); serv_write(encoded, encoded_strlen); @@ -1780,13 +1102,13 @@ void post_message(void) char buf[1024]; StrBuf *encoded_subject = NULL; static long dont_post = (-1L); - wc_attachment *att; + wc_mime_attachment *att; int is_anonymous = 0; const StrBuf *display_name = NULL; - struct wcsession *WCC = WC; + wcsession *WCC = WC; if (havebstr("force_room")) { - gotoroom(bstr("force_room")); + gotoroom(sbstr("force_room")); } if (havebstr("display_name")) { @@ -1804,38 +1126,39 @@ void post_message(void) lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length); /** There's an attachment. Save it to this struct... */ - att = malloc(sizeof(wc_attachment)); - memset(att, 0, sizeof(wc_attachment)); + att = malloc(sizeof(wc_mime_attachment)); + memset(att, 0, sizeof(wc_mime_attachment )); att->length = WCC->upload_length; - att->content_type = NewStrBufPlain(WCC->upload_content_type, -1); - att->filename = NewStrBufPlain(WCC->upload_filename, -1); + att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1); + att->FileName = NewStrBufPlain(WCC->upload_filename, -1); 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, free_attachment); + 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. */ - pch = strrchr(ChrPtr(att->filename), '/'); + pch = strrchr(ChrPtr(att->FileName), '/'); if (pch != NULL) { - StrBufCutLeft(att->filename, pch - ChrPtr(att->filename)); + StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1); } - pch = strrchr(ChrPtr(att->filename), '\\'); + pch = strrchr(ChrPtr(att->FileName), '\\'); if (pch != NULL) { - StrBufCutLeft(att->filename, pch - ChrPtr(att->filename)); + StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName) + 1); } /** * Transfer control of this memory from the upload struct * to the attachment struct. */ - att->data = WCC->upload; + att->Data = NewStrBufPlain(WCC->upload, WCC->upload_length); + free(WCC->upload); WCC->upload_length = 0; WCC->upload = NULL; display_enter(); @@ -1859,16 +1182,17 @@ void post_message(void) const StrBuf *Bcc = NULL; const StrBuf *Wikipage = NULL; const StrBuf *my_email_addr = NULL; - StrBuf *CmdBuf = NULL;; + StrBuf *CmdBuf = NULL; StrBuf *references = NULL; if (havebstr("references")) { const StrBuf *ref = sbstr("references"); references = NewStrBufPlain(ChrPtr(ref), StrLength(ref)); - lprintf(9, "Converting: %s\n", ChrPtr(references)); + if (*ChrPtr(references) == '|') { /* remove leading '|' if present */ + StrBufCutLeft(references, 1); + } StrBufReplaceChars(references, '|', '!'); - lprintf(9, "Converted: %s\n", ChrPtr(references)); } if (havebstr("subject")) { const StrBuf *Subj; @@ -1909,7 +1233,7 @@ void post_message(void) ChrPtr(references)); FreeStrBuf(&references); - lprintf(9, "%s\n", CmdBuf); + lprintf(9, "%s\n", ChrPtr(CmdBuf)); serv_puts(ChrPtr(CmdBuf)); serv_getln(buf, sizeof buf); FreeStrBuf(&CmdBuf); @@ -1954,7 +1278,7 @@ void post_message(void) * Otherwise, just go to the "read messages" loop. */ else { - readloop("readnew"); + readloop(readnew); } } @@ -1967,21 +1291,18 @@ void post_message(void) void display_enter(void) { char buf[SIZ]; - StrBuf *ebuf; long now; const StrBuf *display_name = NULL; - /////wc_attachment *att; int recipient_required = 0; int subject_required = 0; int recipient_bad = 0; int is_anonymous = 0; - long existing_page = (-1L); - struct wcsession *WCC = WC; + wcsession *WCC = WC; now = time(NULL); if (havebstr("force_room")) { - gotoroom(bstr("force_room")); + gotoroom(sbstr("force_room")); } display_name = sbstr("display_name"); @@ -1999,7 +1320,7 @@ void display_enter(void) } else if (buf[0] != '2') { /** Any other error means that we cannot continue */ sprintf(WCC->ImportantMessage, "%s", &buf[4]); - readloop("readnew"); + readloop(readnew); return; } @@ -2013,7 +1334,7 @@ void display_enter(void) * message" command really means "add new entry." */ if (WCC->wc_default_view == VIEW_ADDRESSBOOK) { - do_edit_vcard(-1, "", "", WCC->wc_roomname); + do_edit_vcard(-1, "", NULL, NULL, "", ChrPtr(WCC->wc_roomname)); return; } @@ -2039,22 +1360,13 @@ void display_enter(void) * Otherwise proceed normally. * Do a custom room banner with no navbar... */ - output_headers(1, 1, 2, 0, 0, 0); -/* - wprintf("
\n"); - embed_room_banner(NULL, navbar_none); - wprintf("
\n"); - wprintf("
\n" - "
"); -*/ - /* Now check our actual recipients if there are any */ if (recipient_required) { const StrBuf *Recp = NULL; const StrBuf *Cc = NULL; const StrBuf *Bcc = NULL; const StrBuf *Wikipage = NULL; - StrBuf *CmdBuf = NULL;; + StrBuf *CmdBuf = NULL; const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s"; Recp = sbstr("recp"); @@ -2090,325 +1402,21 @@ void display_enter(void) } } else if (buf[0] != '2') { /** Any other error means that we cannot continue */ - wprintf("%s
\n", &buf[4]);/// -> important message - goto DONE; + wprintf("%s
\n", &buf[4]);/*TODO -> important message */ + return; } } - DoTemplate(HKEY("edit_message"), NULL, NULL, CTX_NONE); - address_book_popup(); - wDumpContent(1); + svputlong("RCPTREQUIRED", recipient_required); + svputlong("SUBJREQUIRED", recipient_required || subject_required); + begin_burst(); + output_headers(1, 0, 0, 0, 1, 0); + DoTemplate(HKEY("edit_message"), NULL, &NoCtx); + end_burst(); return; - - - /** If we got this far, we can display the message entry screen. */ - - /* begin message entry screen */ - wprintf("
\n"); - wprintf("\n", now); - if (WCC->wc_view == VIEW_WIKI) { - wprintf("\n", bstr("wikipage")); - } - wprintf("\n", bstr("return_to")); - wprintf("\n", WCC->nonce); - wprintf("wc_roomname); - wprintf("\">\n"); - wprintf("\n"); - - /** submit or cancel buttons */ - wprintf("

"); - wprintf(" " - "\n", _("Cancel")); - wprintf("

"); - - /** header bar */ - - wprintf(""); - wprintf(" "); /** header bar */ - webcit_fmt_date(buf, now, 0); - wprintf("%s", buf); - wprintf("\n"); /** header bar */ - - wprintf(""); - wprintf(""); - wprintf(""); - - wprintf(""); - - if (recipient_required) { - char *ccraw; - char *copy; - size_t len; - wprintf("" - ""); - - wprintf("" - ""); - - wprintf("" - ""); - - /** Initialize the autocomplete ajax helpers (found in wclib.js) */ - wprintf(" \n" - ); - - } - - wprintf("" - ""); - - wprintf("
"); - - /* Allow the user to select any of his valid screen names */ - - wprintf("\n"); - - /* If this is an email (not a post), allow the user to select any of his - * valid email addresses. - */ - if (recipient_required) { - serv_puts("GVEA"); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - wprintf("\n"); - } - } - - wprintf(_(" in ")); - escputs(WCC->wc_roomname); - - wprintf("
"); - wprintf("
"); - wprintf("
"); - - /** Pop open an address book -- begin **/ - wprintf( - "" - "" - " %s", - _("To:"), _("CC:"), _("BCC:"), - _("Contacts"), _("Contacts") - ); - /** Pop open an address book -- end **/ - - wprintf("
"); - wprintf("
"); - wprintf("
"); - wprintf("
"); - wprintf("
" - "\n"); - wprintf("
\n"); - - wprintf("\n"); - - /** Make sure we only insert our signature once */ - /** We don't care if it was there or not before, it needs to be there now. */ - wprintf("\n"); - - /** - * The following template embeds the TinyMCE richedit control, and automatically - * transforms the textarea into a richedit textarea. - */ - do_template("richedit", NULL); - - /** Enumerate any attachments which are already in place... */ - wprintf("
"); - wprintf(_("Attachments:")); - wprintf(" "); - wprintf(""); - - /** Now offer the ability to attach additional files... */ - wprintf("   "); - wprintf(_("Attach file:")); - wprintf(" \n  " - "\n", _("Add")); - wprintf("
"); /* End of "attachment buttons" div */ - - - wprintf("
"); - - wprintf("
\n"); - wprintf("
\n"); /* end of "fix_scrollbar_bug" div */ - - /* NOTE: address_book_popup() will close the "content" div. Don't close it here. */ -DONE: address_book_popup(); - wDumpContent(1); } - /** * \brief delete a message */ @@ -2429,7 +1437,7 @@ void delete_msg(void) serv_getln(buf, sizeof buf); sprintf(WC->ImportantMessage, "%s", &buf[4]); - readloop("readnew"); + readloop(readnew); } @@ -2452,7 +1460,7 @@ void move_msg(void) sprintf(WC->ImportantMessage, (_("The message was not moved."))); } - readloop("readnew"); + readloop(readnew); } @@ -2512,26 +1520,232 @@ void confirm_move_msg(void) wDumpContent(1); } -void readnew(void) { readloop("readnew");} -void readold(void) { readloop("readold");} -void readfwd(void) { readloop("readfwd");} -void headers(void) { readloop("headers");} -void do_search(void) { readloop("do_search");} + +/* + * Generic function to output an arbitrary MIME attachment from + * message being composed + * + * partnum The MIME part to be output + * filename Fake filename to give + * force_download Nonzero to force set the Content-Type: header to "application/octet-stream" + */ +void postpart(StrBuf *partnum, StrBuf *filename, int force_download) +{ + void *vPart; + StrBuf *content_type; + wc_mime_attachment *part; + + if (GetHash(WC->attachments, SKEY(partnum), &vPart) && + (vPart != NULL)) { + part = (wc_mime_attachment*) vPart; + if (force_download) { + content_type = NewStrBufPlain(HKEY("application/octet-stream")); + } + else { + content_type = NewStrBufDup(part->ContentType); + } + StrBufAppendBuf(WC->WBuf, part->Data, 0); + http_transmit_thing(ChrPtr(content_type), 0); + } else { + hprintf("HTTP/1.1 404 %s\n", ChrPtr(partnum)); + output_headers(0, 0, 0, 0, 0, 0); + hprintf("Content-Type: text/plain\r\n"); + wprintf(_("An error occurred while retrieving this part: %s/%s\n"), + ChrPtr(partnum), ChrPtr(filename)); + end_burst(); + } + FreeStrBuf(&content_type); +} + + +/* + * Generic function to output an arbitrary MIME part from an arbitrary + * message number on the server. + * + * msgnum Number of the item on the citadel server + * partnum The MIME part to be output + * force_download Nonzero to force set the Content-Type: header to "application/octet-stream" + */ +void mimepart(int force_download) +{ + wcsession *WCC = WC; + StrBuf *Buf; + off_t bytes; + StrBuf *ContentType = NewStrBufPlain(HKEY("application/octet-stream")); + const char *CT; + + Buf = NewStrBuf(); + serv_printf("OPNA %s|%s", ChrPtr(WCC->UrlFragment2), ChrPtr(WCC->UrlFragment3)); + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) == 2) { + StrBufCutLeft(Buf, 4); + bytes = StrBufExtract_long(Buf, 0, '|'); + if (!force_download) { + StrBufExtract_token(ContentType, Buf, 3, '|'); + } + + read_server_binary(WCC->WBuf, bytes, Buf); + serv_puts("CLOS"); + StrBuf_ServGetln(Buf); + CT = ChrPtr(ContentType); + + if (!force_download) { + if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) { + CT = GuessMimeByFilename(SKEY(WCC->UrlFragment4)); + } + if (!strcasecmp(ChrPtr(ContentType), "application/octet-stream")) { + CT = GuessMimeType(SKEY(WCC->WBuf)); + } + } + http_transmit_thing(CT, 0); + } else { + StrBufCutLeft(Buf, 4); + hprintf("HTTP/1.1 404 %s\n", ChrPtr(Buf)); + output_headers(0, 0, 0, 0, 0, 0); + hprintf("Content-Type: text/plain\r\n"); + wprintf(_("An error occurred while retrieving this part: %s\n"), + ChrPtr(Buf)); + end_burst(); + } + FreeStrBuf(&ContentType); + FreeStrBuf(&Buf); +} + + +/* + * Read any MIME part of a message, from the server, into memory. + */ +StrBuf *load_mimepart(long msgnum, char *partnum) +{ + off_t bytes; + StrBuf *Buf; + + Buf = NewStrBuf(); + serv_printf("DLAT %ld|%s", msgnum, partnum); + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) == 6) { + StrBufCutLeft(Buf, 4); + bytes = StrBufExtract_long(Buf, 0, '|'); + + StrBuf_ServGetBLOBBuffered(Buf, bytes); + return(Buf); + } + else { + FreeStrBuf(&Buf); + return(NULL); + } +} + +/* + * Read any MIME part of a message, from the server, into memory. + */ +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? */ + serv_printf("DLAT %ld|%s", Mime->msgnum, ChrPtr(Mime->PartNum)); + Buf = NewStrBuf(); + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) == 6) { + bytes = extract_long(&(ChrPtr(Buf)[4]), 0); + + if (Mime->Data == NULL) + Mime->Data = NewStrBufPlain(NULL, bytes); + StrBuf_ServGetBLOBBuffered(Mime->Data, bytes); + } + else { + FlushStrBuf(Mime->Data); + /* TODO XImportant message */ + } + FreeStrBuf(&Buf); +} + + +void view_mimepart(void) { + mimepart(0); +} + +void download_mimepart(void) { + mimepart(1); +} + +void view_postpart(void) { + postpart(WC->UrlFragment2, + WC->UrlFragment3, + 0); +} +void download_postpart(void) { + postpart(WC->UrlFragment2, + WC->UrlFragment3, + 1); +} +void h_readnew(void) { readloop(readnew);} +void h_readold(void) { readloop(readold);} +void h_readfwd(void) { readloop(readfwd);} +void h_headers(void) { readloop(headers);} +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(); +} +/* 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(); +} +/** 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; +} void InitModule_MSG (void) { - WebcitAddUrlHandler(HKEY("readnew"), readnew, 0); - WebcitAddUrlHandler(HKEY("readold"), readold, 0); - WebcitAddUrlHandler(HKEY("readfwd"), readfwd, 0); - WebcitAddUrlHandler(HKEY("headers"), headers, 0); - WebcitAddUrlHandler(HKEY("do_search"), do_search, 0); + RegisterPreference("use_sig", + _("Attach signature to email messages?"), + PRF_YESNO, + NULL); + RegisterPreference("signature", _("Use this signature:"), PRF_QP_STRING, NULL); + RegisterPreference("default_header_charset", + _("Default character set for email headers:"), + PRF_STRING, + NULL); + RegisterPreference("defaultfrom", _("Preferred email address"), PRF_STRING, NULL); + RegisterPreference("defaultname", + _("Preferred display name for email messages"), + PRF_STRING, + NULL); + RegisterPreference("defaulthandle", + _("Preferred display name for bulletin board posts"), + PRF_STRING, + NULL); + RegisterPreference("mailbox",_("Mailbox view mode"), PRF_STRING, NULL); + + WebcitAddUrlHandler(HKEY("readnew"), h_readnew, NEED_URL); + WebcitAddUrlHandler(HKEY("readold"), h_readold, NEED_URL); + WebcitAddUrlHandler(HKEY("readfwd"), h_readfwd, NEED_URL); + WebcitAddUrlHandler(HKEY("headers"), h_headers, NEED_URL); + WebcitAddUrlHandler(HKEY("do_search"), h_do_search, 0); WebcitAddUrlHandler(HKEY("display_enter"), display_enter, 0); WebcitAddUrlHandler(HKEY("post"), post_message, 0); WebcitAddUrlHandler(HKEY("move_msg"), move_msg, 0); @@ -2542,5 +1756,19 @@ InitModule_MSG WebcitAddUrlHandler(HKEY("mobilemsg"), mobile_message_view, NEED_URL); WebcitAddUrlHandler(HKEY("msgheaders"), display_headers, NEED_URL); + WebcitAddUrlHandler(HKEY("mimepart"), view_mimepart, NEED_URL); + WebcitAddUrlHandler(HKEY("mimepart_download"), download_mimepart, NEED_URL); + WebcitAddUrlHandler(HKEY("postpart"), view_postpart, NEED_URL); + WebcitAddUrlHandler(HKEY("postpart_download"), download_postpart, NEED_URL); + + /* json */ + WebcitAddUrlHandler(HKEY("roommsgs"), jsonMessageList,0); return ; } + +void +SessionDetachModule_MSG +(wcsession *sess) +{ + DeleteHash(&sess->summ); +}