X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fmessages.c;h=190f6061e4d4772689aa7173dfd996c6191bc2a1;hb=bc17ed696720ce8a4726f6aaa70171f061319763;hp=41a976cca06a9544f66d0f44c78fb885d7e6db54;hpb=8eacce58cbc89e62b60fbdd0d3e1c5238f2bef1a;p=citadel.git diff --git a/webcit/messages.c b/webcit/messages.c index 41a976cca..190f6061e 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -5,27 +5,10 @@ * */ -#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" /* Address book entry (keep it short and sweet, it's just a quickie lookup @@ -37,6 +20,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 +162,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 ) @@ -91,20 +170,57 @@ char buf[]; } +/* + * Turn a vCard "n" (name) field into something displayable. + */ +void vcard_n_prettyize(char *name) +{ + char *original_name; + int i; + + original_name = strdup(name); + for (i=0; i<5; ++i) { + if (strlen(original_name) > 0) { + if (original_name[strlen(original_name)-1] == ' ') { + original_name[strlen(original_name)-1] = 0; + } + if (original_name[strlen(original_name)-1] == ';') { + original_name[strlen(original_name)-1] = 0; + } + } + } + strcpy(name, ""); + for (i=0; inumprops) for (i=0; i<(v->numprops); ++i) { - if (!strcasecmp(v->prop[i].name, "n")) { - strcpy(storename, v->prop[i].value); - } + + name = vcard_get_prop(v, "n", 1, 0, 0); + if (name != NULL) { + strcpy(storename, name); + /* vcard_n_prettyize(storename); */ } + } @@ -132,20 +248,27 @@ void display_parsed_vcard(struct vCard *v, int full) { int pass; char displayname[SIZ]; + char title[SIZ]; + char org[SIZ]; char phone[SIZ]; char mailto[SIZ]; strcpy(displayname, ""); strcpy(phone, ""); strcpy(mailto, ""); + strcpy(title, ""); + strcpy(org, ""); if (!full) { wprintf(""); name = vcard_get_prop(v, "fn", 1, 0, 0); - if (name == NULL) name = vcard_get_prop(v, "n", 1, 0, 0); if (name != NULL) { - strcpy(buf, name); - escputs(buf); + escputs(name); + } + else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) { + strcpy(displayname, name); + vcard_n_prettyize(displayname); + escputs(displayname); } else { wprintf(" "); @@ -154,16 +277,16 @@ void display_parsed_vcard(struct vCard *v, int full) { return; } - wprintf(""); + wprintf("
"); for (pass=1; pass<=2; ++pass) { if (v->numprops) for (i=0; i<(v->numprops); ++i) { thisname = strdup(v->prop[i].name); - extract_token(firsttoken, thisname, 0, ';'); + extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken); for (j=0; j 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")) { if (strlen(phone) > 0) strcat(phone, "
"); strcat(phone, thisvalue); for (j=0; j
\n"); @@ -258,6 +400,8 @@ void display_parsed_vcard(struct vCard *v, int full) { /* ignore */ } else { + + /*** Don't show extra fields. They're ugly. if (pass == 2) { wprintf("\n"); } + ***/ } free(thisname); @@ -274,20 +419,37 @@ void display_parsed_vcard(struct vCard *v, int full) { if (pass == 1) { wprintf("" "\n"); + wprintf(""); + if (strlen(title) > 0) { + wprintf("
"); + escputs(title); + wprintf("
"); + } + if (strlen(org) > 0) { + wprintf("
"); + escputs(org); + wprintf("
"); + } + wprintf("\n"); - if (strlen(phone) > 0) - wprintf("\n", phone); - if (strlen(mailto) > 0) - wprintf("\n", mailto); + if (strlen(phone) > 0) { + wprintf("\n", phone); + } + if (strlen(mailto) > 0) { + wprintf("\n", mailto); + } } } - wprintf("
Address:"); + wprintf("
"); + wprintf(_("Address:")); + wprintf(""); for (j=0; j 0) { escputs(buf); - wprintf("
"); + if (j<3) wprintf("
"); + else wprintf(" "); } } wprintf("
"); escputs(thisname); @@ -265,6 +409,7 @@ void display_parsed_vcard(struct vCard *v, int full) { escputs(thisvalue); wprintf("
" - "" + "" ""); escputs(displayname); - wprintf("
Telephone:%s
E-mail:%s
"); + wprintf(_("Telephone:")); + wprintf("%s
"); + wprintf(_("E-mail:")); + wprintf("%s
\n"); + wprintf("\n"); } @@ -317,9 +479,10 @@ void display_vcard(char *vcard_source, char alpha, int full, char *storename) { if (storename != NULL) { fetchname_parsed_vcard(v, storename); } - else if ( (alpha == 0) - || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) ) - || ((!isalpha(alpha)) && (!isalpha(this_alpha))) ) { + else if ( (alpha == 0) + || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) ) + || ((!isalpha(alpha)) && (!isalpha(this_alpha))) + ) { display_parsed_vcard(v, full); } @@ -327,50 +490,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 now[SIZ]; + 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_gets(buf); + 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"); + if (!printable_view) { + wprintf("
\n"); + wprintf("
\n"); + } /* begin message header table */ wprintf(""); strcpy(m_subject, ""); + strcpy(m_cc, ""); - while (serv_gets(buf), strcasecmp(buf, "text")) { + 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; } @@ -393,21 +579,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("<"); @@ -425,42 +627,55 @@ 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)) { - fmt_date(now, atol(&buf[5])); + fmt_date(now, atol(&buf[5]), 0); wprintf("%s ", now); } if (!strncasecmp(buf, "part=", 5)) { - extract(mime_filename, &buf[5], 1); - extract(mime_partnum, &buf[5], 2); - extract(mime_disposition, &buf[5], 3); - extract(mime_content_type, &buf[5], 4); + 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_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 ***/ @@ -499,45 +714,104 @@ 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" - "
\n"); - wprintf("
\n"); - wprintf("\n", - msgnum); - wprintf("\n"); - - if (!strncasecmp(m_subject, "Re:", 2)) { - wprintf("\n"); - } - else if (strlen(m_subject) > 0) { - 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); + wprintf("?subject="); + if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20"); + urlescputs(m_subject); + wprintf("\">[%s] ", _("Reply")); + } + + /* ReplyQuoted */ + if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { + if (!WC->is_mailbox) { + wprintf("[%s] ", _("ReplyQuoted")); + } + } - wprintf("\n"); + /* 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")); + + /* Delete */ + wprintf("" + "[%s] ", msgnum, _("Delete this message?"), _("Delete") + ); + } - if (WC->is_room_aide) { - wprintf("\n" - "\n"); + wprintf("" + "[%s]", msgnum, msgnum, _("Print")); + + wprintf("
\n"); + wprintf("
\n"); /* Begin body */ wprintf(" 0)) { + while (serv_getln(buf, sizeof buf), (strlen(buf) > 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) && @@ -589,19 +907,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); - while (serv_gets(buf), strcmp(buf, "000")) { } + 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); } @@ -611,12 +940,14 @@ void read_message(long msgnum) { if (part_source != NULL) { /* If it's my vCard I can edit it */ - if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) - || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) { - wprintf("", + if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) + || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) + || (WC->wc_view == VIEW_ADDRESSBOOK) + ) { + wprintf("", msgnum, vcard_partnum); - wprintf("(edit)"); + wprintf("[%s]", _("edit")); } /* In all cases, display the full card */ @@ -642,84 +973,396 @@ ENDBODY: wprintf("
\n"); /* end everythingamundo table */ - wprintf("

\n"); + if (!printable_view) { + wprintf("\n"); + wprintf("
\n"); + } + +#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 + */ +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 + */ +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); } -void summarize_message(long msgnum, int is_new) { + +/* + * 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(from, &buf[5]); + wprintf(_("from ")); +#ifdef HAVE_ICONV + utf8ify_rfc822_string(from); +#endif + 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)) + && (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, "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) + ) { + wprintf("@%s ", &buf[5]); + } + } + if (!strncasecmp(buf, "rcpt=", 5)) { + wprintf(_("to ")); + wprintf("%s ", &buf[5]); + } + if (!strncasecmp(buf, "time=", 5)) { + 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"); + } + + } + + 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 + ); - struct { - char date[SIZ]; - char from[SIZ]; - char to[SIZ]; - char subj[SIZ]; - int hasattachments; - } summ; + wprintf(""); + escputs(WC->summ[num].subj); + wprintf(""); - memset(&summ, 0, sizeof(summ)); - strcpy(summ.subj, "(no subject)"); + wprintf(""); + escputs(WC->summ[num].from); + wprintf(""); - sprintf(buf, "MSG0 %ld|3", msgnum); /* ask for headers only with no MIME */ - serv_puts(buf); - serv_gets(buf); - if (buf[0] != '1') return; + wprintf(""); + fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ + escputs(datebuf); + wprintf(""); - while (serv_gets(buf), strcmp(buf, "000")) { - if (!strncasecmp(buf, "from=", 5)) { - strcpy(summ.from, &buf[5]); - } - if (!strncasecmp(buf, "subj=", 5)) { - strcpy(summ.subj, &buf[5]); - } - if (!strncasecmp(buf, "rfca=", 5)) { - strcat(summ.from, " <"); - strcat(summ.from, &buf[5]); - strcat(summ.from, ">"); - } + wprintf("", + WC->summ[num].msgnum + ); + wprintf(""); - if (!strncasecmp(buf, "node=", 5)) { - if ( ((WC->room_flags & QR_NETWORK) - || ((strcasecmp(&buf[5], serv_info.serv_nodename) - && (strcasecmp(&buf[5], serv_info.serv_fqdn))))) - ) { - strcat(summ.from, " @ "); - strcat(summ.from, &buf[5]); - } - } + wprintf("\n"); +} - if (!strncasecmp(buf, "rcpt=", 5)) { - strcpy(summ.to, &buf[5]); - } - if (!strncasecmp(buf, "time=", 5)) { - fmt_date(summ.date, atol(&buf[5])); - } - } - - 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; -} @@ -732,29 +1375,22 @@ 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); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] != '1') return; - while (serv_gets(buf), strcmp(buf, "000")) { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { if (!strncasecmp(buf, "part=", 5)) { - extract(mime_filename, &buf[5], 1); - extract(mime_partnum, &buf[5], 2); - extract(mime_disposition, &buf[5], 3); - extract(mime_content_type, &buf[5], 4); + 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")) { @@ -772,12 +1408,14 @@ void display_addressbook(long msgnum, char alpha) { display_vcard(vcard_source, alpha, 0, NULL); /* If it's my vCard I can edit it */ - if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) - || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))) { - wprintf("", + if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) + || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) + || (WC->wc_view == VIEW_ADDRESSBOOK) + ) { + wprintf("", msgnum, vcard_partnum); - wprintf("(edit)"); + wprintf("[%s]", _("edit")); } free(vcard_source); @@ -802,7 +1440,7 @@ void lastfirst_firstlast(char *namebuf) { i = num_tokens(namebuf, ' '); if (i < 2) return; - extract_token(lastname, namebuf, i-1, ' '); + extract_token(lastname, namebuf, i-1, ' ', sizeof lastname); remove_token(namebuf, i-1, ' '); strcpy(firstname, namebuf); sprintf(namebuf, "%s; %s", lastname, firstname); @@ -819,32 +1457,25 @@ 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); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] != '1') return; - while (serv_gets(buf), strcmp(buf, "000")) { + while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { if (!strncasecmp(buf, "part=", 5)) { - extract(mime_filename, &buf[5], 1); - extract(mime_partnum, &buf[5], 2); - extract(mime_disposition, &buf[5], 3); - extract(mime_content_type, &buf[5], 4); + 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")) { @@ -870,7 +1501,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; } @@ -930,7 +1563,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(""); @@ -972,9 +1605,10 @@ 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"); ++displayed; @@ -989,29 +1623,169 @@ 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 displayname[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); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] != '1') { wprintf("%s
\n", &buf[4]); return (nummsgs); } - while (serv_gets(buf), strcmp(buf, "000")) { + 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(displayname, buf, 2, '|', sizeof displayname); + 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(displayname) > 0) { + safestrncpy(WC->summ[nummsgs-1].from, displayname, 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 @@ -1021,11 +1795,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; @@ -1033,23 +1807,45 @@ void readloop(char *oper) int is_calendar = 0; int is_tasks = 0; int is_notes = 0; - int remaining_messages; 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; /* FIXME we need to set/reset this option now. It works. */ 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. @@ -1072,11 +1868,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 @@ -1086,7 +1884,7 @@ void readloop(char *oper) strcpy(old_msgs, ""); if (is_summary) { serv_puts("GTSN"); - serv_gets(buf); + serv_getln(buf, sizeof buf); if (buf[0] == '2') { strcpy(old_msgs, &buf[4]); } @@ -1110,76 +1908,135 @@ 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; } } + } + } + + if (startmsg == 0L) { + if (bbs_reverse) { + startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; + } + else { + startmsg = WC->msgarr[0]; + } + } - /* 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 (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 a tabular view, set up the line */ - if (is_summary) { - bg = 1 - bg; - wprintf("", - (bg ? "DDDDDD" : "FFFFFF") - ); - } + 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(""); /* end of 'content' div */ + + wprintf("
" + + "
\n" + + "\n" + + "" + "
SubjectSenderDate
" + "" + "" + "" + "" + "\n" + , + _("Subject"), subjsort_button, + _("Sender"), sendsort_button, + _("Date"), datesort_button, + _("Delete") + ); + } + + 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); @@ -1200,24 +2057,50 @@ 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 (displayed_msgs != NULL) { + if (bbs_reverse) { + qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r); + } + + for (a=0; a\n"); + wprintf("
%s %s%s %s%s %s
" + "\n"); /* end of 'fix_scrollbar_bug' div */ + wprintf(""); /* end of 'message_list' div */ + + wprintf("
"); /* The preview pane will initially be empty */ + + /* Now register each message (whose element ID is "m9999", + * where "9999" is the message number) as draggable. + * (NOTE: uses script.aculo.us draggables, which will probably not be + * adequate for this purpose.) + wprintf("\n"); + */ } /* Bump these because although we're thinking in zero base, the user @@ -1226,106 +2109,93 @@ void readloop(char *oper) ++lowest_displayed; ++highest_displayed; - /* 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)) { - - wprintf("
" - "\n" - "
" - "Reading #%d of %d messages.", - lowest_displayed, nummsgs); - - if (is_summary) { - wprintf("\n"); - } - - if (pn_previous > 0L) { - wprintf("" - "Previous \n", - oper, - pn_previous ); - } - - if (pn_next > 0L) { - wprintf("" - "Next \n", - oper, - pn_next ); - } - - wprintf("" - "Summary" - "", - oper, - WC->msgarr[0]); - - } - } - /* * 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)) { + && (!is_notes) && (!is_singlecard) && (!is_summary)) { - wprintf("Reading #", lowest_displayed, highest_displayed); + wprintf("
\n"); - wprintf(" "); + wprintf(_("of %d messages."), nummsgs); - wprintf(" of %d messages.", nummsgs); + /* forward/reverse */ + wprintf(" \n"); - } + wprintf("\n", + (bbs_reverse ? "" : "selected"), + oper + ); + + wprintf("\n", + (bbs_reverse ? "selected" : ""), + oper + ); + + wprintf(""); + wprintf("
\n"); } } - if (is_summary) wprintf("\n"); DONE: if (is_tasks) { @@ -1340,8 +2210,21 @@ DONE: do_addrbook_view(addrbook, num_ab); /* Render the address book */ } + /* Put the data transfer hidden iframe in a hidden div, to make it *really* hidden */ + wprintf("" + "
\n" + "\n" + ); + + /* Note: wDumpContent() will output one additional
tag. */ wDumpContent(1); if (addrbook != NULL) free(addrbook); + + /* free the summary */ + if (WC->num_summ != 0) { + WC->num_summ = 0; + free(WC->summ); + } } @@ -1357,6 +2240,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; @@ -1376,7 +2262,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); @@ -1448,8 +2334,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) { @@ -1469,34 +2355,41 @@ 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|0|4|%s|||%s|%s", bstr("recp"), - bstr("subject") ); + bstr("subject"), + bstr("cc"), + bstr("bcc") + ); serv_puts(buf); - serv_gets(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; } } @@ -1513,8 +2406,12 @@ 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; if (strlen(bstr("force_room")) > 0) { gotoroom(bstr("force_room")); @@ -1546,119 +2443,245 @@ 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); - serv_gets(buf); + wprintf("
\n" + "
" + "
"); - 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; + /* First test to see whether this is a room that requires recipients to be entered */ + serv_puts("ENT0 0"); + serv_getln(buf, sizeof buf); + if (!strncmp(buf, "570", 3)) { /* 570 means that we need a recipient here */ + 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|0|0||||%s|%s", bstr("recp"), 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); - fmt_date(buf, now); - strcat(&buf[strlen(buf)], " from "); + fmt_date(buf, now, 0); + strcat(&buf[strlen(buf)], _(" from ")); stresc(&buf[strlen(buf)], WC->wc_username, 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); + wprintf("\n", now); + wprintf("\""); wprintf("%s
\n", buf); /* header bar */ - wprintf("\""); - /* "onLoad=\"document.enterform.msgtext.focus();\" " */ - wprintf("Subject (optional):" + + 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"); - - /* begin richedit box */ - wprintf("
\n"); - - wprintf("\n" - " \n" - " \n" - " \n"); - wprintf("
\n"); /* end richedit box */ - /* Here comes the "do attachments" section on the bottom */ - wprintf("
\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("
"); + } + + /* 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(""); /* Now offer the ability to attach additional files... */ wprintf("   " "Attach file: \n  " - "\n"); + "SIZE=16 TYPE=\"file\">\n  " + "\n", _("Add")); + + /* Seth asked for these to be at the top *and* bottom... */ + wprintf(" " + "\n", _("Cancel")); - wprintf("
\n"); /* end attachments section */ + /* Make sure we only insert our signature once */ + if (strcmp(bstr("sig_inserted"), "yes")) { + wprintf("\n"); + } - wprintf("\n"); + wprintf("\n"); - wprintf("\n"); + wprintf("
\n"); DONE: wDumpContent(1); } @@ -1676,11 +2699,10 @@ 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); - sprintf(buf, "DELE %ld", msgid); - serv_puts(buf); - serv_gets(buf); + serv_printf("MOVE %ld|_TRASH_|0", msgid); + serv_getln(buf, sizeof buf); wprintf("%s
\n", &buf[4]); wDumpContent(1); @@ -1700,28 +2722,30 @@ 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("
\n"); + wprintf("", _("Cancel")); + wprintf("\n"); wprintf("\n"); wDumpContent(1); @@ -1748,54 +2772,26 @@ 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_gets(buf); - wprintf("%s
\n", &buf[4]); + serv_getln(buf, sizeof buf); + sprintf(WC->ImportantMessage, "%s", &buf[4]); } else { - wprintf("Message not moved.
\n"); - } - - wDumpContent(1); -} - -/* - * This gets called when a user selects Reply/Move/Del etc. on *one* message. - */ -void do_stuff_to_one_msg(void) { - char *msg_oper; - - msg_oper = bstr("msg_oper"); - - if (!strcasecmp(msg_oper, "Delete")) { - delete_msg(); /* It's already been confirmed using JS */ - return; - } - if (!strcasecmp(msg_oper, "Move")) { - confirm_move_msg(); - return; - } - if (!strcasecmp(msg_oper, "Reply")) { - display_enter(); /* recp and subject already set */ - return; + sprintf(WC->ImportantMessage, (_("The message was not moved."))); } - /* should never get here. FIXME: display an error */ + readloop("readnew"); } - /* * 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]; + char buf[256]; struct stuff_t { struct stuff_t *next; @@ -1804,28 +2800,31 @@ void do_stuff_to_msgs(void) { struct stuff_t *stuff = NULL; struct stuff_t *ptr; + int delete_button_pressed = 0; serv_puts("MSGS ALL"); - serv_gets(buf); + serv_getln(buf, sizeof buf); - if (buf[0] == '1') while (serv_gets(buf), strcmp(buf, "000")) { + 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; } - strcpy(sc, bstr("sc")); + if (strlen(bstr("delete_button")) > 0) { + delete_button_pressed = 1; + } 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_gets(buf); + if (delete_button_pressed) { + serv_printf("MOVE %ld|_TRASH_|0", stuff->msgnum); + serv_getln(buf, sizeof buf); } }