X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=webcit%2Fmessages.c;h=90340896472e3686b4485c4fbbe558f4a8594816;hb=5685e790de93f77083ea240f474a4870b194c550;hp=61ce15f9dfe250238828233c058f9a1fd242278c;hpb=d998d3601407d81916625728dd26287a1bce71f4;p=citadel.git diff --git a/webcit/messages.c b/webcit/messages.c index 61ce15f9d..903408964 100644 --- a/webcit/messages.c +++ b/webcit/messages.c @@ -1,14 +1,11 @@ /* * $Id$ - */ -/** - * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages. - * \ingroup WebcitDisplayItems + * + * Functions which deal with the fetching and displaying of messages. * */ -/*@{*/ + #include "webcit.h" -#include "vcard.h" #include "webserver.h" #include "groupdav.h" @@ -16,7 +13,9 @@ #define SENDER_COL_WIDTH_PCT 30 /**< Mailbox view column width */ #define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /**< Mailbox view column width */ -/** +void display_enter(void); + +/* * 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) */ @@ -29,13 +28,13 @@ struct addrbookent { #ifdef HAVE_ICONV -/** - * \brief Wrapper around iconv_open() - * Our version adds aliases for non-standard Microsoft charsets - * such as 'MS950', aliasing them to names like 'CP950' +/* + * Wrapper around iconv_open() + * Our version adds aliases for non-standard Microsoft charsets + * such as 'MS950', aliasing them to names like 'CP950' * - * \param tocode Target encoding - * \param fromcode Source encoding + * tocode Target encoding + * fromcode Source encoding */ iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode) { @@ -54,14 +53,30 @@ iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode) } -/** - * \brief Handle subjects with RFC2047 encoding - * such as: + +inline char *FindNextEnd (char *bptr) +{ + char * end; + /* Find the next ?Q? */ + end = strchr(bptr + 2, '?'); + if (end == NULL) return NULL; + if (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) && + (*(end + 2) == '?')) { + /* skip on to the end of the cluster, the next ?= */ + end = strstr(end + 3, "?="); + } + else + /* sort of half valid encoding, try to find an end. */ + end = strstr(bptr, "?="); + return end; +} + +/* + * Handle subjects with RFC2047 encoding such as: * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?= - * \param buf the stringbuffer to process */ void utf8ify_rfc822_string(char *buf) { - char *start, *end; + char *start, *end, *next, *nextend, *ptr; char newbuf[1024]; char charset[128]; char encoding[16]; @@ -74,25 +89,28 @@ void utf8ify_rfc822_string(char *buf) { char *isav; /**< Saved pointer to input buffer */ char *osav; /**< Saved pointer to output buffer */ int passes = 0; - int i; + int i, len, delta; int illegal_non_rfc2047_encoding = 0; - /** Sometimes, badly formed messages contain strings which were simply + /* Sometimes, badly formed messages contain strings which were simply * written out directly in some foreign character set instead of * using RFC2047 encoding. This is illegal but we will attempt to * handle it anyway by converting from a user-specified default * charset to UTF-8 if we see any nonprintable characters. */ - for (i=0; i 126)) { illegal_non_rfc2047_encoding = 1; + i = len; ///< take a shortcut, it won't be more than one. } } if (illegal_non_rfc2047_encoding) { - char default_header_charset[128]; - get_preference("default_header_charset", default_header_charset, sizeof default_header_charset); - if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) { - ic = ctdl_iconv_open("UTF-8", default_header_charset); + StrBuf *default_header_charset; + get_preference("default_header_charset", &default_header_charset); + if ( (strcasecmp(ChrPtr(default_header_charset), "UTF-8")) && + (strcasecmp(ChrPtr(default_header_charset), "us-ascii")) ) { + ic = ctdl_iconv_open("UTF-8", ChrPtr(default_header_charset)); if (ic != (iconv_t)(-1) ) { ibuf = malloc(1024); isav = ibuf; @@ -111,10 +129,58 @@ void utf8ify_rfc822_string(char *buf) { } } - /** Now we handle foreign character sets properly encoded - * in RFC2047 format. + /* pre evaluate the first pair */ + nextend = end = NULL; + len = strlen(buf); + start = strstr(buf, "=?"); + if (start != NULL) + end = FindNextEnd (start); + + while ((start != NULL) && (end != NULL)) + { + next = strstr(end, "=?"); + if (next != NULL) + nextend = FindNextEnd(next); + if (nextend == NULL) + next = NULL; + + /* did we find two partitions */ + if ((next != NULL) && + ((next - end) > 2)) + { + ptr = end + 2; + while ((ptr < next) && + (isspace(*ptr) || + (*ptr == '\r') || + (*ptr == '\n') || + (*ptr == '\t'))) + ptr ++; + /* did we find a gab just filled with blanks? */ + if (ptr == next) + { + memmove (end + 2, + next, + len - (next - start)); + + /* now terminate the gab at the end */ + delta = (next - end) - 2; + len -= delta; + buf[len] = '\0'; + + /* move next to its new location. */ + next -= delta; + nextend -= delta; + } + } + /* our next-pair is our new first pair now. */ + start = next; + end = nextend; + } + + /* Now we handle foreign character sets properly encoded + * in RFC2047 format. */ - while (start=strstr(buf, "=?"), end=strstr(buf, "?="), + while (start=strstr(buf, "=?"), end=FindNextEnd((start != NULL)? start : buf), ((start != NULL) && (end != NULL) && (end > start)) ) { extract_token(charset, start, 1, '?', sizeof charset); @@ -183,7 +249,7 @@ void utf8ify_rfc822_string(char *buf) { 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 @@ -194,6 +260,9 @@ void utf8ify_rfc822_string(char *buf) { } } +#else +inline void utf8ify_rfc822_string(char *a){}; + #endif @@ -207,97 +276,141 @@ void utf8ify_rfc822_string(char *buf) { * \param target Target buffer. * \param maxlen Maximum size of target buffer. * \param source Source string to be encoded. + * \param SourceLen Length of the source string + * \returns encoded length; -1 if non success. */ -void rfc2047encode(char *target, int maxlen, char *source) +int webcit_rfc2047encode(char *target, int maxlen, char *source, long SourceLen) { + const char headerStr[] = "=?UTF-8?Q?"; int need_to_encode = 0; - int i; + int i = 0; + int len; unsigned char ch; - if (target == NULL) return; + if ((source == NULL) || + (target == NULL) || + (SourceLen > maxlen)) return -1; - for (i=0; i 126)) { + while ((!IsEmptyStr (&source[i])) && + (need_to_encode == 0) && + (i < SourceLen) ) { + if (((unsigned char) source[i] < 32) || + ((unsigned char) source[i] > 126)) { need_to_encode = 1; } + i++; } if (!need_to_encode) { - safestrncpy(target, source, maxlen); - return; + memcpy (target, source, SourceLen); + target[SourceLen] = '\0'; + return SourceLen; } - - strcpy(target, "=?UTF-8?Q?"); - for (i=0; i maxlen) + return -1; + memcpy (target, headerStr, sizeof (headerStr)); + len = sizeof (headerStr) - 1; + for (i=0; (i < SourceLen) && (len + 3< maxlen) ; ++i) { ch = (unsigned char) source[i]; if ((ch < 32) || (ch > 126) || (ch == 61)) { - sprintf(&target[strlen(target)], "=%02X", ch); + sprintf(&target[len], "=%02X", ch); + len += 3; } else { - sprintf(&target[strlen(target)], "%c", ch); + sprintf(&target[len], "%c", ch); + len ++; } } - strcat(target, "?="); + if (len + 2 < maxlen) { + strcat(&target[len], "?="); + len +=2; + return len; + } + else + return -1; } -/** - * \brief 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. - * \param buf the message buffer +/* + * Look for URL's embedded in a buffer and make them linkable. We use a + * target window in order to keep the Citadel session in its own window. */ -void url(char *buf) +void url(char *buf, size_t bufsize) { - - int pos; - int start, end; + int len, UrlLen, Offset, TrailerLen, outpos; + char *start, *end, *pos; char urlbuf[SIZ]; - char outbuf[1024]; - start = (-1); - end = strlen(buf); + char outbuf[SIZ]; - for (pos = 0; pos < strlen(buf); ++pos) { - if (!strncasecmp(&buf[pos], "http://", 7)) + start = NULL; + len = strlen(buf); + if (len > bufsize) { + lprintf(1, "URL: content longer than buffer!"); + return; + } + end = buf + len; + for (pos = buf; (pos < end) && (start == NULL); ++pos) { + if (!strncasecmp(pos, "http://", 7)) start = pos; - if (!strncasecmp(&buf[pos], "ftp://", 6)) + if (!strncasecmp(pos, "ftp://", 6)) start = pos; } - if (start < 0) + if (start == NULL) return; - for (pos = strlen(buf); pos > start; --pos) { - if ( (!isprint(buf[pos])) - || (isspace(buf[pos])) - || (buf[pos] == '{') - || (buf[pos] == '}') - || (buf[pos] == '|') - || (buf[pos] == '\\') - || (buf[pos] == '^') - || (buf[pos] == '[') - || (buf[pos] == ']') - || (buf[pos] == '`') - || (buf[pos] == '<') - || (buf[pos] == '>') - || (buf[pos] == '(') - || (buf[pos] == ')') + for (pos = buf+len; pos > start; --pos) { + if ( (!isprint(*pos)) + || (isspace(*pos)) + || (*pos == '{') + || (*pos == '}') + || (*pos == '|') + || (*pos == '\\') + || (*pos == '^') + || (*pos == '[') + || (*pos == ']') + || (*pos == '`') + || (*pos == '<') + || (*pos == '>') + || (*pos == '(') + || (*pos == ')') ) { end = pos; } } + + UrlLen = end - start; + if (UrlLen > sizeof(urlbuf)){ + lprintf(1, "URL: content longer than buffer!"); + return; + } + memcpy(urlbuf, start, UrlLen); + urlbuf[UrlLen] = '\0'; + + Offset = start - buf; + if ((Offset != 0) && (Offset < sizeof(outbuf))) + memcpy(outbuf, buf, Offset); + outpos = snprintf(&outbuf[Offset], sizeof(outbuf) - Offset, + "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c", + LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB); + if (outpos >= sizeof(outbuf) - Offset) { + lprintf(1, "URL: content longer than buffer!"); + return; + } - strncpy(urlbuf, &buf[start], end - start); - 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", - LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB); - strcat(outbuf, &buf[end]); - if ( strlen(outbuf) < 250 ) - strcpy(buf, outbuf); + TrailerLen = len - (end - start); + if (TrailerLen > 0) + memcpy(outbuf + Offset + outpos, end, TrailerLen); + if (Offset + outpos + TrailerLen > bufsize) { + lprintf(1, "URL: content longer than buffer!"); + return; + } + memcpy (buf, outbuf, Offset + outpos + TrailerLen); + *(buf + Offset + outpos + TrailerLen) = '\0'; } @@ -308,29 +421,32 @@ void url(char *buf) void vcard_n_prettyize(char *name) { char *original_name; - int i; + int i, j, len; original_name = strdup(name); + len = strlen(original_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 (len > 0) { + if (original_name[len-1] == ' ') { + original_name[--len] = 0; } - if (original_name[strlen(original_name)-1] == ';') { - original_name[strlen(original_name)-1] = 0; + if (original_name[len-1] == ';') { + original_name[--len] = 0; } } } strcpy(name, ""); - for (i=0; inumprops) for (i=0; i<(v->numprops); ++i) { - + int len; thisname = strdup(v->prop[i].name); extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken); @@ -437,16 +554,23 @@ void display_parsed_vcard(struct vCard *v, int full) { remove_token(thisname, j, ';'); } } - + + len = strlen(v->prop[i].value); + /* if we have some untagged QP, detect it here. */ + if (!is_qp && (strstr(v->prop[i].value, "=?")!=NULL)) + utf8ify_rfc822_string(v->prop[i].value); + if (is_qp) { - thisvalue = malloc(strlen(v->prop[i].value) + 50); + // %ff can become 6 bytes in utf8 + thisvalue = malloc(len * 2 + 3); j = CtdlDecodeQuotedPrintable( thisvalue, v->prop[i].value, - strlen(v->prop[i].value) ); + len); thisvalue[j] = 0; } else if (is_b64) { - thisvalue = malloc(strlen(v->prop[i].value) + 50); + // ff will become one byte.. + thisvalue = malloc(len + 50); CtdlDecodeBase64( thisvalue, v->prop[i].value, strlen(v->prop[i].value) ); @@ -459,7 +583,7 @@ 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(fullname) == 0) { + if (IsEmptyStr(fullname)) { strcpy(fullname, thisvalue); vcard_n_prettyize(fullname); } @@ -481,22 +605,30 @@ void display_parsed_vcard(struct vCard *v, int full) { } else if (!strcasecmp(firsttoken, "email")) { - if (strlen(mailto) > 0) strcat(mailto, "
"); + size_t len; + if (!IsEmptyStr(mailto)) strcat(mailto, "
"); strcat(mailto, ""); + len = strlen(mailto); + urlesc(&mailto[len], SIZ - len, "\""); + len = strlen(mailto); + urlesc(&mailto[len], SIZ - len, fullname); + len = strlen(mailto); + urlesc(&mailto[len], SIZ - len, "\" <"); + len = strlen(mailto); + urlesc(&mailto[len], SIZ - len, thisvalue); + len = strlen(mailto); + urlesc(&mailto[len], SIZ - len, ">"); strcat(mailto, "\">"); - stresc(&mailto[strlen(mailto)], thisvalue, 1, 1); + len = strlen(mailto); + stresc(mailto+len, SIZ - len, thisvalue, 1, 1); strcat(mailto, ""); } else if (!strcasecmp(firsttoken, "tel")) { - if (strlen(phone) > 0) strcat(phone, "
"); + if (!IsEmptyStr(phone)) strcat(phone, "
"); strcat(phone, thisvalue); for (j=0; j"); for (j=0; j 0) { + if (!IsEmptyStr(buf)) { escputs(buf); if (j<3) wprintf("
"); else wprintf(" "); @@ -531,6 +663,14 @@ void display_parsed_vcard(struct vCard *v, int full) { wprintf("\n"); } } + /* else if (!strcasecmp(firsttoken, "photo") && full && pass == 2) { + // Only output on second pass + wprintf(""); + wprintf(_("Photo:")); + wprintf(""); + wprintf("\"Contact",msgnum); + wprintf("\n"); + } */ else if (!strcasecmp(firsttoken, "version")) { /* ignore */ } @@ -564,24 +704,24 @@ void display_parsed_vcard(struct vCard *v, int full) { ""); escputs(fullname); wprintf(""); - if (strlen(title) > 0) { + if (!IsEmptyStr(title)) { wprintf("
"); escputs(title); wprintf("
"); } - if (strlen(org) > 0) { + if (!IsEmptyStr(org)) { wprintf("
"); escputs(org); wprintf("
"); } wprintf("\n"); - if (strlen(phone) > 0) { + if (!IsEmptyStr(phone)) { wprintf(""); wprintf(_("Telephone:")); wprintf("%s\n", phone); } - if (strlen(mailto) > 0) { + if (!IsEmptyStr(mailto)) { wprintf(""); wprintf(_("E-mail:")); wprintf("%s\n", mailto); @@ -605,8 +745,10 @@ void display_parsed_vcard(struct vCard *v, int full) { * \param alpha what??? * \param full should we usse all lines? * \param storename where to store??? + * \param msgnum Citadel message pointer */ -void display_vcard(char *vcard_source, char alpha, int full, char *storename) { +void display_vcard(char *vcard_source, char alpha, int full, char *storename, + long msgnum) { struct vCard *v; char *name; char buf[SIZ]; @@ -617,6 +759,7 @@ void display_vcard(char *vcard_source, char alpha, int full, char *storename) { name = vcard_get_prop(v, "n", 1, 0, 0); if (name != NULL) { + utf8ify_rfc822_string(name); strcpy(buf, name); this_alpha = buf[0]; } @@ -628,7 +771,7 @@ void display_vcard(char *vcard_source, char alpha, int full, char *storename) { || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) ) || ((!isalpha(alpha)) && (!isalpha(this_alpha))) ) { - display_parsed_vcard(v, full); + display_parsed_vcard(v, full,msgnum); } vcard_free(v); @@ -641,40 +784,45 @@ struct attach_link { }; -/** - * \brief I wanna SEE that message! - * \param msgnum the citadel number of the message to display - * \param printable_view are we doing a print view? - * \param section Optional for encapsulated message/rfc822 submessage) +/* + * I wanna SEE that message! + * + * msgnum Message number to display + * printable_view Nonzero to display a printable view + * section Optional for encapsulated message/rfc822 submessage */ void read_message(long msgnum, int printable_view, char *section) { char buf[SIZ]; - char mime_partnum[256]; - char mime_name[256]; - char mime_filename[256]; - char mime_content_type[256]; - char mime_charset[256]; - char mime_disposition[256]; + char mime_partnum[256] = ""; + char mime_name[256] = ""; + char mime_filename[256] = ""; + char escaped_mime_filename[256] = ""; + char mime_content_type[256] = ""; + const char *mime_content_type_ptr; + char mime_charset[256] = ""; + char mime_disposition[256] = ""; int mime_length; struct attach_link *attach_links = NULL; int num_attach_links = 0; - char mime_submessages[256]; - char m_subject[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]; + char mime_submessages[256] = ""; + char m_subject[1024] = ""; + char m_cc[1024] = ""; + char from[256] = ""; + char node[256] = ""; + char rfca[256] = ""; + char reply_to[512] = ""; + char reply_all[4096] = ""; + char reply_references[1024] = ""; + char reply_inreplyto[256] = ""; + char now[64] = ""; int format_type = 0; int nhdr = 0; int bq = 0; int i = 0; - char vcard_partnum[256]; - char cal_partnum[256]; + char vcard_partnum[256] = ""; + char cal_partnum[256] = ""; char *part_source = NULL; - char msg4_partnum[32]; + char msg4_partnum[32] = ""; #ifdef HAVE_ICONV iconv_t ic = (iconv_t)(-1) ; char *ibuf; /**< Buffer of characters to be converted */ @@ -684,13 +832,6 @@ void read_message(long msgnum, int printable_view, char *section) { 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_content_type, "text/plain"); strcpy(mime_charset, "us-ascii"); strcpy(mime_submessages, ""); @@ -713,6 +854,7 @@ void read_message(long msgnum, int printable_view, char *section) { /** begin message header table */ wprintf("
"); + strcpy(m_subject, ""); strcpy(m_cc, ""); @@ -734,9 +876,7 @@ void read_message(long msgnum, int printable_view, char *section) { strcpy(from, &buf[5]); wprintf(_("from ")); wprintf(""); escputs(from); @@ -745,13 +885,21 @@ void read_message(long msgnum, int printable_view, char *section) { if (!strncasecmp(buf, "subj=", 5)) { safestrncpy(m_subject, &buf[5], sizeof m_subject); } + if (!strncasecmp(buf, "msgn=", 5)) { + safestrncpy(reply_inreplyto, &buf[5], sizeof reply_inreplyto); + } + if (!strncasecmp(buf, "wefw=", 5)) { + safestrncpy(reply_references, &buf[5], sizeof reply_references); + } if (!strncasecmp(buf, "cccc=", 5)) { + int len; safestrncpy(m_cc, &buf[5], sizeof m_cc); - if (strlen(reply_all) > 0) { + if (!IsEmptyStr(reply_all)) { strcat(reply_all, ", "); } - safestrncpy(&reply_all[strlen(reply_all)], &buf[5], - (sizeof reply_all - strlen(reply_all)) ); + len = strlen(reply_all); + safestrncpy(&reply_all[len], &buf[5], + (sizeof reply_all - len) ); } if ((!strncasecmp(buf, "hnod=", 5)) && (strcasecmp(&buf[5], serv_info.serv_humannode))) { @@ -759,7 +907,7 @@ void read_message(long msgnum, int printable_view, char *section) { } if ((!strncasecmp(buf, "room=", 5)) && (strcasecmp(&buf[5], WC->wc_roomname)) - && (strlen(&buf[5])>0) ) { + && (!IsEmptyStr(&buf[5])) ) { wprintf(_("in ")); wprintf("%s> ", &buf[5]); } @@ -775,27 +923,29 @@ void read_message(long msgnum, int printable_view, char *section) { if ( ((WC->room_flags & QR_NETWORK) || ((strcasecmp(&buf[5], serv_info.serv_nodename) && (strcasecmp(&buf[5], serv_info.serv_fqdn))))) - && (strlen(rfca)==0) + && (IsEmptyStr(rfca)) ) { wprintf("@%s ", &buf[5]); } } if (!strncasecmp(buf, "rcpt=", 5)) { + int len; wprintf(_("to ")); - if (strlen(reply_all) > 0) { + if (!IsEmptyStr(reply_all)) { strcat(reply_all, ", "); } - safestrncpy(&reply_all[strlen(reply_all)], &buf[5], - (sizeof reply_all - strlen(reply_all)) ); -#ifdef HAVE_ICONV + len = strlen(reply_all); + safestrncpy(&reply_all[len], &buf[5], + (sizeof reply_all - len) ); utf8ify_rfc822_string(&buf[5]); -#endif escputs(&buf[5]); wprintf(" "); } if (!strncasecmp(buf, "time=", 5)) { - fmt_date(now, atol(&buf[5]), 0); + webcit_fmt_date(now, atol(&buf[5]), 0); + wprintf(""); wprintf("%s ", now); + wprintf(""); } if (!strncasecmp(buf, "part=", 5)) { @@ -808,12 +958,12 @@ void read_message(long msgnum, int printable_view, char *section) { striplt(mime_name); striplt(mime_filename); - if ( (strlen(mime_filename) == 0) && (strlen(mime_name) > 0) ) { + if ( (IsEmptyStr(mime_filename)) && (!IsEmptyStr(mime_name)) ) { strcpy(mime_filename, mime_name); } if (!strcasecmp(mime_content_type, "message/rfc822")) { - if (strlen(mime_submessages) > 0) { + if (!IsEmptyStr(mime_submessages)) { strcat(mime_submessages, "|"); } strcat(mime_submessages, mime_partnum); @@ -831,14 +981,22 @@ void read_message(long msgnum, int printable_view, char *section) { else if ( ( (!strcasecmp(mime_disposition, "attachment")) || (!strcasecmp(mime_disposition, "inline")) || (!strcasecmp(mime_disposition, "")) - ) && (strlen(mime_content_type) > 0) + ) && (!IsEmptyStr(mime_content_type)) ) { ++num_attach_links; attach_links = realloc(attach_links, (num_attach_links*sizeof(struct attach_link))); safestrncpy(attach_links[num_attach_links-1].partnum, mime_partnum, 32); + utf8ify_rfc822_string(mime_filename); + + mime_content_type_ptr = mime_content_type; + if (strcasecmp(mime_content_type, "application/octet-stream") == 0) { + mime_content_type_ptr = GuessMimeByFilename(mime_filename, + strlen(mime_filename)); + } + urlesc(escaped_mime_filename, 265, mime_filename); snprintf(attach_links[num_attach_links-1].html, 1024, - "\n" "%s (%s, %d bytes) [ " "%s" " ]
\n", + mime_content_type_ptr, mime_filename, mime_content_type, mime_length, - msgnum, mime_partnum, mime_filename, + msgnum, mime_partnum, escaped_mime_filename, msgnum, mime_partnum, _("View"), - msgnum, mime_partnum, mime_filename, + msgnum, mime_partnum, escaped_mime_filename, _("Download") ); } @@ -862,7 +1021,8 @@ void read_message(long msgnum, int printable_view, char *section) { strcpy(vcard_partnum, mime_partnum); } - if (!strcasecmp(mime_content_type, "text/calendar")) { + if ( (!strcasecmp(mime_content_type, "text/calendar")) + || (!strcasecmp(mime_content_type, "application/ics")) ) { strcpy(cal_partnum, mime_partnum); } @@ -872,9 +1032,18 @@ void read_message(long msgnum, int printable_view, char *section) { } - /** Generate a reply-to address */ - if (strlen(rfca) > 0) { - if (strlen(from) > 0) { + /* Trim down excessively long lists of thread references. We eliminate the + * second one in the list so that the thread root remains intact. + */ + int rrtok = num_tokens(reply_references, '|'); + int rrlen = strlen(reply_references); + if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) { + remove_token(reply_references, 1, '|'); + } + + /* Generate a reply-to address */ + if (!IsEmptyStr(rfca)) { + if (!IsEmptyStr(from)) { snprintf(reply_to, sizeof(reply_to), "%s <%s>", from, rfca); } else { @@ -882,7 +1051,7 @@ void read_message(long msgnum, int printable_view, char *section) { } } else { - if ( (strlen(node) > 0) + if ((!IsEmptyStr(node)) && (strcasecmp(node, serv_info.serv_nodename)) && (strcasecmp(node, serv_info.serv_humannode)) ) { snprintf(reply_to, sizeof(reply_to), "%s @ %s", @@ -897,33 +1066,15 @@ void read_message(long msgnum, int printable_view, char *section) { wprintf("****"); } - 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("
"); - wprintf(_("Subject:")); - wprintf(" "); - escputs(m_subject); - wprintf("
"); - } - /** start msg buttons */ + if (!printable_view) { - wprintf("
\n",msgnum); + wprintf("

\n",msgnum); - /** Reply */ + /* Reply */ if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { wprintf("is_mailbox) { @@ -931,31 +1082,43 @@ void read_message(long msgnum, int printable_view, char *section) { } wprintf("?recp="); urlescputs(reply_to); - if (strlen(m_subject) > 0) { + if (!IsEmptyStr(m_subject)) { wprintf("?subject="); - if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20"); + if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%%20"); urlescputs(m_subject); } + wprintf("?references="); + if (!IsEmptyStr(reply_references)) { + urlescputs(reply_references); + urlescputs("|"); + } + urlescputs(reply_inreplyto); wprintf("\">[%s] ", _("Reply")); } - /** ReplyQuoted */ + /* ReplyQuoted */ if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) { if (!WC->is_mailbox) { wprintf("[%s] ", _("ReplyQuoted")); } } - /** ReplyAll */ + /* ReplyAll */ if (WC->wc_view == VIEW_MAILBOX) { wprintf("[%s] ", _("ReplyAll")); } - /** Forward */ + /* 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 this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */ if ( (WC->is_room_aide) || (WC->is_mailbox) || (WC->room_flags2 & QR2_COLLABDEL) ) { /** Move */ wprintf("[%s] ", @@ -993,33 +1162,46 @@ void read_message(long msgnum, int printable_view, char *section) { ); } - /** Headers */ + /* Headers */ wprintf("" "[%s]", msgnum, msgnum, _("Headers")); - /** Print */ + /* Print */ wprintf("" "[%s]", msgnum, msgnum, _("Print")); - wprintf("

"); + wprintf("

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

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

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

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

"); + } + wprintf(""); - /** Begin body */ + /* Begin body */ wprintf("
"); - /** + /* * Learn the content type */ strcpy(mime_content_type, "text/plain"); - while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) { + while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) { if (!strcmp(buf, "000")) { - wprintf(""); - wprintf(_("unexpected end of message")); - wprintf(" (2)

\n"); + /* This is not necessarily an error condition. See bug #226. */ goto ENDBODY; } if (!strncasecmp(buf, "X-Citadel-MSG4-Partnum:", 23)) { @@ -1027,28 +1209,33 @@ void read_message(long msgnum, int printable_view, char *section) { striplt(msg4_partnum); } if (!strncasecmp(buf, "Content-type:", 13)) { + int len; safestrncpy(mime_content_type, &buf[13], sizeof(mime_content_type)); striplt(mime_content_type); - for (i=0; i 0) && buf[len-1] == '\n') buf[--len] = 0; + if ((len > 0) && buf[len-1] == '\r') buf[--len] = 0; #ifdef HAVE_ICONV if (ic != (iconv_t)(-1) ) { @@ -1089,8 +1279,9 @@ void read_message(long msgnum, int printable_view, char *section) { } #endif - while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1]))) - buf[strlen(buf) - 1] = 0; + len = strlen(buf); + while ((!IsEmptyStr(buf)) && (isspace(buf[len-1]))) + buf[--len] = 0; if ((bq == 0) && ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { wprintf("
"); @@ -1101,27 +1292,28 @@ void read_message(long msgnum, int printable_view, char *section) { bq = 0; } wprintf(""); - url(buf); + url(buf, sizeof(buf)); escputs(buf); wprintf("
\n"); } wprintf("
"); } - else /** HTML is fun, but we've got to strip it first */ - if (!strcasecmp(mime_content_type, "text/html")) { + /* HTML is fun, but we've got to strip it first */ + else if (!strcasecmp(mime_content_type, "text/html")) { output_html(mime_charset, (WC->wc_view == VIEW_WIKI ? 1 : 0)); } - /** Unknown weirdness */ + /* Unknown weirdness */ else { wprintf(_("I don't know how to display %s"), mime_content_type); - wprintf("
\n", mime_content_type); + wprintf("
\n"); while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { } } - /** If there are attached submessages, display them now... */ - if ( (strlen(mime_submessages) > 0) && (!section[0]) ) { +ENDBODY: /* If there are attached submessages, display them now... */ + + if ( (!IsEmptyStr(mime_submessages)) && (!section[0]) ) { for (i=0; i 0) && (!section[0]) ) { for (i=0; i 0) { + /* Handler for vCard parts */ + if (!IsEmptyStr(vcard_partnum)) { part_source = load_mimepart(msgnum, vcard_partnum); if (part_source != NULL) { @@ -1151,23 +1343,21 @@ void read_message(long msgnum, int printable_view, char *section) { || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM)) || (WC->wc_view == VIEW_ADDRESSBOOK) ) { - wprintf("", + wprintf("", msgnum, vcard_partnum); wprintf("[%s]", _("edit")); } - /** In all cases, display the full card */ - display_vcard(part_source, 0, 1, NULL); + /* In all cases, display the full card */ + display_vcard(part_source, 0, 1, NULL,msgnum); } } - /** Handler for calendar parts */ - if (strlen(cal_partnum) > 0) { + /* Handler for calendar parts */ + if (!IsEmptyStr(cal_partnum)) { part_source = load_mimepart(msgnum, cal_partnum); if (part_source != NULL) { - cal_process_attachment(part_source, - msgnum, cal_partnum); + cal_process_attachment(part_source, msgnum, cal_partnum); } } @@ -1176,10 +1366,9 @@ void read_message(long msgnum, int printable_view, char *section) { part_source = NULL; } -ENDBODY: wprintf("
\n"); - /** end everythingamundo table */ + /* end everythingamundo table */ if (!printable_view) { wprintf("\n"); } @@ -1197,41 +1386,40 @@ ENDBODY: -/** - * \brief Unadorned HTML output of an individual message, suitable +/* + * Unadorned HTML output of an individual message, suitable * for placing in a hidden iframe, for printing, or whatever * - * \param msgnum_as_string Message number, as a string instead of as a long int + * msgnum_as_string == Message number, as a string instead of as a long int */ -void embed_message(char *msgnum_as_string) { +void embed_message(void) { long msgnum = 0L; - msgnum = atol(msgnum_as_string); - begin_ajax_response(); + msgnum = StrTol(WC->UrlFragment1); read_message(msgnum, 0, ""); - end_ajax_response(); } -/** - * \brief Printable view of a message +/* + * Printable view of a message * - * \param msgnum_as_string Message number, as a string instead of as a long int + * msgnum_as_string == Message number, as a string instead of as a long int */ -void print_message(char *msgnum_as_string) { +void print_message(void) { long msgnum = 0L; - msgnum = atol(msgnum_as_string); + msgnum = StrTol(WC->UrlFragment1); output_headers(0, 0, 0, 0, 0, 0); - wprintf("Content-type: text/html\r\n" + hprintf("Content-type: text/html\r\n" "Server: %s\r\n" - "Connection: close\r\n", - SERVER); + "Connection: close\r\n" + PACKAGE_STRING); begin_burst(); - wprintf("\r\n\r\n\n" - "Printable view\n" + wprintf("\r\n\n"); + escputs(WC->wc_fullname); + wprintf("\n" "\n" ); @@ -1248,17 +1436,17 @@ void print_message(char *msgnum_as_string) { * * \param msgnum_as_string Message number, as a string instead of as a long int */ -void display_headers(char *msgnum_as_string) { +void display_headers(void) { long msgnum = 0L; char buf[1024]; - msgnum = atol(msgnum_as_string); + msgnum = StrTol(WC->UrlFragment1); output_headers(0, 0, 0, 0, 0, 0); - wprintf("Content-type: text/plain\r\n" + hprintf("Content-type: text/plain\r\n" "Server: %s\r\n" "Connection: close\r\n", - SERVER); + PACKAGE_STRING); begin_burst(); serv_printf("MSG2 %ld|3", msgnum); @@ -1297,10 +1485,11 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers char *ptr = NULL; int num_attachments = 0; struct wc_attachment *att, *aptr; - char m_subject[256]; + char m_subject[1024]; char from[256]; char node[256]; char rfca[256]; + char to[256]; char reply_to[512]; char now[256]; int format_type = 0; @@ -1348,9 +1537,7 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers 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)) { @@ -1362,7 +1549,7 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers } if ((!strncasecmp(buf, "room=", 5)) && (strcasecmp(&buf[5], WC->wc_roomname)) - && (strlen(&buf[5])>0) ) { + && (!IsEmptyStr(&buf[5])) ) { wprintf(_("in ")); wprintf("%s> ", &buf[5]); } @@ -1372,23 +1559,24 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers 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) + && (IsEmptyStr(rfca)) ) { wprintf("@%s ", &buf[5]); } } if (!strncasecmp(buf, "rcpt=", 5)) { wprintf(_("to ")); - wprintf("%s ", &buf[5]); + strcpy(to, &buf[5]); + utf8ify_rfc822_string(to); + wprintf("%s ", to); } if (!strncasecmp(buf, "time=", 5)) { - fmt_date(now, atol(&buf[5]), 0); + webcit_fmt_date(now, atol(&buf[5]), 0); wprintf("%s ", now); } } @@ -1416,10 +1604,8 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers if (include_headers) { wprintf("
"); -#ifdef HAVE_ICONV utf8ify_rfc822_string(m_subject); -#endif - if (strlen(m_subject) > 0) { + if (!IsEmptyStr(m_subject)) { wprintf(_("Subject:")); wprintf(" "); msgescputs(m_subject); @@ -1436,12 +1622,13 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers * Learn the content type */ strcpy(mime_content_type, "text/plain"); - while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) { + while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) { if (!strcmp(buf, "000")) { wprintf("%s (4)", _("unexpected end of message")); goto ENDBODY; } if (!strncasecmp(buf, "Content-type: ", 14)) { + int len; safestrncpy(mime_content_type, &buf[14], sizeof(mime_content_type)); for (i=0; i 0) && (isspace(buf[strlen(buf) - 1]))) - buf[strlen(buf) - 1] = 0; + len = strlen(buf); + while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1]))) + buf[--len] = 0; if ((bq == 0) && ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) { wprintf("
"); @@ -1514,8 +1708,8 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers bq = 0; } wprintf(""); - url(buf); - msgescputs(buf); + url(buf, sizeof(buf)); + msgescputs1(buf); wprintf("
"); } wprintf("
"); @@ -1527,7 +1721,7 @@ void pullquote_message(long msgnum, int forward_attachments, int include_headers strcat(buf, "\n"); msgescputs(buf); } - } + }//// TODO: charset? utf8? /** Unknown weirdness ... don't know how to handle this content type */ else { @@ -1604,7 +1798,6 @@ ENDBODY: */ void display_summarized(int num) { char datebuf[64]; - wprintf("", WC->summ[num].msgnum, @@ -1613,7 +1806,8 @@ void display_summarized(int num) { ); wprintf("", SUBJ_COL_WIDTH_PCT); - escputs(WC->summ[num].subj); + + escputs(WC->summ[num].subj);//////////////////////////////////TODO: QP DECODE wprintf(""); wprintf("", SENDER_COL_WIDTH_PCT); @@ -1621,14 +1815,32 @@ void display_summarized(int num) { wprintf(""); wprintf("", DATE_PLUS_BUTTONS_WIDTH_PCT); - fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ + webcit_fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ escputs(datebuf); wprintf(""); wprintf("\n"); } - - +/** + * \brief Output a message row for the mobile view + * \param The row number + */ +void display_mobile_summary(int num) { + char datebuf[64]; + wprintf("\n
", + WC->summ[num].msgnum, + (WC->summ[num].is_new ? "bold" : "normal"), + WC->summ[num].msgnum + ); + wprintf("%s",WC->summ[num].from); + wprintf(""); + webcit_fmt_date(datebuf, WC->summ[num].date, 1); /* brief */ + escputs(datebuf); + wprintf("
"); + wprintf(WC->summ[num].subj); + wprintf("
",WC->summ[num].msgnum); +} /** * \brief display the adressbook overview @@ -1670,12 +1882,12 @@ void display_addressbook(long msgnum, char alpha) { } } - if (strlen(vcard_partnum) > 0) { + if (!IsEmptyStr(vcard_partnum)) { vcard_source = load_mimepart(msgnum, vcard_partnum); if (vcard_source != NULL) { /** Display the summary line */ - display_vcard(vcard_source, alpha, 0, NULL); + display_vcard(vcard_source, alpha, 0, NULL,msgnum); /** If it's my vCard I can edit it */ if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM)) @@ -1731,7 +1943,7 @@ void fetch_ab_name(long msgnum, char *namebuf) { int mime_length; char vcard_partnum[SIZ]; char *vcard_source = NULL; - int i; + int i, len; struct message_summary summ; if (namebuf == NULL) return; @@ -1761,12 +1973,12 @@ void fetch_ab_name(long msgnum, char *namebuf) { } } - if (strlen(vcard_partnum) > 0) { + if (!IsEmptyStr(vcard_partnum)) { vcard_source = load_mimepart(msgnum, vcard_partnum); if (vcard_source != NULL) { /* Grab the name off the card */ - display_vcard(vcard_source, 0, 0, namebuf); + display_vcard(vcard_source, 0, 0, namebuf,msgnum); free(vcard_source); } @@ -1774,7 +1986,8 @@ void fetch_ab_name(long msgnum, char *namebuf) { lastfirst_firstlast(namebuf); striplt(namebuf); - for (i=0; i (num_ab - 1)) tablast = (num_ab - 1); - nametab(tabfirst_label, addrbook[tabfirst].ab_name); - nametab(tablast_label, addrbook[tablast].ab_name); + nametab(tabfirst_label, 64, addrbook[tabfirst].ab_name); + nametab(tablast_label, 64, addrbook[tablast].ab_name); sprintf(this_tablabel, "%s - %s", tabfirst_label, tablast_label); tablabels[i] = strdup(this_tablabel); } @@ -1890,7 +2103,7 @@ void do_addrbook_view(struct addrbookent *addrbook, int num_ab) { wprintf("", bstr("alpha")); + wprintf("?maxmsgs=1?is_summary=0?alpha=%s\">", bstr("alpha")); vcard_n_prettyize(addrbook[i].ab_name); escputs(addrbook[i].ab_name); wprintf("\n"); @@ -1920,8 +2133,10 @@ int load_msg_ptrs(char *servcmd, int with_headers) char fullname[128]; char nodename[128]; char inetaddr[128]; - char subject[256]; + char subject[1024]; + char *ptr; int nummsgs; + int sbjlen; int maxload = 0; int num_summ_alloc = 0; @@ -1964,47 +2179,49 @@ int load_msg_ptrs(char *servcmd, int with_headers) 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 + if (!IsEmptyStr(subject)) { /** 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], "..."); + utf8ify_rfc822_string(subject); + safestrncpy(WC->summ[nummsgs-1].subj, subject, + sizeof WC->summ[nummsgs-1].subj); } + sbjlen = Ctdl_Utf8StrLen(WC->summ[nummsgs-1].subj); + if (sbjlen > 75) { + ptr = Ctdl_Utf8StrCut(WC->summ[nummsgs-1].subj, 72); - 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); - } + strcpy(ptr, "..."); } - WC->summ[nummsgs-1].date = datestamp; - -#ifdef HAVE_ICONV + if (!IsEmptyStr(fullname)) { /** 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], "..."); + utf8ify_rfc822_string(fullname); + safestrncpy(WC->summ[nummsgs-1].from, + fullname, sizeof WC->summ[nummsgs-1].from); + } + if ((!IsEmptyStr(nodename)) && + ( ((WC->room_flags & QR_NETWORK) + || ((strcasecmp(nodename, serv_info.serv_nodename) + && (strcasecmp(nodename, serv_info.serv_fqdn))))))) + { + strcat(WC->summ[nummsgs-1].from, " @ "); + strcat(WC->summ[nummsgs-1].from, nodename); + + } + sbjlen = Ctdl_Utf8StrLen(WC->summ[nummsgs-1].from); + if (sbjlen > 25) { + ptr = Ctdl_Utf8StrCut(WC->summ[nummsgs-1].from, 23); + strcpy(ptr, "..."); } + + WC->summ[nummsgs-1].date = datestamp; } } } return (nummsgs); } + +typedef int (*QSortFunction) (const void*, const void*); /** * \brief qsort() compatible function to compare two longs in descending order. * @@ -2121,6 +2338,84 @@ int summcmp_rdate(const void *s1, const void *s2) { } +enum { + eUp, + eDown, + eNone +}; + +const char* SortIcons[3] = { + "static/up_pointer.gif", + "static/down_pointer.gif", + "static/sort_none.gif" +}; + + enum {/// SortByEnum + eDate, + eRDate, + eSubject, + eRSubject, + eSender, + eRSender, + eReverse, + eUnSet +}; + +/** SortEnum to plain string representation */ +static const char* SortByStrings[] = { + "date", + "rdate", + "subject", + "rsubject", + "sender", + "rsender", + "reverse", + "unset" +}; + +/** SortEnum to sort-Function Table */ +const QSortFunction SortFuncs[eUnSet] = { + summcmp_date, + summcmp_rdate, + summcmp_subj, + summcmp_rsubj, + summcmp_sender, + summcmp_rsender, + summcmp_rdate +}; + +/** given a SortEnum, which icon should we choose? */ +const int SortDateToIcon[eUnSet] = { eUp, eDown, eNone, eNone, eNone, eNone, eNone}; +const int SortSubjectToIcon[eUnSet] = { eNone, eNone, eUp, eDown, eNone, eNone, eNone}; +const int SortSenderToIcon[eUnSet] = { eNone, eNone, eNone, eNone, eUp, eDown, eNone}; + +/** given a SortEnum, which would be the "opposite" search option? */ +const int DateInvertSortString[eUnSet] = { eRDate, eDate, eDate, eDate, eDate, eDate, eDate}; +const int SubjectInvertSortString[eUnSet] = { eSubject, eSubject, eRSubject, eUnSet, eSubject, eSubject, eSubject}; +const int SenderInvertSortString[eUnSet] = { eSender, eSender, eSender, eSender, eRSender, eUnSet, eSender}; + + +/** + * \Brief Translates sortoption String to its SortEnum representation + * \param SortBy string to translate + * \return the enum matching the string; defaults to RDate + */ +//SortByEnum +int StrToESort (StrBuf *sortby) +{ + int result = eDate; + + if (!IsEmptyStr(ChrPtr(sortby))) while (result < eUnSet){ + if (!strcasecmp(ChrPtr(sortby), + SortByStrings[result])) + return result; + result ++; + } + return eRDate; +} + + + /** * \brief command loop for reading messages @@ -2129,7 +2424,7 @@ int summcmp_rdate(const void *s1, const void *s2) { */ void readloop(char *oper) { - char cmd[256]; + char cmd[256] = ""; char buf[SIZ]; char old_msgs[SIZ]; int a, b; @@ -2150,39 +2445,41 @@ void readloop(char *oper) int highest_displayed = 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; + StrBuf *sortby = NULL; + //SortByEnum + int SortBy = eRDate; + StrBuf *sortpref_name; + StrBuf *sortpref_value; int bbs_reverse = 0; + struct wcsession *WCC = WC; /* This is done to make it run faster; WC is a function */ - if (WC->wc_view == VIEW_WIKI) { - sprintf(buf, "wiki?room=%s?page=home", WC->wc_roomname); + if (WCC->wc_view == VIEW_WIKI) { + sprintf(buf, "wiki?room=%s?page=home", WCC->wc_roomname); http_redirect(buf); return; } - startmsg = atol(bstr("startmsg")); - maxmsgs = atoi(bstr("maxmsgs")); - is_summary = atoi(bstr("summary")); + startmsg = lbstr("startmsg"); + maxmsgs = ibstr("maxmsgs"); + is_summary = (ibstr("is_summary") && !WCC->is_mobile); if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS; - snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname); - get_preference(sortpref_name, sortpref_value, sizeof sortpref_value); + sortpref_name = NewStrBuf (); + StrBufPrintf(sortpref_name, "sort %s", WCC->wc_roomname); + get_pref(sortpref_name, &sortpref_value); - sortby = bstr("sortby"); - if ( (strlen(sortby) > 0) && (strcasecmp(sortby, sortpref_value)) ) { - set_preference(sortpref_name, sortby, 1); + sortby = NewStrBufPlain(bstr("sortby"), -1); + if ( (!IsEmptyStr(ChrPtr(sortby))) && + (strcasecmp(ChrPtr(sortby), ChrPtr(sortpref_value)) != 0)) { + set_pref(sortpref_name, sortby, 1); + sortpref_value = NULL; + sortpref_value = sortby; } - if (strlen(sortby) == 0) sortby = sortpref_value; - - /** mailbox sort */ - if (strlen(sortby) == 0) sortby = "rdate"; - + + FreeStrBuf(&sortpref_name); + SortBy = StrToESort(sortpref_value); /** message board sort */ - if (!strcasecmp(sortby, "reverse")) { + if (SortBy == eReverse) { bbs_reverse = 1; } else { @@ -2202,26 +2499,26 @@ void readloop(char *oper) strcpy(cmd, "MSGS OLD"); } else if (!strcmp(oper, "do_search")) { - sprintf(cmd, "MSGS SEARCH|%s", bstr("query")); + snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); } else { strcpy(cmd, "MSGS ALL"); } - if ((WC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1)) { + if ((WCC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1) && !WCC->is_mobile) { is_summary = 1; if (!strcmp(oper, "do_search")) { - sprintf(cmd, "MSGS SEARCH|%s", bstr("query")); + snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); } else { strcpy(cmd, "MSGS ALL"); } } - if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) { + if ((WCC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) { is_addressbook = 1; if (!strcmp(oper, "do_search")) { - sprintf(cmd, "MSGS SEARCH|%s", bstr("query")); + snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query")); } else { strcpy(cmd, "MSGS ALL"); @@ -2229,13 +2526,17 @@ void readloop(char *oper) maxmsgs = 9999999; } - if (is_summary) { /**< fetch header summary */ - snprintf(cmd, sizeof cmd, "MSGS %s|%s||1", + if (is_summary || WCC->is_mobile) { /**< fetch header summary */ + snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1", (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"), (!strcmp(oper, "do_search") ? bstr("query") : "") ); startmsg = 1; maxmsgs = 9999999; + } + if (WCC->is_mobile) { + maxmsgs = 20; + SortBy = eRDate; } /** @@ -2244,7 +2545,7 @@ void readloop(char *oper) * new messages. */ strcpy(old_msgs, ""); - if (is_summary) { + if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){ serv_puts("GTSN"); serv_getln(buf, sizeof buf); if (buf[0] == '2') { @@ -2252,30 +2553,29 @@ void readloop(char *oper) } } - is_singlecard = atoi(bstr("is_singlecard")); + is_singlecard = ibstr("is_singlecard"); - if (WC->wc_default_view == VIEW_CALENDAR) { /**< calendar */ + if (WCC->wc_default_view == VIEW_CALENDAR) { /**< calendar */ is_calendar = 1; - strcpy(cmd, "MSGS ALL"); + strcpy(cmd, "MSGS ALL|||1"); maxmsgs = 32767; } - if (WC->wc_default_view == VIEW_TASKS) { /**< tasks */ + if (WCC->wc_default_view == VIEW_TASKS) { /**< tasks */ is_tasks = 1; strcpy(cmd, "MSGS ALL"); maxmsgs = 32767; } - if (WC->wc_default_view == VIEW_NOTES) { /**< notes */ + if (WCC->wc_default_view == VIEW_NOTES) { /**< notes */ is_notes = 1; strcpy(cmd, "MSGS ALL"); maxmsgs = 32767; } if (is_notes) { - wprintf("
%s
\n", _("Click on any note to edit it.")); wprintf("
\n"); } - nummsgs = load_msg_ptrs(cmd, is_summary); + nummsgs = load_msg_ptrs(cmd, (is_summary || WCC->is_mobile)); if (nummsgs == 0) { if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) { @@ -2293,15 +2593,15 @@ void readloop(char *oper) goto DONE; } - if (is_summary) { + if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){ for (a = 0; a < nummsgs; ++a) { /** Are you a new message, or an old message? */ if (is_summary) { - if (is_msg_in_mset(old_msgs, WC->msgarr[a])) { - WC->summ[a].is_new = 0; + if (is_msg_in_mset(old_msgs, WCC->msgarr[a])) { + WCC->summ[a].is_new = 0; } else { - WC->summ[a].is_new = 1; + WCC->summ[a].is_new = 1; } } } @@ -2309,69 +2609,18 @@ void readloop(char *oper) if (startmsg == 0L) { if (bbs_reverse) { - startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; + startmsg = WCC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0]; } else { - startmsg = WC->msgarr[0]; + startmsg = WCC->msgarr[0]; } } - 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 (is_summary || WCC->is_mobile) { + qsort(WCC->summ, WCC->num_summ, + sizeof(struct message_summary), SortFuncs[SortBy]); } - 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) { @@ -2389,9 +2638,9 @@ void readloop(char *oper) "" "" ); - wprintf("" - "" - "\n" + "\n" + "
%s %s%s %s%s %s" + wprintf("%s %s %s \n" " " "\n" , SUBJ_COL_WIDTH_PCT, - _("Subject"), subjsort_button, + _("Subject"), + SortByStrings[SubjectInvertSortString[SortBy]], + SortIcons[SortSubjectToIcon[SortBy]], SENDER_COL_WIDTH_PCT, - _("Sender"), sendsort_button, + _("Sender"), + SortByStrings[SenderInvertSortString[SortBy]], + SortIcons[SortSenderToIcon[SortBy]], DATE_PLUS_BUTTONS_WIDTH_PCT, - _("Date"), datesort_button, + _("Date"), + SortByStrings[DateInvertSortString[SortBy]], + SortIcons[SortDateToIcon[SortBy]], _("Delete") ); wprintf("
\n"); - wprintf("
" "
\n" - "" ); + } else if (WCC->is_mobile) { + wprintf("
"); } + + /** + * Set the "is_bbview" variable if it appears that we are looking at + * a classic bulletin board view. + */ + if ((!is_tasks) && (!is_calendar) && (!is_addressbook) + && (!is_notes) && (!is_singlecard) && (!is_summary)) { + is_bbview = 1; + } + + /** + * If we're not currently looking at ALL requested + * messages, then display the selector bar + */ + if (is_bbview) { + /** begin bbview scroller */ + wprintf("
\n

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

\n"); + /** end bbview scroller */ + } for (a = 0; a < nummsgs; ++a) { - if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) { + if ((WCC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) { /** Display the message */ if (is_summary) { display_summarized(a); } else if (is_addressbook) { - fetch_ab_name(WC->msgarr[a], buf); + fetch_ab_name(WCC->msgarr[a], buf); ++num_ab; addrbook = realloc(addrbook, (sizeof(struct addrbookent) * num_ab) ); safestrncpy(addrbook[num_ab-1].ab_name, buf, sizeof(addrbook[num_ab-1].ab_name)); - addrbook[num_ab-1].ab_msgnum = WC->msgarr[a]; + addrbook[num_ab-1].ab_msgnum = WCC->msgarr[a]; } else if (is_calendar) { - display_calendar(WC->msgarr[a]); + display_calendar(WCC->msgarr[a], WCC->summ[a].is_new); } else if (is_tasks) { - display_task(WC->msgarr[a]); + display_task(WCC->msgarr[a], WCC->summ[a].is_new); } else if (is_notes) { - display_note(WC->msgarr[a]); + display_note(WCC->msgarr[a], WCC->summ[a].is_new); + } else if (WCC->is_mobile) { + display_mobile_summary(a); } else { if (displayed_msgs == NULL) { displayed_msgs = malloc(sizeof(long) * (maxmsgsmsgarr[a]; + displayed_msgs[num_displayed] = WCC->msgarr[a]; } if (lowest_displayed < 0) lowest_displayed = a; @@ -2458,15 +2808,6 @@ void readloop(char *oper) } } - /** - * Set the "is_bbview" variable if it appears that we are looking at - * a classic bulletin board view. - */ - if ((!is_tasks) && (!is_calendar) && (!is_addressbook) - && (!is_notes) && (!is_singlecard) && (!is_summary)) { - is_bbview = 1; - } - /** Output loop */ if (displayed_msgs != NULL) { if (bbs_reverse) { @@ -2489,7 +2830,7 @@ void readloop(char *oper) 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("
" @@ -2498,6 +2839,8 @@ void readloop(char *oper) ); wprintf("
"); /**< The preview pane will initially be empty */ + } else if (WCC->is_mobile) { + wprintf("
"); } /** @@ -2513,8 +2856,8 @@ void readloop(char *oper) */ if (is_bbview) { /** begin bbview scroller */ - wprintf("
"); - wprintf(_("Reading #"), lowest_displayed, highest_displayed); + wprintf(" \n

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

\n"); + wprintf(">"); + wprintf(_("newest to oldest")); + wprintf("\n"); + + wprintf("

\n"); /** end bbview scroller */ } - + DONE: if (is_tasks) { do_tasks_view(); /** Render the task list */ @@ -2603,91 +2947,114 @@ DONE: } /** Note: wDumpContent() will output one additional
tag. */ + wprintf("\n"); /** end of 'content' div */ wDumpContent(1); - if (addrbook != NULL) free(addrbook); /** free the summary */ - if (WC->summ != NULL) { - free(WC->summ); - WC->num_summ = 0; - WC->summ = NULL; + if (WCC->summ != NULL) { + free(WCC->summ); + WCC->num_summ = 0; + WCC->summ = NULL; } -wprintf("\n"); /** end of 'content' div */ + if (addrbook != NULL) free(addrbook); } -/** - * \brief Back end for post_message() +/* + * Back end for post_message() * ... this is where the actual message gets transmitted to the server. */ void post_mime_to_server(void) { - char boundary[SIZ]; + char top_boundary[SIZ]; + char alt_boundary[SIZ]; int is_multipart = 0; static int seq = 0; struct wc_attachment *att; char *encoded; size_t encoded_length; + size_t encoded_strlen; + char *txtmail = NULL; + + sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x", + serv_info.serv_fqdn, + getpid(), + ++seq + ); + sprintf(alt_boundary, "Citadel--Multipart--%s--%04x--%04x", + serv_info.serv_fqdn, + getpid(), + ++seq + ); - /** RFC2045 requires this, and some clients look for it... */ + /* RFC2045 requires this, and some clients look for it... */ serv_puts("MIME-Version: 1.0"); - serv_puts("X-Mailer: " SERVER); + serv_puts("X-Mailer: " PACKAGE_STRING); - /** If there are attachments, we have to do multipart/mixed */ + /* If there are attachments, we have to do multipart/mixed */ if (WC->first_attachment != NULL) { is_multipart = 1; } if (is_multipart) { - sprintf(boundary, "Citadel--Multipart--%s--%04x--%04x", - serv_info.serv_fqdn, - getpid(), - ++seq - ); - - /** Remember, serv_printf() appends an extra newline */ - serv_printf("Content-type: multipart/mixed; " - "boundary=\"%s\"\n", boundary); + /* Remember, serv_printf() appends an extra newline */ + serv_printf("Content-type: multipart/mixed; boundary=\"%s\"\n", top_boundary); serv_printf("This is a multipart message in MIME format.\n"); - serv_printf("--%s", boundary); + serv_printf("--%s", top_boundary); } + /* Remember, serv_printf() appends an extra newline */ + serv_printf("Content-type: multipart/alternative; " + "boundary=\"%s\"\n", alt_boundary); + serv_printf("This is a multipart message in MIME format.\n"); + serv_printf("--%s", alt_boundary); + + serv_puts("Content-type: text/plain; charset=utf-8"); + serv_puts("Content-Transfer-Encoding: quoted-printable"); + serv_puts(""); + txtmail = html_to_ascii(bstr("msgtext"), 0, 80, 0); + text_to_server_qp(txtmail); /* Transmit message in quoted-printable encoding */ + free(txtmail); + + serv_printf("--%s", alt_boundary); + serv_puts("Content-type: text/html; charset=utf-8"); serv_puts("Content-Transfer-Encoding: quoted-printable"); serv_puts(""); serv_puts("\r\n"); - text_to_server_qp(bstr("msgtext")); /** Transmit message in quoted-printable encoding */ + text_to_server_qp(bstr("msgtext")); /* Transmit message in quoted-printable encoding */ serv_puts("\r\n"); + + serv_printf("--%s--", alt_boundary); if (is_multipart) { - /** Add in the attachments */ + /* Add in the attachments */ for (att = WC->first_attachment; att!=NULL; att=att->next) { encoded_length = ((att->length * 150) / 100); encoded = malloc(encoded_length); if (encoded == NULL) break; - CtdlEncodeBase64(encoded, att->data, att->length, 1); + encoded_strlen = CtdlEncodeBase64(encoded, att->data, att->length, 1); - serv_printf("--%s", boundary); + serv_printf("--%s", top_boundary); serv_printf("Content-type: %s", att->content_type); - serv_printf("Content-disposition: attachment; " - "filename=\"%s\"", att->filename); + serv_printf("Content-disposition: attachment; filename=\"%s\"", att->filename); serv_puts("Content-transfer-encoding: base64"); serv_puts(""); - serv_write(encoded, strlen(encoded)); + serv_write(encoded, encoded_strlen); serv_puts(""); serv_puts(""); free(encoded); } - serv_printf("--%s--", boundary); + serv_printf("--%s--", top_boundary); } serv_puts("000"); } -/** - * \brief Post message (or don't post message) +/* + * Post message (or don't post message) * * Note regarding the "dont_post" variable: * A random value (actually, it's just a timestamp) is inserted as a hidden @@ -2699,36 +3066,52 @@ void post_mime_to_server(void) { */ void post_message(void) { + urlcontent *u; + void *U; char buf[1024]; - char encoded_subject[1024]; + char *encoded_subject = NULL; static long dont_post = (-1L); struct wc_attachment *att, *aptr; int is_anonymous = 0; - char *display_name; + const char *display_name; + long dpLen = 0; + struct wcsession *WCC = WC; + char *ptr = NULL; + + if (havebstr("force_room")) { + gotoroom(bstr("force_room")); + } - display_name = bstr("display_name"); + if (GetHash(WC->urlstrings, HKEY("display_name"), &U)) { + u = (urlcontent*) U; + display_name = u->url_data; + dpLen = u->url_data_size; + } + else { + display_name=""; + } if (!strcmp(display_name, "__ANONYMOUS__")) { display_name = ""; is_anonymous = 1; } - if (WC->upload_length > 0) { + if (WCC->upload_length > 0) { - lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WC->upload_length); + lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length); /** There's an attachment. Save it to this struct... */ att = malloc(sizeof(struct wc_attachment)); memset(att, 0, sizeof(struct wc_attachment)); - att->length = WC->upload_length; - strcpy(att->content_type, WC->upload_content_type); - strcpy(att->filename, WC->upload_filename); + att->length = WCC->upload_length; + strcpy(att->content_type, WCC->upload_content_type); + strcpy(att->filename, WCC->upload_filename); att->next = NULL; /** And add it to the list. */ - if (WC->first_attachment == NULL) { - WC->first_attachment = att; + if (WCC->first_attachment == NULL) { + WCC->first_attachment = att; } else { - aptr = WC->first_attachment; + aptr = WCC->first_attachment; while (aptr->next != NULL) aptr = aptr->next; aptr->next = att; } @@ -2749,49 +3132,102 @@ void post_message(void) * Transfer control of this memory from the upload struct * to the attachment struct. */ - att->data = WC->upload; - WC->upload_length = 0; - WC->upload = NULL; + att->data = WCC->upload; + WCC->upload_length = 0; + WCC->upload = NULL; display_enter(); return; } - if (strlen(bstr("cancel_button")) > 0) { - sprintf(WC->ImportantMessage, + if (havebstr("cancel_button")) { + sprintf(WCC->ImportantMessage, _("Cancelled. Message was not posted.")); - } else if (strlen(bstr("attach_button")) > 0) { + } else if (havebstr("attach_button")) { display_enter(); return; - } else if (atol(bstr("postseq")) == dont_post) { - sprintf(WC->ImportantMessage, + } else if (lbstr("postseq") == dont_post) { + sprintf(WCC->ImportantMessage, _("Automatically cancelled because you have already " "saved this message.")); } else { - rfc2047encode(encoded_subject, sizeof encoded_subject, bstr("subject")); - sprintf(buf, "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s", - bstr("recp"), + const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s"; + const char *Recp = ""; + const char *Cc = ""; + const char *Bcc = ""; + const char *Wikipage = ""; + const char *my_email_addr = ""; + char *CmdBuf = NULL;; + long len = 0; + size_t nLen; + char references[SIZ] = ""; + size_t references_len = 0; + + safestrncpy(references, bstr("references"), sizeof references); + lprintf(9, "Converting: %s\n", references); + for (ptr=references; *ptr != 0; ++ptr) { + if (*ptr == '|') *ptr = '!'; + ++references_len; + } + lprintf(9, "Converted: %s\n", references); + + if (havebstr("subject")) { + char *Subj; + size_t SLen; + /* + * make enough room for the encoded string; + * plus the QP header + */ + Subj = xbstr("subject", &SLen); + len = SLen * 3 + 32; + encoded_subject = malloc (len); + len = webcit_rfc2047encode(encoded_subject, len, Subj, SLen); + if (len < 0) { + free (encoded_subject); + return; + } + } + len += sizeof (CMD) + dpLen; + Recp = xbstr("recp", &nLen); + len += nLen; + Cc = xbstr("cc", &nLen); + len += nLen; + Bcc = xbstr("bcc", &nLen); + len += nLen; + Wikipage = xbstr("wikipage", &nLen); + len += nLen; + my_email_addr = xbstr("my_email_addr", &nLen); + len += nLen; + len += references_len; + + CmdBuf = (char*) malloc (len + 11); + + snprintf(CmdBuf, len + 1, CMD, + Recp, is_anonymous, - encoded_subject, + (encoded_subject ? encoded_subject : ""), display_name, - bstr("cc"), - bstr("bcc"), - bstr("wikipage"), - bstr("my_email_addr") - ); - serv_puts(buf); + Cc, + Bcc, + Wikipage, + my_email_addr, + references); + lprintf(9, "%s\n", CmdBuf); + serv_puts(CmdBuf); serv_getln(buf, sizeof buf); + free (CmdBuf); + if (encoded_subject) free(encoded_subject); if (buf[0] == '4') { post_mime_to_server(); - if ( (strlen(bstr("recp")) > 0) - || (strlen(bstr("cc")) > 0) - || (strlen(bstr("bcc")) > 0) + if ( (havebstr("recp")) + || (havebstr("cc" )) + || (havebstr("bcc" )) ) { - sprintf(WC->ImportantMessage, _("Message has been sent.\n")); + sprintf(WCC->ImportantMessage, _("Message has been sent.\n")); } else { sprintf(WC->ImportantMessage, _("Message has been posted.\n")); } - dont_post = atol(bstr("postseq")); + dont_post = lbstr("postseq"); } else { lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, buf); sprintf(WC->ImportantMessage, "%s", &buf[4]); @@ -2800,19 +3236,19 @@ void post_message(void) } } - free_attachments(WC); + free_attachments(WCC); /** * We may have been supplied with instructions regarding the location * to which we must return after posting. If found, go there. */ - if (strlen(bstr("return_to")) > 0) { + if (havebstr("return_to")) { http_redirect(bstr("return_to")); } /** * If we were editing a page in a wiki room, go to that page now. */ - else if (strlen(bstr("wikipage")) > 0) { + else if (havebstr("wikipage")) { snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage")); http_redirect(buf); } @@ -2833,26 +3269,28 @@ void post_message(void) void display_enter(void) { char buf[SIZ]; - char ebuf[SIZ]; + StrBuf *ebuf; long now; char *display_name; struct wc_attachment *att; int recipient_required = 0; int subject_required = 0; int recipient_bad = 0; - int i; int is_anonymous = 0; long existing_page = (-1L); + size_t dplen; + struct wcsession *WCC = WC; now = time(NULL); - if (strlen(bstr("force_room")) > 0) { + if (havebstr("force_room")) { gotoroom(bstr("force_room")); } - display_name = bstr("display_name"); + display_name = xbstr("display_name", &dplen); if (!strcmp(display_name, "__ANONYMOUS__")) { display_name = ""; + dplen = 0; is_anonymous = 1; } @@ -2864,47 +3302,44 @@ void display_enter(void) recipient_required = 1; } else if (buf[0] != '2') { /** Any other error means that we cannot continue */ - sprintf(WC->ImportantMessage, "%s", &buf[4]); + sprintf(WCC->ImportantMessage, "%s", &buf[4]); readloop("readnew"); return; } - if ((buf[3] != '\0') && - (buf[4] != '\0') && - !strncmp(&(buf[5]), "SUBJECTREQ", 10)) { - subject_required = 1; + /* Is the server strongly recommending that the user enter a message subject? */ + if ((buf[3] != '\0') && (buf[4] != '\0')) { + subject_required = extract_int(&buf[4], 1); } /** * Are we perhaps in an address book view? If so, then an "enter * message" command really means "add new entry." */ - if (WC->wc_default_view == VIEW_ADDRESSBOOK) { - do_edit_vcard(-1, "", ""); + if (WCC->wc_default_view == VIEW_ADDRESSBOOK) { + do_edit_vcard(-1, "", "", WCC->wc_roomname); return; } -#ifdef WEBCIT_WITH_CALENDAR_SERVICE - /** + /* * Are we perhaps in a calendar room? If so, then an "enter * message" command really means "add new calendar item." */ - if (WC->wc_default_view == VIEW_CALENDAR) { + if (WCC->wc_default_view == VIEW_CALENDAR) { display_edit_event(); return; } - /** + /* * Are we perhaps in a tasks view? If so, then an "enter * message" command really means "add new task." */ - if (WC->wc_default_view == VIEW_TASKS) { + if (WCC->wc_default_view == VIEW_TASKS) { display_edit_task(); return; } -#endif - /** + /* * Otherwise proceed normally. * Do a custom room banner with no navbar... */ @@ -2915,18 +3350,41 @@ void display_enter(void) wprintf("
\n" "
"); - /** Now check our actual recipients if there are any */ + /* Now check our actual recipients if there are any */ if (recipient_required) { - sprintf(buf, "ENT0 0|%s|%d|0||%s||%s|%s|%s", - bstr("recp"), - is_anonymous, - display_name, - bstr("cc"), bstr("bcc"), bstr("wikipage")); - serv_puts(buf); + const char *Recp = ""; + const char *Cc = ""; + const char *Bcc = ""; + const char *Wikipage = ""; + char *CmdBuf = NULL;; + size_t len = 0; + size_t nLen; + const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s"; + + len = sizeof(CMD) + dplen; + Recp = xbstr("recp", &nLen); + len += nLen; + Cc = xbstr("cc", &nLen); + len += nLen; + Bcc = xbstr("bcc", &nLen); + len += nLen; + Wikipage = xbstr("wikipage", &nLen); + len += nLen; + + + CmdBuf = (char*) malloc (len + 1); + + snprintf(CmdBuf, len, CMD, + Recp, is_anonymous, + display_name, + Cc, Bcc, Wikipage); + serv_puts(CmdBuf); 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) { + if (havebstr("recp") && + havebstr("cc" ) && + havebstr("bcc" )) { recipient_bad = 1; } } @@ -2938,7 +3396,7 @@ void display_enter(void) /** If we got this far, we can display the message entry screen. */ - /** begin message entry screen */ + /* begin message entry screen */ wprintf("
\n"); wprintf("\n", now); - if (WC->wc_view == VIEW_WIKI) { + if (WCC->wc_view == VIEW_WIKI) { wprintf("\n", bstr("wikipage")); } wprintf("\n", bstr("return_to")); + wprintf("\n", WCC->nonce); + wprintf("wc_roomname); + wprintf("\">\n"); + wprintf("\n"); + + /** submit or cancel buttons */ + wprintf("

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

"); /** header bar */ wprintf(""); wprintf(" "); /** header bar */ - fmt_date(buf, now, 0); + webcit_fmt_date(buf, now, 0); wprintf("%s", buf); wprintf("\n"); /** header bar */ - wprintf("
"); - wprintf("
\n"); + wprintf("
\n"); /* end of "fix_scrollbar_bug" div */ + /* NOTE: address_book_popup() will close the "content" div. Don't close it here. */ DONE: address_book_popup(); wDumpContent(1); } @@ -3202,7 +3706,7 @@ void delete_msg(void) long msgid; char buf[SIZ]; - msgid = atol(bstr("msgid")); + msgid = lbstr("msgid"); if (WC->wc_is_trash) { /** Delete from Trash is a real delete */ serv_printf("DELE %ld", msgid); @@ -3226,9 +3730,9 @@ void move_msg(void) long msgid; char buf[SIZ]; - msgid = atol(bstr("msgid")); + msgid = lbstr("msgid"); - if (strlen(bstr("move_button")) > 0) { + if (havebstr("move_button")) { sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room")); serv_puts(buf); serv_getln(buf, sizeof buf); @@ -3244,8 +3748,8 @@ void move_msg(void) -/** - * \brief Confirm move of a message +/* + * Confirm move of a message */ void confirm_move_msg(void) { @@ -3253,17 +3757,17 @@ void confirm_move_msg(void) char buf[SIZ]; char targ[SIZ]; - msgid = atol(bstr("msgid")); + msgid = lbstr("msgid"); output_headers(1, 1, 2, 0, 0, 0); wprintf("
\n"); - wprintf("
"); - wprintf(""); + wprintf("

"); wprintf(_("Confirm move of message")); - wprintf("\n"); - wprintf("

\n"); - wprintf("
\n
\n"); + wprintf(""); + wprintf("
\n"); + + wprintf("
\n"); wprintf("
"); @@ -3271,6 +3775,7 @@ void confirm_move_msg(void) wprintf("
\n"); wprintf("
\n"); + wprintf("\n", WC->nonce); wprintf("\n", bstr("msgid")); wprintf("