4 * Functions which deal with the fetching and displaying of messages.
10 #include "webserver.h"
14 /* Address book entry (keep it short and sweet, it's just a quickie lookup
15 * which we can use to get to the real meat and bones later)
25 /* Handle subjects with RFC2047 encoding, such as:
26 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
28 void utf8ify_rfc822_string(char *buf) {
34 iconv_t ic = (iconv_t)(-1) ;
35 char *ibuf; /* Buffer of characters to be converted */
36 char *obuf; /* Buffer for converted characters */
37 size_t ibuflen; /* Length of input buffer */
38 size_t obuflen; /* Length of output buffer */
39 char *isav; /* Saved pointer to input buffer */
40 char *osav; /* Saved pointer to output buffer */
42 while (start=strstr(buf, "=?"), end=strstr(buf, "?="),
43 ((start != NULL) && (end != NULL) && (end > start)) )
45 extract_token(charset, start, 1, '?', sizeof charset);
46 extract_token(encoding, start, 2, '?', sizeof encoding);
47 extract_token(istr, start, 3, '?', sizeof istr);
55 if (!strcasecmp(encoding, "B")) { /* base64 */
56 ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
58 else if (!strcasecmp(encoding, "Q")) { /* quoted-printable */
59 ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, strlen(istr));
62 strcpy(ibuf, istr); /* huh? */
63 ibuflen = strlen(istr);
66 ic = iconv_open("UTF-8", charset);
67 if (ic != (iconv_t)(-1) ) {
70 obuf = (char *) malloc(obuflen);
72 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
73 osav[1024-obuflen] = 0;
78 remove_token(end, 0, '?');
79 remove_token(end, 0, '?');
80 remove_token(end, 0, '?');
81 remove_token(end, 0, '?');
84 snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
90 snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
102 * Look for URL's embedded in a buffer and make them linkable. We use a
103 * target window in order to keep the BBS session in its own window.
119 for (pos = 0; pos < strlen(buf); ++pos) {
120 if (!strncasecmp(&buf[pos], "http://", 7))
122 if (!strncasecmp(&buf[pos], "ftp://", 6))
129 if ((start > 0) && (buf[start - 1] == '<'))
131 if ((start > 0) && (buf[start - 1] == '['))
133 if ((start > 0) && (buf[start - 1] == '('))
135 if ((start > 0) && (buf[start - 1] == '{'))
138 for (pos = strlen(buf); pos > start; --pos) {
139 if ((buf[pos] == ' ') || (buf[pos] == ench))
143 strncpy(urlbuf, &buf[start], end - start);
144 urlbuf[end - start] = 0;
146 strncpy(outbuf, buf, start);
147 sprintf(&outbuf[start], "%cA HREF=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
148 LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
149 strcat(outbuf, &buf[end]);
150 if ( strlen(outbuf) < 250 )
156 * Turn a vCard "n" (name) field into something displayable.
158 void vcard_n_prettyize(char *name)
163 original_name = strdup(name);
164 for (i=0; i<5; ++i) {
165 if (strlen(original_name) > 0) {
166 if (original_name[strlen(original_name)-1] == ' ') {
167 original_name[strlen(original_name)-1] = 0;
169 if (original_name[strlen(original_name)-1] == ';') {
170 original_name[strlen(original_name)-1] = 0;
175 for (i=0; i<strlen(original_name); ++i) {
176 if (original_name[i] == ';') {
180 name[strlen(name)+1] = 0;
181 name[strlen(name)] = original_name[i];
190 /* display_vcard() calls this after parsing the textual vCard into
191 * our 'struct vCard' data object.
192 * This gets called instead of display_parsed_vcard() if we are only looking
193 * to extract the person's name instead of displaying the card.
195 void fetchname_parsed_vcard(struct vCard *v, char *storename) {
198 strcpy(storename, "");
200 name = vcard_get_prop(v, "n", 1, 0, 0);
202 strcpy(storename, name);
203 /* vcard_n_prettyize(storename); */
210 /* display_vcard() calls this after parsing the textual vCard into
211 * our 'struct vCard' data object.
213 * Set 'full' to nonzero to display the full card, otherwise it will only
214 * show a summary line.
216 * This code is a bit ugly, so perhaps an explanation is due: we do this
217 * in two passes through the vCard fields. On the first pass, we process
218 * fields we understand, and then render them in a pretty fashion at the
219 * end. Then we make a second pass, outputting all the fields we don't
220 * understand in a simple two-column name/value format.
222 void display_parsed_vcard(struct vCard *v, int full) {
228 char *thisname, *thisvalue;
229 char firsttoken[SIZ];
232 char displayname[SIZ];
238 strcpy(displayname, "");
246 name = vcard_get_prop(v, "fn", 1, 0, 0);
250 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
251 strcpy(displayname, name);
252 vcard_n_prettyize(displayname);
253 escputs(displayname);
262 wprintf("<div align=center><table bgcolor=#aaaaaa width=50%%>");
263 for (pass=1; pass<=2; ++pass) {
265 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
267 thisname = strdup(v->prop[i].name);
268 extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
270 for (j=0; j<num_tokens(thisname, ';'); ++j) {
271 extract_token(buf, thisname, j, ';', sizeof buf);
272 if (!strcasecmp(buf, "encoding=quoted-printable")) {
274 remove_token(thisname, j, ';');
276 if (!strcasecmp(buf, "encoding=base64")) {
278 remove_token(thisname, j, ';');
283 thisvalue = malloc(strlen(v->prop[i].value) + 50);
284 j = CtdlDecodeQuotedPrintable(
285 thisvalue, v->prop[i].value,
286 strlen(v->prop[i].value) );
290 thisvalue = malloc(strlen(v->prop[i].value) + 50);
292 thisvalue, v->prop[i].value,
293 strlen(v->prop[i].value) );
296 thisvalue = strdup(v->prop[i].value);
299 /*** Various fields we may encounter ***/
301 /* N is name, but only if there's no FN already there */
302 if (!strcasecmp(firsttoken, "n")) {
303 if (strlen(displayname) == 0) {
304 strcpy(displayname, thisvalue);
305 vcard_n_prettyize(displayname);
309 /* FN (full name) is a true 'display name' field */
310 else if (!strcasecmp(firsttoken, "fn")) {
311 strcpy(displayname, thisvalue);
315 else if (!strcasecmp(firsttoken, "title")) {
316 strcpy(title, thisvalue);
320 else if (!strcasecmp(firsttoken, "org")) {
321 strcpy(org, thisvalue);
324 else if (!strcasecmp(firsttoken, "email")) {
325 if (strlen(mailto) > 0) strcat(mailto, "<br />");
327 "<A HREF=\"/display_enter"
328 "?force_room=_MAIL_?recp=");
330 urlesc(&mailto[strlen(mailto)], displayname);
331 urlesc(&mailto[strlen(mailto)], " <");
332 urlesc(&mailto[strlen(mailto)], thisvalue);
333 urlesc(&mailto[strlen(mailto)], ">");
335 strcat(mailto, "\">");
336 stresc(&mailto[strlen(mailto)], thisvalue, 1, 1);
337 strcat(mailto, "</A>");
339 else if (!strcasecmp(firsttoken, "tel")) {
340 if (strlen(phone) > 0) strcat(phone, "<br />");
341 strcat(phone, thisvalue);
342 for (j=0; j<num_tokens(thisname, ';'); ++j) {
343 extract_token(buf, thisname, j, ';', sizeof buf);
344 if (!strcasecmp(buf, "tel"))
346 else if (!strcasecmp(buf, "work"))
347 strcat(phone, _(" (work)"));
348 else if (!strcasecmp(buf, "home"))
349 strcat(phone, _(" (home)"));
350 else if (!strcasecmp(buf, "cell"))
351 strcat(phone, _(" (cell)"));
359 else if (!strcasecmp(firsttoken, "adr")) {
362 wprintf(_("Address:"));
363 wprintf("</TD><TD>");
364 for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
365 extract_token(buf, thisvalue, j, ';', sizeof buf);
366 if (strlen(buf) > 0) {
368 if (j<3) wprintf("<br />");
372 wprintf("</TD></TR>\n");
375 else if (!strcasecmp(firsttoken, "version")) {
378 else if (!strcasecmp(firsttoken, "rev")) {
381 else if (!strcasecmp(firsttoken, "label")) {
386 /*** Don't show extra fields. They're ugly.
390 wprintf("</TD><TD>");
392 wprintf("</TD></TR>\n");
402 wprintf("<TR BGCOLOR=\"#AAAAAA\">"
403 "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
404 "<IMG ALIGN=CENTER SRC=\"/static/viewcontacts_48x.gif\">"
405 "<FONT SIZE=+1><B>");
406 escputs(displayname);
407 wprintf("</B></FONT>");
408 if (strlen(title) > 0) {
409 wprintf("<div align=right>");
413 if (strlen(org) > 0) {
414 wprintf("<div align=right>");
418 wprintf("</TD></TR>\n");
420 if (strlen(phone) > 0) {
422 wprintf(_("Telephone:"));
423 wprintf("</td><td>%s</td></tr>\n", phone);
425 if (strlen(mailto) > 0) {
427 wprintf(_("E-mail:"));
428 wprintf("</td><td>%s</td></tr>\n", mailto);
434 wprintf("</table></div>\n");
440 * Display a textual vCard
441 * (Converts to a vCard object and then calls the actual display function)
442 * Set 'full' to nonzero to display the whole card instead of a one-liner.
443 * Or, if "storename" is non-NULL, just store the person's name in that
444 * buffer instead of displaying the card at all.
446 void display_vcard(char *vcard_source, char alpha, int full, char *storename) {
452 v = vcard_load(vcard_source);
453 if (v == NULL) return;
455 name = vcard_get_prop(v, "n", 1, 0, 0);
461 if (storename != NULL) {
462 fetchname_parsed_vcard(v, storename);
464 else if ( (alpha == 0)
465 || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
466 || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
468 display_parsed_vcard(v, full);
476 * I wanna SEE that message!
478 void read_message(long msgnum, int printable_view) {
480 char mime_partnum[256];
481 char mime_filename[256];
482 char mime_content_type[256];
483 char mime_charset[256];
484 char mime_disposition[256];
493 char reply_all[4096];
499 char vcard_partnum[256];
500 char cal_partnum[256];
501 char *part_source = NULL;
503 iconv_t ic = (iconv_t)(-1) ;
504 char *ibuf; /* Buffer of characters to be converted */
505 char *obuf; /* Buffer for converted characters */
506 size_t ibuflen; /* Length of input buffer */
507 size_t obuflen; /* Length of output buffer */
508 char *osav; /* Saved pointer to output buffer */
514 strcpy(reply_to, "");
515 strcpy(reply_all, "");
516 strcpy(vcard_partnum, "");
517 strcpy(cal_partnum, "");
518 strcpy(mime_http, "");
519 strcpy(mime_content_type, "text/plain");
520 strcpy(mime_charset, "us-ascii");
522 serv_printf("MSG4 %ld", msgnum);
523 serv_getln(buf, sizeof buf);
526 wprintf(_("ERROR:"));
527 wprintf("</STRONG> %s<br />\n", &buf[4]);
531 /* begin everythingamundo table */
532 if (!printable_view) {
533 wprintf("<div id=\"fix_scrollbar_bug\">\n");
534 wprintf("<table width=100%% border=1 cellspacing=0 "
535 "cellpadding=0><TR><TD>\n");
538 /* begin message header table */
539 wprintf("<TABLE WIDTH=100%% BORDER=0 CELLSPACING=0 "
540 "CELLPADDING=1 BGCOLOR=\"#CCCCCC\"><TR><TD>\n");
542 wprintf("<SPAN CLASS=\"message_header\">");
543 strcpy(m_subject, "");
546 while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
547 if (!strcmp(buf, "000")) {
549 wprintf(_("unexpected end of message"));
550 wprintf("</I><br /><br />\n");
551 wprintf("</SPAN>\n");
554 if (!strncasecmp(buf, "nhdr=yes", 8))
558 if (!strncasecmp(buf, "type=", 5))
559 format_type = atoi(&buf[5]);
560 if (!strncasecmp(buf, "from=", 5)) {
561 strcpy(from, &buf[5]);
563 wprintf("<A HREF=\"/showuser?who=");
565 utf8ify_rfc822_string(from);
572 if (!strncasecmp(buf, "subj=", 5)) {
573 safestrncpy(m_subject, &buf[5], sizeof m_subject);
575 if (!strncasecmp(buf, "cccc=", 5)) {
576 safestrncpy(m_cc, &buf[5], sizeof m_cc);
577 if (strlen(reply_all) > 0) {
578 strcat(reply_all, ", ");
580 safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
581 (sizeof reply_all - strlen(reply_all)) );
583 if ((!strncasecmp(buf, "hnod=", 5))
584 && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
585 wprintf("(%s) ", &buf[5]);
587 if ((!strncasecmp(buf, "room=", 5))
588 && (strcasecmp(&buf[5], WC->wc_roomname))
589 && (strlen(&buf[5])>0) ) {
591 wprintf("%s> ", &buf[5]);
593 if (!strncasecmp(buf, "rfca=", 5)) {
594 strcpy(rfca, &buf[5]);
600 if (!strncasecmp(buf, "node=", 5)) {
601 strcpy(node, &buf[5]);
602 if ( ((WC->room_flags & QR_NETWORK)
603 || ((strcasecmp(&buf[5], serv_info.serv_nodename)
604 && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
607 wprintf("@%s ", &buf[5]);
610 if (!strncasecmp(buf, "rcpt=", 5)) {
614 if (strlen(reply_all) > 0) {
615 strcat(reply_all, ", ");
617 safestrncpy(&reply_all[strlen(reply_all)], &buf[5],
618 (sizeof reply_all - strlen(reply_all)) );
620 if (!strncasecmp(buf, "time=", 5)) {
621 fmt_date(now, atol(&buf[5]), 0);
625 if (!strncasecmp(buf, "part=", 5)) {
626 extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
627 extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
628 extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
629 extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
630 mime_length = extract_int(&buf[5], 5);
632 if ((!strcasecmp(mime_disposition, "inline"))
633 && (!strncasecmp(mime_content_type, "image/", 6)) ){
634 snprintf(&mime_http[strlen(mime_http)],
635 (sizeof(mime_http) - strlen(mime_http) - 1),
636 "<IMG SRC=\"/mimepart/%ld/%s/%s\">",
637 msgnum, mime_partnum, mime_filename);
639 else if ( (!strcasecmp(mime_disposition, "attachment"))
640 || (!strcasecmp(mime_disposition, "inline")) ) {
641 snprintf(&mime_http[strlen(mime_http)],
642 (sizeof(mime_http) - strlen(mime_http) - 1),
643 "<A HREF=\"/mimepart/%ld/%s/%s\" "
644 "TARGET=\"wc.%ld.%s\">"
645 "<IMG SRC=\"/static/diskette_24x.gif\" "
646 "BORDER=0 ALIGN=MIDDLE>\n"
647 "Part %s: %s (%s, %d bytes)</A><br />\n",
648 msgnum, mime_partnum, mime_filename,
649 msgnum, mime_partnum,
650 mime_partnum, mime_filename,
651 mime_content_type, mime_length);
654 /*** begin handler prep ***/
655 if (!strcasecmp(mime_content_type, "text/x-vcard")) {
656 strcpy(vcard_partnum, mime_partnum);
659 if (!strcasecmp(mime_content_type, "text/calendar")) {
660 strcpy(cal_partnum, mime_partnum);
663 /*** end handler prep ***/
669 /* Generate a reply-to address */
670 if (strlen(rfca) > 0) {
671 strcpy(reply_to, rfca);
674 if ( (strlen(node) > 0)
675 && (strcasecmp(node, serv_info.serv_nodename))
676 && (strcasecmp(node, serv_info.serv_humannode)) ) {
677 snprintf(reply_to, sizeof(reply_to), "%s @ %s",
681 snprintf(reply_to, sizeof(reply_to), "%s", from);
691 utf8ify_rfc822_string(m_cc);
692 utf8ify_rfc822_string(m_subject);
694 if (strlen(m_cc) > 0) {
696 "<SPAN CLASS=\"message_subject\">");
702 if (strlen(m_subject) > 0) {
704 "<SPAN CLASS=\"message_subject\">");
705 wprintf(_("Subject:"));
712 /* start msg buttons */
713 if (!printable_view) {
714 wprintf("<td align=right>\n");
717 if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
718 wprintf("<a href=\"/display_enter");
719 if (WC->is_mailbox) {
720 wprintf("?replyquote=%ld", msgnum);
723 urlescputs(reply_to);
724 wprintf("?subject=");
725 if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
726 urlescputs(m_subject);
727 wprintf("\">[%s]</a> ", _("Reply"));
731 if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
732 if (!WC->is_mailbox) {
733 wprintf("<a href=\"/display_enter");
734 wprintf("?replyquote=%ld", msgnum);
736 urlescputs(reply_to);
737 wprintf("?subject=");
738 if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
739 urlescputs(m_subject);
740 wprintf("\">[%s]</a> ", _("ReplyQuoted"));
745 if (WC->wc_view == VIEW_MAILBOX) {
746 wprintf("<a href=\"/display_enter");
747 wprintf("?replyquote=%ld", msgnum);
749 urlescputs(reply_to);
751 urlescputs(reply_all);
752 wprintf("?subject=");
753 if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
754 urlescputs(m_subject);
755 wprintf("\">[%s]</a> ", _("ReplyAll"));
759 if (WC->wc_view == VIEW_MAILBOX) {
760 wprintf("<a href=\"/display_enter?fwdquote=%ld?subject=", msgnum);
761 if (strncasecmp(m_subject, "Fwd:", 4)) wprintf("Fwd:%20");
762 urlescputs(m_subject);
763 wprintf("\">[%s]</a> ", _("Forward"));
766 /* If this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */
767 if ( (WC->is_room_aide) || (WC->is_mailbox) ) {
769 wprintf("<a href=\"/confirm_move_msg?msgid=%ld\">[%s]</a> ",
773 wprintf("<a href=\"/delete_msg?msgid=%ld\" "
774 "onClick=\"return confirm('%s');\">"
775 "[%s]</a> ", msgnum, _("Delete this message?"), _("Delete")
779 wprintf("<a href=\"#\" onClick=\"window.open('/printmsg?msgnum=%ld', 'print%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
780 "[%s]</a>", msgnum, msgnum, _("Print"));
785 wprintf("</TR></TABLE>\n");
788 wprintf("<TABLE BORDER=0 WIDTH=100%% BGCOLOR=#FFFFFF "
789 "CELLPADDING=1 CELLSPACING=0><TR><TD>");
792 * Learn the content type
794 strcpy(mime_content_type, "text/plain");
795 while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
796 if (!strcmp(buf, "000")) {
798 wprintf(_("unexpected end of message"));
799 wprintf("</I><br /><br />\n");
802 if (!strncasecmp(buf, "Content-type: ", 14)) {
803 safestrncpy(mime_content_type, &buf[14],
804 sizeof(mime_content_type));
805 for (i=0; i<strlen(mime_content_type); ++i) {
806 if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
807 safestrncpy(mime_charset, &mime_content_type[i+8],
808 sizeof mime_charset);
811 for (i=0; i<strlen(mime_content_type); ++i) {
812 if (mime_content_type[i] == ';') {
813 mime_content_type[i] = 0;
819 /* Set up a character set conversion if we need to (and if we can) */
821 if ( (strcasecmp(mime_charset, "us-ascii"))
822 && (strcasecmp(mime_charset, "UTF-8")) ) {
823 ic = iconv_open("UTF-8", mime_charset);
824 if (ic == (iconv_t)(-1) ) {
825 lprintf(5, "iconv_open() failed: %s\n", strerror(errno));
830 /* Messages in legacy Citadel variformat get handled thusly... */
831 if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
835 /* Boring old 80-column fixed format text gets handled this way... */
836 else if ( (!strcasecmp(mime_content_type, "text/plain"))
837 || (!strcasecmp(mime_content_type, "text")) ) {
838 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
839 if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
840 if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
843 if (ic != (iconv_t)(-1) ) {
845 ibuflen = strlen(ibuf);
847 obuf = (char *) malloc(obuflen);
849 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
850 osav[SIZ-obuflen] = 0;
851 safestrncpy(buf, osav, sizeof buf);
856 while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
857 buf[strlen(buf) - 1] = 0;
859 ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
860 wprintf("<BLOCKQUOTE>");
862 } else if ((bq == 1) &&
863 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
864 wprintf("</BLOCKQUOTE>");
870 wprintf("</TT><br />\n");
872 wprintf("</I><br />");
875 else /* HTML is fun, but we've got to strip it first */
876 if (!strcasecmp(mime_content_type, "text/html")) {
877 output_html(mime_charset);
880 /* Unknown weirdness */
882 wprintf(_("I don't know how to display %s"), mime_content_type);
883 wprintf("<br />\n", mime_content_type);
884 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
887 /* Afterwards, offer links to download attachments 'n' such */
888 if (strlen(mime_http) > 0) {
889 wprintf("%s", mime_http);
892 /* Handler for vCard parts */
893 if (strlen(vcard_partnum) > 0) {
894 part_source = load_mimepart(msgnum, vcard_partnum);
895 if (part_source != NULL) {
897 /* If it's my vCard I can edit it */
898 if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
899 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
900 || (WC->wc_view == VIEW_ADDRESSBOOK)
902 wprintf("<A HREF=\"/edit_vcard?"
903 "msgnum=%ld?partnum=%s\">",
904 msgnum, vcard_partnum);
905 wprintf("[%s]</A>", _("edit"));
908 /* In all cases, display the full card */
909 display_vcard(part_source, 0, 1, NULL);
913 /* Handler for calendar parts */
914 if (strlen(cal_partnum) > 0) {
915 part_source = load_mimepart(msgnum, cal_partnum);
916 if (part_source != NULL) {
917 cal_process_attachment(part_source,
918 msgnum, cal_partnum);
928 wprintf("</TD></TR></TABLE>\n");
930 /* end everythingamundo table */
931 if (!printable_view) {
932 wprintf("</TD></TR></TABLE>\n");
933 wprintf("</div><br />\n");
937 if (ic != (iconv_t)(-1) ) {
946 * Unadorned HTML output of an individual message, suitable
947 * for placing in a hidden iframe, for printing, or whatever
949 void embed_message(void) {
952 msgnum = atol(bstr("msgnum"));
953 begin_ajax_response();
954 read_message(msgnum, 0);
960 * Printable view of a message
962 void print_message(void) {
965 msgnum = atol(bstr("msgnum"));
966 output_headers(0, 0, 0, 0, 0, 0);
968 wprintf("Content-type: text/html\r\n"
970 "Connection: close\r\n",
974 wprintf("\r\n\r\n<html>\n"
975 "<head><title>Printable view</title></head>\n"
976 "<body onLoad=\" window.print(); window.close(); \">\n"
979 read_message(msgnum, 1);
981 wprintf("\n</body></html>\n\n");
988 * Read message in simple, JavaScript-embeddable form for 'forward'
989 * or 'reply quoted' operations.
991 * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks
992 * in this function. Doing so would throw a JavaScript error in the
993 * 'supplied text' argument to the editor.
995 void pullquote_message(long msgnum, int forward_attachments) {
997 char mime_partnum[256];
998 char mime_filename[256];
999 char mime_content_type[256];
1000 char mime_charset[256];
1001 char mime_disposition[256];
1003 char mime_http[SIZ];
1004 char *attachments = NULL;
1005 int num_attachments = 0;
1006 struct wc_attachment *att, *aptr;
1007 char m_subject[256];
1013 int format_type = 0;
1018 iconv_t ic = (iconv_t)(-1) ;
1019 char *ibuf; /* Buffer of characters to be converted */
1020 char *obuf; /* Buffer for converted characters */
1021 size_t ibuflen; /* Length of input buffer */
1022 size_t obuflen; /* Length of output buffer */
1023 char *osav; /* Saved pointer to output buffer */
1029 strcpy(reply_to, "");
1030 strcpy(mime_http, "");
1031 strcpy(mime_content_type, "text/plain");
1032 strcpy(mime_charset, "us-ascii");
1034 serv_printf("MSG4 %ld", msgnum);
1035 serv_getln(buf, sizeof buf);
1036 if (buf[0] != '1') {
1037 wprintf(_("ERROR:"));
1038 wprintf("%s<br />", &buf[4]);
1042 strcpy(m_subject, "");
1044 while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
1045 if (!strcmp(buf, "000")) {
1046 wprintf(_("unexpected end of message"));
1049 if (!strncasecmp(buf, "nhdr=yes", 8))
1053 if (!strncasecmp(buf, "type=", 5))
1054 format_type = atoi(&buf[5]);
1055 if (!strncasecmp(buf, "from=", 5)) {
1056 strcpy(from, &buf[5]);
1057 wprintf(_("from "));
1059 utf8ify_rfc822_string(from);
1063 if (!strncasecmp(buf, "subj=", 5)) {
1064 strcpy(m_subject, &buf[5]);
1066 if ((!strncasecmp(buf, "hnod=", 5))
1067 && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
1068 wprintf("(%s) ", &buf[5]);
1070 if ((!strncasecmp(buf, "room=", 5))
1071 && (strcasecmp(&buf[5], WC->wc_roomname))
1072 && (strlen(&buf[5])>0) ) {
1074 wprintf("%s> ", &buf[5]);
1076 if (!strncasecmp(buf, "rfca=", 5)) {
1077 strcpy(rfca, &buf[5]);
1083 if (!strncasecmp(buf, "node=", 5)) {
1084 strcpy(node, &buf[5]);
1085 if ( ((WC->room_flags & QR_NETWORK)
1086 || ((strcasecmp(&buf[5], serv_info.serv_nodename)
1087 && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
1088 && (strlen(rfca)==0)
1090 wprintf("@%s ", &buf[5]);
1093 if (!strncasecmp(buf, "rcpt=", 5)) {
1095 wprintf("%s ", &buf[5]);
1097 if (!strncasecmp(buf, "time=", 5)) {
1098 fmt_date(now, atol(&buf[5]), 0);
1099 wprintf("%s ", now);
1103 * Save attachment info for later. We can't start downloading them
1104 * yet because we're in the middle of a server transaction.
1106 if (!strncasecmp(buf, "part=", 5)) {
1108 attachments = realloc(attachments, (num_attachments * 1024));
1109 strcat(attachments, &buf[5]);
1110 strcat(attachments, "\n");
1118 utf8ify_rfc822_string(m_subject);
1120 if (strlen(m_subject) > 0) {
1121 wprintf(_("Subject:"));
1123 msgescputs(m_subject);
1133 * Learn the content type
1135 strcpy(mime_content_type, "text/plain");
1136 while (serv_getln(buf, sizeof buf), (strlen(buf) > 0)) {
1137 if (!strcmp(buf, "000")) {
1138 wprintf(_("unexpected end of message"));
1141 if (!strncasecmp(buf, "Content-type: ", 14)) {
1142 safestrncpy(mime_content_type, &buf[14],
1143 sizeof(mime_content_type));
1144 for (i=0; i<strlen(mime_content_type); ++i) {
1145 if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
1146 safestrncpy(mime_charset, &mime_content_type[i+8],
1147 sizeof mime_charset);
1150 for (i=0; i<strlen(mime_content_type); ++i) {
1151 if (mime_content_type[i] == ';') {
1152 mime_content_type[i] = 0;
1158 /* Set up a character set conversion if we need to (and if we can) */
1160 if ( (strcasecmp(mime_charset, "us-ascii"))
1161 && (strcasecmp(mime_charset, "UTF-8")) ) {
1162 ic = iconv_open("UTF-8", mime_charset);
1163 if (ic == (iconv_t)(-1) ) {
1164 lprintf(5, "iconv_open() failed: %s\n", strerror(errno));
1169 /* Messages in legacy Citadel variformat get handled thusly... */
1170 if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
1174 /* Boring old 80-column fixed format text gets handled this way... */
1175 else if (!strcasecmp(mime_content_type, "text/plain")) {
1176 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1177 if (buf[strlen(buf)-1] == '\n') buf[strlen(buf)-1] = 0;
1178 if (buf[strlen(buf)-1] == '\r') buf[strlen(buf)-1] = 0;
1181 if (ic != (iconv_t)(-1) ) {
1183 ibuflen = strlen(ibuf);
1185 obuf = (char *) malloc(obuflen);
1187 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
1188 osav[SIZ-obuflen] = 0;
1189 safestrncpy(buf, osav, sizeof buf);
1194 while ((strlen(buf) > 0) && (isspace(buf[strlen(buf) - 1])))
1195 buf[strlen(buf) - 1] = 0;
1197 ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) || (!strncmp(buf, " :-)", 4)))) {
1198 wprintf("<BLOCKQUOTE>");
1200 } else if ((bq == 1) &&
1201 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) && (strncmp(buf, " :-)", 4))) {
1202 wprintf("</BLOCKQUOTE>");
1208 wprintf("</TT><br />");
1210 wprintf("</I><br />");
1213 /* HTML just gets escaped and stuffed back into the editor */
1214 else if (!strcasecmp(mime_content_type, "text/html")) {
1215 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1220 /* Unknown weirdness ... don't know how to handle this content type */
1222 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
1226 /* end of body handler */
1229 * If there were attachments, we have to download them and insert them
1230 * into the attachment chain for the forwarded message we are composing.
1232 if ( (forward_attachments) && (num_attachments) ) {
1233 for (i=0; i<num_attachments; ++i) {
1234 extract_token(buf, attachments, i, '\n', sizeof buf);
1235 extract_token(mime_filename, buf, 1, '|', sizeof mime_filename);
1236 extract_token(mime_partnum, buf, 2, '|', sizeof mime_partnum);
1237 extract_token(mime_disposition, buf, 3, '|', sizeof mime_disposition);
1238 extract_token(mime_content_type, buf, 4, '|', sizeof mime_content_type);
1239 mime_length = extract_int(buf, 5);
1242 * tracing ... uncomment if necessary
1244 lprintf(9, "fwd filename: %s\n", mime_filename);
1245 lprintf(9, "fwd partnum : %s\n", mime_partnum);
1246 lprintf(9, "fwd conttype: %s\n", mime_content_type);
1247 lprintf(9, "fwd dispose : %s\n", mime_disposition);
1248 lprintf(9, "fwd length : %d\n", mime_length);
1251 if ( (!strcasecmp(mime_disposition, "inline"))
1252 || (!strcasecmp(mime_disposition, "attachment")) ) {
1254 /* Create an attachment struct from this mime part... */
1255 att = malloc(sizeof(struct wc_attachment));
1256 memset(att, 0, sizeof(struct wc_attachment));
1257 att->length = mime_length;
1258 strcpy(att->content_type, mime_content_type);
1259 strcpy(att->filename, mime_filename);
1261 att->data = load_mimepart(msgnum, mime_partnum);
1263 /* And add it to the list. */
1264 if (WC->first_attachment == NULL) {
1265 WC->first_attachment = att;
1268 aptr = WC->first_attachment;
1269 while (aptr->next != NULL) aptr = aptr->next;
1279 if (ic != (iconv_t)(-1) ) {
1290 void display_summarized(int num) {
1294 if (WC->summ[num].is_new) wprintf("<B>");
1295 escputs(WC->summ[num].subj);
1296 if (WC->summ[num].is_new) wprintf("</B>");
1297 wprintf("</TD><TD>");
1298 if (WC->summ[num].is_new) wprintf("<B>");
1299 escputs(WC->summ[num].from);
1300 if (WC->summ[num].is_new) wprintf("</B>");
1301 wprintf(" </TD><TD>");
1302 if (WC->summ[num].is_new) wprintf("<B>");
1303 fmt_date(datebuf, WC->summ[num].date, 1); /* brief */
1305 if (WC->summ[num].is_new) wprintf("</B>");
1308 "<INPUT TYPE=\"checkbox\" NAME=\"msg_%ld\" VALUE=\"yes\">"
1310 WC->summ[num].msgnum
1318 void display_addressbook(long msgnum, char alpha) {
1320 char mime_partnum[SIZ];
1321 char mime_filename[SIZ];
1322 char mime_content_type[SIZ];
1323 char mime_disposition[SIZ];
1325 char vcard_partnum[SIZ];
1326 char *vcard_source = NULL;
1327 struct message_summary summ;
1329 memset(&summ, 0, sizeof(summ));
1330 safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
1332 sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */
1334 serv_getln(buf, sizeof buf);
1335 if (buf[0] != '1') return;
1337 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1338 if (!strncasecmp(buf, "part=", 5)) {
1339 extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
1340 extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
1341 extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
1342 extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
1343 mime_length = extract_int(&buf[5], 5);
1345 if (!strcasecmp(mime_content_type, "text/x-vcard")) {
1346 strcpy(vcard_partnum, mime_partnum);
1352 if (strlen(vcard_partnum) > 0) {
1353 vcard_source = load_mimepart(msgnum, vcard_partnum);
1354 if (vcard_source != NULL) {
1356 /* Display the summary line */
1357 display_vcard(vcard_source, alpha, 0, NULL);
1359 /* If it's my vCard I can edit it */
1360 if ( (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
1361 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
1362 || (WC->wc_view == VIEW_ADDRESSBOOK)
1364 wprintf("<A HREF=\"/edit_vcard?"
1365 "msgnum=%ld?partnum=%s\">",
1366 msgnum, vcard_partnum);
1367 wprintf("[%s]</A>", _("edit"));
1378 /* If it's an old "Firstname Lastname" style record, try to
1381 void lastfirst_firstlast(char *namebuf) {
1382 char firstname[SIZ];
1386 if (namebuf == NULL) return;
1387 if (strchr(namebuf, ';') != NULL) return;
1389 i = num_tokens(namebuf, ' ');
1392 extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
1393 remove_token(namebuf, i-1, ' ');
1394 strcpy(firstname, namebuf);
1395 sprintf(namebuf, "%s; %s", lastname, firstname);
1399 void fetch_ab_name(long msgnum, char *namebuf) {
1401 char mime_partnum[SIZ];
1402 char mime_filename[SIZ];
1403 char mime_content_type[SIZ];
1404 char mime_disposition[SIZ];
1406 char vcard_partnum[SIZ];
1407 char *vcard_source = NULL;
1409 struct message_summary summ;
1411 if (namebuf == NULL) return;
1412 strcpy(namebuf, "");
1414 memset(&summ, 0, sizeof(summ));
1415 safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
1417 sprintf(buf, "MSG0 %ld|1", msgnum); /* ask for headers only */
1419 serv_getln(buf, sizeof buf);
1420 if (buf[0] != '1') return;
1422 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1423 if (!strncasecmp(buf, "part=", 5)) {
1424 extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
1425 extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
1426 extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
1427 extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
1428 mime_length = extract_int(&buf[5], 5);
1430 if (!strcasecmp(mime_content_type, "text/x-vcard")) {
1431 strcpy(vcard_partnum, mime_partnum);
1437 if (strlen(vcard_partnum) > 0) {
1438 vcard_source = load_mimepart(msgnum, vcard_partnum);
1439 if (vcard_source != NULL) {
1441 /* Grab the name off the card */
1442 display_vcard(vcard_source, 0, 0, namebuf);
1448 lastfirst_firstlast(namebuf);
1450 for (i=0; i<strlen(namebuf); ++i) {
1451 if (namebuf[i] != ';') return;
1453 strcpy(namebuf, _("(no name)"));
1459 * Record compare function for sorting address book indices
1461 int abcmp(const void *ab1, const void *ab2) {
1463 (((const struct addrbookent *)ab1)->ab_name),
1464 (((const struct addrbookent *)ab2)->ab_name)
1470 * Helper function for do_addrbook_view()
1471 * Converts a name into a three-letter tab label
1473 void nametab(char *tabbuf, char *name) {
1474 stresc(tabbuf, name, 0, 0);
1475 tabbuf[0] = toupper(tabbuf[0]);
1476 tabbuf[1] = tolower(tabbuf[1]);
1477 tabbuf[2] = tolower(tabbuf[2]);
1483 * Render the address book using info we gathered during the scan
1485 void do_addrbook_view(struct addrbookent *addrbook, int num_ab) {
1489 static int NAMESPERPAGE = 60;
1493 char tabfirst_label[SIZ];
1495 char tablast_label[SIZ];
1498 wprintf("<br /><br /><br /><div align=\"center\"><i>");
1499 wprintf(_("This address book is empty."));
1500 wprintf("</i></div>\n");
1505 qsort(addrbook, num_ab, sizeof(struct addrbookent), abcmp);
1508 num_pages = num_ab / NAMESPERPAGE;
1510 page = atoi(bstr("page"));
1513 for (i=0; i<=num_pages; ++i) {
1515 wprintf("<A HREF=\"/readfwd?page=%d\">", i);
1520 tabfirst = i * NAMESPERPAGE;
1521 tablast = tabfirst + NAMESPERPAGE - 1;
1522 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
1523 nametab(tabfirst_label, addrbook[tabfirst].ab_name);
1524 nametab(tablast_label, addrbook[tablast].ab_name);
1525 wprintf("[%s - %s]",
1526 tabfirst_label, tablast_label
1535 wprintf("<br />\n");
1537 wprintf("<TABLE border=0 cellspacing=0 "
1538 "cellpadding=3 width=100%%>\n"
1541 for (i=0; i<num_ab; ++i) {
1543 if ((i / NAMESPERPAGE) == page) {
1545 if ((displayed % 4) == 0) {
1546 if (displayed > 0) {
1550 wprintf("<TR BGCOLOR=\"#%s\">",
1551 (bg ? "DDDDDD" : "FFFFFF")
1557 wprintf("<A HREF=\"/readfwd?startmsg=%ld&is_singlecard=1",
1558 addrbook[i].ab_msgnum);
1559 wprintf("?maxmsgs=1?summary=0?alpha=%s\">", bstr("alpha"));
1560 vcard_n_prettyize(addrbook[i].ab_name);
1561 escputs(addrbook[i].ab_name);
1562 wprintf("</A></TD>\n");
1567 wprintf("</TR></TABLE>\n");
1573 * load message pointers from the server
1575 int load_msg_ptrs(char *servcmd, int with_headers)
1579 char displayname[128];
1586 int num_summ_alloc = 0;
1589 if (WC->num_summ != 0) {
1594 num_summ_alloc = 100;
1596 WC->summ = malloc(num_summ_alloc * sizeof(struct message_summary));
1599 maxload = sizeof(WC->msgarr) / sizeof(long) ;
1601 serv_getln(buf, sizeof buf);
1602 if (buf[0] != '1') {
1603 wprintf("<EM>%s</EM><br />\n", &buf[4]);
1606 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1607 if (nummsgs < maxload) {
1608 WC->msgarr[nummsgs] = extract_long(buf, 0);
1609 datestamp = extract_long(buf, 1);
1610 extract_token(displayname, buf, 2, '|', sizeof displayname);
1611 extract_token(nodename, buf, 3, '|', sizeof nodename);
1612 extract_token(inetaddr, buf, 4, '|', sizeof inetaddr);
1613 extract_token(subject, buf, 5, '|', sizeof subject);
1617 if (nummsgs > num_summ_alloc) {
1618 num_summ_alloc *= 2;
1619 WC->summ = realloc(WC->summ, num_summ_alloc * sizeof(struct message_summary));
1623 memset(&WC->summ[nummsgs-1], 0, sizeof(struct message_summary));
1624 WC->summ[nummsgs-1].msgnum = WC->msgarr[nummsgs-1];
1625 safestrncpy(WC->summ[nummsgs-1].subj, _("(no subject)"), sizeof WC->summ[nummsgs-1].subj);
1626 if (strlen(displayname) > 0) {
1627 safestrncpy(WC->summ[nummsgs-1].from, displayname, sizeof WC->summ[nummsgs-1].from);
1629 if (strlen(subject) > 0) {
1630 safestrncpy(WC->summ[nummsgs-1].subj, subject,
1631 sizeof WC->summ[nummsgs-1].subj);
1634 /* Handle subjects with RFC2047 encoding */
1635 utf8ify_rfc822_string(WC->summ[nummsgs-1].subj);
1637 if (strlen(WC->summ[nummsgs-1].subj) > 75) {
1638 strcpy(&WC->summ[nummsgs-1].subj[72], "...");
1641 if (strlen(nodename) > 0) {
1642 if ( ((WC->room_flags & QR_NETWORK)
1643 || ((strcasecmp(nodename, serv_info.serv_nodename)
1644 && (strcasecmp(nodename, serv_info.serv_fqdn)))))
1646 strcat(WC->summ[nummsgs-1].from, " @ ");
1647 strcat(WC->summ[nummsgs-1].from, nodename);
1651 WC->summ[nummsgs-1].date = datestamp;
1654 /* Handle senders with RFC2047 encoding */
1655 utf8ify_rfc822_string(WC->summ[nummsgs-1].from);
1657 if (strlen(WC->summ[nummsgs-1].from) > 25) {
1658 strcpy(&WC->summ[nummsgs-1].from[22], "...");
1667 int summcmp_subj(const void *s1, const void *s2) {
1668 struct message_summary *summ1;
1669 struct message_summary *summ2;
1671 summ1 = (struct message_summary *)s1;
1672 summ2 = (struct message_summary *)s2;
1673 return strcasecmp(summ1->subj, summ2->subj);
1676 int summcmp_rsubj(const void *s1, const void *s2) {
1677 struct message_summary *summ1;
1678 struct message_summary *summ2;
1680 summ1 = (struct message_summary *)s1;
1681 summ2 = (struct message_summary *)s2;
1682 return strcasecmp(summ2->subj, summ1->subj);
1685 int summcmp_sender(const void *s1, const void *s2) {
1686 struct message_summary *summ1;
1687 struct message_summary *summ2;
1689 summ1 = (struct message_summary *)s1;
1690 summ2 = (struct message_summary *)s2;
1691 return strcasecmp(summ1->from, summ2->from);
1694 int summcmp_rsender(const void *s1, const void *s2) {
1695 struct message_summary *summ1;
1696 struct message_summary *summ2;
1698 summ1 = (struct message_summary *)s1;
1699 summ2 = (struct message_summary *)s2;
1700 return strcasecmp(summ2->from, summ1->from);
1703 int summcmp_date(const void *s1, const void *s2) {
1704 struct message_summary *summ1;
1705 struct message_summary *summ2;
1707 summ1 = (struct message_summary *)s1;
1708 summ2 = (struct message_summary *)s2;
1710 if (summ1->date < summ2->date) return -1;
1711 else if (summ1->date > summ2->date) return +1;
1715 int summcmp_rdate(const void *s1, const void *s2) {
1716 struct message_summary *summ1;
1717 struct message_summary *summ2;
1719 summ1 = (struct message_summary *)s1;
1720 summ2 = (struct message_summary *)s2;
1722 if (summ1->date < summ2->date) return +1;
1723 else if (summ1->date > summ2->date) return -1;
1728 * command loop for reading messages
1730 void readloop(char *oper)
1739 int num_displayed = 0;
1741 int is_addressbook = 0;
1742 int is_singlecard = 0;
1743 int is_calendar = 0;
1746 int remaining_messages;
1748 int lowest_displayed = (-1);
1749 int highest_displayed = 0;
1750 long pn_previous = 0L;
1751 long pn_current = 0L;
1754 struct addrbookent *addrbook = NULL;
1756 char *sortby = NULL;
1757 char sortpref_name[128];
1758 char sortpref_value[128];
1759 char *subjsort_button;
1760 char *sendsort_button;
1761 char *datesort_button;
1763 startmsg = atol(bstr("startmsg"));
1764 maxmsgs = atoi(bstr("maxmsgs"));
1765 is_summary = atoi(bstr("summary"));
1766 if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
1768 snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname);
1769 get_preference(sortpref_name, sortpref_value, sizeof sortpref_value);
1771 sortby = bstr("sortby");
1772 if ( (strlen(sortby) > 0) && (strcasecmp(sortby, sortpref_value)) ) {
1773 set_preference(sortpref_name, sortby, 1);
1775 if (strlen(sortby) == 0) sortby = sortpref_value;
1776 if (strlen(sortby) == 0) sortby = "msgid";
1778 output_headers(1, 1, 1, 0, 0, 0);
1780 /* When in summary mode, always show ALL messages instead of just
1781 * new or old. Otherwise, show what the user asked for.
1783 if (!strcmp(oper, "readnew")) {
1784 strcpy(cmd, "MSGS NEW");
1786 else if (!strcmp(oper, "readold")) {
1787 strcpy(cmd, "MSGS OLD");
1790 strcpy(cmd, "MSGS ALL");
1793 if ((WC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1)) {
1795 strcpy(cmd, "MSGS ALL");
1798 if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) {
1800 strcpy(cmd, "MSGS ALL");
1805 strcpy(cmd, "MSGS ALL|||1"); /* fetch header summary */
1810 /* Are we doing a summary view? If so, we need to know old messages
1811 * and new messages, so we can do that pretty boldface thing for the
1814 strcpy(old_msgs, "");
1817 serv_getln(buf, sizeof buf);
1818 if (buf[0] == '2') {
1819 strcpy(old_msgs, &buf[4]);
1823 is_singlecard = atoi(bstr("is_singlecard"));
1825 if (WC->wc_view == VIEW_CALENDAR) { /* calendar */
1827 strcpy(cmd, "MSGS ALL");
1830 if (WC->wc_view == VIEW_TASKS) { /* tasks */
1832 strcpy(cmd, "MSGS ALL");
1835 if (WC->wc_view == VIEW_NOTES) { /* notes */
1837 strcpy(cmd, "MSGS ALL");
1841 nummsgs = load_msg_ptrs(cmd, is_summary);
1844 if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) {
1846 if (!strcmp(oper, "readnew")) {
1847 wprintf(_("No new messages."));
1848 } else if (!strcmp(oper, "readold")) {
1849 wprintf(_("No old messages."));
1851 wprintf(_("No messages here."));
1860 for (a = 0; a < nummsgs; ++a) {
1861 /* Are you a new message, or an old message? */
1863 if (is_msg_in_mset(old_msgs, WC->msgarr[a])) {
1864 WC->summ[a].is_new = 0;
1867 WC->summ[a].is_new = 1;
1873 if (startmsg == 0L) startmsg = WC->msgarr[0];
1874 remaining_messages = 0;
1876 for (a = 0; a < nummsgs; ++a) {
1877 if (WC->msgarr[a] >= startmsg) {
1878 ++remaining_messages;
1883 if (!strcasecmp(sortby, "subject")) {
1884 qsort(WC->summ, WC->num_summ,
1885 sizeof(struct message_summary), summcmp_subj);
1887 else if (!strcasecmp(sortby, "rsubject")) {
1888 qsort(WC->summ, WC->num_summ,
1889 sizeof(struct message_summary), summcmp_rsubj);
1891 else if (!strcasecmp(sortby, "sender")) {
1892 qsort(WC->summ, WC->num_summ,
1893 sizeof(struct message_summary), summcmp_sender);
1895 else if (!strcasecmp(sortby, "rsender")) {
1896 qsort(WC->summ, WC->num_summ,
1897 sizeof(struct message_summary), summcmp_rsender);
1899 else if (!strcasecmp(sortby, "date")) {
1900 qsort(WC->summ, WC->num_summ,
1901 sizeof(struct message_summary), summcmp_date);
1903 else if (!strcasecmp(sortby, "rdate")) {
1904 qsort(WC->summ, WC->num_summ,
1905 sizeof(struct message_summary), summcmp_rdate);
1909 if (!strcasecmp(sortby, "subject")) {
1910 subjsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsubject\"><img border=\"0\" src=\"/static/down_pointer.gif\" /></a>" ;
1912 else if (!strcasecmp(sortby, "rsubject")) {
1913 subjsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"/static/up_pointer.gif\" /></a>" ;
1916 subjsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"/static/sort_none.gif\" /></a>" ;
1919 if (!strcasecmp(sortby, "sender")) {
1920 sendsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsender\"><img border=\"0\" src=\"/static/down_pointer.gif\" /></a>" ;
1922 else if (!strcasecmp(sortby, "rsender")) {
1923 sendsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"/static/up_pointer.gif\" /></a>" ;
1926 sendsort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"/static/sort_none.gif\" /></a>" ;
1929 if (!strcasecmp(sortby, "date")) {
1930 datesort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"/static/down_pointer.gif\" /></a>" ;
1932 else if (!strcasecmp(sortby, "rdate")) {
1933 datesort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=date\"><img border=\"0\" src=\"/static/up_pointer.gif\" /></a>" ;
1936 datesort_button = "<a href=\"/readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=date\"><img border=\"0\" src=\"/static/sort_none.gif\" /></a>" ;
1940 wprintf("</div>"); /* end of 'content' div */
1942 wprintf("<div id=\"message_list\">"
1944 "<div id=\"fix_scrollbar_bug\">\n"
1946 "<form name=\"msgomatic\" "
1947 "method=\"POST\" action=\"/do_stuff_to_msgs\">\n"
1949 "<span class=\"mailbox_summary\">"
1950 "<table border=0 cellspacing=0 "
1951 "cellpadding=0 width=100%%>\n"
1953 "<TD align=center><b><i>%s</i></b> %s</TD>"
1954 "<TD align=center><b><i>%s</i></b> %s</TD>"
1955 "<TD align=center><b><i>%s</i></b> %s</TD>"
1956 "<TD><INPUT TYPE=\"submit\" NAME=\"delete_button\" "
1957 "STYLE=\"font-family: Bitstream Vera Sans,Arial,Helvetica,sans-serif;"
1958 " font-size: 6pt;\" "
1959 "VALUE=\"%s\"></TD>"
1962 _("Subject"), subjsort_button,
1963 _("Sender"), sendsort_button,
1964 _("Date"), datesort_button,
1969 for (a = 0; a < nummsgs; ++a) {
1970 if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
1972 /* Learn which msgs "Prev" & "Next" buttons go to */
1973 pn_current = WC->msgarr[a];
1974 if (a > 0) pn_previous = WC->msgarr[a-1];
1975 if (a < (nummsgs-1)) pn_next = WC->msgarr[a+1];
1977 /* If a tabular view, set up the line */
1980 wprintf("<tr bgcolor=\"#%s\" ",
1981 (bg ? "DDDDDD" : "FFFFFF")
1984 wprintf("onClick=\" new Ajax.Updater('preview_pane', '/msg', { method: 'get', parameters: 'msgnum=%ld' } ); \" ", WC->summ[a].msgnum);
1989 /* Display the message */
1991 display_summarized(a);
1993 else if (is_addressbook) {
1994 fetch_ab_name(WC->msgarr[a], buf);
1996 addrbook = realloc(addrbook,
1997 (sizeof(struct addrbookent) * num_ab) );
1998 safestrncpy(addrbook[num_ab-1].ab_name, buf,
1999 sizeof(addrbook[num_ab-1].ab_name));
2000 addrbook[num_ab-1].ab_msgnum = WC->msgarr[a];
2002 else if (is_calendar) {
2003 display_calendar(WC->msgarr[a]);
2005 else if (is_tasks) {
2006 display_task(WC->msgarr[a]);
2008 else if (is_notes) {
2009 display_note(WC->msgarr[a]);
2012 read_message(WC->msgarr[a], 0);
2015 /* If a tabular view, finish the line */
2020 if (lowest_displayed < 0) lowest_displayed = a;
2021 highest_displayed = a;
2024 --remaining_messages;
2029 wprintf("</table></span></form>"
2030 "</div>\n"); /* end of 'fix_scrollbar_bug' div */
2031 wprintf("</div>"); /* end of 'message_list' div */
2033 wprintf("<div id=\"ml_slider\"></div>"); /* slider */
2035 wprintf("<div id=\"preview_pane\">"); /* The preview pane will initially be empty */
2038 /* Bump these because although we're thinking in zero base, the user
2039 * is a drooling idiot and is thinking in one base.
2042 ++highest_displayed;
2044 /* If we're only looking at one message, do a prev/next thing */
2045 if (num_displayed == 1) {
2046 if ((!is_tasks) && (!is_calendar) && (!is_addressbook) && (!is_notes) && (!is_singlecard)) {
2048 wprintf("<div id=\"fix_scrollbar_bug\">"
2049 "<table border=0 width=100%% bgcolor=\"#dddddd\"><tr><td>");
2050 wprintf(_("Reading #%d of %d messages."), lowest_displayed, nummsgs);
2051 wprintf("</TD><TD ALIGN=RIGHT><FONT SIZE=+1>");
2053 if (pn_previous > 0L) {
2054 wprintf("<A HREF=\"/%s"
2065 wprintf("<A HREF=\"/%s"
2075 wprintf("<A HREF=\"/%s?startmsg=%ld"
2076 "?maxmsgs=%d?summary=1\">"
2085 wprintf("</td></tr></table></div>\n");
2090 * If we're not currently looking at ALL requested
2091 * messages, then display the selector bar
2093 if (num_displayed > 1) {
2094 if ((!is_tasks) && (!is_calendar) && (!is_addressbook)
2095 && (!is_notes) && (!is_singlecard) && (!is_summary)) {
2097 wprintf("<form name=\"msgomatic\" "
2098 "method=\"POST\" action=\"/do_stuff_to_msgs\">\n");
2100 wprintf(_("Reading #"), lowest_displayed, highest_displayed);
2102 wprintf("<select name=\"whichones\" size=\"1\" "
2103 "OnChange=\"location.href=msgomatic.whichones.options"
2104 "[selectedIndex].value\">\n");
2106 for (b=0; b<nummsgs; b = b + maxmsgs) {
2109 if (hi > nummsgs) hi = nummsgs;
2110 wprintf("<option %s value="
2115 "%d-%d</option> \n",
2116 ((WC->msgarr[b] == startmsg) ? "selected" : ""),
2123 wprintf("<option value=\"/%s?startmsg=%ld"
2124 "?maxmsgs=9999999?summary=%d\">"
2128 WC->msgarr[0], is_summary);
2130 wprintf("</select> ");
2131 wprintf(_("of %d messages."), nummsgs);
2132 wprintf("</form>\n");
2138 do_tasks_view(); /* Render the task list */
2142 do_calendar_view(); /* Render the calendar */
2145 if (is_addressbook) {
2146 do_addrbook_view(addrbook, num_ab); /* Render the address book */
2149 /* Put the data transfer hidden iframe in a hidden div, to make it *really* hidden */
2151 "<div display=\"hidden\">\n"
2152 "<iframe name=\"msgloader1\" id=\"msgloader1\" width=\"1\"></iframe>\n"
2155 /* Note: wDumpContent() will output one additional </div> tag. */
2157 if (addrbook != NULL) free(addrbook);
2159 /* free the summary */
2160 if (WC->num_summ != 0) {
2165 /* If we got here via a mailbox view and are reading a single
2166 * message, mark it as "seen." We do this after rendering the web page
2167 * so it doesn't keep the user waiting.
2169 if ( (maxmsgs == 1) && (WC->wc_view == VIEW_MAILBOX) ) {
2170 serv_printf("SEEN %ld|1", startmsg);
2171 serv_getln(buf, sizeof buf);
2177 * Back end for post_message() ... this is where the actual message
2178 * gets transmitted to the server.
2180 void post_mime_to_server(void) {
2182 int is_multipart = 0;
2184 struct wc_attachment *att;
2186 size_t encoded_length;
2188 /* RFC2045 requires this, and some clients look for it... */
2189 serv_puts("MIME-Version: 1.0");
2191 /* If there are attachments, we have to do multipart/mixed */
2192 if (WC->first_attachment != NULL) {
2197 sprintf(boundary, "---Citadel-Multipart-%s-%04x%04x---",
2198 serv_info.serv_fqdn,
2203 /* Remember, serv_printf() appends an extra newline */
2204 serv_printf("Content-type: multipart/mixed; "
2205 "boundary=\"%s\"\n", boundary);
2206 serv_printf("This is a multipart message in MIME format.\n");
2207 serv_printf("--%s", boundary);
2210 serv_puts("Content-type: text/html; charset=utf-8");
2212 serv_puts("<HTML><BODY>\n");
2213 text_to_server(bstr("msgtext"), 0);
2214 serv_puts("</BODY></HTML>\n");
2219 /* Add in the attachments */
2220 for (att = WC->first_attachment; att!=NULL; att=att->next) {
2222 encoded_length = ((att->length * 150) / 100);
2223 encoded = malloc(encoded_length);
2224 if (encoded == NULL) break;
2225 CtdlEncodeBase64(encoded, att->data, att->length);
2227 serv_printf("--%s", boundary);
2228 serv_printf("Content-type: %s", att->content_type);
2229 serv_printf("Content-disposition: attachment; "
2230 "filename=\"%s\"", att->filename);
2231 serv_puts("Content-transfer-encoding: base64");
2233 serv_write(encoded, strlen(encoded));
2238 serv_printf("--%s--", boundary);
2246 * Post message (or don't post message)
2248 * Note regarding the "dont_post" variable:
2249 * A random value (actually, it's just a timestamp) is inserted as a hidden
2250 * field called "postseq" when the display_enter page is generated. This
2251 * value is checked when posting, using the static variable dont_post. If a
2252 * user attempts to post twice using the same dont_post value, the message is
2253 * discarded. This prevents the accidental double-saving of the same message
2254 * if the user happens to click the browser "back" button.
2256 void post_message(void)
2259 static long dont_post = (-1L);
2260 struct wc_attachment *att, *aptr;
2262 if (WC->upload_length > 0) {
2264 /* There's an attachment. Save it to this struct... */
2265 att = malloc(sizeof(struct wc_attachment));
2266 memset(att, 0, sizeof(struct wc_attachment));
2267 att->length = WC->upload_length;
2268 strcpy(att->content_type, WC->upload_content_type);
2269 strcpy(att->filename, WC->upload_filename);
2272 /* And add it to the list. */
2273 if (WC->first_attachment == NULL) {
2274 WC->first_attachment = att;
2277 aptr = WC->first_attachment;
2278 while (aptr->next != NULL) aptr = aptr->next;
2282 /* Mozilla sends a simple filename, which is what we want,
2283 * but Satan's Browser sends an entire pathname. Reduce
2284 * the path to just a filename if we need to.
2286 while (num_tokens(att->filename, '/') > 1) {
2287 remove_token(att->filename, 0, '/');
2289 while (num_tokens(att->filename, '\\') > 1) {
2290 remove_token(att->filename, 0, '\\');
2293 /* Transfer control of this memory from the upload struct
2294 * to the attachment struct.
2296 att->data = WC->upload;
2297 WC->upload_length = 0;
2303 if (strlen(bstr("cancel_button")) > 0) {
2304 sprintf(WC->ImportantMessage,
2305 _("Cancelled. Message was not posted."));
2306 } else if (strlen(bstr("attach_button")) > 0) {
2309 } else if (atol(bstr("postseq")) == dont_post) {
2310 sprintf(WC->ImportantMessage,
2311 _("Automatically cancelled because you have already "
2312 "saved this message."));
2314 sprintf(buf, "ENT0 1|%s|0|4|%s|||%s|%s",
2321 serv_getln(buf, sizeof buf);
2322 if (buf[0] == '4') {
2323 post_mime_to_server();
2324 if ( (strlen(bstr("recp")) > 0)
2325 || (strlen(bstr("cc")) > 0)
2326 || (strlen(bstr("bcc")) > 0)
2328 sprintf(WC->ImportantMessage, _("Message has been sent.\n"));
2331 sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
2333 dont_post = atol(bstr("postseq"));
2335 sprintf(WC->ImportantMessage, "%s", &buf[4]);
2341 free_attachments(WC);
2342 readloop("readnew");
2349 * display the message entry screen
2351 void display_enter(void)
2356 struct wc_attachment *att;
2357 int recipient_required = 0;
2358 int recipient_bad = 0;
2361 if (strlen(bstr("force_room")) > 0) {
2362 gotoroom(bstr("force_room"));
2365 /* Are we perhaps in an address book view? If so, then an "enter
2366 * message" command really means "add new entry."
2368 if (WC->wc_view == VIEW_ADDRESSBOOK) {
2369 do_edit_vcard(-1, "", "");
2373 #ifdef WEBCIT_WITH_CALENDAR_SERVICE
2374 /* Are we perhaps in a calendar view? If so, then an "enter
2375 * message" command really means "add new calendar item."
2377 if (WC->wc_view == VIEW_CALENDAR) {
2378 display_edit_event();
2382 /* Are we perhaps in a tasks view? If so, then an "enter
2383 * message" command really means "add new task."
2385 if (WC->wc_view == VIEW_TASKS) {
2386 display_edit_task();
2392 * Otherwise proceed normally.
2393 * Do a custom room banner with no navbar...
2395 output_headers(1, 1, 2, 0, 0, 0);
2396 wprintf("<div id=\"banner\">\n");
2397 embed_room_banner(NULL, navbar_none);
2398 wprintf("</div>\n");
2399 wprintf("<div id=\"content\">\n"
2400 "<div id=\"fix_scrollbar_bug\">"
2401 "<table width=100%% border=0 bgcolor=\"#ffffff\"><tr><td>");
2403 /* First test to see whether this is a room that requires recipients to be entered */
2404 serv_puts("ENT0 0");
2405 serv_getln(buf, sizeof buf);
2406 if (!strncmp(buf, "570", 3)) { /* 570 means that we need a recipient here */
2407 recipient_required = 1;
2409 else if (buf[0] != '2') { /* Any other error means that we cannot continue */
2410 wprintf("<EM>%s</EM><br />\n", &buf[4]);
2414 /* Now check our actual recipients if there are any */
2415 if (recipient_required) {
2416 sprintf(buf, "ENT0 0|%s|0|0||||%s|%s", bstr("recp"), bstr("cc"), bstr("bcc"));
2418 serv_getln(buf, sizeof buf);
2420 if (!strncmp(buf, "570", 3)) { /* 570 means we have an invalid recipient listed */
2421 if (strlen(bstr("recp")) + strlen(bstr("cc")) + strlen(bstr("bcc")) > 0) {
2425 else if (buf[0] != '2') { /* Any other error means that we cannot continue */
2426 wprintf("<EM>%s</EM><br />\n", &buf[4]);
2431 /* If we got this far, we can display the message entry screen. */
2434 fmt_date(buf, now, 0);
2435 strcat(&buf[strlen(buf)], _(" <I>from</I> "));
2436 stresc(&buf[strlen(buf)], WC->wc_username, 1, 1);
2438 /* Don't need this anymore, it's in the input box below
2439 if (strlen(bstr("recp")) > 0) {
2440 strcat(&buf[strlen(buf)], _(" <I>to</I> "));
2441 stresc(&buf[strlen(buf)], bstr("recp"), 1, 1);
2445 strcat(&buf[strlen(buf)], _(" <I>in</I> "));
2446 stresc(&buf[strlen(buf)], WC->wc_roomname, 1, 1);
2448 /* begin message entry screen */
2449 wprintf("<form enctype=\"multipart/form-data\" "
2450 "method=\"POST\" action=\"/post\" "
2451 "name=\"enterform\""
2453 wprintf("<input type=\"hidden\" name=\"postseq\" value=\"%ld\">\n", now);
2455 wprintf("<img src=\"static/newmess3_24x.gif\" align=middle alt=\" \">");
2456 wprintf("%s<br>\n", buf); /* header bar */
2458 wprintf("<table border=\"0\" width=\"100%%\">\n");
2459 if (recipient_required) {
2461 wprintf("<tr><td>");
2462 wprintf("<font size=-1>");
2466 "<input autocomplete=\"off\" type=\"text\" name=\"recp\" id=\"recp_id\" value=\"");
2467 escputs(bstr("recp"));
2468 wprintf("\" size=50 maxlength=1000 />");
2469 wprintf("<div class=\"auto_complete\" id=\"recp_name_choices\"></div>");
2470 wprintf("</td><td></td></tr>\n");
2472 wprintf("<tr><td>");
2473 wprintf("<font size=-1>");
2477 "<input autocomplete=\"off\" type=\"text\" name=\"cc\" id=\"cc_id\" value=\"");
2478 escputs(bstr("cc"));
2479 wprintf("\" size=50 maxlength=1000 />");
2480 wprintf("<div class=\"auto_complete\" id=\"cc_name_choices\"></div>");
2481 wprintf("</td><td></td></tr>\n");
2483 wprintf("<tr><td>");
2484 wprintf("<font size=-1>");
2488 "<input autocomplete=\"off\" type=\"text\" name=\"bcc\" id=\"bcc_id\" value=\"");
2489 escputs(bstr("bcc"));
2490 wprintf("\" size=50 maxlength=1000 />");
2491 wprintf("<div class=\"auto_complete\" id=\"bcc_name_choices\"></div>");
2492 wprintf("</td><td></td></tr>\n");
2494 /* Initialize the autocomplete ajax helpers (found in wclib.js) */
2495 wprintf("<script type=\"text/javascript\"> \n"
2496 " activate_entmsg_autocompleters(); \n"
2501 wprintf("<tr><td>");
2502 wprintf("<font size=-1>");
2503 wprintf(_("Subject (optional):"));
2506 "<input type=\"text\" name=\"subject\" value=\"");
2507 escputs(bstr("subject"));
2508 wprintf("\" size=50 maxlength=70></td><td>\n");
2510 wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
2511 if (recipient_required) {
2512 wprintf(_("Send message"));
2514 wprintf(_("Post message"));
2517 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
2518 wprintf("</td></tr></table>\n");
2520 wprintf("<center>");
2522 wprintf("<textarea name=\"msgtext\" cols=\"80\" rows=\"15\">");
2524 /* If we're continuing from a previous edit, put our partially-composed message back... */
2525 msgescputs(bstr("msgtext"));
2527 /* If we're forwarding a message, insert it here... */
2528 if (atol(bstr("fwdquote")) > 0L) {
2529 wprintf("<br><div align=center><i>");
2530 wprintf(_("--- forwarded message ---"));
2531 wprintf("</i></div><br>");
2532 pullquote_message(atol(bstr("fwdquote")), 1);
2535 /* If we're replying quoted, insert the quote here... */
2536 else if (atol(bstr("replyquote")) > 0L) {
2539 pullquote_message(atol(bstr("replyquote")), 0);
2540 wprintf("</blockquote>");
2543 /* Insert our signature if appropriate... */
2544 if ( (WC->is_mailbox) && (strcmp(bstr("sig_inserted"), "yes")) ) {
2545 get_preference("use_sig", buf, sizeof buf);
2546 if (!strcasecmp(buf, "yes")) {
2547 get_preference("signature", ebuf, sizeof ebuf);
2548 euid_unescapize(buf, ebuf);
2549 wprintf("<br>--<br>");
2550 for (i=0; i<strlen(buf); ++i) {
2551 if (buf[i] == '\n') {
2554 else if (buf[i] == '<') {
2557 else if (buf[i] == '>') {
2560 else if (buf[i] == '&') {
2563 else if (buf[i] == '\"') {
2566 else if (buf[i] == '\'') {
2569 else if (isprint(buf[i])) {
2570 wprintf("%c", buf[i]);
2576 wprintf("</textarea>");
2577 wprintf("</center><br />\n");
2580 * The following script embeds the TinyMCE richedit control, and automatically
2581 * transforms the textarea into a richedit textarea.
2584 "<script language=\"javascript\" type=\"text/javascript\" src=\"tiny_mce/tiny_mce.js\"></script>\n"
2585 "<script language=\"javascript\" type=\"text/javascript\">"
2587 " mode : \"textareas\", width : \"100%%\", browsers : \"msie,gecko\" "
2592 /* Enumerate any attachments which are already in place... */
2593 wprintf("<img src=\"/static/diskette_24x.gif\" border=0 "
2594 "align=middle height=16 width=16> Attachments: ");
2595 wprintf("<select name=\"which_attachment\" size=1>");
2596 for (att = WC->first_attachment; att != NULL; att = att->next) {
2597 wprintf("<option value=\"");
2598 urlescputs(att->filename);
2600 escputs(att->filename);
2601 /* wprintf(" (%s, %d bytes)",att->content_type,att->length); */
2602 wprintf("</option>\n");
2604 wprintf("</select>");
2606 /* Now offer the ability to attach additional files... */
2607 wprintf(" "
2608 "Attach file: <input NAME=\"attachfile\" "
2609 "SIZE=16 TYPE=\"file\">\n "
2610 "<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
2612 /* Seth asked for these to be at the top *and* bottom... */
2613 wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
2614 if (recipient_required) {
2615 wprintf(_("Send message"));
2617 wprintf(_("Post message"));
2620 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
2622 /* Make sure we only insert our signature once */
2623 if (strcmp(bstr("sig_inserted"), "yes")) {
2624 wprintf("<INPUT TYPE=\"hidden\" NAME=\"sig_inserted\" VALUE=\"yes\">\n");
2627 wprintf("</form>\n");
2629 wprintf("</td></tr></table></div>\n");
2630 DONE: wDumpContent(1);
2640 void delete_msg(void)
2645 msgid = atol(bstr("msgid"));
2647 output_headers(1, 1, 1, 0, 0, 0);
2649 sprintf(buf, "DELE %ld", msgid);
2651 serv_getln(buf, sizeof buf);
2652 wprintf("<EM>%s</EM><br />\n", &buf[4]);
2661 * Confirm move of a message
2663 void confirm_move_msg(void)
2669 msgid = atol(bstr("msgid"));
2672 output_headers(1, 1, 2, 0, 0, 0);
2673 wprintf("<div id=\"banner\">\n");
2674 wprintf("<TABLE WIDTH=100%% BORDER=0><TR><TD>");
2675 wprintf("<SPAN CLASS=\"titlebar\">");
2676 wprintf(_("Confirm move of message"));
2677 wprintf("</SPAN>\n");
2678 wprintf("</TD></TR></TABLE>\n");
2679 wprintf("</div>\n<div id=\"content\">\n");
2681 wprintf("<CENTER>");
2683 wprintf(_("Move this message to:"));
2684 wprintf("<br />\n");
2686 wprintf("<form METHOD=\"POST\" ACTION=\"/move_msg\">\n");
2687 wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
2689 wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
2691 serv_getln(buf, sizeof buf);
2692 if (buf[0] == '1') {
2693 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
2694 extract_token(targ, buf, 0, '|', sizeof targ);
2695 wprintf("<OPTION>");
2700 wprintf("</SELECT>\n");
2701 wprintf("<br />\n");
2703 wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
2705 wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
2706 wprintf("</form></CENTER>\n");
2708 wprintf("</CENTER>\n");
2719 msgid = atol(bstr("msgid"));
2721 if (strlen(bstr("move_button")) > 0) {
2722 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
2724 serv_getln(buf, sizeof buf);
2725 sprintf(WC->ImportantMessage, "%s", &buf[4]);
2727 sprintf(WC->ImportantMessage, (_("The message was not moved.")));
2730 readloop("readnew");
2735 * This gets called when a user selects multiple messages in a summary
2736 * list and then clicks to perform a transformation of some sort on them
2737 * (such as deleting them).
2739 void do_stuff_to_msgs(void) {
2743 struct stuff_t *next;
2747 struct stuff_t *stuff = NULL;
2748 struct stuff_t *ptr;
2749 int delete_button_pressed = 0;
2752 serv_puts("MSGS ALL");
2753 serv_getln(buf, sizeof buf);
2755 if (buf[0] == '1') while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
2756 ptr = malloc(sizeof(struct stuff_t));
2757 ptr->msgnum = atol(buf);
2762 if (strlen(bstr("delete_button")) > 0) {
2763 delete_button_pressed = 1;
2766 while (stuff != NULL) {
2768 sprintf(buf, "msg_%ld", stuff->msgnum);
2769 if (!strcasecmp(bstr(buf), "yes")) {
2771 if (delete_button_pressed) {
2772 serv_printf("DELE %ld", stuff->msgnum);
2773 serv_getln(buf, sizeof buf);
2783 readloop("readfwd");