X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fmessages.c;h=ea2171d1512b73b794598099635fc5741467a2b4;hb=d120028b4c45f3c26d05186c8ba488fd2feef13f;hp=6e4890d54971f61ce3f27efc03571b40d76afbe8;hpb=0da9ad1ea67516356d0fdc1f5ae3e06cee96f373;p=citadel.git diff --git a/webcit/messages.c b/webcit/messages.c index 6e4890d54..ea2171d15 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -5,28 +5,14 @@ * */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include #include "webcit.h" #include "vcard.h" #include "webserver.h" +#include "groupdav.h" +#define SUBJ_COL_WIDTH_PCT 50 +#define SENDER_COL_WIDTH_PCT 30 +#define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /* 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) @@ -37,6 +23,102 @@ struct addrbookent { }; + +#ifdef HAVE_ICONV +/* Handle subjects with RFC2047 encoding, such as: + * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= + */ +void utf8ify_rfc822_string(char *buf) { + char *start, *end; + 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; + + while (start=strstr(buf, "=?"), end=strstr(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); + + /*strcpy(start, ""); + ++end; + ++end;*/ + + ibuf = malloc(1024); + isav = ibuf; + if (!strcasecmp(encoding, "B")) { /* base64 */ + ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr)); + } + else if (!strcasecmp(encoding, "Q")) { /* quoted-printable */ + ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, strlen(istr)); + } + else { + strcpy(ibuf, istr); /* huh? */ + ibuflen = strlen(istr); + } + + ic = iconv_open("UTF-8", charset); + if (ic != (iconv_t)(-1) ) { + obuf = malloc(1024); + 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 { + 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); + } + + 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; + } + +} +#endif + + /* * Look for URL's embedded in a buffer and make them linkable. We use a * target window in order to keep the BBS session in its own window. @@ -83,7 +165,7 @@ char buf[]; urlbuf[end - start] = 0; strncpy(outbuf, buf, start); - sprintf(&outbuf[start], "%cA HREF=%c%s%c TARGET=%c%s%c%c%s%c/A%c", + sprintf(&outbuf[start], "%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); strcat(outbuf, &buf[end]); if ( strlen(outbuf) < 250 ) @@ -168,13 +250,13 @@ void display_parsed_vcard(struct vCard *v, int full) { char firsttoken[SIZ]; int pass; - char displayname[SIZ]; + char fullname[SIZ]; char title[SIZ]; char org[SIZ]; char phone[SIZ]; char mailto[SIZ]; - strcpy(displayname, ""); + strcpy(fullname, ""); strcpy(phone, ""); strcpy(mailto, ""); strcpy(title, ""); @@ -187,9 +269,9 @@ void display_parsed_vcard(struct vCard *v, int full) { escputs(name); } else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) { - strcpy(displayname, name); - vcard_n_prettyize(displayname); - escputs(displayname); + strcpy(fullname, name); + vcard_n_prettyize(fullname); + escputs(fullname); } else { wprintf(" "); @@ -239,15 +321,15 @@ void display_parsed_vcard(struct vCard *v, int full) { /* N is name, but only if there's no FN already there */ if (!strcasecmp(firsttoken, "n")) { - if (strlen(displayname) == 0) { - strcpy(displayname, thisvalue); - vcard_n_prettyize(displayname); + if (strlen(fullname) == 0) { + strcpy(fullname, thisvalue); + vcard_n_prettyize(fullname); } } /* FN (full name) is a true 'display name' field */ else if (!strcasecmp(firsttoken, "fn")) { - strcpy(displayname, thisvalue); + strcpy(fullname, thisvalue); } /* title */ @@ -263,11 +345,16 @@ void display_parsed_vcard(struct vCard *v, int full) { else if (!strcasecmp(firsttoken, "email")) { if (strlen(mailto) > 0) strcat(mailto, "
"); strcat(mailto, - ""); + strcat(mailto, "\">"); - urlesc(&mailto[strlen(mailto)], thisvalue); + stresc(&mailto[strlen(mailto)], thisvalue, 1, 1); strcat(mailto, ""); } else if (!strcasecmp(firsttoken, "tel")) { @@ -278,11 +365,11 @@ void display_parsed_vcard(struct vCard *v, int full) { if (!strcasecmp(buf, "tel")) strcat(phone, ""); else if (!strcasecmp(buf, "work")) - strcat(phone, " (work)"); + strcat(phone, _(" (work)")); else if (!strcasecmp(buf, "home")) - strcat(phone, " (home)"); + strcat(phone, _(" (home)")); else if (!strcasecmp(buf, "cell")) - strcat(phone, " (cell)"); + strcat(phone, _(" (cell)")); else { strcat(phone, " ("); strcat(phone, buf); @@ -292,7 +379,9 @@ void display_parsed_vcard(struct vCard *v, int full) { } else if (!strcasecmp(firsttoken, "adr")) { if (pass == 2) { - wprintf("Address:"); + wprintf(""); + wprintf(_("Address:")); + wprintf(""); for (j=0; j 0) { @@ -333,9 +422,9 @@ void display_parsed_vcard(struct vCard *v, int full) { if (pass == 1) { wprintf("" "" - "" + "" ""); - escputs(displayname); + escputs(fullname); wprintf(""); if (strlen(title) > 0) { wprintf("
"); @@ -349,10 +438,16 @@ void display_parsed_vcard(struct vCard *v, int full) { } wprintf("\n"); - if (strlen(phone) > 0) - wprintf("Telephone:%s\n", phone); - if (strlen(mailto) > 0) - wprintf("E-mail:%s\n", mailto); + if (strlen(phone) > 0) { + wprintf(""); + wprintf(_("Telephone:")); + wprintf("%s\n", phone); + } + if (strlen(mailto) > 0) { + wprintf(""); + wprintf(_("E-mail:")); + wprintf("%s\n", mailto); + } } } @@ -398,50 +493,70 @@ void display_vcard(char *vcard_source, char alpha, int full, char *storename) { } - - /* - * I wanna SEE that message! + * I wanna SEE that message! (Optional 'section' for encapsulated message/rfc822 submessage) */ -void read_message(long msgnum) { +void read_message(long msgnum, int printable_view, char *section) { char buf[SIZ]; - char mime_partnum[SIZ]; - char mime_filename[SIZ]; - char mime_content_type[SIZ]; - char mime_disposition[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 mime_http[SIZ]; - char m_subject[SIZ]; - char from[SIZ]; - char node[SIZ]; - char rfca[SIZ]; + char mime_submessages[256]; + char m_subject[256]; + char m_cc[1024]; + char from[256]; + char node[256]; + char rfca[256]; char reply_to[512]; + char reply_all[4096]; + char now[64]; int format_type = 0; int nhdr = 0; int bq = 0; - char vcard_partnum[SIZ]; - char cal_partnum[SIZ]; + int i = 0; + char vcard_partnum[256]; + char cal_partnum[256]; char *part_source = NULL; +#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(reply_all, ""); strcpy(vcard_partnum, ""); strcpy(cal_partnum, ""); strcpy(mime_http, ""); + strcpy(mime_content_type, "text/plain"); + strcpy(mime_charset, "us-ascii"); + strcpy(mime_submessages, ""); - serv_printf("MSG4 %ld", msgnum); + serv_printf("MSG4 %ld|%s", msgnum, section); serv_getln(buf, sizeof buf); if (buf[0] != '1') { - wprintf("ERROR: %s
\n", &buf[4]); + wprintf(""); + wprintf(_("ERROR:")); + wprintf(" %s
\n", &buf[4]); return; } /* begin everythingamundo table */ - wprintf("
\n"); - wprintf("
\n"); + if (!printable_view) { + wprintf("
\n"); + wprintf("
\n"); + } /* 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("unexpected end of message

\n"); + wprintf(""); + wprintf(_("unexpected end of message")); + wprintf("

\n"); wprintf("\n"); return; } @@ -464,21 +582,37 @@ void read_message(long msgnum) { format_type = atoi(&buf[5]); if (!strncasecmp(buf, "from=", 5)) { strcpy(from, &buf[5]); - wprintf("from "); escputs(from); wprintf(" "); } - if (!strncasecmp(buf, "subj=", 5)) - strcpy(m_subject, &buf[5]); + if (!strncasecmp(buf, "subj=", 5)) { + safestrncpy(m_subject, &buf[5], sizeof m_subject); + } + if (!strncasecmp(buf, "cccc=", 5)) { + safestrncpy(m_cc, &buf[5], sizeof m_cc); + if (strlen(reply_all) > 0) { + strcat(reply_all, ", "); + } + safestrncpy(&reply_all[strlen(reply_all)], &buf[5], + (sizeof reply_all - strlen(reply_all)) ); + } if ((!strncasecmp(buf, "hnod=", 5)) - && (strcasecmp(&buf[5], serv_info.serv_humannode))) + && (strcasecmp(&buf[5], serv_info.serv_humannode))) { wprintf("(%s) ", &buf[5]); + } if ((!strncasecmp(buf, "room=", 5)) && (strcasecmp(&buf[5], WC->wc_roomname)) - && (strlen(&buf[5])>0) ) - wprintf("in %s> ", &buf[5]); + && (strlen(&buf[5])>0) ) { + wprintf(_("in ")); + wprintf("%s> ", &buf[5]); + } if (!strncasecmp(buf, "rfca=", 5)) { strcpy(rfca, &buf[5]); wprintf("<"); @@ -496,12 +630,19 @@ void read_message(long msgnum) { wprintf("@%s ", &buf[5]); } } - if (!strncasecmp(buf, "rcpt=", 5)) - wprintf("to %s ", &buf[5]); + if (!strncasecmp(buf, "rcpt=", 5)) { + wprintf(_("to ")); + escputs(&buf[5]); + wprintf(" "); + if (strlen(reply_all) > 0) { + strcat(reply_all, ", "); + } + safestrncpy(&reply_all[strlen(reply_all)], &buf[5], + (sizeof reply_all - strlen(reply_all)) ); + } if (!strncasecmp(buf, "time=", 5)) { - wprintf(" ", &buf[5]); + fmt_date(now, atol(&buf[5]), 0); + wprintf("%s ", now); } if (!strncasecmp(buf, "part=", 5)) { @@ -511,28 +652,33 @@ void read_message(long msgnum) { extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type); mime_length = extract_int(&buf[5], 5); - if (!strcasecmp(mime_disposition, "attachment")) { + if (!strcasecmp(mime_content_type, "message/rfc822")) { + if (strlen(mime_submessages) > 0) { + strcat(mime_submessages, "|"); + } + strcat(mime_submessages, mime_partnum); + } + else if ((!strcasecmp(mime_disposition, "inline")) + && (!strncasecmp(mime_content_type, "image/", 6)) ){ snprintf(&mime_http[strlen(mime_http)], (sizeof(mime_http) - strlen(mime_http) - 1), - "" - "\n" - "Part %s: %s (%s, %d bytes)
\n", - msgnum, mime_partnum, - msgnum, mime_partnum, - mime_partnum, mime_filename, - mime_content_type, mime_length); + "", + msgnum, mime_partnum, mime_filename); } - - if ((!strcasecmp(mime_disposition, "inline")) - && (!strncasecmp(mime_content_type, "image/", 6)) ){ + else if ( (!strcasecmp(mime_disposition, "attachment")) + || (!strcasecmp(mime_disposition, "inline")) ) { snprintf(&mime_http[strlen(mime_http)], (sizeof(mime_http) - strlen(mime_http) - 1), - "", - msgnum, mime_partnum); + "" + "\n" + "%s (%s, %d bytes)
\n", + msgnum, mime_partnum, mime_filename, + msgnum, mime_partnum, + mime_filename, + mime_content_type, mime_length + ); } /*** begin handler prep ***/ @@ -571,40 +717,114 @@ void read_message(long msgnum) { } wprintf(""); +#ifdef HAVE_ICONV + utf8ify_rfc822_string(m_cc); + utf8ify_rfc822_string(m_subject); +#endif + if (strlen(m_cc) > 0) { + wprintf("
" + ""); + wprintf(_("CC:")); + wprintf(" "); + escputs(m_cc); + wprintf(""); + } if (strlen(m_subject) > 0) { wprintf("
" - "" - "Subject: %s" - "", m_subject - ); + ""); + wprintf(_("Subject:")); + wprintf(" "); + escputs(m_subject); + wprintf(""); } wprintf("\n"); /* start msg buttons */ - wprintf(""); } - wprintf("
\n"); + if (!printable_view) { + wprintf("\n"); + + /* 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 (strlen(m_subject) > 0) { + wprintf("?subject="); + if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20"); + urlescputs(m_subject); + } + wprintf("\">[%s] ", _("Reply")); + } - /* Reply */ - wprintf("[Reply] "); + /* ReplyQuoted */ + if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { + if (!WC->is_mailbox) { + wprintf("[%s] ", _("ReplyQuoted")); + } + } - if (WC->is_room_aide) { + /* 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) ) { + /* Move */ + wprintf("[%s] ", + msgnum, _("Move")); - /* Move */ - wprintf("[Move] ", - msgnum); + /* Delete */ + wprintf("" + "[%s] ", msgnum, _("Delete this message?"), _("Delete") + ); + } + + /* Headers */ + wprintf("" + "[%s]", msgnum, msgnum, _("Headers")); + - /* Delete */ - wprintf("" - "[Delete]", msgnum); - + /* Print */ + wprintf("" + "[%s]", msgnum, msgnum, _("Print")); + + wprintf("
\n"); + wprintf("
\n"); /* Begin body */ wprintf(" 0)) { if (!strcmp(buf, "000")) { - wprintf("unexpected end of message

\n"); + wprintf(""); + wprintf(_("unexpected end of message")); + wprintf("

\n"); goto ENDBODY; } if (!strncasecmp(buf, "Content-type: ", 14)) { safestrncpy(mime_content_type, &buf[14], sizeof(mime_content_type)); + for (i=0; i 0) && (isspace(buf[strlen(buf) - 1]))) buf[strlen(buf) - 1] = 0; if ((bq == 0) && @@ -656,19 +920,30 @@ void read_message(long msgnum) { else /* HTML is fun, but we've got to strip it first */ if (!strcasecmp(mime_content_type, "text/html")) { - output_html(); + output_html(mime_charset); } /* Unknown weirdness */ else { - wprintf("I don't know how to display %s
\n", - mime_content_type); + wprintf(_("I don't know how to display %s"), mime_content_type); + wprintf("
\n", mime_content_type); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { } } + /* If there are attached submessages, display them now... */ + if ( (strlen(mime_submessages) > 0) && (!section[0]) ) { + for (i=0; i"); + read_message(msgnum, 1, buf); + wprintf(""); + } + } + /* Afterwards, offer links to download attachments 'n' such */ - if (strlen(mime_http) > 0) { + if ( (strlen(mime_http) > 0) && (!section[0]) ) { wprintf("%s", mime_http); } @@ -682,10 +957,10 @@ void read_message(long msgnum) { || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) || (WC->wc_view == VIEW_ADDRESSBOOK) ) { - wprintf("", + wprintf("", msgnum, vcard_partnum); - wprintf("[edit]"); + wprintf("[%s]", _("edit")); } /* In all cases, display the full card */ @@ -711,98 +986,423 @@ ENDBODY: wprintf("
\n"); /* end everythingamundo table */ - wprintf("
\n"); - wprintf("

\n"); + if (!printable_view) { + wprintf("\n"); + wprintf("

\n"); + } + +#ifdef HAVE_ICONV + if (ic != (iconv_t)(-1) ) { + iconv_close(ic); + } +#endif } -void summarize_message(long msgnum, int is_new) { - char buf[SIZ]; - struct { - char date[SIZ]; - char from[SIZ]; - char to[SIZ]; - char subj[SIZ]; - int hasattachments; - } summ; +/* + * Unadorned HTML output of an individual message, suitable + * for placing in a hidden iframe, for printing, or whatever + */ +void embed_message(char *msgnum_as_string) { + long msgnum = 0L; - memset(&summ, 0, sizeof(summ)); - strcpy(summ.subj, "(no subject)"); + msgnum = atol(msgnum_as_string); + begin_ajax_response(); + read_message(msgnum, 0, ""); + end_ajax_response(); +} - /* ask for headers only with no MIME */ - sprintf(buf, "MSG0 %ld|3", msgnum); - serv_puts(buf); + +/* + * Printable view of a message + */ +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", + SERVER); + begin_burst(); + + wprintf("\r\n\r\n\n" + "Printable view\n" + "\n" + ); + + read_message(msgnum, 1, ""); + + wprintf("\n\n\n"); + wDumpContent(0); +} + + + +/* + * Display a message's headers + */ +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", + SERVER); + begin_burst(); + + serv_printf("MSG2 %ld|3", msgnum); serv_getln(buf, sizeof buf); - if (buf[0] != '1') return; + if (buf[0] == '1') { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + wprintf("%s\n", buf); + } + } - while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { + wDumpContent(0); +} + + + +/* + * 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. + */ +void pullquote_message(long msgnum, int forward_attachments) { + 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 mime_http[SIZ]; + char *attachments = NULL; + int num_attachments = 0; + struct wc_attachment *att, *aptr; + char m_subject[256]; + char from[256]; + char node[256]; + char rfca[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_http, ""); + 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(_("unexpected end of message")); + 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(summ.from, &buf[5]); + strcpy(from, &buf[5]); + wprintf(_("from ")); +#ifdef HAVE_ICONV + utf8ify_rfc822_string(from); +#endif + msgescputs(from); } if (!strncasecmp(buf, "subj=", 5)) { - if (strlen(&buf[5]) > 0) { - strcpy(summ.subj, &buf[5]); - if (strlen(summ.subj) > 75) { - strcpy(&summ.subj[72], "..."); - } - } + 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)) + && (strlen(&buf[5])>0) ) { + wprintf(_("in ")); + wprintf("%s> ", &buf[5]); + } + if (!strncasecmp(buf, "rfca=", 5)) { + strcpy(rfca, &buf[5]); + wprintf("<"); + msgescputs(rfca); + wprintf("> "); } - /* if (!strncasecmp(buf, "rfca=", 5)) { - strcat(summ.from, " <"); - strcat(summ.from, &buf[5]); - strcat(summ.from, ">"); - } */ 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))))) + && (strlen(rfca)==0) ) { - strcat(summ.from, " @ "); - strcat(summ.from, &buf[5]); + wprintf("@%s ", &buf[5]); } } - if (!strncasecmp(buf, "rcpt=", 5)) { - strcpy(summ.to, &buf[5]); + wprintf(_("to ")); + wprintf("%s ", &buf[5]); } - if (!strncasecmp(buf, "time=", 5)) { - fmt_date(summ.date, atol(&buf[5]), 1); /* brief */ + 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)) { + ++num_attachments; + attachments = realloc(attachments, (num_attachments * 1024)); + strcat(attachments, &buf[5]); + strcat(attachments, "\n"); + } + } - - if (strlen(summ.from) > 25) { - strcpy(&summ.from[22], "..."); - } - - wprintf(""); - if (is_new) wprintf(""); - wprintf("", - msgnum); - escputs(summ.subj); - wprintf(""); - if (is_new) wprintf(""); - wprintf(""); - if (is_new) wprintf(""); - escputs(summ.from); - if (is_new) wprintf(""); - wprintf(" "); - if (is_new) wprintf(""); - escputs(summ.date); - if (is_new) wprintf(""); - wprintf(" "); - wprintf("" - "" - "\n"); - - return; + + wprintf("
"); + +#ifdef HAVE_ICONV + utf8ify_rfc822_string(m_subject); +#endif + if (strlen(m_subject) > 0) { + 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), (strlen(buf) > 0)) { + if (!strcmp(buf, "000")) { + wprintf(_("unexpected end of message")); + goto ENDBODY; + } + if (!strncasecmp(buf, "Content-type: ", 14)) { + safestrncpy(mime_content_type, &buf[14], + sizeof(mime_content_type)); + for (i=0; i 0) && (isspace(buf[strlen(buf) - 1]))) + buf[strlen(buf) - 1] = 0; + if ((bq == 0) && + ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) { + wprintf("
"); + bq = 1; + } else if ((bq == 1) && + (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) { + wprintf("
"); + bq = 0; + } + wprintf(""); + url(buf); + msgescputs(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")) { + msgescputs(buf); + } + } + + /* 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; + } + } + + } + free(attachments); + } + +#ifdef HAVE_ICONV + if (ic != (iconv_t)(-1) ) { + iconv_close(ic); + } +#endif +} + + +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); + wprintf(""); + + wprintf("", SENDER_COL_WIDTH_PCT); + escputs(WC->summ[num].from); + wprintf(""); + + wprintf("", DATE_PLUS_BUTTONS_WIDTH_PCT); + fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ + escputs(datebuf); + wprintf(""); + + wprintf("\n"); } + + void display_addressbook(long msgnum, char alpha) { char buf[SIZ]; char mime_partnum[SIZ]; @@ -812,17 +1412,10 @@ void display_addressbook(long msgnum, char alpha) { int mime_length; char vcard_partnum[SIZ]; char *vcard_source = NULL; - - struct { - char date[SIZ]; - char from[SIZ]; - char to[SIZ]; - char subj[SIZ]; - int hasattachments; - } summ; + struct message_summary summ; memset(&summ, 0, sizeof(summ)); - strcpy(summ.subj, "(no subject)"); + safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj); sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ serv_puts(buf); @@ -856,10 +1449,10 @@ void display_addressbook(long msgnum, char alpha) { || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) || (WC->wc_view == VIEW_ADDRESSBOOK) ) { - wprintf("", + wprintf("", msgnum, vcard_partnum); - wprintf("[edit]"); + wprintf("[%s]", _("edit")); } free(vcard_source); @@ -901,20 +1494,13 @@ void fetch_ab_name(long msgnum, char *namebuf) { char vcard_partnum[SIZ]; char *vcard_source = NULL; int i; - - struct { - char date[SIZ]; - char from[SIZ]; - char to[SIZ]; - char subj[SIZ]; - int hasattachments; - } summ; + struct message_summary summ; if (namebuf == NULL) return; strcpy(namebuf, ""); memset(&summ, 0, sizeof(summ)); - strcpy(summ.subj, "(no subject)"); + safestrncpy(summ.subj, "(no subject)", sizeof summ.subj); sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */ serv_puts(buf); @@ -952,7 +1538,7 @@ void fetch_ab_name(long msgnum, char *namebuf) { for (i=0; iThis address book is empty.\n"); + wprintf("


"); + wprintf(_("This address book is empty.")); + wprintf("
\n"); return; } @@ -1012,7 +1600,7 @@ void do_addrbook_view(struct addrbookent *addrbook, int num_ab) { wprintf("Page: "); for (i=0; i<=num_pages; ++i) { if (i != page) { - wprintf("", i); + wprintf("", i); } else { wprintf(""); @@ -1054,9 +1642,9 @@ void do_addrbook_view(struct addrbookent *addrbook, int num_ab) { wprintf(""); - wprintf("", bstr("alpha")); + wprintf("?maxmsgs=1?summary=0?alpha=%s\">", bstr("alpha")); vcard_n_prettyize(addrbook[i].ab_name); escputs(addrbook[i].ab_name); wprintf("\n"); @@ -1072,12 +1660,29 @@ void do_addrbook_view(struct addrbookent *addrbook, int num_ab) { /* * load message pointers from the server */ -int load_msg_ptrs(char *servcmd) +int load_msg_ptrs(char *servcmd, int with_headers) { - char buf[SIZ]; + char buf[1024]; + time_t datestamp; + char fullname[128]; + char nodename[128]; + char inetaddr[128]; + char subject[256]; int nummsgs; int maxload = 0; + int num_summ_alloc = 0; + + if (with_headers) { + if (WC->num_summ != 0) { + free(WC->summ); + WC->num_summ = 0; + } + } + 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); @@ -1088,13 +1693,136 @@ int load_msg_ptrs(char *servcmd) } while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { if (nummsgs < maxload) { - WC->msgarr[nummsgs] = atol(buf); + 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 (strlen(fullname) > 0) { + safestrncpy(WC->summ[nummsgs-1].from, fullname, sizeof WC->summ[nummsgs-1].from); + } + if (strlen(subject) > 0) { + safestrncpy(WC->summ[nummsgs-1].subj, subject, + sizeof WC->summ[nummsgs-1].subj); + } +#ifdef HAVE_ICONV + /* Handle subjects with RFC2047 encoding */ + utf8ify_rfc822_string(WC->summ[nummsgs-1].subj); +#endif + if (strlen(WC->summ[nummsgs-1].subj) > 75) { + strcpy(&WC->summ[nummsgs-1].subj[72], "..."); + } + + if (strlen(nodename) > 0) { + if ( ((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); + } + } + + WC->summ[nummsgs-1].date = datestamp; + +#ifdef HAVE_ICONV + /* Handle senders with RFC2047 encoding */ + utf8ify_rfc822_string(WC->summ[nummsgs-1].from); +#endif + if (strlen(WC->summ[nummsgs-1].from) > 25) { + strcpy(&WC->summ[nummsgs-1].from[22], "..."); + } + } } } return (nummsgs); } +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); +} + + +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); +} + +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); +} + +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); +} + +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); +} + +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; +} + +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; +} /* * command loop for reading messages @@ -1104,11 +1832,11 @@ void readloop(char *oper) char cmd[SIZ]; char buf[SIZ]; char old_msgs[SIZ]; - int is_new = 0; 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; @@ -1116,23 +1844,46 @@ void readloop(char *oper) int is_calendar = 0; int is_tasks = 0; int is_notes = 0; - int remaining_messages; + int is_bbview = 0; int lo, hi; int lowest_displayed = (-1); int highest_displayed = 0; - long pn_previous = 0L; - long pn_current = 0L; - long pn_next = 0L; - int bg = 0; struct addrbookent *addrbook = NULL; int num_ab = 0; + char *sortby = NULL; + char sortpref_name[128]; + char sortpref_value[128]; + char *subjsort_button; + char *sendsort_button; + char *datesort_button; + int bbs_reverse = 0; startmsg = atol(bstr("startmsg")); maxmsgs = atoi(bstr("maxmsgs")); is_summary = atoi(bstr("summary")); if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; - output_headers(1, 1, 1, 0, 0, 0, 0); + snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname); + get_preference(sortpref_name, sortpref_value, sizeof sortpref_value); + + sortby = bstr("sortby"); + if ( (strlen(sortby) > 0) && (strcasecmp(sortby, sortpref_value)) ) { + set_preference(sortpref_name, sortby, 1); + } + if (strlen(sortby) == 0) sortby = sortpref_value; + + /* mailbox sort */ + if (strlen(sortby) == 0) sortby = "rdate"; + + /* message board sort */ + if (!strcasecmp(sortby, "reverse")) { + bbs_reverse = 1; + } + 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. @@ -1155,11 +1906,13 @@ void readloop(char *oper) if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) { is_addressbook = 1; strcpy(cmd, "MSGS ALL"); - maxmsgs = 32767; + maxmsgs = 9999999; } if (is_summary) { - strcpy(cmd, "MSGS ALL"); + strcpy(cmd, "MSGS ALL|||1"); /* fetch header summary */ + startmsg = 1; + maxmsgs = 9999999; } /* Are we doing a summary view? If so, we need to know old messages @@ -1193,76 +1946,156 @@ void readloop(char *oper) maxmsgs = 32767; } - nummsgs = load_msg_ptrs(cmd); + nummsgs = load_msg_ptrs(cmd, is_summary); if (nummsgs == 0) { - if ((!is_tasks) && (!is_calendar) && (!is_notes)) { + if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) { + wprintf(""); if (!strcmp(oper, "readnew")) { - wprintf("No new messages.\n"); + wprintf(_("No new messages.")); } else if (!strcmp(oper, "readold")) { - wprintf("No old messages.\n"); + wprintf(_("No old messages.")); } else { - wprintf("No messages here.\n"); + wprintf(_("No messages here.")); } + wprintf("\n"); } goto DONE; } - if (startmsg == 0L) startmsg = WC->msgarr[0]; - remaining_messages = 0; - - for (a = 0; a < nummsgs; ++a) { - if (WC->msgarr[a] >= startmsg) { - ++remaining_messages; - } - } - - wprintf("
\n"); if (is_summary) { - wprintf("
" - "\n" - "" - "" - "" - "" - "" - "\n" - ); - } - - for (a = 0; a < nummsgs; ++a) { - if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) { - + for (a = 0; a < nummsgs; ++a) { /* Are you a new message, or an old message? */ - is_new = 0; if (is_summary) { if (is_msg_in_mset(old_msgs, WC->msgarr[a])) { - is_new = 0; + WC->summ[a].is_new = 0; } else { - is_new = 1; + WC->summ[a].is_new = 1; } } + } + } - /* Learn which msgs "Prev" & "Next" buttons go to */ - pn_current = WC->msgarr[a]; - if (a > 0) pn_previous = WC->msgarr[a-1]; - if (a < (nummsgs-1)) pn_next = WC->msgarr[a+1]; + if (startmsg == 0L) { + if (bbs_reverse) { + startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; + } + else { + startmsg = WC->msgarr[0]; + } + } - /* If a tabular view, set up the line */ - if (is_summary) { - bg = 1 - bg; - wprintf("", - (bg ? "DDDDDD" : "FFFFFF") - ); - } + if (is_summary) { + if (!strcasecmp(sortby, "subject")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_subj); + } + else if (!strcasecmp(sortby, "rsubject")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_rsubj); + } + else if (!strcasecmp(sortby, "sender")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_sender); + } + else if (!strcasecmp(sortby, "rsender")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_rsender); + } + else if (!strcasecmp(sortby, "date")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_date); + } + else if (!strcasecmp(sortby, "rdate")) { + qsort(WC->summ, WC->num_summ, + sizeof(struct message_summary), summcmp_rdate); + } + } + + if (!strcasecmp(sortby, "subject")) { + subjsort_button = "" ; + } + else if (!strcasecmp(sortby, "rsubject")) { + subjsort_button = "" ; + } + else { + subjsort_button = "" ; + } + + if (!strcasecmp(sortby, "sender")) { + sendsort_button = "" ; + } + else if (!strcasecmp(sortby, "rsender")) { + sendsort_button = "" ; + } + else { + sendsort_button = "" ; + } + + if (!strcasecmp(sortby, "date")) { + datesort_button = "" ; + } + else if (!strcasecmp(sortby, "rdate")) { + datesort_button = "" ; + } + else { + datesort_button = "" ; + } + + if (is_summary) { + wprintf("\n"); /* end of 'content' div */ + + wprintf("\n" + ); + + /* note that Date and Delete are now in the same column */ + wprintf("
" + "
" + "
SubjectSenderDate
" + "" + ); + wprintf("" + "" + "" + "\n" + , + SUBJ_COL_WIDTH_PCT, + _("Subject"), subjsort_button, + SENDER_COL_WIDTH_PCT, + _("Sender"), sendsort_button, + DATE_PLUS_BUTTONS_WIDTH_PCT, + _("Date"), datesort_button, + _("Delete") + ); + wprintf("
%s %s%s %s%s %s" + " " + "" + "
\n"); + + wprintf("
" + + "
\n" + + "" + ); + } + + for (a = 0; a < nummsgs; ++a) { + if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) { /* Display the message */ if (is_summary) { - summarize_message(WC->msgarr[a], is_new); + display_summarized(a); } else if (is_addressbook) { fetch_ab_name(WC->msgarr[a], buf); @@ -1283,128 +2116,151 @@ void readloop(char *oper) display_note(WC->msgarr[a]); } else { - read_message(WC->msgarr[a]); - } - - /* If a tabular view, finish the line */ - if (is_summary) { - wprintf("\n"); + if (displayed_msgs == NULL) { + displayed_msgs = malloc(sizeof(long) * + (maxmsgsmsgarr[a]; } if (lowest_displayed < 0) lowest_displayed = a; highest_displayed = a; ++num_displayed; - --remaining_messages; } } - if (is_summary) { - wprintf("
\n"); - } - - /* Bump these because although we're thinking in zero base, the user - * is a drooling idiot and is thinking in one base. + /* Set the "is_bbview" variable if it appears that we are looking at + * a classic bulletin board view. */ - ++lowest_displayed; - ++highest_displayed; + if ((!is_tasks) && (!is_calendar) && (!is_addressbook) + && (!is_notes) && (!is_singlecard) && (!is_summary)) { + is_bbview = 1; + } - /* If we're only looking at one message, do a prev/next thing */ - if (num_displayed == 1) { - if ((!is_tasks) && (!is_calendar) && (!is_addressbook) && (!is_notes) && (!is_singlecard)) { + /* Output loop */ + if (displayed_msgs != NULL) { + if (bbs_reverse) { + qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r); + } - wprintf("
" - "\n" - "
" - "Reading #%d of %d messages.", - lowest_displayed, nummsgs); + /* if we do a split bbview in the future, begin messages div here */ - if (is_summary) { - wprintf("\n"); + for (a=0; a 0L) { - wprintf("" - "Previous \n", - oper, - pn_previous ); - } + /* if we do a split bbview in the future, end messages div here */ - if (pn_next > 0L) { - wprintf("" - "Next \n", - oper, - pn_next ); - } + free(displayed_msgs); + displayed_msgs = NULL; + } - wprintf("" - "Summary" - "", - oper, - WC->msgarr[0], - DEFAULT_MAXMSGS + 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("\n"); - } + wprintf("
"); /* The preview pane will initially be empty */ } + /* Bump these because although we're thinking in zero base, the user + * is a drooling idiot and is thinking in one base. + */ + ++lowest_displayed; + ++highest_displayed; + /* * If we're not currently looking at ALL requested * messages, then display the selector bar */ - if (num_displayed > 1) { - if ((!is_tasks) && (!is_calendar) && (!is_addressbook) - && (!is_notes) && (!is_singlecard)) { - - wprintf("Reading #", lowest_displayed, highest_displayed); + if (is_bbview) { + /* begin bbview scroller */ + wprintf(""); + wprintf(_("Reading #"), lowest_displayed, highest_displayed); - wprintf(" of %d messages.", nummsgs); + wprintf(" "); + wprintf(_("of %d messages."), nummsgs); - if (is_summary) { - wprintf("\n"); - } + /* forward/reverse */ + wprintf(" \n"); + /* end bbview scroller */ } - wprintf("\n"); DONE: if (is_tasks) { @@ -1419,16 +2275,14 @@ DONE: do_addrbook_view(addrbook, num_ab); /* Render the address book */ } + /* Note: wDumpContent() will output one additional
tag. */ wDumpContent(1); if (addrbook != NULL) free(addrbook); - /* If we got here via a mailbox view and are reading a single - * message, mark it as "seen." We do this after rendering the web page - * so it doesn't keep the user waiting. - */ - if ( (maxmsgs == 1) && (WC->wc_view == VIEW_MAILBOX) ) { - serv_printf("SEEN %ld|1", startmsg); - serv_getln(buf, sizeof buf); + /* free the summary */ + if (WC->num_summ != 0) { + WC->num_summ = 0; + free(WC->summ); } } @@ -1445,6 +2299,9 @@ void post_mime_to_server(void) { char *encoded; size_t encoded_length; + /* RFC2045 requires this, and some clients look for it... */ + serv_puts("MIME-Version: 1.0"); + /* If there are attachments, we have to do multipart/mixed */ if (WC->first_attachment != NULL) { is_multipart = 1; @@ -1464,7 +2321,7 @@ void post_mime_to_server(void) { serv_printf("--%s", boundary); } - serv_puts("Content-type: text/html"); + serv_puts("Content-type: text/html; charset=utf-8"); serv_puts(""); serv_puts("\n"); text_to_server(bstr("msgtext"), 0); @@ -1515,6 +2372,11 @@ void post_message(void) char buf[SIZ]; static long dont_post = (-1L); struct wc_attachment *att, *aptr; + int is_anonymous = 0; + + if (!strcasecmp(bstr("is_anonymous"), "yes")) { + is_anonymous = 1; + } if (WC->upload_length > 0) { @@ -1536,8 +2398,8 @@ void post_message(void) aptr->next = att; } - /* Netscape sends a simple filename, which is what we want, - * but Satan's browser sends an entire pathname. Reduce + /* 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) { @@ -1557,34 +2419,42 @@ void post_message(void) return; } - if (!strcasecmp(bstr("sc"), "Cancel")) { + if (strlen(bstr("cancel_button")) > 0) { sprintf(WC->ImportantMessage, - "Cancelled. Message was not posted."); - } else if (!strcasecmp(bstr("attach"), "Add")) { + _("Cancelled. Message was not posted.")); + } else if (strlen(bstr("attach_button")) > 0) { display_enter(); return; } else if (atol(bstr("postseq")) == dont_post) { sprintf(WC->ImportantMessage, - "Automatically cancelled because you have already " - "saved this message."); + _("Automatically cancelled because you have already " + "saved this message.")); } else { - sprintf(buf, "ENT0 1|%s|0|4|%s", + sprintf(buf, "ENT0 1|%s|%d|4|%s|||%s|%s", bstr("recp"), - bstr("subject") ); + is_anonymous, + bstr("subject"), + bstr("cc"), + bstr("bcc") + ); serv_puts(buf); serv_getln(buf, sizeof buf); if (buf[0] == '4') { post_mime_to_server(); - if (strlen(bstr("recp")) > 0) { - sprintf(WC->ImportantMessage, "Message has been sent.\n"); + if ( (strlen(bstr("recp")) > 0) + || (strlen(bstr("cc")) > 0) + || (strlen(bstr("bcc")) > 0) + ) { + sprintf(WC->ImportantMessage, _("Message has been sent.\n")); } else { - sprintf(WC->ImportantMessage, "Message has been posted.\n"); + sprintf(WC->ImportantMessage, _("Message has been posted.\n")); } dont_post = atol(bstr("postseq")); } else { - sprintf(WC->ImportantMessage, - "%s", &buf[4]); + sprintf(WC->ImportantMessage, "%s", &buf[4]); + display_enter(); + return; } } @@ -1601,13 +2471,22 @@ void post_message(void) void display_enter(void) { char buf[SIZ]; + char ebuf[SIZ]; long now; struct wc_attachment *att; + int recipient_required = 0; + int recipient_bad = 0; + int i; + int is_anonymous = 0; if (strlen(bstr("force_room")) > 0) { gotoroom(bstr("force_room")); } + if (!strcasecmp(bstr("is_anonymous"), "yes")) { + is_anonymous = 1; + } + /* Are we perhaps in an address book view? If so, then an "enter * message" command really means "add new entry." */ @@ -1634,101 +2513,225 @@ void display_enter(void) } #endif - /* Otherwise proceed normally. Do a custom room banner with no navbar... */ - output_headers(1, 1, 2, 0, 0, 0, 0); + /* + * 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" - "
" + "
" "
"); - sprintf(buf, "ENT0 0|%s|0|0", bstr("recp")); - serv_puts(buf); + /* First test to see whether this is a room that requires recipients to be entered */ + serv_puts("ENT0 0"); serv_getln(buf, sizeof buf); - - if (!strncmp(buf, "570", 3)) { - if (strlen(bstr("recp")) > 0) { - svprintf("RECPERROR", WCS_STRING, - "%s
\n", - &buf[4] - ); - } - do_template("prompt_for_recipient"); - goto DONE; + if (!strncmp(buf, "570", 3)) { /* 570 means that we need a recipient here */ + recipient_required = 1; } - if (buf[0] != '2') { + else if (buf[0] != '2') { /* Any other error means that we cannot continue */ wprintf("%s
\n", &buf[4]); goto DONE; } + /* Now check our actual recipients if there are any */ + if (recipient_required) { + sprintf(buf, "ENT0 0|%s|%d|0||||%s|%s", bstr("recp"), is_anonymous, bstr("cc"), bstr("bcc")); + serv_puts(buf); + serv_getln(buf, sizeof buf); + + if (!strncmp(buf, "570", 3)) { /* 570 means we have an invalid recipient listed */ + if (strlen(bstr("recp")) + strlen(bstr("cc")) + strlen(bstr("bcc")) > 0) { + recipient_bad = 1; + } + } + 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. */ + now = time(NULL); - strcpy(buf, ""); - strcat(&buf[strlen(buf)], " from "); - stresc(&buf[strlen(buf)], WC->wc_username, 1, 1); + fmt_date(buf, now, 0); + strcat(&buf[strlen(buf)], _(" from ")); + stresc(&buf[strlen(buf)], WC->wc_fullname, 1, 1); + + /* Don't need this anymore, it's in the input box below if (strlen(bstr("recp")) > 0) { - strcat(&buf[strlen(buf)], " to "); + strcat(&buf[strlen(buf)], _(" to ")); stresc(&buf[strlen(buf)], bstr("recp"), 1, 1); } - strcat(&buf[strlen(buf)], " in "); + */ + + strcat(&buf[strlen(buf)], _(" in ")); stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1); /* begin message entry screen */ - // wprintf("
\n"); - wprintf("
\n"); - wprintf("\n", - bstr("recp")); - wprintf("\n", - now); - - /* header bar */ - wprintf(" ", now); - wprintf("%s
\n", buf); - wprintf("\""); - /* "onLoad=\"document.enterform.msgtext.focus();\" " */ - wprintf("Subject (optional):" + wprintf("\n", now); + + wprintf("\""); + wprintf("%s\n", buf); /* header bar */ + if (WC->room_flags & QR_ANONOPT) { + wprintf(" " + "", + (is_anonymous ? "checked" : "") + ); + wprintf("Anonymous"); + } + wprintf("
\n"); /* header bar */ + + wprintf("\n"); + if (recipient_required) { + + wprintf("\n"); + + wprintf("\n"); + + wprintf("\n"); + + /* Initialize the autocomplete ajax helpers (found in wclib.js) */ + wprintf(" \n" + ); + } + + wprintf("
"); + wprintf(""); + wprintf(_("To:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("CC:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("BCC:")); + wprintf(""); + wprintf("" + ""); + wprintf("
"); + wprintf("
"); + wprintf(""); + wprintf(_("Subject (optional):")); + wprintf(""); + wprintf("" "" - " " - ); + wprintf("\" size=50 maxlength=70>\n"); - wprintf(" 0) { - wprintf("Send message"); + wprintf(" " - "\n"); - - wprintf("
\n" - " \n" - " \n" - "

\n"); + + /* If we're forwarding a message, insert it here... */ + if (atol(bstr("fwdquote")) > 0L) { + wprintf("
"); + wprintf(_("--- forwarded message ---")); + wprintf("

"); + pullquote_message(atol(bstr("fwdquote")), 1); + } + + /* If we're replying quoted, insert the quote here... */ + else if (atol(bstr("replyquote")) > 0L) { + wprintf("
" + "
"); + pullquote_message(atol(bstr("replyquote")), 0); + wprintf("
\n\n"); + } + + /* Insert our signature if appropriate... */ + if ( (WC->is_mailbox) && (strcmp(bstr("sig_inserted"), "yes")) ) { + get_preference("use_sig", buf, sizeof buf); + if (!strcasecmp(buf, "yes")) { + get_preference("signature", ebuf, sizeof ebuf); + euid_unescapize(buf, ebuf); + wprintf("
--
"); + for (i=0; i"); + } + else if (buf[i] == '<') { + wprintf("<"); + } + else if (buf[i] == '>') { + wprintf(">"); + } + else if (buf[i] == '&') { + wprintf("&"); + } + else if (buf[i] == '\"') { + wprintf("""); + } + else if (buf[i] == '\'') { + wprintf("'"); + } + else if (isprint(buf[i])) { + wprintf("%c", buf[i]); + } + } + } + } + + wprintf(""); + wprintf("
\n"); + + /* + * The following script embeds the TinyMCE richedit control, and automatically + * transforms the textarea into a richedit textarea. + */ + wprintf( + "\n" + "\n" + ); + /* Enumerate any attachments which are already in place... */ - wprintf(" Attachments: "); + wprintf(" "); + wprintf(_("Attachments:")); + wprintf(" "); wprintf("\n  " - "\n"); + "\n", _("Add")); + + /* Seth asked for these to be at the top *and* bottom... */ + wprintf(" " + "\n", _("Cancel")); + + /* Make sure we only insert our signature once */ + if (strcmp(bstr("sig_inserted"), "yes")) { + wprintf("\n"); + } wprintf("\n"); @@ -1755,10 +2774,6 @@ DONE: wDumpContent(1); - - - - void delete_msg(void) { long msgid; @@ -1766,10 +2781,15 @@ void delete_msg(void) msgid = atol(bstr("msgid")); - output_headers(1, 1, 1, 0, 0, 0, 0); + output_headers(1, 1, 1, 0, 0, 0); + + if (WC->wc_is_trash) { /* Delete from Trash is a real delete */ + serv_printf("DELE %ld", msgid); + } + else { /* Otherwise move it to Trash */ + serv_printf("MOVE %ld|_TRASH_|0", msgid); + } - sprintf(buf, "DELE %ld", msgid); - serv_puts(buf); serv_getln(buf, sizeof buf); wprintf("%s
\n", &buf[4]); @@ -1790,22 +2810,23 @@ void confirm_move_msg(void) msgid = atol(bstr("msgid")); - output_headers(1, 1, 1, 0, 0, 0, 0); - wprintf("
" - "
"); - wprintf("Confirm move of message\n"); - wprintf("
\n"); + output_headers(1, 1, 2, 0, 0, 0); + wprintf("
\n"); + wprintf("
"); + wprintf(""); + wprintf(_("Confirm move of message")); + wprintf("\n"); + wprintf("
\n"); + wprintf("
\n
\n"); wprintf("
"); - wprintf("Move this message to:
\n"); - - wprintf("
\n"); - wprintf("\n", - bstr("msgid")); + wprintf(_("Move this message to:")); + wprintf("
\n"); + wprintf("\n"); + wprintf("\n", bstr("msgid")); wprintf("\n"); wprintf("
\n"); - wprintf(""); + wprintf("", _("Move")); wprintf(" "); - wprintf(""); + wprintf("", _("Cancel")); wprintf("
\n"); wprintf("\n"); @@ -1839,66 +2860,15 @@ void move_msg(void) msgid = atol(bstr("msgid")); - output_headers(1, 1, 1, 0, 0, 0, 0); - - if (!strcasecmp(bstr("yesno"), "Move")) { + if (strlen(bstr("move_button")) > 0) { sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room")); serv_puts(buf); serv_getln(buf, sizeof buf); - wprintf("%s
\n", &buf[4]); + sprintf(WC->ImportantMessage, "%s", &buf[4]); } else { - wprintf("Message not moved.
\n"); - } - - wDumpContent(1); -} - -/* - * This gets called when a user selects multiple messages in a summary - * list and then clicks to perform a transformation of some sort on them - * (such as deleting them). - */ -void do_stuff_to_msgs(void) { - char buf[SIZ]; - char sc[SIZ]; - - struct stuff_t { - struct stuff_t *next; - long msgnum; - }; - - struct stuff_t *stuff = NULL; - struct stuff_t *ptr; - - - serv_puts("MSGS ALL"); - serv_getln(buf, sizeof buf); - - if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { - ptr = malloc(sizeof(struct stuff_t)); - ptr->msgnum = atol(buf); - ptr->next = stuff; - stuff = ptr; + sprintf(WC->ImportantMessage, (_("The message was not moved."))); } - strcpy(sc, bstr("sc")); - - while (stuff != NULL) { - - sprintf(buf, "msg_%ld", stuff->msgnum); - if (!strcasecmp(bstr(buf), "yes")) { - - if (!strcasecmp(sc, "Delete selected")) { - serv_printf("DELE %ld", stuff->msgnum); - serv_getln(buf, sizeof buf); - } - - } - - ptr = stuff->next; - free(stuff); - stuff = ptr; - } + readloop("readnew"); - readloop("readfwd"); }