X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fmessages.c;h=c159a055acf38399a4e693eafde6051224bf61ec;hb=5ce2f5c9a18d4b8a9fd7fcc82fea11dc0f2b3d32;hp=3d20e3b148bddfdf776bc248073473cb662fd8c0;hpb=0b1310a64450f9f94ddc15a446133a38a0e04f60;p=citadel.git diff --git a/webcit/messages.c b/webcit/messages.c index 3d20e3b14..c159a055a 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -9,2582 +9,710 @@ #include "webserver.h" #include "groupdav.h" +HashList *MsgHeaderHandler = NULL; +HashList *MsgEvaluators = NULL; +HashList *MimeRenderHandler = NULL; + #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 */ -/* - * Address book entry (keep it short and sweet, it's just a quickie lookup - * which we can use to get to the real meat and bones later) - */ -struct addrbookent { - char ab_name[64]; /**< name string */ - long ab_msgnum; /**< message number of address book entry */ -}; +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); -#ifdef HAVE_ICONV +/*----------------------------------------------------------------------------*/ -/* - * Wrapper around iconv_open() - * Our version adds aliases for non-standard Microsoft charsets - * such as 'MS950', aliasing them to names like 'CP950' - * - * tocode Target encoding - * fromcode Source encoding - */ -iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode) -{ - iconv_t ic = (iconv_t)(-1) ; - ic = iconv_open(tocode, fromcode); - if (ic == (iconv_t)(-1) ) { - char alias_fromcode[64]; - if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) { - safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode); - alias_fromcode[0] = 'C'; - alias_fromcode[1] = 'P'; - ic = iconv_open(tocode, alias_fromcode); - } - } - return(ic); -} +typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf); + + + +/*----------------------------------------------------------------------------*/ -inline char *FindNextEnd (char *bptr) -{ - char * end; - /* Find the next ?Q? */ - end = strchr(bptr + 2, '?'); - if (end == NULL) return NULL; - if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && - (*(end + 2) == '?')) { - /* skip on to the end of the cluster, the next ?= */ - end = strstr(end + 3, "?="); - } - else - /* sort of half valid encoding, try to find an end. */ - end = strstr(bptr, "?="); - return end; -} /* - * Handle subjects with RFC2047 encoding such as: - * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= + * 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 utf8ify_rfc822_string(char *buf) { - char *start, *end, *next, *nextend, *ptr; - char newbuf[1024]; - char charset[128]; - char encoding[16]; - char istr[1024]; - 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 *isav; /**< Saved pointer to input buffer */ - char *osav; /**< Saved pointer to output buffer */ - int passes = 0; - int i, len, delta; - int illegal_non_rfc2047_encoding = 0; - - /* Sometimes, badly formed messages contain strings which were simply - * written out directly in some foreign character set instead of - * using RFC2047 encoding. This is illegal but we will attempt to - * handle it anyway by converting from a user-specified default - * charset to UTF-8 if we see any nonprintable characters. - */ - len = strlen(buf); - for (i=0; i 126)) { - illegal_non_rfc2047_encoding = 1; - i = len; ///< take a shortcut, it won't be more than one. - } - } - if (illegal_non_rfc2047_encoding) { - StrBuf *default_header_charset; - get_preference("default_header_charset", &default_header_charset); - if ( (strcasecmp(ChrPtr(default_header_charset), "UTF-8")) && - (strcasecmp(ChrPtr(default_header_charset), "us-ascii")) ) { - ic = ctdl_iconv_open("UTF-8", ChrPtr(default_header_charset)); - if (ic != (iconv_t)(-1) ) { - ibuf = malloc(1024); - isav = ibuf; - safestrncpy(ibuf, buf, 1024); - ibuflen = strlen(ibuf); - obuflen = 1024; - obuf = (char *) malloc(obuflen); - osav = obuf; - iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); - osav[1024-obuflen] = 0; - strcpy(buf, osav); - free(osav); - iconv_close(ic); - free(isav); - } - } +int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, int printable_view, const StrBuf *PartNum) { + StrBuf *Buf; + StrBuf *HdrToken; + StrBuf *FoundCharset; + HashPos *it; + void *vMime; + message_summary *Msg = NULL; + headereval *Hdr; + void *vHdr; + char buf[SIZ]; + int Done = 0; + int state=0; + long len; + const char *Key; + + Buf = NewStrBuf(); + lprintf(1, "----------%s---------MSG4 %ld|%s--------------\n", tmpl, msgnum, ChrPtr(PartNum)); + serv_printf("MSG4 %ld|%s", msgnum, ChrPtr(PartNum)); + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) != 1) { + StrBufAppendPrintf(Target, ""); + StrBufAppendPrintf(Target, _("ERROR:")); + StrBufAppendPrintf(Target, " %s
\n", &buf[4]); + FreeStrBuf(&Buf); + return 0; } - /* pre evaluate the first pair */ - nextend = end = NULL; - len = strlen(buf); - start = strstr(buf, "=?"); - if (start != NULL) - end = FindNextEnd (start); + /** begin everythingamundo table */ + - while ((start != NULL) && (end != NULL)) - { - next = strstr(end, "=?"); - if (next != NULL) - nextend = FindNextEnd(next); - if (nextend == NULL) - next = NULL; - - /* did we find two partitions */ - if ((next != NULL) && - ((next - end) > 2)) + HdrToken = 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)); + FoundCharset = NewStrBuf(); + while ((StrBuf_ServGetln(Buf)>=0) && !Done) { + if ( (StrLength(Buf)==3) && + !strcmp(ChrPtr(Buf), "000")) { - ptr = end + 2; - while ((ptr < next) && - (isspace(*ptr) || - (*ptr == '\r') || - (*ptr == '\n') || - (*ptr == '\t'))) - ptr ++; - /* did we find a gab just filled with blanks? */ - if (ptr == next) + Done = 1; + if (state < 2) { + lprintf(1, _("unexpected end of message")); + + 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 */ + if (StrLength(Buf) == 0) { + state ++; + break; + } + StrBufExtract_token(HdrToken, Buf, 0, '='); + StrBufCutLeft(Buf, StrLength(HdrToken) + 1); + + lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf)); + /* 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(HdrToken)); + break; + case 1:/* Message Mime Header */ + if (StrLength(Buf) == 0) { + state++; + if (Msg->MsgBody->ContentType == NULL) + /* end of header or no header? */ + Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/plain")); + /* usual end of mime header */ + } + else { - memmove (end + 2, - next, - len - (next - start)); - - /* now terminate the gab at the end */ - delta = (next - end) - 2; - len -= delta; - buf[len] = '\0'; - - /* move next to its new location. */ - next -= delta; - nextend -= delta; + StrBufExtract_token(HdrToken, Buf, 0, ':'); + if (StrLength(HdrToken) > 0) { + StrBufCutLeft(Buf, StrLength(HdrToken) + 1); + lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf)); + /* 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); + } + break; + } + } + case 2: /* Message Body */ + + if (Msg->MsgBody->size_known > 0) { + StrBuf_ServGetBLOB(Msg->MsgBody->Data, Msg->MsgBody->length); + state ++; + /// 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); } + break; + case 3: + StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0); + break; } - /* our next-pair is our new first pair now. */ - start = next; - end = nextend; } - /* Now we handle foreign character sets properly encoded - * in RFC2047 format. - */ - while (start=strstr(buf, "=?"), end=FindNextEnd((start != NULL)? start : buf), - ((start != NULL) && (end != NULL) && (end > start)) ) - { - extract_token(charset, start, 1, '?', sizeof charset); - extract_token(encoding, start, 2, '?', sizeof encoding); - extract_token(istr, start, 3, '?', sizeof istr); - - ibuf = malloc(1024); - isav = ibuf; - if (!strcasecmp(encoding, "B")) { /**< base64 */ - ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr)); + 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); + + /* 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)) { + 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) ) { + StrBufRemove_token(Msg->reply_references, 1, '|'); } - else if (!strcasecmp(encoding, "Q")) { /**< quoted-printable */ - size_t len; - long pos; - - len = strlen(istr); - pos = 0; - while (pos < len) - { - if (istr[pos] == '_') istr[pos] = ' '; - pos++; - } + } - ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len); + /* Generate a reply-to address */ + if (StrLength(Msg->Rfca) > 0) { + if (Msg->reply_to == NULL) + Msg->reply_to = NewStrBuf(); + if (StrLength(Msg->from) > 0) { + StrBufPrintf(Msg->reply_to, "%s <%s>", ChrPtr(Msg->from), ChrPtr(Msg->Rfca)); } else { - strcpy(ibuf, istr); /**< unknown encoding */ - ibuflen = strlen(istr); + FlushStrBuf(Msg->reply_to); + StrBufAppendBuf(Msg->reply_to, Msg->Rfca, 0); } - - ic = ctdl_iconv_open("UTF-8", charset); - if (ic != (iconv_t)(-1) ) { - obuflen = 1024; - obuf = (char *) malloc(obuflen); - osav = obuf; - iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen); - osav[1024-obuflen] = 0; - - end = start; - end++; - strcpy(start, ""); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - strcpy(end, &end[1]); - - snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end); - strcpy(buf, newbuf); - free(osav); - iconv_close(ic); + } + else + { + if ((StrLength(Msg->OtherNode)>0) && + (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_nodename)) && + (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_humannode)) ) + { + if (Msg->reply_to == NULL) + Msg->reply_to = NewStrBuf(); + StrBufPrintf(Msg->reply_to, + "%s @ %s", + ChrPtr(Msg->from), + ChrPtr(Msg->OtherNode)); } else { - end = start; - end++; - strcpy(start, ""); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - remove_token(end, 0, '?'); - strcpy(end, &end[1]); - - snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end); - strcpy(buf, newbuf); + if (Msg->reply_to == NULL) + Msg->reply_to = NewStrBuf(); + FlushStrBuf(Msg->reply_to); + StrBufAppendBuf(Msg->reply_to, Msg->from, 0); } - - free(isav); - - /* - * Since spammers will go to all sorts of absurd lengths to get their - * messages through, there are LOTS of corrupt headers out there. - * So, prevent a really badly formed RFC2047 header from throwing - * this function into an infinite loop. - */ - ++passes; - if (passes > 20) return; } -} -#else -inline void utf8ify_rfc822_string(char *a){}; + /* 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); -#endif + DoTemplate(tmpl, tmpllen, Target, Msg, CTX_MAILSUM); + DestroyMessageSummary(Msg); + FreeStrBuf(&FoundCharset); + FreeStrBuf(&HdrToken); + FreeStrBuf(&Buf); + return 1; +} -/** - * \brief RFC2047-encode a header field if necessary. - * If no non-ASCII characters are found, the string - * will be copied verbatim without encoding. +/* + * Unadorned HTML output of an individual message, suitable + * for placing in a hidden iframe, for printing, or whatever * - * \param target Target buffer. - * \param maxlen Maximum size of target buffer. - * \param source Source string to be encoded. - * \param SourceLen Length of the source string - * \returns encoded length; -1 if non success. + * msgnum_as_string == Message number, as a string instead of as a long int */ -int webcit_rfc2047encode(char *target, int maxlen, char *source, long SourceLen) -{ - const char headerStr[] = "=?UTF-8?Q?"; - int need_to_encode = 0; - int i = 0; - int len; - unsigned char ch; - - if ((source == NULL) || - (target == NULL) || - (SourceLen > maxlen)) return -1; - - while ((!IsEmptyStr (&source[i])) && - (need_to_encode == 0) && - (i < SourceLen) ) { - if (((unsigned char) source[i] < 32) || - ((unsigned char) source[i] > 126)) { - need_to_encode = 1; - } - i++; - } +void embed_message(void) { + long msgnum = 0L; + const StrBuf *Tmpl = sbstr("template"); - if (!need_to_encode) { - memcpy (target, source, SourceLen); - target[SourceLen] = '\0'; - return SourceLen; - } - - if (sizeof (headerStr + SourceLen + 2) > maxlen) - return -1; - memcpy (target, headerStr, sizeof (headerStr)); - len = sizeof (headerStr) - 1; - for (i=0; (i < SourceLen) && (len + 3< maxlen) ; ++i) { - ch = (unsigned char) source[i]; - if ((ch < 32) || (ch > 126) || (ch == 61)) { - sprintf(&target[len], "=%02X", ch); - len += 3; - } - else { - sprintf(&target[len], "%c", ch); - len ++; - } - } - - if (len + 2 < maxlen) { - strcat(&target[len], "?="); - len +=2; - return len; - } - else - return -1; + msgnum = StrTol(WC->UrlFragment2); + if (StrLength(Tmpl) > 0) + read_message(WC->WBuf, SKEY(Tmpl), msgnum, 0, NULL); + else + read_message(WC->WBuf, HKEY("view_message"), msgnum, 0, NULL); } - - /* - * Look for URL's embedded in a buffer and make them linkable. We use a - * target window in order to keep the Citadel session in its own window. + * Printable view of a message + * + * msgnum_as_string == Message number, as a string instead of as a long int */ -void url(char *buf, size_t bufsize) -{ - int len, UrlLen, Offset, TrailerLen, outpos; - char *start, *end, *pos; - char urlbuf[SIZ]; - char outbuf[SIZ]; - - start = NULL; - len = strlen(buf); - if (len > bufsize) { - lprintf(1, "URL: content longer than buffer!"); - return; - } - end = buf + len; - for (pos = buf; (pos < end) && (start == NULL); ++pos) { - if (!strncasecmp(pos, "http://", 7)) - start = pos; - if (!strncasecmp(pos, "ftp://", 6)) - start = pos; - } +void print_message(void) { + long msgnum = 0L; - if (start == NULL) - return; + msgnum = StrTol(WC->UrlFragment2); + output_headers(0, 0, 0, 0, 0, 0); - for (pos = buf+len; pos > start; --pos) { - if ( (!isprint(*pos)) - || (isspace(*pos)) - || (*pos == '{') - || (*pos == '}') - || (*pos == '|') - || (*pos == '\\') - || (*pos == '^') - || (*pos == '[') - || (*pos == ']') - || (*pos == '`') - || (*pos == '<') - || (*pos == '>') - || (*pos == '(') - || (*pos == ')') - ) { - end = pos; - } - } - - UrlLen = end - start; - if (UrlLen > sizeof(urlbuf)){ - lprintf(1, "URL: content longer than buffer!"); - return; - } - memcpy(urlbuf, start, UrlLen); - urlbuf[UrlLen] = '\0'; - - Offset = start - buf; - if ((Offset != 0) && (Offset < sizeof(outbuf))) - memcpy(outbuf, buf, Offset); - outpos = snprintf(&outbuf[Offset], sizeof(outbuf) - Offset, - "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c", - LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB); - if (outpos >= sizeof(outbuf) - Offset) { - lprintf(1, "URL: content longer than buffer!"); - return; - } + hprintf("Content-type: text/html\r\n" + "Server: " PACKAGE_STRING "\r\n" + "Connection: close\r\n"); - TrailerLen = len - (end - start); - if (TrailerLen > 0) - memcpy(outbuf + Offset + outpos, end, TrailerLen); - if (Offset + outpos + TrailerLen > bufsize) { - lprintf(1, "URL: content longer than buffer!"); - return; - } - memcpy (buf, outbuf, Offset + outpos + TrailerLen); - *(buf + Offset + outpos + TrailerLen) = '\0'; -} + begin_burst(); + read_message(WC->WBuf, HKEY("view_message_print"), msgnum, 1, NULL); -/** - * \brief Turn a vCard "n" (name) field into something displayable. - * \param name the name field to convert - */ -void vcard_n_prettyize(char *name) -{ - char *original_name; - int i, j, len; - - original_name = strdup(name); - len = strlen(original_name); - for (i=0; i<5; ++i) { - if (len > 0) { - if (original_name[len-1] == ' ') { - original_name[--len] = 0; - } - if (original_name[len-1] == ';') { - original_name[--len] = 0; - } - } - } - strcpy(name, ""); - j=0; - for (i=0; iUrlFragment2); + output_headers(1, 0, 0, 0, 0, 1); + begin_burst(); + do_template("msgcontrols", NULL); + read_message(WC->WBuf, HKEY("view_message"), msgnum,1, NULL); + wDumpContent(0); } - - /** - * \brief html print a vcard - * display_vcard() calls this after parsing the textual vCard into - * our 'struct vCard' data object. - * - * Set 'full' to nonzero to display the full card, otherwise it will only - * show a summary line. + * \brief Display a message's headers * - * This code is a bit ugly, so perhaps an explanation is due: we do this - * in two passes through the vCard fields. On the first pass, we process - * fields we understand, and then render them in a pretty fashion at the - * end. Then we make a second pass, outputting all the fields we don't - * understand in a simple two-column name/value format. - * \param v the vCard to display - * \param full display all items of the vcard? - * \param msgnum Citadel message pointer + * \param msgnum_as_string Message number, as a string instead of as a long int */ -void display_parsed_vcard(struct vCard *v, int full, long msgnum) { - int i, j; - char buf[SIZ]; - char *name; - int is_qp = 0; - int is_b64 = 0; - char *thisname, *thisvalue; - char firsttoken[SIZ]; - int pass; - - char fullname[SIZ]; - char title[SIZ]; - char org[SIZ]; - char phone[SIZ]; - char mailto[SIZ]; - - strcpy(fullname, ""); - strcpy(phone, ""); - strcpy(mailto, ""); - strcpy(title, ""); - strcpy(org, ""); - - if (!full) { - wprintf(""); - name = vcard_get_prop(v, "fn", 1, 0, 0); - if (name != NULL) { - escputs(name); - } - else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) { - strcpy(fullname, name); - vcard_n_prettyize(fullname); - escputs(fullname); - } - else { - wprintf(" "); - } - wprintf(""); - return; - } - - wprintf("
" - ""); - for (pass=1; pass<=2; ++pass) { +void display_headers(void) { + long msgnum = 0L; + char buf[1024]; - if (v->numprops) for (i=0; i<(v->numprops); ++i) { - int len; - thisname = strdup(v->prop[i].name); - extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken); - - for (j=0; jprop[i].value); - /* if we have some untagged QP, detect it here. */ - if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL)) - utf8ify_rfc822_string(v->prop[i].value); - - if (is_qp) { - // %ff can become 6 bytes in utf8 - thisvalue = malloc(len * 2 + 3); - j = CtdlDecodeQuotedPrintable( - thisvalue, v->prop[i].value, - len); - thisvalue[j] = 0; - } - else if (is_b64) { - // ff will become one byte.. - thisvalue = malloc(len + 50); - CtdlDecodeBase64( - thisvalue, v->prop[i].value, - strlen(v->prop[i].value) ); - } - else { - thisvalue = strdup(v->prop[i].value); - } - - /** Various fields we may encounter ***/ - - /** N is name, but only if there's no FN already there */ - if (!strcasecmp(firsttoken, "n")) { - if (IsEmptyStr(fullname)) { - strcpy(fullname, thisvalue); - vcard_n_prettyize(fullname); - } - } - - /** FN (full name) is a true 'display name' field */ - else if (!strcasecmp(firsttoken, "fn")) { - strcpy(fullname, thisvalue); - } + msgnum = StrTol(WC->UrlFragment2); + output_headers(0, 0, 0, 0, 0, 0); - /** title */ - else if (!strcasecmp(firsttoken, "title")) { - strcpy(title, thisvalue); - } - - /** organization */ - else if (!strcasecmp(firsttoken, "org")) { - strcpy(org, thisvalue); - } - - else if (!strcasecmp(firsttoken, "email")) { - size_t len; - if (!IsEmptyStr(mailto)) strcat(mailto, "
"); - strcat(mailto, - ""); - - strcat(mailto, "\">"); - len = strlen(mailto); - stresc(mailto+len, SIZ - len, thisvalue, 1, 1); - strcat(mailto, ""); - } - else if (!strcasecmp(firsttoken, "tel")) { - if (!IsEmptyStr(phone)) strcat(phone, "
"); - strcat(phone, thisvalue); - for (j=0; j
\n"); - } - } - else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { - // Only output on second pass - wprintf("\n"); - } - else if (!strcasecmp(firsttoken, "version")) { - /* ignore */ - } - else if (!strcasecmp(firsttoken, "rev")) { - /* ignore */ - } - else if (!strcasecmp(firsttoken, "label")) { - /* ignore */ - } - else { + hprintf("Content-type: text/plain\r\n" + "Server: %s\r\n" + "Connection: close\r\n", + PACKAGE_STRING); + begin_burst(); - /*** Don't show extra fields. They're ugly. - if (pass == 2) { - wprintf("\n"); - } - ***/ - } - - free(thisname); - free(thisvalue); - } - - if (pass == 1) { - wprintf("" - "\n"); - - if (!IsEmptyStr(phone)) { - wprintf("\n", phone); - } - if (!IsEmptyStr(mailto)) { - wprintf("\n", mailto); - } + serv_printf("MSG2 %ld|3", msgnum); + serv_getln(buf, sizeof buf); + if (buf[0] == '1') { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + wprintf("%s\n", buf); } - } - wprintf("
"); - wprintf(_("Address:")); - wprintf(""); - for (j=0; j"); - else wprintf(" "); - } - } - wprintf("
"); - wprintf(_("Photo:")); - wprintf(""); - wprintf("\"Contact",msgnum); - wprintf("
"); - escputs(thisname); - wprintf(""); - escputs(thisvalue); - wprintf("
" - "" - ""); - escputs(fullname); - wprintf(""); - if (!IsEmptyStr(title)) { - wprintf("
"); - escputs(title); - wprintf("
"); - } - if (!IsEmptyStr(org)) { - wprintf("
"); - escputs(org); - wprintf("
"); - } - wprintf("
"); - wprintf(_("Telephone:")); - wprintf("%s
"); - wprintf(_("E-mail:")); - wprintf("%s
\n"); + wDumpContent(0); } +message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) +{ + void *vEval; + MsgPartEvaluatorFunc Eval; + message_summary *Msg; + StrBuf *Buf; + const char *buf; + const char *ebuf; + int nBuf; + long len; + + Buf = NewStrBuf(); -/** - * \brief Display a textual vCard - * (Converts to a vCard object and then calls the actual display function) - * Set 'full' to nonzero to display the whole card instead of a one-liner. - * Or, if "storename" is non-NULL, just store the person's name in that - * buffer instead of displaying the card at all. - * \param vcard_source the buffer containing the vcard text - * \param alpha what??? - * \param full should we usse all lines? - * \param storename where to store??? - * \param msgnum Citadel message pointer - */ -void display_vcard(char *vcard_source, char alpha, int full, char *storename, - long msgnum) { - struct vCard *v; - char *name; - char buf[SIZ]; - char this_alpha = 0; - - v = vcard_load(vcard_source); - if (v == NULL) return; - - name = vcard_get_prop(v, "n", 1, 0, 0); - if (name != NULL) { - utf8ify_rfc822_string(name); - strcpy(buf, name); - this_alpha = buf[0]; - } - - if (storename != NULL) { - fetchname_parsed_vcard(v, storename); - } - else if ( (alpha == 0) - || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) ) - || ((!isalpha(alpha)) && (!isalpha(this_alpha))) - ) { - display_parsed_vcard(v, full,msgnum); - } - - vcard_free(v); + serv_printf("MSG0 %ld|1", MsgNum); /* ask for headers only */ + + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) == 1) { + FreeStrBuf(&Buf); + return NULL; + } + + Msg = (message_summary*)malloc(sizeof(message_summary)); + memset(Msg, 0, sizeof(message_summary)); + while (len = StrBuf_ServGetln(Buf), + ((len != 3) || + strcmp(ChrPtr(Buf), "000")== 0)){ + buf = ChrPtr(Buf); + ebuf = strchr(ChrPtr(Buf), '='); + nBuf = ebuf - buf; + if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) { + Eval = (MsgPartEvaluatorFunc) vEval; + StrBufCutLeft(Buf, nBuf + 1); + Eval(Msg, Buf); + } + else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf)); + } + return Msg; } -struct attach_link { - char partnum[32]; - char html[1024]; -}; + /* - * I wanna SEE that message! + * load message pointers from the server for a "read messages" operation * - * msgnum Message number to display - * printable_view Nonzero to display a printable view - * section Optional for encapsulated message/rfc822 submessage + * servcmd: the citadel command to send to the citserver + * with_headers: also include some of the headers with the message numbers (more expensive) */ -void read_message(long msgnum, int printable_view, char *section) { - char buf[SIZ]; - char mime_partnum[256] = ""; - char mime_name[256] = ""; - char mime_filename[256] = ""; - char escaped_mime_filename[256] = ""; - char mime_content_type[256] = ""; - const char *mime_content_type_ptr; - char mime_charset[256] = ""; - char mime_disposition[256] = ""; - int mime_length; - struct attach_link *attach_links = NULL; - int num_attach_links = 0; - char mime_submessages[256] = ""; - char m_subject[1024] = ""; - char m_cc[1024] = ""; - char from[256] = ""; - char node[256] = ""; - char rfca[256] = ""; - char reply_to[512] = ""; - char reply_all[4096] = ""; - char reply_references[1024] = ""; - char reply_inreplyto[256] = ""; - char now[64] = ""; - int format_type = 0; - int nhdr = 0; - int bq = 0; - int i = 0; - char vcard_partnum[256] = ""; - char cal_partnum[256] = ""; - char *part_source = NULL; - char msg4_partnum[32] = ""; -#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(mime_content_type, "text/plain"); - strcpy(mime_charset, "us-ascii"); - strcpy(mime_submessages, ""); - - serv_printf("MSG4 %ld|%s", msgnum, section); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - wprintf(""); - wprintf(_("ERROR:")); - wprintf(" %s
\n", &buf[4]); - return; +int load_msg_ptrs(char *servcmd, int with_headers) +{ + StrBuf* FoundCharset = NULL; + wcsession *WCC = WC; + message_summary *Msg; + StrBuf *Buf, *Buf2; + int nummsgs = 0; + int maxload = 0; + long len; + int n; + + if (WCC->summ != NULL) { + if (WCC->summ != NULL) + DeleteHash(&WCC->summ); + } + WCC->summ = NewHash(1, Flathash); + maxload = 10000; + + Buf = NewStrBuf(); + serv_puts(servcmd); + StrBuf_ServGetln(Buf); + if (GetServerStatus(Buf, NULL) != 1) { + 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) { + Msg = (message_summary*)malloc(sizeof(message_summary)); + memset(Msg, 0, sizeof(message_summary)); - /** begin everythingamundo table */ - if (!printable_view) { - wprintf("
", msgnum); - } - - /** begin message header table */ - wprintf("
"); - - strcpy(m_subject, ""); - strcpy(m_cc, ""); - - while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) { - if (!strcmp(buf, "000")) { - wprintf(""); - wprintf(_("unexpected end of message")); - wprintf(" (1)

\n"); - wprintf("
\n"); - return; - } - 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 ")); - wprintf(""); - escputs(from); - wprintf(" "); - } - if (!strncasecmp(buf, "subj=", 5)) { - safestrncpy(m_subject, &buf[5], sizeof m_subject); - } - if (!strncasecmp(buf, "msgn=", 5)) { - safestrncpy(reply_inreplyto, &buf[5], sizeof reply_inreplyto); - } - if (!strncasecmp(buf, "wefw=", 5)) { - safestrncpy(reply_references, &buf[5], sizeof reply_references); - } - if (!strncasecmp(buf, "cccc=", 5)) { - int len; - safestrncpy(m_cc, &buf[5], sizeof m_cc); - if (!IsEmptyStr(reply_all)) { - strcat(reply_all, ", "); - } - len = strlen(reply_all); - safestrncpy(&reply_all[len], &buf[5], - (sizeof reply_all - len) ); - } - 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("<"); - escputs(rfca); - wprintf("> "); - } + Msg->msgnum = StrBufExtract_long(Buf, 0, '|'); + Msg->date = StrBufExtract_long(Buf, 1, '|'); - 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]); + 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); } - } - if (!strncasecmp(buf, "rcpt=", 5)) { - int len; - wprintf(_("to ")); - if (!IsEmptyStr(reply_all)) { - strcat(reply_all, ", "); + + /** 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); } - len = strlen(reply_all); - safestrncpy(&reply_all[len], &buf[5], - (sizeof reply_all - len) ); - utf8ify_rfc822_string(&buf[5]); - escputs(&buf[5]); - wprintf(" "); - } - if (!strncasecmp(buf, "time=", 5)) { - webcit_fmt_date(now, atol(&buf[5]), 0); - wprintf(""); - wprintf("%s ", now); - wprintf(""); - } - if (!strncasecmp(buf, "part=", 5)) { - extract_token(mime_name, &buf[5], 0, '|', sizeof mime_filename); - 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); - - striplt(mime_name); - striplt(mime_filename); - if ( (IsEmptyStr(mime_filename)) && (!IsEmptyStr(mime_name)) ) { - strcpy(mime_filename, mime_name); - } + /** Not used: + StrBufExtract_token(Msg->inetaddr, Buf, 4, '|'); + */ - if (!strcasecmp(mime_content_type, "message/rfc822")) { - if (!IsEmptyStr(mime_submessages)) { - strcat(mime_submessages, "|"); - } - strcat(mime_submessages, mime_partnum); - } - else if ((!strcasecmp(mime_disposition, "inline")) - && (!strncasecmp(mime_content_type, "image/", 6)) ){ - ++num_attach_links; - attach_links = realloc(attach_links, - (num_attach_links*sizeof(struct attach_link))); - safestrncpy(attach_links[num_attach_links-1].partnum, mime_partnum, 32); - snprintf(attach_links[num_attach_links-1].html, 1024, - "", - msgnum, mime_partnum, mime_filename); - } - else if ( ( (!strcasecmp(mime_disposition, "attachment")) - || (!strcasecmp(mime_disposition, "inline")) - || (!strcasecmp(mime_disposition, "")) - ) && (!IsEmptyStr(mime_content_type)) - ) { - ++num_attach_links; - attach_links = realloc(attach_links, - (num_attach_links*sizeof(struct attach_link))); - safestrncpy(attach_links[num_attach_links-1].partnum, mime_partnum, 32); - utf8ify_rfc822_string(mime_filename); - - mime_content_type_ptr = mime_content_type; - if (strcasecmp(mime_content_type, "application/octet-stream") == 0) { - mime_content_type_ptr = GuessMimeByFilename(mime_filename, - strlen(mime_filename)); + Msg->subj = NewStrBufPlain(NULL, StrLength(Buf)); + StrBufExtract_token(Buf2, Buf, 5, '|'); + if (StrLength(Buf2) == 0) + StrBufAppendBufPlain(Msg->subj, _("(no subj)"), -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); } - urlesc(escaped_mime_filename, 265, mime_filename); - snprintf(attach_links[num_attach_links-1].html, 1024, - "\n" - "%s (%s, %d bytes) [ " - "%s" - " | " - "%s" - " ]
\n", - mime_content_type_ptr, - mime_filename, - mime_content_type, mime_length, - msgnum, mime_partnum, escaped_mime_filename, - msgnum, mime_partnum, - _("View"), - msgnum, mime_partnum, escaped_mime_filename, - _("Download") - ); } - /** begin handler prep ***/ - if ( (!strcasecmp(mime_content_type, "text/x-vcard")) - || (!strcasecmp(mime_content_type, "text/vcard")) ) { - strcpy(vcard_partnum, mime_partnum); - } - if ( (!strcasecmp(mime_content_type, "text/calendar")) - || (!strcasecmp(mime_content_type, "application/ics")) ) { - strcpy(cal_partnum, mime_partnum); + if ((StrLength(Msg->from) > 25) && + (StrBuf_Utf8StrLen(Msg->from) > 25)) { + StrBuf_Utf8StrCut(Msg->from, 23); + StrBufAppendBufPlain(Msg->from, HKEY("..."), 0); } - - /** end handler prep ***/ - + n = Msg->msgnum; + Put(WCC->summ, (const char *)&n, sizeof(n), Msg, DestroyMessageSummary); } - + nummsgs++; } + FreeStrBuf(&Buf2); + FreeStrBuf(&Buf); + return (nummsgs); +} - /* 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(reply_references, '|'); - int rrlen = strlen(reply_references); - if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) { - remove_token(reply_references, 1, '|'); - } - /* Generate a reply-to address */ - if (!IsEmptyStr(rfca)) { - if (!IsEmptyStr(from)) { - snprintf(reply_to, sizeof(reply_to), "%s <%s>", from, rfca); - } - else { - strcpy(reply_to, rfca); - } - } - else { - if ((!IsEmptyStr(node)) - && (strcasecmp(node, serv_info.serv_nodename)) - && (strcasecmp(node, serv_info.serv_humannode)) ) { - snprintf(reply_to, sizeof(reply_to), "%s @ %s", - from, node); - } - else { - snprintf(reply_to, sizeof(reply_to), "%s", from); - } - } - - if (nhdr == 1) { - wprintf("****"); - } - - utf8ify_rfc822_string(m_cc); - utf8ify_rfc822_string(m_subject); - - /** start msg buttons */ - - if (!printable_view) { - wprintf("

\n",msgnum); - - /* Reply */ - if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { - wprintf("is_mailbox) { - wprintf("?replyquote=%ld", msgnum); - } - wprintf("?recp="); - urlescputs(reply_to); - if (!IsEmptyStr(m_subject)) { - wprintf("?subject="); - if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%%20"); - urlescputs(m_subject); - } - wprintf("?references="); - if (!IsEmptyStr(reply_references)) { - urlescputs(reply_references); - urlescputs("|"); - } - urlescputs(reply_inreplyto); - wprintf("\">[%s] ", _("Reply")); - } - - /* ReplyQuoted */ - if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { - if (!WC->is_mailbox) { - wprintf("[%s] ", _("ReplyQuoted")); - } - } - - /* ReplyAll */ - if (WC->wc_view == VIEW_MAILBOX) { - wprintf("[%s] ", _("ReplyAll")); - } - - /* Forward */ - if (WC->wc_view == VIEW_MAILBOX) { - wprintf("[%s] ", _("Forward")); - } - - /* If this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */ - if ( (WC->is_room_aide) || (WC->is_mailbox) || (WC->room_flags2 & QR2_COLLABDEL) ) { - /** Move */ - wprintf("[%s] ", - msgnum, _("Move")); - - /** Delete */ - wprintf("" - "[%s] " - " ", msgnum, _("Delete this message?"), _("Delete") - ); - } - - /* Headers */ - wprintf("" - "[%s]", msgnum, msgnum, _("Headers")); - - - /* Print */ - wprintf("" - "[%s]", msgnum, msgnum, _("Print")); - - wprintf("

"); - - } - - if (!IsEmptyStr(m_cc)) { - wprintf("

"); - wprintf(_("CC:")); - wprintf(" "); - escputs(m_cc); - wprintf("

"); - } - if (!IsEmptyStr(m_subject)) { - wprintf("

"); - wprintf(_("Subject:")); - wprintf(" "); - escputs(m_subject); - wprintf("

"); - } - - 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")) { - /* This is not necessarily an error condition. See bug #226. */ - goto ENDBODY; - } - if (!strncasecmp(buf, "X-Citadel-MSG4-Partnum:", 23)) { - safestrncpy(msg4_partnum, &buf[23], sizeof(msg4_partnum)); - striplt(msg4_partnum); - } - if (!strncasecmp(buf, "Content-type:", 13)) { - int len; - safestrncpy(mime_content_type, &buf[13], sizeof(mime_content_type)); - striplt(mime_content_type); - len = strlen(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 = strlen(ibuf); - 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)); - escputs(buf); - wprintf("
\n"); - } - wprintf("
"); - } - - /* HTML is fun, but we've got to strip it first */ - else if (!strcasecmp(mime_content_type, "text/html")) { - output_html(mime_charset, (WC->wc_view == VIEW_WIKI ? 1 : 0)); - } - - /* Unknown weirdness */ - else { - wprintf(_("I don't know how to display %s"), mime_content_type); - wprintf("
\n"); - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { } - } - -ENDBODY: /* If there are attached submessages, display them now... */ - - if ( (!IsEmptyStr(mime_submessages)) && (!section[0]) ) { - for (i=0; i"); - read_message(msgnum, 1, buf); - wprintf(""); - } - } - - - /* Afterwards, offer links to download attachments 'n' such */ - if ( (num_attach_links > 0) && (!section[0]) ) { - for (i=0; iwc_roomname, USERCONFIGROOM)) - || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) - || (WC->wc_view == VIEW_ADDRESSBOOK) - ) { - wprintf("", - msgnum, vcard_partnum); - wprintf("[%s]", _("edit")); - } - - /* In all cases, display the full card */ - display_vcard(part_source, 0, 1, NULL,msgnum); - } - } - - /* Handler for calendar parts */ - if (!IsEmptyStr(cal_partnum)) { - part_source = load_mimepart(msgnum, cal_partnum); - if (part_source != NULL) { - cal_process_attachment(part_source, msgnum, cal_partnum); - } - } - - if (part_source) { - free(part_source); - part_source = NULL; - } - - wprintf("
\n"); - - /* end everythingamundo table */ - if (!printable_view) { - wprintf("\n"); - } - - if (num_attach_links > 0) { - free(attach_links); - } - -#ifdef HAVE_ICONV - if (ic != (iconv_t)(-1) ) { - iconv_close(ic); - } -#endif -} - - - -/* - * Unadorned HTML output of an individual message, suitable - * for placing in a hidden iframe, for printing, or whatever - * - * msgnum_as_string == Message number, as a string instead of as a long int - */ -void embed_message(char *msgnum_as_string) { - long msgnum = 0L; - - msgnum = atol(msgnum_as_string); - begin_ajax_response(); - read_message(msgnum, 0, ""); - end_ajax_response(); -} - - -/* - * Printable view of a message - * - * msgnum_as_string == Message number, as a string instead of as a long int - */ -void print_message(char *msgnum_as_string) { - long msgnum = 0L; - - msgnum = atol(msgnum_as_string); - output_headers(0, 0, 0, 0, 0, 0); - - wprintf("Content-type: text/html\r\n" - "Server: %s\r\n" - "Connection: close\r\n", - PACKAGE_STRING); - begin_burst(); - - wprintf("\r\n\r\n\n"); - escputs(WC->wc_fullname); - wprintf("\n" - "\n" - ); - - read_message(msgnum, 1, ""); - - wprintf("\n\n\n"); - wDumpContent(0); -} - - - -/** - * \brief Display a message's headers - * - * \param msgnum_as_string Message number, as a string instead of as a long int - */ -void display_headers(char *msgnum_as_string) { - long msgnum = 0L; - char buf[1024]; - - msgnum = atol(msgnum_as_string); - output_headers(0, 0, 0, 0, 0, 0); - - wprintf("Content-type: text/plain\r\n" - "Server: %s\r\n" - "Connection: close\r\n", - PACKAGE_STRING); - begin_burst(); - - serv_printf("MSG2 %ld|3", msgnum); - serv_getln(buf, sizeof buf); - if (buf[0] == '1') { - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - wprintf("%s\n", buf); - } - } - - wDumpContent(0); -} - - - -/** - * \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) { - 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; - struct wc_attachment *att, *aptr; - 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", 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; - strcpy(att->content_type, mime_content_type); - strcpy(att->filename, mime_filename); - att->next = NULL; - att->data = load_mimepart(msgnum, mime_partnum); - - /* And add it to the list. */ - if (WC->first_attachment == NULL) { - WC->first_attachment = att; - } - else { - aptr = WC->first_attachment; - while (aptr->next != NULL) aptr = aptr->next; - aptr->next = att; - } - } - - } - } - -#ifdef HAVE_ICONV - if (ic != (iconv_t)(-1) ) { - iconv_close(ic); - } -#endif - - if (attachments != NULL) { - free(attachments); - } -} - -/** - * \brief Display one row in the mailbox summary view - * - * \param num The row number to be displayed - */ -void display_summarized(int num) { - char datebuf[64]; - wprintf("", - WC->summ[num].msgnum, - (WC->summ[num].is_new ? "bold" : "normal"), - WC->summ[num].msgnum - ); - - wprintf("", SUBJ_COL_WIDTH_PCT); - - escputs(WC->summ[num].subj);//////////////////////////////////TODO: QP DECODE - wprintf(""); - - wprintf("", SENDER_COL_WIDTH_PCT); - escputs(WC->summ[num].from); - wprintf(""); - - wprintf("", DATE_PLUS_BUTTONS_WIDTH_PCT); - webcit_fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ - escputs(datebuf); - wprintf(""); - - wprintf("\n"); -} -/** - * \brief Output a message row for the mobile view - * \param The row number - */ -void display_mobile_summary(int num) { - char datebuf[64]; - wprintf("\n
", - WC->summ[num].msgnum, - (WC->summ[num].is_new ? "bold" : "normal"), - WC->summ[num].msgnum - ); - wprintf("%s",WC->summ[num].from); - wprintf(""); - webcit_fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ - escputs(datebuf); - wprintf("
"); - wprintf(WC->summ[num].subj); - wprintf("
",WC->summ[num].msgnum); -} - -/** - * \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; - struct message_summary summ; - - memset(&summ, 0, sizeof(summ)); - safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj); - - sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ - 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(msgnum, vcard_partnum); - if (vcard_source != NULL) { - - /** Display the summary line */ - display_vcard(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(long msgnum, 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; - struct message_summary summ; - - if (namebuf == NULL) return; - strcpy(namebuf, ""); - - memset(&summ, 0, sizeof(summ)); - safestrncpy(summ.subj, "(no subject)", sizeof summ.subj); - - sprintf(buf, "MSG0 %ld|0", 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(msgnum, vcard_partnum); - if (vcard_source != NULL) { - - /* Grab the name off the card */ - display_vcard(vcard_source, 0, 0, namebuf,msgnum); - - free(vcard_source); - } - } - - lastfirst_firstlast(namebuf); - striplt(namebuf); - len = strlen(namebuf); - for (i=0; iab_name), - (((const struct addrbookent *)ab2)->ab_name) - )); -} - - -/** - * \brief Helper function for do_addrbook_view() - * Converts a name into a three-letter tab label - * \param tabbuf the tabbuffer to add name to - * \param name the name to add to the tabbuffer - */ -void nametab(char *tabbuf, long len, char *name) { - stresc(tabbuf, len, name, 0, 0); - tabbuf[0] = toupper(tabbuf[0]); - tabbuf[1] = tolower(tabbuf[1]); - tabbuf[2] = tolower(tabbuf[2]); - tabbuf[3] = 0; +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; } -/** - * \brief Render the address book using info we gathered during the scan - * \param addrbook the addressbook to render - * \param num_ab the number of the addressbook - */ -void do_addrbook_view(struct addrbookent *addrbook, int num_ab) { +void DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg) +{ + StrBuf *TmpBuf; + wcsession *WCC = WC; + message_summary* Msg; + int lo, hi, n; int i = 0; - int displayed = 0; - int bg = 0; - static int NAMESPERPAGE = 60; - int num_pages = 0; - int tabfirst = 0; - char tabfirst_label[64]; - int tablast = 0; - char tablast_label[64]; - char this_tablabel[64]; - int page = 0; - char **tablabels; - - if (num_ab == 0) { - wprintf("


"); - wprintf(_("This address book is empty.")); - wprintf("
\n"); - return; - } - - if (num_ab > 1) { - qsort(addrbook, num_ab, sizeof(struct addrbookent), abcmp); - } - - num_pages = (num_ab / NAMESPERPAGE) + 1; - - tablabels = malloc(num_pages * sizeof (char *)); - if (tablabels == NULL) { - wprintf("


"); - wprintf(_("An internal error has occurred.")); - wprintf("
\n"); - return; - } - - for (i=0; i (num_ab - 1)) tablast = (num_ab - 1); - nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name); - nametab(tablast_label, 64, addrbook[tablast].ab_name); - sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label); - tablabels[i] = strdup(this_tablabel); - } - - tabbed_dialog(num_pages, tablabels); - page = (-1); - - for (i=0; i 0) { - wprintf("\n"); - end_tab(page-1, num_pages); - } - begin_tab(page, num_pages); - wprintf("\n"); - displayed = 0; - } - - if ((displayed % 4) == 0) { - if (displayed > 0) { - wprintf("\n"); - } - bg = 1 - bg; - wprintf("", - (bg ? "DDDDDD" : "FFFFFF") - ); - } + long StartMsg = 0; + void *vMsg; + long hklen; + const char *key; + int done = 0; + int nItems; + HashPos *At; + long vector[16]; + int nMessages = DEFAULT_MAXMSGS; + + TmpBuf = NewStrBuf(); + At = GetNewHashPos(WCC->summ, (lbstr("SortOrder") == 1)? -nMessages : nMessages); + nItems = GetCount(WCC->summ); - wprintf("\n"); - ++displayed; - } - - wprintf("
"); - - wprintf("", bstr("alpha")); - vcard_n_prettyize(addrbook[i].ab_name); - escputs(addrbook[i].ab_name); - wprintf("
\n"); - end_tab((num_pages-1), num_pages); - - for (i=0; isumm != NULL) { - free(WC->summ); - WC->num_summ = 0; - WC->summ = NULL; - } - num_summ_alloc = 100; - WC->num_summ = 0; - WC->summ = malloc(num_summ_alloc * sizeof(struct message_summary)); - - nummsgs = 0; - maxload = sizeof(WC->msgarr) / sizeof(long) ; - serv_puts(servcmd); - serv_getln(buf, sizeof buf); - if (buf[0] != '1') { - return (nummsgs); - } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - if (nummsgs < maxload) { - WC->msgarr[nummsgs] = extract_long(buf, 0); - datestamp = extract_long(buf, 1); - extract_token(fullname, buf, 2, '|', sizeof fullname); - extract_token(nodename, buf, 3, '|', sizeof nodename); - extract_token(inetaddr, buf, 4, '|', sizeof inetaddr); - extract_token(subject, buf, 5, '|', sizeof subject); - ++nummsgs; - - if (with_headers) { - if (nummsgs > num_summ_alloc) { - num_summ_alloc *= 2; - WC->summ = realloc(WC->summ, - num_summ_alloc * sizeof(struct message_summary)); - } - ++WC->num_summ; - - memset(&WC->summ[nummsgs-1], 0, sizeof(struct message_summary)); - WC->summ[nummsgs-1].msgnum = WC->msgarr[nummsgs-1]; - safestrncpy(WC->summ[nummsgs-1].subj, - _("(no subject)"), sizeof WC->summ[nummsgs-1].subj); - if (!IsEmptyStr(subject)) { - /** Handle subjects with RFC2047 encoding */ - utf8ify_rfc822_string(subject); - safestrncpy(WC->summ[nummsgs-1].subj, subject, - sizeof WC->summ[nummsgs-1].subj); - } - sbjlen = Ctdl_Utf8StrLen(WC->summ[nummsgs-1].subj); - if (sbjlen > 75) { - ptr = Ctdl_Utf8StrCut(WC->summ[nummsgs-1].subj, 72); - - strcpy(ptr, "..."); - } - - if (!IsEmptyStr(fullname)) { - /** Handle senders with RFC2047 encoding */ - utf8ify_rfc822_string(fullname); - safestrncpy(WC->summ[nummsgs-1].from, - fullname, sizeof WC->summ[nummsgs-1].from); - } - if ((!IsEmptyStr(nodename)) && - ( ((WC->room_flags & QR_NETWORK) - || ((strcasecmp(nodename, serv_info.serv_nodename) - && (strcasecmp(nodename, serv_info.serv_fqdn))))))) - { - strcat(WC->summ[nummsgs-1].from, " @ "); - strcat(WC->summ[nummsgs-1].from, nodename); - - } - sbjlen = Ctdl_Utf8StrLen(WC->summ[nummsgs-1].from); - if (sbjlen > 25) { - ptr = Ctdl_Utf8StrCut(WC->summ[nummsgs-1].from, 23); - strcpy(ptr, "..."); - } - - WC->summ[nummsgs-1].date = datestamp; - } + while (!done) { + lo = GetHashPosCounter(At); + if (lo + nMessages > nItems) { + hi = nItems; } + else { + hi = lo + nMessages; + } + done = !GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg); + Msg = (message_summary*) vMsg; + n = (Msg==NULL)? 0 : Msg->msgnum; + if (i == 0) + StartMsg = n; + vector[4] = lo; + vector[5] = hi; + vector[6] = n; + FlushStrBuf(TmpBuf); + DoTemplate(HKEY("select_messageindex"), TmpBuf, &vector, CTX_LONGVECTOR); + StrBufAppendBuf(Selector, TmpBuf, 0); + i++; } - return (nummsgs); -} - - -typedef int (*QSortFunction) (const void*, const void*); -/** - * \brief qsort() compatible function to compare two longs in descending order. - * - * \param s1 first number to compare - * \param s2 second number to compare - */ -int longcmp_r(const void *s1, const void *s2) { - long l1; - long l2; - - l1 = *(long *)s1; - l2 = *(long *)s2; - - if (l1 > l2) return(-1); - if (l1 < l2) return(+1); - return(0); -} - - -/** - * \brief qsort() compatible function to compare two message summary structs by ascending subject. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_subj(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - return strcasecmp(summ1->subj, summ2->subj); -} - -/** - * \brief qsort() compatible function to compare two message summary structs by descending subject. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_rsubj(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - return strcasecmp(summ2->subj, summ1->subj); -} - -/** - * \brief qsort() compatible function to compare two message summary structs by ascending sender. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_sender(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - return strcasecmp(summ1->from, summ2->from); -} - -/** - * \brief qsort() compatible function to compare two message summary structs by descending sender. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_rsender(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - return strcasecmp(summ2->from, summ1->from); -} - -/** - * \brief qsort() compatible function to compare two message summary structs by ascending date. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_date(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - - if (summ1->date < summ2->date) return -1; - else if (summ1->date > summ2->date) return +1; - else return 0; -} - -/** - * \brief qsort() compatible function to compare two message summary structs by descending date. - * - * \param s1 first item to compare - * \param s2 second item to compare - */ -int summcmp_rdate(const void *s1, const void *s2) { - struct message_summary *summ1; - struct message_summary *summ2; - - summ1 = (struct message_summary *)s1; - summ2 = (struct message_summary *)s2; - - if (summ1->date < summ2->date) return +1; - else if (summ1->date > summ2->date) return -1; - else return 0; -} - - -enum { - eUp, - eDown, - eNone -}; - -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 QSortFunction 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}; - - -/** - * \Brief Translates sortoption String to its SortEnum representation - * \param SortBy string to translate - * \return the enum matching the string; defaults to RDate - */ -//SortByEnum -int StrToESort (StrBuf *sortby) -{ - int result = eDate; - - if (!IsEmptyStr(ChrPtr(sortby))) while (result < eUnSet){ - if (!strcasecmp(ChrPtr(sortby), - SortByStrings[result])) - return result; - result ++; - } - return eRDate; + vector[6] = StartMsg; + FlushStrBuf(TmpBuf); + vector[1] = lbstr("maxmsgs") == 9999999; + vector[2] = 0; + DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &vector, CTX_LONGVECTOR); + StrBufAppendBuf(Selector, TmpBuf, 0); + FreeStrBuf(&TmpBuf); + DeleteHashPos(&At); } - - - -/** - * \brief command loop for reading messages - * - * \param oper Set to "readnew" or "readold" or "readfwd" or "headers" - */ -void readloop(char *oper) +void load_seen_flags(void) { - char cmd[256] = ""; - char buf[SIZ]; - char old_msgs[SIZ]; - int a, b; - int nummsgs; - long startmsg; - int maxmsgs; - long *displayed_msgs = NULL; - int num_displayed = 0; - int is_summary = 0; - int is_addressbook = 0; - int is_singlecard = 0; - int is_calendar = 0; - int is_tasks = 0; - int is_notes = 0; - int is_bbview = 0; - int lo, hi; - int lowest_displayed = (-1); - int highest_displayed = 0; - struct addrbookent *addrbook = NULL; - int num_ab = 0; - StrBuf *sortby = NULL; - //SortByEnum - int SortBy = eRDate; - StrBuf *sortpref_name; - StrBuf *sortpref_value; - int bbs_reverse = 0; - struct wcsession *WCC = WC; /* This is done to make it run faster; WC is a function */ - - 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_name = NewStrBuf (); - StrBufPrintf(sortpref_name, "sort %s", WCC->wc_roomname); - get_pref(sortpref_name, &sortpref_value); - - sortby = NewStrBufPlain(bstr("sortby"), -1); - if ( (!IsEmptyStr(ChrPtr(sortby))) && - (strcasecmp(ChrPtr(sortby), ChrPtr(sortpref_value)) != 0)) { - set_pref(sortpref_name, sortby, 1); - sortpref_value = NULL; - sortpref_value = sortby; - } - - FreeStrBuf(&sortpref_name); - SortBy = StrToESort(sortpref_value); - /** message board sort */ - if (SortBy == eReverse) { - bbs_reverse = 1; + message_summary *Msg; + const char *HashKey; + long HKLen; + StrBuf *OldMsg = NewStrBuf();; + wcsession *WCC = WC; + HashPos *at; + void *vMsg; + + serv_puts("GTSN"); + StrBuf_ServGetln(OldMsg); + if (ChrPtr(OldMsg)[0] == '2') { + StrBufCutLeft(OldMsg, 4); } else { - bbs_reverse = 0; - } - - 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"); - } + FreeStrBuf(&OldMsg); + return; } - - 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"); + 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; } - maxmsgs = 9999999; - } - - if (is_summary || WCC->is_mobile) { /**< 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; - 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 { + Msg->is_new = 1; } } + FreeStrBuf(&OldMsg); + DeleteHashPos(&at); +} - is_singlecard = ibstr("is_singlecard"); +extern readloop_struct rlid[]; + +/* + * command loop for reading messages + * + * Set oper to "readnew" or "readold" or "readfwd" or "headers" + */ +void readloop(long oper) +{ + StrBuf *MessageDropdown = NULL; + StrBuf *BBViewToolBar = NULL; + void *vMsg; + message_summary *Msg; + char cmd[256] = ""; + char buf[SIZ]; + int a = 0; + int nummsgs; + long startmsg = 0; + int maxmsgs = 0; + long *displayed_msgs = NULL; + int num_displayed = 0; + int is_summary = 0; + int is_singlecard = 0; + struct calview calv; - if (WCC->wc_default_view == VIEW_CALENDAR) { /**< calendar */ - is_calendar = 1; + int is_bbview = 0; + int lowest_displayed = (-1); + int highest_displayed = 0; + addrbookent *addrbook = NULL; + int num_ab = 0; + int bbs_reverse = 0; + wcsession *WCC = WC; + HashPos *at; + const char *HashKey; + long HKLen; + int care_for_empty_list = 0; + int load_seen = 0; + int sortit = 0; + + switch (WCC->wc_view) { + case VIEW_WIKI: + sprintf(buf, "wiki?room=%s&page=home", WCC->wc_roomname); + http_redirect(buf); + return; + case VIEW_CALENDAR: + load_seen = 1; strcpy(cmd, "MSGS ALL|||1"); maxmsgs = 32767; - } - if (WCC->wc_default_view == VIEW_TASKS) { /**< tasks */ - is_tasks = 1; + parse_calendar_view_request(&calv); + 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; + default: + care_for_empty_list = 1; + startmsg = lbstr("startmsg"); + if (havebstr("maxmsgs")) + maxmsgs = ibstr("maxmsgs"); + is_summary = (ibstr("is_summary") && !WCC->is_mobile); + if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; + + + + /* + * When in summary mode, always show ALL messages instead of just + * new or old. Otherwise, show what the user asked for. + */ + rlid[oper].cmd(cmd, sizeof(cmd)); + + if ((WCC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1) && !WCC->is_mobile) { + is_summary = 1; + if (oper != do_search) { + strcpy(cmd, "MSGS ALL"); + } + } + + is_bbview = !is_summary; + if (is_summary) { /**< fetch header summary */ + load_seen = 1; + snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", + (oper == do_search) ? "SEARCH" : "ALL", + (oper == do_search) ? bstr("query") : "" + ); + startmsg = 1; + maxmsgs = 9999999; + } + + if (is_bbview) { + if (havebstr("SortOrder")) { + bbs_reverse = lbstr("SortOrder") == 2; + } + else { + StrBuf *Buf = NewStrBufPlain(HKEY("1")); + putbstr("SortOrder", Buf); + Buf = NewStrBufPlain(HKEY("date")); + putbstr("SortBy", Buf); + bbs_reverse = 0; + } + } + sortit = is_summary || WCC->is_mobile; + if (WCC->is_mobile) { + maxmsgs = 20; + snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", + ((oper == do_search) ? "SEARCH" : "ALL"), + ((oper == do_search) ? bstr("query") : "") + ); + } } + output_headers(1, 1, 1, 0, 0, 0); nummsgs = load_msg_ptrs(cmd, (is_summary || WCC->is_mobile)); if (nummsgs == 0) { - - if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) { + if (care_for_empty_list) { wprintf("

"); - if (!strcmp(oper, "readnew")) { + 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"); @@ -2593,231 +721,101 @@ void readloop(char *oper) goto DONE; } - if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){ - for (a = 0; a < nummsgs; ++a) { - /** Are you a new message, or an old message? */ - if (is_summary) { - if (is_msg_in_mset(old_msgs, WCC->msgarr[a])) { - WCC->summ[a].is_new = 0; - } - else { - WCC->summ[a].is_new = 1; - } - } - } + if (sortit) { + CompareFunc SortIt; + SortIt = RetrieveSort(CTX_MAILSUM, NULL, + HKEY("date"), 2); + if (SortIt != NULL) + SortByPayload(WCC->summ, SortIt); } - if (startmsg == 0L) { + if (is_bbview && (startmsg == 0L)) { if (bbs_reverse) { - startmsg = WCC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; + Msg = GetMessagePtrAt((nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0, WCC->summ); + startmsg = (Msg==NULL)? 0 : Msg->msgnum; } else { - startmsg = WCC->msgarr[0]; + Msg = GetMessagePtrAt(0, WCC->summ); + startmsg = (Msg==NULL)? 0 : Msg->msgnum; } } - if (is_summary || WCC->is_mobile) { - qsort(WCC->summ, WCC->num_summ, - sizeof(struct message_summary), 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("
"); - } - - - /** - * Set the "is_bbview" variable if it appears that we are looking at - * a classic bulletin board view. - */ - if ((!is_tasks) && (!is_calendar) && (!is_addressbook) - && (!is_notes) && (!is_singlecard) && (!is_summary)) { - is_bbview = 1; - } + if (load_seen) load_seen_flags(); + if (is_summary) do_template("summary_header", NULL); + - /** + /** * 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 */ + DrawMessageDropdown(MessageDropdown, maxmsgs, startmsg); + + DoTemplate(HKEY("msg_listselector_top"), BBViewToolBar, MessageDropdown, CTX_STRBUF); + StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0); + FlushStrBuf(BBViewToolBar); } - for (a = 0; a < nummsgs; ++a) { - if ((WCC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) { - - /** Display the message */ - if (is_summary) { - display_summarized(a); - } - else if (is_addressbook) { - fetch_ab_name(WCC->msgarr[a], buf); + + at = GetNewHashPos(WCC->summ, 0); + while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) { + Msg = (message_summary*) vMsg; + if ((Msg->msgnum >= startmsg) && (num_displayed < maxmsgs)) { + switch (WCC->wc_view) { + case VIEW_WIKI: + break; + 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: + fetch_ab_name(Msg, buf); ++num_ab; addrbook = realloc(addrbook, - (sizeof(struct addrbookent) * num_ab) ); + (sizeof(addrbookent) * num_ab) ); safestrncpy(addrbook[num_ab-1].ab_name, buf, - sizeof(addrbook[num_ab-1].ab_name)); - addrbook[num_ab-1].ab_msgnum = WCC->msgarr[a]; - } - else if (is_calendar) { - display_calendar(WCC->msgarr[a], WCC->summ[a].is_new); - } - else if (is_tasks) { - display_task(WCC->msgarr[a], WCC->summ[a].is_new); - } - else if (is_notes) { - display_note(WCC->msgarr[a], WCC->summ[a].is_new); - } else if (WCC->is_mobile) { - display_mobile_summary(a); - } - else { - if (displayed_msgs == NULL) { - displayed_msgs = malloc(sizeof(long) * - (maxmsgsmsgnum; + break; + default: + /** Display the message */ + if (is_summary) { + DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM); + } + else { + if (displayed_msgs == NULL) { + displayed_msgs = malloc(sizeof(long) * + (maxmsgsmsgnum; } - displayed_msgs[num_displayed] = WCC->msgarr[a]; + + if (lowest_displayed < 0) lowest_displayed = a; + highest_displayed = a; + + ++num_displayed; } - - if (lowest_displayed < 0) lowest_displayed = a; - highest_displayed = a; - - ++num_displayed; } } + DeleteHashPos(&at); /** Output loop */ if (displayed_msgs != NULL) { if (bbs_reverse) { - qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r); + ////TODOqsort(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 */ @@ -2827,20 +825,7 @@ void readloop(char *oper) } 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("
"); + do_template("summary_trailer", NULL); } /** @@ -2855,108 +840,41 @@ void readloop(char *oper) * messages, then display the selector bar */ 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) { + switch (WCC->wc_view) { + case VIEW_WIKI: + break; + case VIEW_CALENDAR: + render_calendar_view(&calv); + break; + case VIEW_TASKS: do_tasks_view(); /** Render the task list */ - } - - if (is_calendar) { - do_calendar_view(); /** Render the calendar */ - } - - if (is_addressbook) { + break; + case VIEW_NOTES: + break; + case VIEW_ADDRESSBOOK: do_addrbook_view(addrbook, num_ab); /** Render the address book */ + break; + default: + break; } - /** Note: wDumpContent() will output one additional tag. */ wprintf("\n"); /** end of 'content' div */ wDumpContent(1); /** free the summary */ if (WCC->summ != NULL) { - free(WCC->summ); - WCC->num_summ = 0; - WCC->summ = NULL; + DeleteHash(&WCC->summ); } if (addrbook != NULL) free(addrbook); + FreeStrBuf(&BBViewToolBar); } @@ -2965,11 +883,12 @@ DONE: * ... this is where the actual message gets transmitted to the server. */ void post_mime_to_server(void) { + wcsession *WCC = WC; char top_boundary[SIZ]; char alt_boundary[SIZ]; int is_multipart = 0; static int seq = 0; - struct wc_attachment *att; + wc_mime_attachment *att; char *encoded; size_t encoded_length; size_t encoded_strlen; @@ -2991,7 +910,7 @@ void post_mime_to_server(void) { serv_puts("X-Mailer: " PACKAGE_STRING); /* If there are attachments, we have to do multipart/mixed */ - if (WC->first_attachment != NULL) { + if (GetCount(WCC->attachments) > 0) { is_multipart = 1; } @@ -3027,18 +946,23 @@ void post_mime_to_server(void) { serv_printf("--%s--", alt_boundary); if (is_multipart) { + long len; + const char *Key; + void *vAtt; + HashPos *it; /* Add in the attachments */ - for (att = WC->first_attachment; att!=NULL; att=att->next) { - + it = GetNewHashPos(WCC->attachments, 0); + while (GetNextHashPos(WCC->attachments, it, &len, &Key, &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", att->content_type); - serv_printf("Content-disposition: attachment; filename=\"%s\"", 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); @@ -3047,6 +971,7 @@ void post_mime_to_server(void) { free(encoded); } serv_printf("--%s--", top_boundary); + DeleteHashPos(&it); } serv_puts("000"); @@ -3066,73 +991,66 @@ void post_mime_to_server(void) { */ void post_message(void) { - urlcontent *u; - void *U; char buf[1024]; - char *encoded_subject = NULL; + StrBuf *encoded_subject = NULL; static long dont_post = (-1L); - struct wc_attachment *att, *aptr; + wc_mime_attachment *att; int is_anonymous = 0; - const char *display_name; - long dpLen = 0; - struct wcsession *WCC = WC; - char *ptr = NULL; - + const StrBuf *display_name = NULL; + wcsession *WCC = WC; + if (havebstr("force_room")) { gotoroom(bstr("force_room")); } - if (GetHash(WC->urlstrings, HKEY("display_name"), &U)) { - u = (urlcontent*) U; - display_name = u->url_data; - dpLen = u->url_data_size; - } - else { - display_name=""; - } - if (!strcmp(display_name, "__ANONYMOUS__")) { - display_name = ""; - is_anonymous = 1; + if (havebstr("display_name")) { + display_name = sbstr("display_name"); + if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) { + display_name = NULL; + is_anonymous = 1; + } } if (WCC->upload_length > 0) { + const char *pch; + int n; + char N[64]; lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length); /** There's an attachment. Save it to this struct... */ - att = malloc(sizeof(struct wc_attachment)); - memset(att, 0, sizeof(struct wc_attachment)); + att = malloc(sizeof(wc_mime_attachment)); + memset(att, 0, sizeof(wc_mime_attachment )); att->length = WCC->upload_length; - strcpy(att->content_type, WCC->upload_content_type); - strcpy(att->filename, WCC->upload_filename); - att->next = NULL; - - /** And add it to the list. */ - if (WCC->first_attachment == NULL) { - WCC->first_attachment = att; - } - else { - aptr = WCC->first_attachment; - while (aptr->next != NULL) aptr = aptr->next; - aptr->next = att; - } + 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, 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. */ - while (num_tokens(att->filename, '/') > 1) { - remove_token(att->filename, 0, '/'); + pch = strrchr(ChrPtr(att->FileName), '/'); + if (pch != NULL) { + StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName)); } - while (num_tokens(att->filename, '\\') > 1) { - remove_token(att->filename, 0, '\\'); + pch = strrchr(ChrPtr(att->FileName), '\\'); + if (pch != NULL) { + StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName)); } /** * 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(); @@ -3151,71 +1069,66 @@ void post_message(void) "saved this message.")); } else { const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s"; - const char *Recp = ""; - const char *Cc = ""; - const char *Bcc = ""; - const char *Wikipage = ""; - const char *my_email_addr = ""; - char *CmdBuf = NULL;; - long len = 0; - size_t nLen; - char references[SIZ] = ""; - size_t references_len = 0; - - safestrncpy(references, bstr("references"), sizeof references); - lprintf(9, "Converting: %s\n", references); - for (ptr=references; *ptr != 0; ++ptr) { - if (*ptr == '|') *ptr = '!'; - ++references_len; - } - lprintf(9, "Converted: %s\n", references); - + const StrBuf *Recp = NULL; + const StrBuf *Cc = NULL; + const StrBuf *Bcc = NULL; + const StrBuf *Wikipage = NULL; + const StrBuf *my_email_addr = 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)); + StrBufReplaceChars(references, '|', '!'); + lprintf(9, "Converted: %s\n", ChrPtr(references)); + } if (havebstr("subject")) { - char *Subj; - size_t SLen; + const StrBuf *Subj; /* * make enough room for the encoded string; * plus the QP header */ - Subj = xbstr("subject", &SLen); - len = SLen * 3 + 32; - encoded_subject = malloc (len); - len = webcit_rfc2047encode(encoded_subject, len, Subj, SLen); - if (len < 0) { - free (encoded_subject); - return; - } + Subj = sbstr("subject"); + + StrBufRFC2047encode(&encoded_subject, Subj); } - len += sizeof (CMD) + dpLen; - Recp = xbstr("recp", &nLen); - len += nLen; - Cc = xbstr("cc", &nLen); - len += nLen; - Bcc = xbstr("bcc", &nLen); - len += nLen; - Wikipage = xbstr("wikipage", &nLen); - len += nLen; - my_email_addr = xbstr("my_email_addr", &nLen); - len += nLen; - len += references_len; - - CmdBuf = (char*) malloc (len + 11); - - snprintf(CmdBuf, len + 1, CMD, - Recp, - is_anonymous, - (encoded_subject ? encoded_subject : ""), - display_name, - Cc, - Bcc, - Wikipage, - my_email_addr, - references); - lprintf(9, "%s\n", CmdBuf); - serv_puts(CmdBuf); + Recp = sbstr("recp"); + Cc = sbstr("cc"); + Bcc = sbstr("bcc"); + Wikipage = sbstr("wikipage"); + my_email_addr = sbstr("my_email_addr"); + + CmdBuf = NewStrBufPlain(NULL, + sizeof (CMD) + + StrLength(Recp) + + StrLength(encoded_subject) + + StrLength(Cc) + + StrLength(Bcc) + + StrLength(Wikipage) + + StrLength(my_email_addr) + + StrLength(references)); + + StrBufPrintf(CmdBuf, + CMD, + ChrPtr(Recp), + is_anonymous, + ChrPtr(encoded_subject), + ChrPtr(display_name), + ChrPtr(Cc), + ChrPtr(Bcc), + ChrPtr(Wikipage), + ChrPtr(my_email_addr), + ChrPtr(references)); + FreeStrBuf(&references); + + lprintf(9, "%s\n", ChrPtr(CmdBuf)); + serv_puts(ChrPtr(CmdBuf)); serv_getln(buf, sizeof buf); - free (CmdBuf); - if (encoded_subject) free(encoded_subject); + FreeStrBuf(&CmdBuf); + FreeStrBuf(&encoded_subject); if (buf[0] == '4') { post_mime_to_server(); if ( (havebstr("recp")) @@ -3236,7 +1149,7 @@ void post_message(void) } } - free_attachments(WCC); + DeleteHash(&WCC->attachments); /** * We may have been supplied with instructions regarding the location @@ -3256,7 +1169,7 @@ void post_message(void) * Otherwise, just go to the "read messages" loop. */ else { - readloop("readnew"); + readloop(readnew); } } @@ -3269,16 +1182,13 @@ void post_message(void) void display_enter(void) { char buf[SIZ]; - StrBuf *ebuf; long now; - char *display_name; - struct wc_attachment *att; + const StrBuf *display_name = NULL; int recipient_required = 0; int subject_required = 0; int recipient_bad = 0; int is_anonymous = 0; - long existing_page = (-1L); - size_t dplen; + wcsession *WCC = WC; now = time(NULL); @@ -3286,10 +1196,9 @@ void display_enter(void) gotoroom(bstr("force_room")); } - display_name = xbstr("display_name", &dplen); - if (!strcmp(display_name, "__ANONYMOUS__")) { - display_name = ""; - dplen = 0; + display_name = sbstr("display_name"); + if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) { + display_name = NULL; is_anonymous = 1; } @@ -3301,8 +1210,8 @@ void display_enter(void) recipient_required = 1; } else if (buf[0] != '2') { /** Any other error means that we cannot continue */ - sprintf(WC->ImportantMessage, "%s", &buf[4]); - readloop("readnew"); + sprintf(WCC->ImportantMessage, "%s", &buf[4]); + readloop(readnew); return; } @@ -3315,8 +1224,8 @@ void display_enter(void) * Are we perhaps in an address book view? If so, then an "enter * message" command really means "add new entry." */ - if (WC->wc_default_view == VIEW_ADDRESSBOOK) { - do_edit_vcard(-1, "", "", WC->wc_roomname); + if (WCC->wc_default_view == VIEW_ADDRESSBOOK) { + do_edit_vcard(-1, "", "", WCC->wc_roomname); return; } @@ -3324,7 +1233,7 @@ void display_enter(void) * Are we perhaps in a calendar room? If so, then an "enter * message" command really means "add new calendar item." */ - if (WC->wc_default_view == VIEW_CALENDAR) { + if (WCC->wc_default_view == VIEW_CALENDAR) { display_edit_event(); return; } @@ -3333,7 +1242,7 @@ void display_enter(void) * Are we perhaps in a tasks view? If so, then an "enter * message" command really means "add new task." */ - if (WC->wc_default_view == VIEW_TASKS) { + if (WCC->wc_default_view == VIEW_TASKS) { display_edit_task(); return; } @@ -3342,43 +1251,39 @@ 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 char *Recp = ""; - const char *Cc = ""; - const char *Bcc = ""; - const char *Wikipage = ""; - char *CmdBuf = NULL;; - size_t len = 0; - size_t nLen; + const StrBuf *Recp = NULL; + const StrBuf *Cc = NULL; + const StrBuf *Bcc = NULL; + const StrBuf *Wikipage = NULL; + StrBuf *CmdBuf = NULL;; const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s"; - len = sizeof(CMD) + dplen; - Recp = xbstr("recp", &nLen); - len += nLen; - Cc = xbstr("cc", &nLen); - len += nLen; - Bcc = xbstr("bcc", &nLen); - len += nLen; - Wikipage = xbstr("wikipage", &nLen); - len += nLen; + Recp = sbstr("recp"); + Cc = sbstr("cc"); + Bcc = sbstr("bcc"); + Wikipage = sbstr("wikipage"); - - CmdBuf = (char*) malloc (len + 1); - - snprintf(CmdBuf, len, CMD, - Recp, is_anonymous, - display_name, - Cc, Bcc, Wikipage); - serv_puts(CmdBuf); + CmdBuf = NewStrBufPlain(NULL, + sizeof (CMD) + + StrLength(Recp) + + StrLength(display_name) + + StrLength(Cc) + + StrLength(Bcc) + + StrLength(Wikipage)); + + StrBufPrintf(CmdBuf, + CMD, + ChrPtr(Recp), + is_anonymous, + ChrPtr(display_name), + ChrPtr(Cc), + ChrPtr(Bcc), + ChrPtr(Wikipage)); + serv_puts(ChrPtr(CmdBuf)); serv_getln(buf, sizeof buf); + FreeStrBuf(&CmdBuf); if (!strncmp(buf, "570", 3)) { /** 570 means we have an invalid recipient listed */ if (havebstr("recp") && @@ -3388,315 +1293,21 @@ void display_enter(void) } } else if (buf[0] != '2') { /** Any other error means that we cannot continue */ - wprintf("%s
\n", &buf[4]); - goto DONE; - } - } - - /** If we got this far, we can display the message entry screen. */ - - /* begin message entry screen */ - wprintf("
\n"); - wprintf("\n", now); - if (WC->wc_view == VIEW_WIKI) { - wprintf("\n", bstr("wikipage")); - } - wprintf("\n", bstr("return_to")); - wprintf("\n", WC->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(WC->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"); - - /** 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 */ + begin_burst(); + output_headers(1, 0, 0, 0, 1, 0); + DoTemplate(HKEY("edit_message"), NULL, NULL, CTX_NONE); + end_burst(); - /* NOTE: address_book_popup() will close the "content" div. Don't close it here. */ -DONE: address_book_popup(); - wDumpContent(1); + return; } - /** * \brief delete a message */ @@ -3717,7 +1328,7 @@ void delete_msg(void) serv_getln(buf, sizeof buf); sprintf(WC->ImportantMessage, "%s", &buf[4]); - readloop("readnew"); + readloop(readnew); } @@ -3740,7 +1351,7 @@ void move_msg(void) sprintf(WC->ImportantMessage, (_("The message was not moved."))); } - readloop("readnew"); + readloop(readnew); } @@ -3799,3 +1410,193 @@ void confirm_move_msg(void) wprintf("\n"); wDumpContent(1); } + + +/* + * 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); + } + output_headers(0, 0, 0, 0, 0, 0); + 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(const char *msgnum, const char *partnum, int force_download) +{ + char buf[256]; + off_t bytes; + char content_type[256]; + + serv_printf("OPNA %s|%s", msgnum, partnum); + serv_getln(buf, sizeof buf); + if (buf[0] == '2') { + bytes = extract_long(&buf[4], 0); + if (force_download) { + strcpy(content_type, "application/octet-stream"); + } + else { + extract_token(content_type, &buf[4], 3, '|', sizeof content_type); + } + output_headers(0, 0, 0, 0, 0, 0); + + read_server_binary(WC->WBuf, bytes); + serv_puts("CLOS"); + serv_getln(buf, sizeof buf); + http_transmit_thing(content_type, 0); + } else { + hprintf("HTTP/1.1 404 %s\n", &buf[4]); + 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"), &buf[4]); + end_burst(); + } +} + + +/* + * Read any MIME part of a message, from the server, into memory. + */ +char *load_mimepart(long msgnum, char *partnum) +{ + char buf[SIZ]; + off_t bytes; + char content_type[SIZ]; + char *content; + + serv_printf("DLAT %ld|%s", msgnum, partnum); + serv_getln(buf, sizeof buf); + if (buf[0] == '6') { + bytes = extract_long(&buf[4], 0); + extract_token(content_type, &buf[4], 3, '|', sizeof content_type); + + content = malloc(bytes + 2); + serv_read(content, bytes); + + content[bytes] = 0; /* null terminate for good measure */ + return(content); + } + else { + return(NULL); + } +} + +/* + * Read any MIME part of a message, from the server, into memory. + */ +void MimeLoadData(wc_mime_attachment *Mime) +{ + char buf[SIZ]; + 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)); + serv_getln(buf, sizeof buf); + if (buf[0] == '6') { + bytes = extract_long(&buf[4], 0); + + if (Mime->Data == NULL) + Mime->Data = NewStrBufPlain(NULL, bytes); + StrBuf_ServGetBLOB(Mime->Data, bytes); + + } + else { + FlushStrBuf(Mime->Data); + /// TODO XImportant message + } +} + + + + +void view_mimepart(void) { + mimepart(ChrPtr(WC->UrlFragment2), + ChrPtr(WC->UrlFragment3), + 0); +} + +void download_mimepart(void) { + mimepart(ChrPtr(WC->UrlFragment2), + ChrPtr(WC->UrlFragment3), + 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 +InitModule_MSG +(void) +{ + 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); + WebcitAddUrlHandler(HKEY("delete_msg"), delete_msg, 0); + WebcitAddUrlHandler(HKEY("confirm_move_msg"), confirm_move_msg, 0); + WebcitAddUrlHandler(HKEY("msg"), embed_message, NEED_URL|AJAX); + WebcitAddUrlHandler(HKEY("printmsg"), print_message, NEED_URL); + 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); + + return ; +}