Some HTML/CSS changes to return to a more compact header of the messages with buttons
[citadel.git] / webcit / messages.c
1 /*
2  * $Id$
3  */
4 /**
5  * \defgroup MsgDisp Functions which deal with the fetching and displaying of messages.
6  * \ingroup WebcitDisplayItems
7  *
8  */
9 /*@{*/
10 #include "webcit.h"
11 #include "vcard.h"
12 #include "webserver.h"
13 #include "groupdav.h"
14
15 #define SUBJ_COL_WIDTH_PCT              50      /**< Mailbox view column width */
16 #define SENDER_COL_WIDTH_PCT            30      /**< Mailbox view column width */
17 #define DATE_PLUS_BUTTONS_WIDTH_PCT     20      /**< Mailbox view column width */
18
19 /**
20  * Address book entry (keep it short and sweet, it's just a quickie lookup
21  * which we can use to get to the real meat and bones later)
22  */
23 struct addrbookent {
24         char ab_name[64]; /**< name string */
25         long ab_msgnum;   /**< message number of address book entry */
26 };
27
28
29
30 #ifdef HAVE_ICONV
31
32 /**
33  * \brief       Wrapper around iconv_open()
34  *              Our version adds aliases for non-standard Microsoft charsets
35  *            such as 'MS950', aliasing them to names like 'CP950'
36  *
37  * \param       tocode          Target encoding
38  * \param       fromcode        Source encoding
39  */
40 iconv_t ctdl_iconv_open(const char *tocode, const char *fromcode)
41 {
42         iconv_t ic = (iconv_t)(-1) ;
43         ic = iconv_open(tocode, fromcode);
44         if (ic == (iconv_t)(-1) ) {
45                 char alias_fromcode[64];
46                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
47                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
48                         alias_fromcode[0] = 'C';
49                         alias_fromcode[1] = 'P';
50                         ic = iconv_open(tocode, alias_fromcode);
51                 }
52         }
53         return(ic);
54 }
55
56
57 /**
58  * \brief  Handle subjects with RFC2047 encoding
59  *  such as:
60  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
61  * \param buf the stringbuffer to process
62  */
63 void utf8ify_rfc822_string(char *buf) {
64         char *start, *end;
65         char newbuf[1024];
66         char charset[128];
67         char encoding[16];
68         char istr[1024];
69         iconv_t ic = (iconv_t)(-1) ;
70         char *ibuf;                     /**< Buffer of characters to be converted */
71         char *obuf;                     /**< Buffer for converted characters */
72         size_t ibuflen;                 /**< Length of input buffer */
73         size_t obuflen;                 /**< Length of output buffer */
74         char *isav;                     /**< Saved pointer to input buffer */
75         char *osav;                     /**< Saved pointer to output buffer */
76         int passes = 0;
77         int i, len;
78         int illegal_non_rfc2047_encoding = 0;
79
80         /** Sometimes, badly formed messages contain strings which were simply
81          *  written out directly in some foreign character set instead of
82          *  using RFC2047 encoding.  This is illegal but we will attempt to
83          *  handle it anyway by converting from a user-specified default
84          *  charset to UTF-8 if we see any nonprintable characters.
85          */
86         len = strlen(buf);
87         for (i=0; i<len; ++i) {
88                 if ((buf[i] < 32) || (buf[i] > 126)) {
89                         illegal_non_rfc2047_encoding = 1;
90                         i = len; ///< take a shortcut, it won't be more than one.
91                 }
92         }
93         if (illegal_non_rfc2047_encoding) {
94                 char default_header_charset[128];
95                 get_preference("default_header_charset", default_header_charset, sizeof default_header_charset);
96                 if ( (strcasecmp(default_header_charset, "UTF-8")) && (strcasecmp(default_header_charset, "us-ascii")) ) {
97                         ic = ctdl_iconv_open("UTF-8", default_header_charset);
98                         if (ic != (iconv_t)(-1) ) {
99                                 ibuf = malloc(1024);
100                                 isav = ibuf;
101                                 safestrncpy(ibuf, buf, 1024);
102                                 ibuflen = strlen(ibuf);
103                                 obuflen = 1024;
104                                 obuf = (char *) malloc(obuflen);
105                                 osav = obuf;
106                                 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
107                                 osav[1024-obuflen] = 0;
108                                 strcpy(buf, osav);
109                                 free(osav);
110                                 iconv_close(ic);
111                                 free(isav);
112                         }
113                 }
114         }
115
116         /** Now we handle foreign character sets properly encoded
117          *  in RFC2047 format.
118          */
119         while (start=strstr(buf, "=?"), end=strstr(buf, "?="),
120                 ((start != NULL) && (end != NULL) && (end > start)) )
121         {
122                 extract_token(charset, start, 1, '?', sizeof charset);
123                 extract_token(encoding, start, 2, '?', sizeof encoding);
124                 extract_token(istr, start, 3, '?', sizeof istr);
125
126                 ibuf = malloc(1024);
127                 isav = ibuf;
128                 if (!strcasecmp(encoding, "B")) {       /**< base64 */
129                         ibuflen = CtdlDecodeBase64(ibuf, istr, strlen(istr));
130                 }
131                 else if (!strcasecmp(encoding, "Q")) {  /**< quoted-printable */
132                         size_t len;
133                         long pos;
134                         
135                         len = strlen(istr);
136                         pos = 0;
137                         while (pos < len)
138                         {
139                                 if (istr[pos] == '_') istr[pos] = ' ';
140                                 pos++;
141                         }
142
143                         ibuflen = CtdlDecodeQuotedPrintable(ibuf, istr, len);
144                 }
145                 else {
146                         strcpy(ibuf, istr);             /**< unknown encoding */
147                         ibuflen = strlen(istr);
148                 }
149
150                 ic = ctdl_iconv_open("UTF-8", charset);
151                 if (ic != (iconv_t)(-1) ) {
152                         obuflen = 1024;
153                         obuf = (char *) malloc(obuflen);
154                         osav = obuf;
155                         iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
156                         osav[1024-obuflen] = 0;
157
158                         end = start;
159                         end++;
160                         strcpy(start, "");
161                         remove_token(end, 0, '?');
162                         remove_token(end, 0, '?');
163                         remove_token(end, 0, '?');
164                         remove_token(end, 0, '?');
165                         strcpy(end, &end[1]);
166
167                         snprintf(newbuf, sizeof newbuf, "%s%s%s", buf, osav, end);
168                         strcpy(buf, newbuf);
169                         free(osav);
170                         iconv_close(ic);
171                 }
172                 else {
173                         end = start;
174                         end++;
175                         strcpy(start, "");
176                         remove_token(end, 0, '?');
177                         remove_token(end, 0, '?');
178                         remove_token(end, 0, '?');
179                         remove_token(end, 0, '?');
180                         strcpy(end, &end[1]);
181
182                         snprintf(newbuf, sizeof newbuf, "%s(unreadable)%s", buf, end);
183                         strcpy(buf, newbuf);
184                 }
185
186                 free(isav);
187
188                 /**
189                  * Since spammers will go to all sorts of absurd lengths to get their
190                  * messages through, there are LOTS of corrupt headers out there.
191                  * So, prevent a really badly formed RFC2047 header from throwing
192                  * this function into an infinite loop.
193                  */
194                 ++passes;
195                 if (passes > 20) return;
196         }
197
198 }
199 #endif
200
201
202
203
204 /**
205  * \brief       RFC2047-encode a header field if necessary.
206  *              If no non-ASCII characters are found, the string
207  *              will be copied verbatim without encoding.
208  *
209  * \param       target          Target buffer.
210  * \param       maxlen          Maximum size of target buffer.
211  * \param       source          Source string to be encoded.
212  */
213 void rfc2047encode(char *target, int maxlen, char *source)
214 {
215         int need_to_encode = 0;
216         int i, len;
217         unsigned char ch;
218
219         if (target == NULL) return;
220         len = strlen(source);
221         for (i=0; i<len; ++i) {
222                 if ((source[i] < 32) || (source[i] > 126)) {
223                         need_to_encode = 1;
224                         i = len; ///< shortcut. won't become more than 1
225                 }
226         }
227
228         if (!need_to_encode) {
229                 safestrncpy(target, source, maxlen);
230                 return;
231         }
232
233         strcpy(target, "=?UTF-8?Q?");
234         for (i=0; i<len; ++i) {
235                 ch = (unsigned char) source[i];
236                 if ((ch < 32) || (ch > 126) || (ch == 61)) {
237                         sprintf(&target[strlen(target)], "=%02X", ch);
238                 }
239                 else {
240                         sprintf(&target[strlen(target)], "%c", ch);
241                 }
242         }
243         
244         strcat(target, "?=");
245 }
246
247
248
249
250 /**
251  * \brief Look for URL's embedded in a buffer and make them linkable.  We use a
252  * target window in order to keep the BBS session in its own window.
253  * \param buf the message buffer
254  */
255 void url(char *buf)
256 {
257         int pos, len;
258         int start, end;
259         char urlbuf[SIZ];
260         char outbuf[1024];
261         start = (-1);
262         len = end = strlen(buf);
263
264         for (pos = 0; pos < len; ++pos) {
265                 if (!strncasecmp(&buf[pos], "http://", 7))
266                         start = pos;
267                 if (!strncasecmp(&buf[pos], "ftp://", 6))
268                         start = pos;
269         }
270
271         if (start < 0)
272                 return;
273
274         for (pos = len; pos > start; --pos) {
275                 if (  (!isprint(buf[pos]))
276                    || (isspace(buf[pos]))
277                    || (buf[pos] == '{')
278                    || (buf[pos] == '}')
279                    || (buf[pos] == '|')
280                    || (buf[pos] == '\\')
281                    || (buf[pos] == '^')
282                    || (buf[pos] == '[')
283                    || (buf[pos] == ']')
284                    || (buf[pos] == '`')
285                    || (buf[pos] == '<')
286                    || (buf[pos] == '>')
287                    || (buf[pos] == '(')
288                    || (buf[pos] == ')')
289                 ) {
290                         end = pos;
291                 }
292         }
293
294         strncpy(urlbuf, &buf[start], end - start);
295         urlbuf[end - start] = 0;
296
297         strncpy(outbuf, buf, start);
298         sprintf(&outbuf[start], "%ca href=%c%s%c TARGET=%c%s%c%c%s%c/A%c",
299                 LB, QU, urlbuf, QU, QU, TARGET, QU, RB, urlbuf, LB, RB);
300         strcat(outbuf, &buf[end]);
301         if ( strlen(outbuf) < 250 )
302                 strcpy(buf, outbuf);
303 }
304
305
306 /**
307  * \brief Turn a vCard "n" (name) field into something displayable.
308  * \param name the name field to convert
309  */
310 void vcard_n_prettyize(char *name)
311 {
312         char *original_name;
313         int i, j, len;
314
315         original_name = strdup(name);
316         len = strlen(original_name);
317         for (i=0; i<5; ++i) {
318                 if (len > 0) {
319                         if (original_name[len-1] == ' ') {
320                                 original_name[--len] = 0;
321                         }
322                         if (original_name[len-1] == ';') {
323                                 original_name[--len] = 0;
324                         }
325                 }
326         }
327         strcpy(name, "");
328         j=0;
329         for (i=0; i<len; ++i) {
330                 if (original_name[i] == ';') {
331                         name[j++] = ',';
332                         name[j++] = ' ';                        
333                 }
334                 else {
335                         name[j++] = original_name[i];
336                 }
337         }
338         name[j] = '\0';
339         free(original_name);
340 }
341
342
343
344
345 /**
346  * \brief preparse a vcard name
347  * display_vcard() calls this after parsing the textual vCard into
348  * our 'struct vCard' data object.
349  * This gets called instead of display_parsed_vcard() if we are only looking
350  * to extract the person's name instead of displaying the card.
351  * \param v the vcard to retrieve the name from
352  * \param storename where to put the name at
353  */
354 void fetchname_parsed_vcard(struct vCard *v, char *storename) {
355         char *name;
356
357         strcpy(storename, "");
358
359         name = vcard_get_prop(v, "n", 1, 0, 0);
360         if (name != NULL) {
361                 strcpy(storename, name);
362                 /* vcard_n_prettyize(storename); */
363         }
364
365 }
366
367
368
369 /**
370  * \brief html print a vcard
371  * display_vcard() calls this after parsing the textual vCard into
372  * our 'struct vCard' data object.
373  *
374  * Set 'full' to nonzero to display the full card, otherwise it will only
375  * show a summary line.
376  *
377  * This code is a bit ugly, so perhaps an explanation is due: we do this
378  * in two passes through the vCard fields.  On the first pass, we process
379  * fields we understand, and then render them in a pretty fashion at the
380  * end.  Then we make a second pass, outputting all the fields we don't
381  * understand in a simple two-column name/value format.
382  * \param v the vCard to display
383  * \param full display all items of the vcard?
384  */
385 void display_parsed_vcard(struct vCard *v, int full) {
386         int i, j;
387         char buf[SIZ];
388         char *name;
389         int is_qp = 0;
390         int is_b64 = 0;
391         char *thisname, *thisvalue;
392         char firsttoken[SIZ];
393         int pass;
394
395         char fullname[SIZ];
396         char title[SIZ];
397         char org[SIZ];
398         char phone[SIZ];
399         char mailto[SIZ];
400
401         strcpy(fullname, "");
402         strcpy(phone, "");
403         strcpy(mailto, "");
404         strcpy(title, "");
405         strcpy(org, "");
406
407         if (!full) {
408                 wprintf("<TD>");
409                 name = vcard_get_prop(v, "fn", 1, 0, 0);
410                 if (name != NULL) {
411                         escputs(name);
412                 }
413                 else if (name = vcard_get_prop(v, "n", 1, 0, 0), name != NULL) {
414                         strcpy(fullname, name);
415                         vcard_n_prettyize(fullname);
416                         escputs(fullname);
417                 }
418                 else {
419                         wprintf("&nbsp;");
420                 }
421                 wprintf("</TD>");
422                 return;
423         }
424
425         wprintf("<div align=center>"
426                 "<table bgcolor=#aaaaaa width=50%%>");
427         for (pass=1; pass<=2; ++pass) {
428
429                 if (v->numprops) for (i=0; i<(v->numprops); ++i) {
430                         int len;
431                         thisname = strdup(v->prop[i].name);
432                         extract_token(firsttoken, thisname, 0, ';', sizeof firsttoken);
433         
434                         for (j=0; j<num_tokens(thisname, ';'); ++j) {
435                                 extract_token(buf, thisname, j, ';', sizeof buf);
436                                 if (!strcasecmp(buf, "encoding=quoted-printable")) {
437                                         is_qp = 1;
438                                         remove_token(thisname, j, ';');
439                                 }
440                                 if (!strcasecmp(buf, "encoding=base64")) {
441                                         is_b64 = 1;
442                                         remove_token(thisname, j, ';');
443                                 }
444                         }
445                         
446                         len = strlen(v->prop[i].value);
447         
448                         if (is_qp) {
449                                 // %ff can become 6 bytes in utf8 
450                                 thisvalue = malloc(len * 2 + 3); 
451                                 j = CtdlDecodeQuotedPrintable(
452                                         thisvalue, v->prop[i].value,
453                                         len);
454                                 thisvalue[j] = 0;
455                         }
456                         else if (is_b64) {
457                                 // ff will become one byte..
458                                 thisvalue = malloc(len + 50);
459                                 CtdlDecodeBase64(
460                                         thisvalue, v->prop[i].value,
461                                         strlen(v->prop[i].value) );
462                         }
463                         else {
464                                 thisvalue = strdup(v->prop[i].value);
465                         }
466         
467                         /** Various fields we may encounter ***/
468         
469                         /** N is name, but only if there's no FN already there */
470                         if (!strcasecmp(firsttoken, "n")) {
471                                 if (IsEmptyStr(fullname)) {
472                                         strcpy(fullname, thisvalue);
473                                         vcard_n_prettyize(fullname);
474                                 }
475                         }
476         
477                         /** FN (full name) is a true 'display name' field */
478                         else if (!strcasecmp(firsttoken, "fn")) {
479                                 strcpy(fullname, thisvalue);
480                         }
481
482                         /** title */
483                         else if (!strcasecmp(firsttoken, "title")) {
484                                 strcpy(title, thisvalue);
485                         }
486         
487                         /** organization */
488                         else if (!strcasecmp(firsttoken, "org")) {
489                                 strcpy(org, thisvalue);
490                         }
491         
492                         else if (!strcasecmp(firsttoken, "email")) {
493                                 if (!IsEmptyStr(mailto)) strcat(mailto, "<br />");
494                                 strcat(mailto,
495                                         "<a href=\"display_enter"
496                                         "?force_room=_MAIL_?recp=");
497
498                                 urlesc(&mailto[strlen(mailto)], fullname);
499                                 urlesc(&mailto[strlen(mailto)], " <");
500                                 urlesc(&mailto[strlen(mailto)], thisvalue);
501                                 urlesc(&mailto[strlen(mailto)], ">");
502
503                                 strcat(mailto, "\">");
504                                 stresc(&mailto[strlen(mailto)], thisvalue, 1, 1);
505                                 strcat(mailto, "</A>");
506                         }
507                         else if (!strcasecmp(firsttoken, "tel")) {
508                                 if (!IsEmptyStr(phone)) strcat(phone, "<br />");
509                                 strcat(phone, thisvalue);
510                                 for (j=0; j<num_tokens(thisname, ';'); ++j) {
511                                         extract_token(buf, thisname, j, ';', sizeof buf);
512                                         if (!strcasecmp(buf, "tel"))
513                                                 strcat(phone, "");
514                                         else if (!strcasecmp(buf, "work"))
515                                                 strcat(phone, _(" (work)"));
516                                         else if (!strcasecmp(buf, "home"))
517                                                 strcat(phone, _(" (home)"));
518                                         else if (!strcasecmp(buf, "cell"))
519                                                 strcat(phone, _(" (cell)"));
520                                         else {
521                                                 strcat(phone, " (");
522                                                 strcat(phone, buf);
523                                                 strcat(phone, ")");
524                                         }
525                                 }
526                         }
527                         else if (!strcasecmp(firsttoken, "adr")) {
528                                 if (pass == 2) {
529                                         wprintf("<TR><TD>");
530                                         wprintf(_("Address:"));
531                                         wprintf("</TD><TD>");
532                                         for (j=0; j<num_tokens(thisvalue, ';'); ++j) {
533                                                 extract_token(buf, thisvalue, j, ';', sizeof buf);
534                                                 if (!IsEmptyStr(buf)) {
535                                                         escputs(buf);
536                                                         if (j<3) wprintf("<br />");
537                                                         else wprintf(" ");
538                                                 }
539                                         }
540                                         wprintf("</TD></TR>\n");
541                                 }
542                         }
543                         else if (!strcasecmp(firsttoken, "version")) {
544                                 /* ignore */
545                         }
546                         else if (!strcasecmp(firsttoken, "rev")) {
547                                 /* ignore */
548                         }
549                         else if (!strcasecmp(firsttoken, "label")) {
550                                 /* ignore */
551                         }
552                         else {
553
554                                 /*** Don't show extra fields.  They're ugly.
555                                 if (pass == 2) {
556                                         wprintf("<TR><TD>");
557                                         escputs(thisname);
558                                         wprintf("</TD><TD>");
559                                         escputs(thisvalue);
560                                         wprintf("</TD></TR>\n");
561                                 }
562                                 ***/
563                         }
564         
565                         free(thisname);
566                         free(thisvalue);
567                 }
568         
569                 if (pass == 1) {
570                         wprintf("<TR BGCOLOR=\"#AAAAAA\">"
571                         "<TD COLSPAN=2 BGCOLOR=\"#FFFFFF\">"
572                         "<IMG ALIGN=CENTER src=\"static/viewcontacts_48x.gif\">"
573                         "<FONT SIZE=+1><B>");
574                         escputs(fullname);
575                         wprintf("</B></FONT>");
576                         if (!IsEmptyStr(title)) {
577                                 wprintf("<div align=right>");
578                                 escputs(title);
579                                 wprintf("</div>");
580                         }
581                         if (!IsEmptyStr(org)) {
582                                 wprintf("<div align=right>");
583                                 escputs(org);
584                                 wprintf("</div>");
585                         }
586                         wprintf("</TD></TR>\n");
587                 
588                         if (!IsEmptyStr(phone)) {
589                                 wprintf("<tr><td>");
590                                 wprintf(_("Telephone:"));
591                                 wprintf("</td><td>%s</td></tr>\n", phone);
592                         }
593                         if (!IsEmptyStr(mailto)) {
594                                 wprintf("<tr><td>");
595                                 wprintf(_("E-mail:"));
596                                 wprintf("</td><td>%s</td></tr>\n", mailto);
597                         }
598                 }
599
600         }
601
602         wprintf("</table></div>\n");
603 }
604
605
606
607 /**
608  * \brief  Display a textual vCard
609  * (Converts to a vCard object and then calls the actual display function)
610  * Set 'full' to nonzero to display the whole card instead of a one-liner.
611  * Or, if "storename" is non-NULL, just store the person's name in that
612  * buffer instead of displaying the card at all.
613  * \param vcard_source the buffer containing the vcard text
614  * \param alpha what???
615  * \param full should we usse all lines?
616  * \param storename where to store???
617  */
618 void display_vcard(char *vcard_source, char alpha, int full, char *storename) {
619         struct vCard *v;
620         char *name;
621         char buf[SIZ];
622         char this_alpha = 0;
623
624         v = vcard_load(vcard_source);
625         if (v == NULL) return;
626
627         name = vcard_get_prop(v, "n", 1, 0, 0);
628         if (name != NULL) {
629                 strcpy(buf, name);
630                 this_alpha = buf[0];
631         }
632
633         if (storename != NULL) {
634                 fetchname_parsed_vcard(v, storename);
635         }
636         else if (       (alpha == 0)
637                         || ((isalpha(alpha)) && (tolower(alpha) == tolower(this_alpha)) )
638                         || ((!isalpha(alpha)) && (!isalpha(this_alpha)))
639                 ) {
640                 display_parsed_vcard(v, full);
641         }
642
643         vcard_free(v);
644 }
645
646
647 struct attach_link {
648         char partnum[32];
649         char html[1024];
650 };
651
652
653 /**
654  * \brief I wanna SEE that message!  
655  * \param msgnum the citadel number of the message to display
656  * \param printable_view are we doing a print view?
657  * \param section Optional for encapsulated message/rfc822 submessage)
658  */
659 void read_message(long msgnum, int printable_view, char *section) {
660         char buf[SIZ];
661         char mime_partnum[256];
662         char mime_name[256];
663         char mime_filename[256];
664         char mime_content_type[256];
665         char mime_charset[256];
666         char mime_disposition[256];
667         int mime_length;
668         struct attach_link *attach_links = NULL;
669         int num_attach_links = 0;
670         char mime_submessages[256];
671         char m_subject[256];
672         char m_cc[1024];
673         char from[256];
674         char node[256];
675         char rfca[256];
676         char reply_to[512];
677         char reply_all[4096];
678         char now[64];
679         int format_type = 0;
680         int nhdr = 0;
681         int bq = 0;
682         int i = 0;
683         char vcard_partnum[256];
684         char cal_partnum[256];
685         char *part_source = NULL;
686         char msg4_partnum[32];
687 #ifdef HAVE_ICONV
688         iconv_t ic = (iconv_t)(-1) ;
689         char *ibuf;                /**< Buffer of characters to be converted */
690         char *obuf;                /**< Buffer for converted characters      */
691         size_t ibuflen;    /**< Length of input buffer         */
692         size_t obuflen;    /**< Length of output buffer       */
693         char *osav;                /**< Saved pointer to output buffer       */
694 #endif
695
696         strcpy(from, "");
697         strcpy(node, "");
698         strcpy(rfca, "");
699         strcpy(reply_to, "");
700         strcpy(reply_all, "");
701         strcpy(vcard_partnum, "");
702         strcpy(cal_partnum, "");
703         strcpy(mime_content_type, "text/plain");
704         strcpy(mime_charset, "us-ascii");
705         strcpy(mime_submessages, "");
706
707         serv_printf("MSG4 %ld|%s", msgnum, section);
708         serv_getln(buf, sizeof buf);
709         if (buf[0] != '1') {
710                 wprintf("<strong>");
711                 wprintf(_("ERROR:"));
712                 wprintf("</strong> %s<br />\n", &buf[4]);
713                 return;
714         }
715
716         /** begin everythingamundo table */
717         if (!printable_view) {
718                 wprintf("<div class=\"fix_scrollbar_bug message\" ");
719                 wprintf("onMouseOver=document.getElementById(\"msg%ld\").style.visibility=\"visible\" ", msgnum);
720                 wprintf("onMouseOut=document.getElementById(\"msg%ld\").style.visibility=\"hidden\" >", msgnum);
721         }
722
723         /** start msg buttons */
724
725         if (!printable_view) {
726                 wprintf("<p id=\"msg%ld\" class=\"msgbuttons\" >\n",msgnum);
727
728                 /** Reply */
729                 if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
730                         wprintf("<a href=\"display_enter");
731                         if (WC->is_mailbox) {
732                                 wprintf("?replyquote=%ld", msgnum);
733                         }
734                         wprintf("?recp=");
735                         urlescputs(reply_to);
736                         if (!IsEmptyStr(m_subject)) {
737                                 wprintf("?subject=");
738                                 if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
739                                 urlescputs(m_subject);
740                         }
741                         wprintf("\"><span>[</span>%s<span>]</span></a> ", _("Reply"));
742                 }
743
744                 /** ReplyQuoted */
745                 if ( (WC->wc_view == VIEW_MAILBOX) || (WC->wc_view == VIEW_BBS) ) {
746                         if (!WC->is_mailbox) {
747                                 wprintf("<a href=\"display_enter");
748                                 wprintf("?replyquote=%ld", msgnum);
749                                 wprintf("?recp=");
750                                 urlescputs(reply_to);
751                                 if (!IsEmptyStr(m_subject)) {
752                                         wprintf("?subject=");
753                                         if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
754                                         urlescputs(m_subject);
755                                 }
756                                 wprintf("\"><span>[</span>%s<span>]</span></a> ", _("ReplyQuoted"));
757                         }
758                 }
759
760                 /** ReplyAll */
761                 if (WC->wc_view == VIEW_MAILBOX) {
762                         wprintf("<a href=\"display_enter");
763                         wprintf("?replyquote=%ld", msgnum);
764                         wprintf("?recp=");
765                         urlescputs(reply_to);
766                         wprintf("?cc=");
767                         urlescputs(reply_all);
768                         if (!IsEmptyStr(m_subject)) {
769                                 wprintf("?subject=");
770                                 if (strncasecmp(m_subject, "Re:", 3)) wprintf("Re:%20");
771                                 urlescputs(m_subject);
772                         }
773                         wprintf("\"><span>[</span>%s<span>]</span></a> ", _("ReplyAll"));
774                 }
775
776                 /** Forward */
777                 if (WC->wc_view == VIEW_MAILBOX) {
778                         wprintf("<a href=\"display_enter?fwdquote=%ld?subject=", msgnum);
779                         if (strncasecmp(m_subject, "Fwd:", 4)) wprintf("Fwd:%20");
780                         urlescputs(m_subject);
781                         wprintf("\"><span>[</span>%s<span>]</span></a> ", _("Forward"));
782                 }
783
784                 /** If this is one of my own rooms, or if I'm an Aide or Room Aide, I can move/delete */
785                 if ( (WC->is_room_aide) || (WC->is_mailbox) || (WC->room_flags2 & QR2_COLLABDEL) ) {
786                         /** Move */
787                         wprintf("<a href=\"confirm_move_msg?msgid=%ld\"><span>[</span>%s<span>]</span></a> ",
788                                 msgnum, _("Move"));
789         
790                         /** Delete */
791                         wprintf("<a href=\"delete_msg?msgid=%ld\" "
792                                 "onClick=\"return confirm('%s');\">"
793                                 "<span>[</span>%s<span>]</span> "
794                                 "</a> ", msgnum, _("Delete this message?"), _("Delete")
795                         );
796                 }
797
798                 /** Headers */
799                 wprintf("<a href=\"#\" onClick=\"window.open('msgheaders/%ld', 'headers%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
800                         "<span>[</span>%s<span>]</span></a>", msgnum, msgnum, _("Headers"));
801
802
803                 /** Print */
804                 wprintf("<a href=\"#\" onClick=\"window.open('printmsg/%ld', 'print%ld', 'toolbar=no,location=no,directories=no,copyhistory=no,status=yes,scrollbars=yes,resizable=yes,width=600,height=400'); \" >"
805                         "<span>[</span>%s<span>]</span></a>", msgnum, msgnum, _("Print"));
806
807                 wprintf("</p>");
808
809         }
810
811         /** begin message header table */
812         wprintf("<div class=\"message_header\">");
813
814
815
816         strcpy(m_subject, "");
817         strcpy(m_cc, "");
818
819         while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
820                 if (!strcmp(buf, "000")) {
821                         wprintf("<i>");
822                         wprintf(_("unexpected end of message"));
823                         wprintf(" (1)</i><br /><br />\n");
824                         wprintf("</div>\n");
825                         return;
826                 }
827                 if (!strncasecmp(buf, "nhdr=yes", 8))
828                         nhdr = 1;
829                 if (nhdr == 1)
830                         buf[0] = '_';
831                 if (!strncasecmp(buf, "type=", 5))
832                         format_type = atoi(&buf[5]);
833                 if (!strncasecmp(buf, "from=", 5)) {
834                         strcpy(from, &buf[5]);
835                         wprintf(_("from "));
836                         wprintf("<a href=\"showuser?who=");
837 #ifdef HAVE_ICONV
838                         utf8ify_rfc822_string(from);
839 #endif
840                         urlescputs(from);
841                         wprintf("\">");
842                         escputs(from);
843                         wprintf("</a> ");
844                 }
845                 if (!strncasecmp(buf, "subj=", 5)) {
846                         safestrncpy(m_subject, &buf[5], sizeof m_subject);
847                 }
848                 if (!strncasecmp(buf, "cccc=", 5)) {
849                         int len;
850                         safestrncpy(m_cc, &buf[5], sizeof m_cc);
851                         if (!IsEmptyStr(reply_all)) {
852                                 strcat(reply_all, ", ");
853                         }
854                         len = strlen(reply_all);
855                         safestrncpy(&reply_all[len], &buf[5],
856                                 (sizeof reply_all - len) );
857                 }
858                 if ((!strncasecmp(buf, "hnod=", 5))
859                     && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
860                         wprintf("(%s) ", &buf[5]);
861                 }
862                 if ((!strncasecmp(buf, "room=", 5))
863                     && (strcasecmp(&buf[5], WC->wc_roomname))
864                     && (!IsEmptyStr(&buf[5])) ) {
865                         wprintf(_("in "));
866                         wprintf("%s&gt; ", &buf[5]);
867                 }
868                 if (!strncasecmp(buf, "rfca=", 5)) {
869                         strcpy(rfca, &buf[5]);
870                         wprintf("&lt;");
871                         escputs(rfca);
872                         wprintf("&gt; ");
873                 }
874
875                 if (!strncasecmp(buf, "node=", 5)) {
876                         strcpy(node, &buf[5]);
877                         if ( ((WC->room_flags & QR_NETWORK)
878                         || ((strcasecmp(&buf[5], serv_info.serv_nodename)
879                         && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
880                         && (IsEmptyStr(rfca))
881                         ) {
882                                 wprintf("@%s ", &buf[5]);
883                         }
884                 }
885                 if (!strncasecmp(buf, "rcpt=", 5)) {
886                         int len;
887                         wprintf(_("to "));
888                         if (!IsEmptyStr(reply_all)) {
889                                 strcat(reply_all, ", ");
890                         }
891                         len = strlen(reply_all);
892                         safestrncpy(&reply_all[len], &buf[5],
893                                 (sizeof reply_all - len) );
894 #ifdef HAVE_ICONV
895                         utf8ify_rfc822_string(&buf[5]);
896 #endif
897                         escputs(&buf[5]);
898                         wprintf(" ");
899                 }
900                 if (!strncasecmp(buf, "time=", 5)) {
901                         fmt_date(now, atol(&buf[5]), 0);
902                         wprintf("<span>");
903                         wprintf("%s ", now);
904                         wprintf("</span>");
905                 }
906
907                 if (!strncasecmp(buf, "part=", 5)) {
908                         extract_token(mime_name, &buf[5], 0, '|', sizeof mime_filename);
909                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
910                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
911                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
912                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
913                         mime_length = extract_int(&buf[5], 5);
914
915                         striplt(mime_name);
916                         striplt(mime_filename);
917                         if ( (IsEmptyStr(mime_filename)) && (!IsEmptyStr(mime_name)) ) {
918                                 strcpy(mime_filename, mime_name);
919                         }
920
921                         if (!strcasecmp(mime_content_type, "message/rfc822")) {
922                                 if (!IsEmptyStr(mime_submessages)) {
923                                         strcat(mime_submessages, "|");
924                                 }
925                                 strcat(mime_submessages, mime_partnum);
926                         }
927                         else if ((!strcasecmp(mime_disposition, "inline"))
928                            && (!strncasecmp(mime_content_type, "image/", 6)) ){
929                                 ++num_attach_links;
930                                 attach_links = realloc(attach_links,
931                                         (num_attach_links*sizeof(struct attach_link)));
932                                 safestrncpy(attach_links[num_attach_links-1].partnum, mime_partnum, 32);
933                                 snprintf(attach_links[num_attach_links-1].html, 1024,
934                                         "<img src=\"mimepart/%ld/%s/%s\">",
935                                         msgnum, mime_partnum, mime_filename);
936                         }
937                         else if ( ( (!strcasecmp(mime_disposition, "attachment")) 
938                              || (!strcasecmp(mime_disposition, "inline"))
939                              || (!strcasecmp(mime_disposition, ""))
940                              ) && (!IsEmptyStr(mime_content_type))
941                         ) {
942                                 ++num_attach_links;
943                                 attach_links = realloc(attach_links,
944                                         (num_attach_links*sizeof(struct attach_link)));
945                                 safestrncpy(attach_links[num_attach_links-1].partnum, mime_partnum, 32);
946                                 snprintf(attach_links[num_attach_links-1].html, 1024,
947                                         "<img src=\"static/diskette_24x.gif\" "
948                                         "border=0 align=middle>\n"
949                                         "%s (%s, %d bytes) [ "
950                                         "<a href=\"mimepart/%ld/%s/%s\""
951                                         "target=\"wc.%ld.%s\">%s</a>"
952                                         " | "
953                                         "<a href=\"mimepart_download/%ld/%s/%s\">%s</a>"
954                                         " ]<br />\n",
955                                         mime_filename,
956                                         mime_content_type, mime_length,
957                                         msgnum, mime_partnum, mime_filename,
958                                         msgnum, mime_partnum,
959                                         _("View"),
960                                         msgnum, mime_partnum, mime_filename,
961                                         _("Download")
962                                 );
963                         }
964
965                         /** begin handler prep ***/
966                         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
967                            || (!strcasecmp(mime_content_type, "text/vcard")) ) {
968                                 strcpy(vcard_partnum, mime_partnum);
969                         }
970
971                         if (!strcasecmp(mime_content_type, "text/calendar")) {
972                                 strcpy(cal_partnum, mime_partnum);
973                         }
974
975                         /** end handler prep ***/
976
977                 }
978
979         }
980
981         /** Generate a reply-to address */
982         if (!IsEmptyStr(rfca)) {
983                 if (!IsEmptyStr(from)) {
984                         snprintf(reply_to, sizeof(reply_to), "%s <%s>", from, rfca);
985                 }
986                 else {
987                         strcpy(reply_to, rfca);
988                 }
989         }
990         else {
991         if ((!IsEmptyStr(node))
992                    && (strcasecmp(node, serv_info.serv_nodename))
993                    && (strcasecmp(node, serv_info.serv_humannode)) ) {
994                         snprintf(reply_to, sizeof(reply_to), "%s @ %s",
995                                 from, node);
996                 }
997                 else {
998                         snprintf(reply_to, sizeof(reply_to), "%s", from);
999                 }
1000         }
1001
1002         if (nhdr == 1) {
1003                 wprintf("****");
1004         }
1005
1006         wprintf("</div>");
1007
1008 #ifdef HAVE_ICONV
1009         utf8ify_rfc822_string(m_cc);
1010         utf8ify_rfc822_string(m_subject);
1011 #endif
1012         if (!IsEmptyStr(m_cc)) {
1013                 wprintf("<div class=\"message_subject\">");
1014                 wprintf(_("CC:"));
1015                 wprintf(" ");
1016                 escputs(m_cc);
1017                 wprintf("</div>");
1018         }
1019         if (!IsEmptyStr(m_subject)) {
1020                 wprintf("<div class=\"message_subject\">");
1021                 wprintf(_("Subject:"));
1022                 wprintf(" ");
1023                 escputs(m_subject);
1024                 wprintf("</div>");
1025         }
1026
1027         /** Begin body */
1028         wprintf("<div class=\"message_content\">");
1029
1030         /**
1031          * Learn the content type
1032          */
1033         strcpy(mime_content_type, "text/plain");
1034         while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) {
1035                 if (!strcmp(buf, "000")) {
1036                         wprintf("<i>");
1037                         wprintf(_("unexpected end of message"));
1038                         wprintf(" (2)</i><br /><br />\n");
1039                         goto ENDBODY;
1040                 }
1041                 if (!strncasecmp(buf, "X-Citadel-MSG4-Partnum:", 23)) {
1042                         safestrncpy(msg4_partnum, &buf[23], sizeof(msg4_partnum));
1043                         striplt(msg4_partnum);
1044                 }
1045                 if (!strncasecmp(buf, "Content-type:", 13)) {
1046                         int len;
1047                         safestrncpy(mime_content_type, &buf[13], sizeof(mime_content_type));
1048                         striplt(mime_content_type);
1049                         len = strlen(mime_content_type);
1050                         for (i=0; i<len; ++i) {
1051                                 if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
1052                                         safestrncpy(mime_charset, &mime_content_type[i+8],
1053                                                 sizeof mime_charset);
1054                                 }
1055                         }
1056                         for (i=0; i<len; ++i) {
1057                                 if (mime_content_type[i] == ';') {
1058                                         mime_content_type[i] = 0;
1059                                         len = i - 1;
1060                                 }
1061                         }
1062                         len = strlen(mime_charset);
1063                         for (i=0; i<len; ++i) {
1064                                 if (mime_charset[i] == ';') {
1065                                         mime_charset[i] = 0;
1066                                         len = i - 1;
1067                                 }
1068                         }
1069                 }
1070         }
1071
1072         /** Set up a character set conversion if we need to (and if we can) */
1073 #ifdef HAVE_ICONV
1074         if (strchr(mime_charset, ';')) strcpy(strchr(mime_charset, ';'), "");
1075         if ( (strcasecmp(mime_charset, "us-ascii"))
1076            && (strcasecmp(mime_charset, "UTF-8"))
1077            && (strcasecmp(mime_charset, ""))
1078         ) {
1079                 ic = ctdl_iconv_open("UTF-8", mime_charset);
1080                 if (ic == (iconv_t)(-1) ) {
1081                         lprintf(5, "%s:%d iconv_open(UTF-8, %s) failed: %s\n",
1082                                 __FILE__, __LINE__, mime_charset, strerror(errno));
1083                 }
1084         }
1085 #endif
1086
1087         /** Messages in legacy Citadel variformat get handled thusly... */
1088         if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
1089                 fmout("JUSTIFY");
1090         }
1091
1092         /** Boring old 80-column fixed format text gets handled this way... */
1093         else if ( (!strcasecmp(mime_content_type, "text/plain"))
1094                 || (!strcasecmp(mime_content_type, "text")) ) {
1095                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1096                         int len;
1097                         len = strlen(buf);
1098                         if (buf[len-1] == '\n') buf[--len] = 0;
1099                         if (buf[len-1] == '\r') buf[--len] = 0;
1100
1101 #ifdef HAVE_ICONV
1102                         if (ic != (iconv_t)(-1) ) {
1103                                 ibuf = buf;
1104                                 ibuflen = strlen(ibuf);
1105                                 obuflen = SIZ;
1106                                 obuf = (char *) malloc(obuflen);
1107                                 osav = obuf;
1108                                 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
1109                                 osav[SIZ-obuflen] = 0;
1110                                 safestrncpy(buf, osav, sizeof buf);
1111                                 free(osav);
1112                         }
1113 #endif
1114
1115                         len = strlen(buf);
1116                         while ((!IsEmptyStr(buf)) && (isspace(buf[len-1])))
1117                                 buf[--len] = 0;
1118                         if ((bq == 0) &&
1119                         ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
1120                                 wprintf("<blockquote>");
1121                                 bq = 1;
1122                         } else if ((bq == 1) &&
1123                                 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
1124                                 wprintf("</blockquote>");
1125                                 bq = 0;
1126                         }
1127                         wprintf("<tt>");
1128                         url(buf);
1129                         escputs(buf);
1130                         wprintf("</tt><br />\n");
1131                 }
1132                 wprintf("</i><br />");
1133         }
1134
1135         else /** HTML is fun, but we've got to strip it first */
1136         if (!strcasecmp(mime_content_type, "text/html")) {
1137                 output_html(mime_charset, (WC->wc_view == VIEW_WIKI ? 1 : 0));
1138         }
1139
1140         /** Unknown weirdness */
1141         else {
1142                 wprintf(_("I don't know how to display %s"), mime_content_type);
1143                 wprintf("<br />\n", mime_content_type);
1144                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
1145         }
1146
1147         /** If there are attached submessages, display them now... */
1148         if ( (!IsEmptyStr(mime_submessages)) && (!section[0]) ) {
1149                 for (i=0; i<num_tokens(mime_submessages, '|'); ++i) {
1150                         extract_token(buf, mime_submessages, i, '|', sizeof buf);
1151                         /** use printable_view to suppress buttons */
1152                         wprintf("<blockquote>");
1153                         read_message(msgnum, 1, buf);
1154                         wprintf("</blockquote>");
1155                 }
1156         }
1157
1158
1159         /** Afterwards, offer links to download attachments 'n' such */
1160         if ( (num_attach_links > 0) && (!section[0]) ) {
1161                 for (i=0; i<num_attach_links; ++i) {
1162                         if (strcasecmp(attach_links[i].partnum, msg4_partnum)) {
1163                                 wprintf("%s", attach_links[i].html);
1164                         }
1165                 }
1166         }
1167
1168         /** Handler for vCard parts */
1169         if (!IsEmptyStr(vcard_partnum)) {
1170                 part_source = load_mimepart(msgnum, vcard_partnum);
1171                 if (part_source != NULL) {
1172
1173                         /** If it's my vCard I can edit it */
1174                         if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
1175                                 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
1176                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
1177                         ) {
1178                                 wprintf("<a href=\"edit_vcard?"
1179                                         "msgnum=%ld?partnum=%s\">",
1180                                         msgnum, vcard_partnum);
1181                                 wprintf("[%s]</a>", _("edit"));
1182                         }
1183
1184                         /** In all cases, display the full card */
1185                         display_vcard(part_source, 0, 1, NULL);
1186                 }
1187         }
1188
1189         /** Handler for calendar parts */
1190         if (!IsEmptyStr(cal_partnum)) {
1191                 part_source = load_mimepart(msgnum, cal_partnum);
1192                 if (part_source != NULL) {
1193                         cal_process_attachment(part_source,
1194                                                 msgnum, cal_partnum);
1195                 }
1196         }
1197
1198         if (part_source) {
1199                 free(part_source);
1200                 part_source = NULL;
1201         }
1202
1203 ENDBODY:
1204         wprintf("</div>\n");
1205
1206         /** end everythingamundo table */
1207         if (!printable_view) {
1208                 wprintf("</div>\n");
1209         }
1210
1211         if (num_attach_links > 0) {
1212                 free(attach_links);
1213         }
1214
1215 #ifdef HAVE_ICONV
1216         if (ic != (iconv_t)(-1) ) {
1217                 iconv_close(ic);
1218         }
1219 #endif
1220 }
1221
1222
1223
1224 /**
1225  * \brief Unadorned HTML output of an individual message, suitable
1226  * for placing in a hidden iframe, for printing, or whatever
1227  *
1228  * \param msgnum_as_string Message number, as a string instead of as a long int
1229  */
1230 void embed_message(char *msgnum_as_string) {
1231         long msgnum = 0L;
1232
1233         msgnum = atol(msgnum_as_string);
1234         begin_ajax_response();
1235         read_message(msgnum, 0, "");
1236         end_ajax_response();
1237 }
1238
1239
1240 /**
1241  * \brief Printable view of a message
1242  *
1243  * \param msgnum_as_string Message number, as a string instead of as a long int
1244  */
1245 void print_message(char *msgnum_as_string) {
1246         long msgnum = 0L;
1247
1248         msgnum = atol(msgnum_as_string);
1249         output_headers(0, 0, 0, 0, 0, 0);
1250
1251         wprintf("Content-type: text/html\r\n"
1252                 "Server: %s\r\n"
1253                 "Connection: close\r\n",
1254                 SERVER);
1255         begin_burst();
1256
1257         wprintf("\r\n\r\n<html>\n"
1258                 "<head><title>Printable view</title></head>\n"
1259                 "<body onLoad=\" window.print(); window.close(); \">\n"
1260         );
1261         
1262         read_message(msgnum, 1, "");
1263
1264         wprintf("\n</body></html>\n\n");
1265         wDumpContent(0);
1266 }
1267
1268
1269
1270 /**
1271  * \brief Display a message's headers
1272  *
1273  * \param msgnum_as_string Message number, as a string instead of as a long int
1274  */
1275 void display_headers(char *msgnum_as_string) {
1276         long msgnum = 0L;
1277         char buf[1024];
1278
1279         msgnum = atol(msgnum_as_string);
1280         output_headers(0, 0, 0, 0, 0, 0);
1281
1282         wprintf("Content-type: text/plain\r\n"
1283                 "Server: %s\r\n"
1284                 "Connection: close\r\n",
1285                 SERVER);
1286         begin_burst();
1287
1288         serv_printf("MSG2 %ld|3", msgnum);
1289         serv_getln(buf, sizeof buf);
1290         if (buf[0] == '1') {
1291                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1292                         wprintf("%s\n", buf);
1293                 }
1294         }
1295
1296         wDumpContent(0);
1297 }
1298
1299
1300
1301 /**
1302  * \brief Read message in simple, JavaScript-embeddable form for 'forward'
1303  *      or 'reply quoted' operations.
1304  *
1305  * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks
1306  *       in this function.  Doing so would throw a JavaScript error in the
1307  *       'supplied text' argument to the editor.
1308  *
1309  * \param msgnum Message number of the message we want to quote
1310  * \param forward_attachments Nonzero if we want attachments to be forwarded
1311  */
1312 void pullquote_message(long msgnum, int forward_attachments, int include_headers) {
1313         char buf[SIZ];
1314         char mime_partnum[256];
1315         char mime_filename[256];
1316         char mime_content_type[256];
1317         char mime_charset[256];
1318         char mime_disposition[256];
1319         int mime_length;
1320         char *attachments = NULL;
1321         char *ptr = NULL;
1322         int num_attachments = 0;
1323         struct wc_attachment *att, *aptr;
1324         char m_subject[256];
1325         char from[256];
1326         char node[256];
1327         char rfca[256];
1328         char reply_to[512];
1329         char now[256];
1330         int format_type = 0;
1331         int nhdr = 0;
1332         int bq = 0;
1333         int i = 0;
1334 #ifdef HAVE_ICONV
1335         iconv_t ic = (iconv_t)(-1) ;
1336         char *ibuf;                /**< Buffer of characters to be converted */
1337         char *obuf;                /**< Buffer for converted characters      */
1338         size_t ibuflen;    /**< Length of input buffer         */
1339         size_t obuflen;    /**< Length of output buffer       */
1340         char *osav;                /**< Saved pointer to output buffer       */
1341 #endif
1342
1343         strcpy(from, "");
1344         strcpy(node, "");
1345         strcpy(rfca, "");
1346         strcpy(reply_to, "");
1347         strcpy(mime_content_type, "text/plain");
1348         strcpy(mime_charset, "us-ascii");
1349
1350         serv_printf("MSG4 %ld", msgnum);
1351         serv_getln(buf, sizeof buf);
1352         if (buf[0] != '1') {
1353                 wprintf(_("ERROR:"));
1354                 wprintf("%s<br />", &buf[4]);
1355                 return;
1356         }
1357
1358         strcpy(m_subject, "");
1359
1360         while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
1361                 if (!strcmp(buf, "000")) {
1362                         wprintf("%s (3)", _("unexpected end of message"));
1363                         return;
1364                 }
1365                 if (include_headers) {
1366                         if (!strncasecmp(buf, "nhdr=yes", 8))
1367                                 nhdr = 1;
1368                         if (nhdr == 1)
1369                                 buf[0] = '_';
1370                         if (!strncasecmp(buf, "type=", 5))
1371                                 format_type = atoi(&buf[5]);
1372                         if (!strncasecmp(buf, "from=", 5)) {
1373                                 strcpy(from, &buf[5]);
1374                                 wprintf(_("from "));
1375 #ifdef HAVE_ICONV
1376                                 utf8ify_rfc822_string(from);
1377 #endif
1378                                 msgescputs(from);
1379                         }
1380                         if (!strncasecmp(buf, "subj=", 5)) {
1381                                 strcpy(m_subject, &buf[5]);
1382                         }
1383                         if ((!strncasecmp(buf, "hnod=", 5))
1384                             && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
1385                                 wprintf("(%s) ", &buf[5]);
1386                         }
1387                         if ((!strncasecmp(buf, "room=", 5))
1388                             && (strcasecmp(&buf[5], WC->wc_roomname))
1389                             && (!IsEmptyStr(&buf[5])) ) {
1390                                 wprintf(_("in "));
1391                                 wprintf("%s&gt; ", &buf[5]);
1392                         }
1393                         if (!strncasecmp(buf, "rfca=", 5)) {
1394                                 strcpy(rfca, &buf[5]);
1395                                 wprintf("&lt;");
1396                                 msgescputs(rfca);
1397                                 wprintf("&gt; ");
1398                         }
1399         
1400                         if (!strncasecmp(buf, "node=", 5)) {
1401                                 strcpy(node, &buf[5]);
1402                                 if ( ((WC->room_flags & QR_NETWORK)
1403                                 || ((strcasecmp(&buf[5], serv_info.serv_nodename)
1404                                 && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
1405                                 && (IsEmptyStr(rfca))
1406                                 ) {
1407                                         wprintf("@%s ", &buf[5]);
1408                                 }
1409                         }
1410                         if (!strncasecmp(buf, "rcpt=", 5)) {
1411                                 wprintf(_("to "));
1412                                 wprintf("%s ", &buf[5]);
1413                         }
1414                         if (!strncasecmp(buf, "time=", 5)) {
1415                                 fmt_date(now, atol(&buf[5]), 0);
1416                                 wprintf("%s ", now);
1417                         }
1418                 }
1419
1420                 /**
1421                  * Save attachment info for later.  We can't start downloading them
1422                  * yet because we're in the middle of a server transaction.
1423                  */
1424                 if (!strncasecmp(buf, "part=", 5)) {
1425                         ptr = malloc( (strlen(buf) + ((attachments != NULL) ? strlen(attachments) : 0)) ) ;
1426                         if (ptr != NULL) {
1427                                 ++num_attachments;
1428                                 sprintf(ptr, "%s%s\n",
1429                                         ((attachments != NULL) ? attachments : ""),
1430                                         &buf[5]
1431                                 );
1432                                 free(attachments);
1433                                 attachments = ptr;
1434                                 lprintf(9, "attachments=<%s>\n", attachments);
1435                         }
1436                 }
1437
1438         }
1439
1440         if (include_headers) {
1441                 wprintf("<br>");
1442
1443 #ifdef HAVE_ICONV
1444                 utf8ify_rfc822_string(m_subject);
1445 #endif
1446                 if (!IsEmptyStr(m_subject)) {
1447                         wprintf(_("Subject:"));
1448                         wprintf(" ");
1449                         msgescputs(m_subject);
1450                         wprintf("<br />");
1451                 }
1452
1453                 /**
1454                  * Begin body
1455                  */
1456                 wprintf("<br />");
1457         }
1458
1459         /**
1460          * Learn the content type
1461          */
1462         strcpy(mime_content_type, "text/plain");
1463         while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) {
1464                 if (!strcmp(buf, "000")) {
1465                         wprintf("%s (4)", _("unexpected end of message"));
1466                         goto ENDBODY;
1467                 }
1468                 if (!strncasecmp(buf, "Content-type: ", 14)) {
1469                         int len;
1470                         safestrncpy(mime_content_type, &buf[14],
1471                                 sizeof(mime_content_type));
1472                         for (i=0; i<strlen(mime_content_type); ++i) {
1473                                 if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
1474                                         safestrncpy(mime_charset, &mime_content_type[i+8],
1475                                                 sizeof mime_charset);
1476                                 }
1477                         }
1478                         len = strlen(mime_content_type);
1479                         for (i=0; i<len; ++i) {
1480                                 if (mime_content_type[i] == ';') {
1481                                         mime_content_type[i] = 0;
1482                                         len = i - 1;
1483                                 }
1484                         }
1485                         len = strlen(mime_charset);
1486                         for (i=0; i<len; ++i) {
1487                                 if (mime_charset[i] == ';') {
1488                                         mime_charset[i] = 0;
1489                                         len = i - 1;
1490                                 }
1491                         }
1492                 }
1493         }
1494
1495         /** Set up a character set conversion if we need to (and if we can) */
1496 #ifdef HAVE_ICONV
1497         if ( (strcasecmp(mime_charset, "us-ascii"))
1498            && (strcasecmp(mime_charset, "UTF-8"))
1499            && (strcasecmp(mime_charset, ""))
1500         ) {
1501                 ic = ctdl_iconv_open("UTF-8", mime_charset);
1502                 if (ic == (iconv_t)(-1) ) {
1503                         lprintf(5, "%s:%d iconv_open(%s, %s) failed: %s\n",
1504                                 __FILE__, __LINE__, "UTF-8", mime_charset, strerror(errno));
1505                 }
1506         }
1507 #endif
1508
1509         /** Messages in legacy Citadel variformat get handled thusly... */
1510         if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
1511                 pullquote_fmout();
1512         }
1513
1514         /* Boring old 80-column fixed format text gets handled this way... */
1515         else if (!strcasecmp(mime_content_type, "text/plain")) {
1516                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1517                         int len;
1518                         len = strlen(buf);
1519                         if (buf[len-1] == '\n') buf[--len] = 0;
1520                         if (buf[len-1] == '\r') buf[--len] = 0;
1521
1522 #ifdef HAVE_ICONV
1523                         if (ic != (iconv_t)(-1) ) {
1524                                 ibuf = buf;
1525                                 ibuflen = len;
1526                                 obuflen = SIZ;
1527                                 obuf = (char *) malloc(obuflen);
1528                                 osav = obuf;
1529                                 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
1530                                 osav[SIZ-obuflen] = 0;
1531                                 safestrncpy(buf, osav, sizeof buf);
1532                                 free(osav);
1533                         }
1534 #endif
1535
1536                         len = strlen(buf);
1537                         while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1]))) 
1538                                 buf[--len] = 0;
1539                         if ((bq == 0) &&
1540                         ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
1541                                 wprintf("<blockquote>");
1542                                 bq = 1;
1543                         } else if ((bq == 1) &&
1544                                 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
1545                                 wprintf("</blockquote>");
1546                                 bq = 0;
1547                         }
1548                         wprintf("<tt>");
1549                         url(buf);
1550                         msgescputs(buf);
1551                         wprintf("</tt><br />");
1552                 }
1553                 wprintf("</i><br />");
1554         }
1555
1556         /** HTML just gets escaped and stuffed back into the editor */
1557         else if (!strcasecmp(mime_content_type, "text/html")) {
1558                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1559                         strcat(buf, "\n");
1560                         msgescputs(buf);
1561                 }
1562         }
1563
1564         /** Unknown weirdness ... don't know how to handle this content type */
1565         else {
1566                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
1567         }
1568
1569 ENDBODY:
1570         /** end of body handler */
1571
1572         /*
1573          * If there were attachments, we have to download them and insert them
1574          * into the attachment chain for the forwarded message we are composing.
1575          */
1576         if ( (forward_attachments) && (num_attachments) ) {
1577                 for (i=0; i<num_attachments; ++i) {
1578                         extract_token(buf, attachments, i, '\n', sizeof buf);
1579                         extract_token(mime_filename, buf, 1, '|', sizeof mime_filename);
1580                         extract_token(mime_partnum, buf, 2, '|', sizeof mime_partnum);
1581                         extract_token(mime_disposition, buf, 3, '|', sizeof mime_disposition);
1582                         extract_token(mime_content_type, buf, 4, '|', sizeof mime_content_type);
1583                         mime_length = extract_int(buf, 5);
1584
1585                         /*
1586                          * tracing  ... uncomment if necessary
1587                          *
1588                          */
1589                         lprintf(9, "fwd filename: %s\n", mime_filename);
1590                         lprintf(9, "fwd partnum : %s\n", mime_partnum);
1591                         lprintf(9, "fwd conttype: %s\n", mime_content_type);
1592                         lprintf(9, "fwd dispose : %s\n", mime_disposition);
1593                         lprintf(9, "fwd length  : %d\n", mime_length);
1594
1595                         if ( (!strcasecmp(mime_disposition, "inline"))
1596                            || (!strcasecmp(mime_disposition, "attachment")) ) {
1597                 
1598                                 /* Create an attachment struct from this mime part... */
1599                                 att = malloc(sizeof(struct wc_attachment));
1600                                 memset(att, 0, sizeof(struct wc_attachment));
1601                                 att->length = mime_length;
1602                                 strcpy(att->content_type, mime_content_type);
1603                                 strcpy(att->filename, mime_filename);
1604                                 att->next = NULL;
1605                                 att->data = load_mimepart(msgnum, mime_partnum);
1606                 
1607                                 /* And add it to the list. */
1608                                 if (WC->first_attachment == NULL) {
1609                                         WC->first_attachment = att;
1610                                 }
1611                                 else {
1612                                         aptr = WC->first_attachment;
1613                                         while (aptr->next != NULL) aptr = aptr->next;
1614                                         aptr->next = att;
1615                                 }
1616                         }
1617
1618                 }
1619         }
1620
1621 #ifdef HAVE_ICONV
1622         if (ic != (iconv_t)(-1) ) {
1623                 iconv_close(ic);
1624         }
1625 #endif
1626
1627         if (attachments != NULL) {
1628                 free(attachments);
1629         }
1630 }
1631
1632 /**
1633  * \brief Display one row in the mailbox summary view
1634  *
1635  * \param num The row number to be displayed
1636  */
1637 void display_summarized(int num) {
1638         char datebuf[64];
1639
1640         wprintf("<tr id=\"m%ld\" style=\"font-weight:%s;\" "
1641                 "onMouseDown=\"CtdlMoveMsgMouseDown(event,%ld)\">",
1642                 WC->summ[num].msgnum,
1643                 (WC->summ[num].is_new ? "bold" : "normal"),
1644                 WC->summ[num].msgnum
1645         );
1646
1647         wprintf("<td width=%d%%>", SUBJ_COL_WIDTH_PCT);
1648         escputs(WC->summ[num].subj);
1649         wprintf("</td>");
1650
1651         wprintf("<td width=%d%%>", SENDER_COL_WIDTH_PCT);
1652         escputs(WC->summ[num].from);
1653         wprintf("</td>");
1654
1655         wprintf("<td width=%d%%>", DATE_PLUS_BUTTONS_WIDTH_PCT);
1656         fmt_date(datebuf, WC->summ[num].date, 1);       /* brief */
1657         escputs(datebuf);
1658         wprintf("</td>");
1659
1660         wprintf("</tr>\n");
1661 }
1662
1663
1664
1665 /**
1666  * \brief display the adressbook overview
1667  * \param msgnum the citadel message number
1668  * \param alpha what????
1669  */
1670 void display_addressbook(long msgnum, char alpha) {
1671         char buf[SIZ];
1672         char mime_partnum[SIZ];
1673         char mime_filename[SIZ];
1674         char mime_content_type[SIZ];
1675         char mime_disposition[SIZ];
1676         int mime_length;
1677         char vcard_partnum[SIZ];
1678         char *vcard_source = NULL;
1679         struct message_summary summ;
1680
1681         memset(&summ, 0, sizeof(summ));
1682         safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
1683
1684         sprintf(buf, "MSG0 %ld|1", msgnum);     /* ask for headers only */
1685         serv_puts(buf);
1686         serv_getln(buf, sizeof buf);
1687         if (buf[0] != '1') return;
1688
1689         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1690                 if (!strncasecmp(buf, "part=", 5)) {
1691                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
1692                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
1693                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
1694                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
1695                         mime_length = extract_int(&buf[5], 5);
1696
1697                         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
1698                            || (!strcasecmp(mime_content_type, "text/vcard")) ) {
1699                                 strcpy(vcard_partnum, mime_partnum);
1700                         }
1701
1702                 }
1703         }
1704
1705         if (!IsEmptyStr(vcard_partnum)) {
1706                 vcard_source = load_mimepart(msgnum, vcard_partnum);
1707                 if (vcard_source != NULL) {
1708
1709                         /** Display the summary line */
1710                         display_vcard(vcard_source, alpha, 0, NULL);
1711
1712                         /** If it's my vCard I can edit it */
1713                         if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
1714                                 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
1715                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
1716                         ) {
1717                                 wprintf("<a href=\"edit_vcard?"
1718                                         "msgnum=%ld?partnum=%s\">",
1719                                         msgnum, vcard_partnum);
1720                                 wprintf("[%s]</a>", _("edit"));
1721                         }
1722
1723                         free(vcard_source);
1724                 }
1725         }
1726
1727 }
1728
1729
1730
1731 /**
1732  * \brief  If it's an old "Firstname Lastname" style record, try to convert it.
1733  * \param namebuf name to analyze, reverse if nescessary
1734  */
1735 void lastfirst_firstlast(char *namebuf) {
1736         char firstname[SIZ];
1737         char lastname[SIZ];
1738         int i;
1739
1740         if (namebuf == NULL) return;
1741         if (strchr(namebuf, ';') != NULL) return;
1742
1743         i = num_tokens(namebuf, ' ');
1744         if (i < 2) return;
1745
1746         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
1747         remove_token(namebuf, i-1, ' ');
1748         strcpy(firstname, namebuf);
1749         sprintf(namebuf, "%s; %s", lastname, firstname);
1750 }
1751
1752 /**
1753  * \brief fetch what??? name
1754  * \param msgnum the citadel message number
1755  * \param namebuf where to put the name in???
1756  */
1757 void fetch_ab_name(long msgnum, char *namebuf) {
1758         char buf[SIZ];
1759         char mime_partnum[SIZ];
1760         char mime_filename[SIZ];
1761         char mime_content_type[SIZ];
1762         char mime_disposition[SIZ];
1763         int mime_length;
1764         char vcard_partnum[SIZ];
1765         char *vcard_source = NULL;
1766         int i, len;
1767         struct message_summary summ;
1768
1769         if (namebuf == NULL) return;
1770         strcpy(namebuf, "");
1771
1772         memset(&summ, 0, sizeof(summ));
1773         safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
1774
1775         sprintf(buf, "MSG0 %ld|0", msgnum);     /** unfortunately we need the mime info now */
1776         serv_puts(buf);
1777         serv_getln(buf, sizeof buf);
1778         if (buf[0] != '1') return;
1779
1780         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1781                 if (!strncasecmp(buf, "part=", 5)) {
1782                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
1783                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
1784                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
1785                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
1786                         mime_length = extract_int(&buf[5], 5);
1787
1788                         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
1789                            || (!strcasecmp(mime_content_type, "text/vcard")) ) {
1790                                 strcpy(vcard_partnum, mime_partnum);
1791                         }
1792
1793                 }
1794         }
1795
1796         if (!IsEmptyStr(vcard_partnum)) {
1797                 vcard_source = load_mimepart(msgnum, vcard_partnum);
1798                 if (vcard_source != NULL) {
1799
1800                         /* Grab the name off the card */
1801                         display_vcard(vcard_source, 0, 0, namebuf);
1802
1803                         free(vcard_source);
1804                 }
1805         }
1806
1807         lastfirst_firstlast(namebuf);
1808         striplt(namebuf);
1809         len = strlen(namebuf);
1810         for (i=0; i<len; ++i) {
1811                 if (namebuf[i] != ';') return;
1812         }
1813         strcpy(namebuf, _("(no name)"));
1814 }
1815
1816
1817
1818 /**
1819  * \brief Record compare function for sorting address book indices
1820  * \param ab1 adressbook one
1821  * \param ab2 adressbook two
1822  */
1823 int abcmp(const void *ab1, const void *ab2) {
1824         return(strcasecmp(
1825                 (((const struct addrbookent *)ab1)->ab_name),
1826                 (((const struct addrbookent *)ab2)->ab_name)
1827         ));
1828 }
1829
1830
1831 /**
1832  * \brief Helper function for do_addrbook_view()
1833  * Converts a name into a three-letter tab label
1834  * \param tabbuf the tabbuffer to add name to
1835  * \param name the name to add to the tabbuffer
1836  */
1837 void nametab(char *tabbuf, char *name) {
1838         stresc(tabbuf, name, 0, 0);
1839         tabbuf[0] = toupper(tabbuf[0]);
1840         tabbuf[1] = tolower(tabbuf[1]);
1841         tabbuf[2] = tolower(tabbuf[2]);
1842         tabbuf[3] = 0;
1843 }
1844
1845
1846 /**
1847  * \brief Render the address book using info we gathered during the scan
1848  * \param addrbook the addressbook to render
1849  * \param num_ab the number of the addressbook
1850  */
1851 void do_addrbook_view(struct addrbookent *addrbook, int num_ab) {
1852         int i = 0;
1853         int displayed = 0;
1854         int bg = 0;
1855         static int NAMESPERPAGE = 60;
1856         int num_pages = 0;
1857         int tabfirst = 0;
1858         char tabfirst_label[64];
1859         int tablast = 0;
1860         char tablast_label[64];
1861         char this_tablabel[64];
1862         int page = 0;
1863         char **tablabels;
1864
1865         if (num_ab == 0) {
1866                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
1867                 wprintf(_("This address book is empty."));
1868                 wprintf("</i></div>\n");
1869                 return;
1870         }
1871
1872         if (num_ab > 1) {
1873                 qsort(addrbook, num_ab, sizeof(struct addrbookent), abcmp);
1874         }
1875
1876         num_pages = (num_ab / NAMESPERPAGE) + 1;
1877
1878         tablabels = malloc(num_pages * sizeof (char *));
1879         if (tablabels == NULL) {
1880                 wprintf("<br /><br /><br /><div align=\"center\"><i>");
1881                 wprintf(_("An internal error has occurred."));
1882                 wprintf("</i></div>\n");
1883                 return;
1884         }
1885
1886         for (i=0; i<num_pages; ++i) {
1887                 tabfirst = i * NAMESPERPAGE;
1888                 tablast = tabfirst + NAMESPERPAGE - 1;
1889                 if (tablast > (num_ab - 1)) tablast = (num_ab - 1);
1890                 nametab(tabfirst_label, addrbook[tabfirst].ab_name);
1891                 nametab(tablast_label, addrbook[tablast].ab_name);
1892                 sprintf(this_tablabel, "%s&nbsp;-&nbsp;%s", tabfirst_label, tablast_label);
1893                 tablabels[i] = strdup(this_tablabel);
1894         }
1895
1896         tabbed_dialog(num_pages, tablabels);
1897         page = (-1);
1898
1899         for (i=0; i<num_ab; ++i) {
1900
1901                 if ((i / NAMESPERPAGE) != page) {       /* New tab */
1902                         page = (i / NAMESPERPAGE);
1903                         if (page > 0) {
1904                                 wprintf("</tr></table>\n");
1905                                 end_tab(page-1, num_pages);
1906                         }
1907                         begin_tab(page, num_pages);
1908                         wprintf("<table border=0 cellspacing=0 cellpadding=3 width=100%%>\n");
1909                         displayed = 0;
1910                 }
1911
1912                 if ((displayed % 4) == 0) {
1913                         if (displayed > 0) {
1914                                 wprintf("</tr>\n");
1915                         }
1916                         bg = 1 - bg;
1917                         wprintf("<tr bgcolor=\"#%s\">",
1918                                 (bg ? "DDDDDD" : "FFFFFF")
1919                         );
1920                 }
1921         
1922                 wprintf("<td>");
1923
1924                 wprintf("<a href=\"readfwd?startmsg=%ld&is_singlecard=1",
1925                         addrbook[i].ab_msgnum);
1926                 wprintf("?maxmsgs=1?summary=0?alpha=%s\">", bstr("alpha"));
1927                 vcard_n_prettyize(addrbook[i].ab_name);
1928                 escputs(addrbook[i].ab_name);
1929                 wprintf("</a></td>\n");
1930                 ++displayed;
1931         }
1932
1933         wprintf("</tr></table>\n");
1934         end_tab((num_pages-1), num_pages);
1935
1936         for (i=0; i<num_pages; ++i) {
1937                 free(tablabels[i]);
1938         }
1939         free(tablabels);
1940 }
1941
1942
1943
1944 /**
1945  * \brief load message pointers from the server
1946  * \param servcmd the citadel command to send to the citserver
1947  * \param with_headers what headers???
1948  */
1949 int load_msg_ptrs(char *servcmd, int with_headers)
1950 {
1951         char buf[1024];
1952         time_t datestamp;
1953         char fullname[128];
1954         char nodename[128];
1955         char inetaddr[128];
1956         char subject[256];
1957         int nummsgs;
1958         int maxload = 0;
1959
1960         int num_summ_alloc = 0;
1961
1962         if (WC->summ != NULL) {
1963                 free(WC->summ);
1964                 WC->num_summ = 0;
1965                 WC->summ = NULL;
1966         }
1967         num_summ_alloc = 100;
1968         WC->num_summ = 0;
1969         WC->summ = malloc(num_summ_alloc * sizeof(struct message_summary));
1970
1971         nummsgs = 0;
1972         maxload = sizeof(WC->msgarr) / sizeof(long) ;
1973         serv_puts(servcmd);
1974         serv_getln(buf, sizeof buf);
1975         if (buf[0] != '1') {
1976                 return (nummsgs);
1977         }
1978         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1979                 if (nummsgs < maxload) {
1980                         WC->msgarr[nummsgs] = extract_long(buf, 0);
1981                         datestamp = extract_long(buf, 1);
1982                         extract_token(fullname, buf, 2, '|', sizeof fullname);
1983                         extract_token(nodename, buf, 3, '|', sizeof nodename);
1984                         extract_token(inetaddr, buf, 4, '|', sizeof inetaddr);
1985                         extract_token(subject, buf, 5, '|', sizeof subject);
1986                         ++nummsgs;
1987
1988                         if (with_headers) {
1989                                 if (nummsgs > num_summ_alloc) {
1990                                         num_summ_alloc *= 2;
1991                                         WC->summ = realloc(WC->summ,
1992                                                 num_summ_alloc * sizeof(struct message_summary));
1993                                 }
1994                                 ++WC->num_summ;
1995
1996                                 memset(&WC->summ[nummsgs-1], 0, sizeof(struct message_summary));
1997                                 WC->summ[nummsgs-1].msgnum = WC->msgarr[nummsgs-1];
1998                                 safestrncpy(WC->summ[nummsgs-1].subj,
1999                                         _("(no subject)"), sizeof WC->summ[nummsgs-1].subj);
2000                                 if (!IsEmptyStr(fullname)) {
2001                                         safestrncpy(WC->summ[nummsgs-1].from,
2002                                                 fullname, sizeof WC->summ[nummsgs-1].from);
2003                                 }
2004                                 if (!IsEmptyStr(subject)) {
2005                                 safestrncpy(WC->summ[nummsgs-1].subj, subject,
2006                                         sizeof WC->summ[nummsgs-1].subj);
2007                                 }
2008 #ifdef HAVE_ICONV
2009                                 /** Handle subjects with RFC2047 encoding */
2010                                 utf8ify_rfc822_string(WC->summ[nummsgs-1].subj);
2011 #endif
2012                                 if (strlen(WC->summ[nummsgs-1].subj) > 75) {
2013                                         strcpy(&WC->summ[nummsgs-1].subj[72], "...");
2014                                 }
2015
2016                                 if (!IsEmptyStr(nodename)) {
2017                                         if ( ((WC->room_flags & QR_NETWORK)
2018                                            || ((strcasecmp(nodename, serv_info.serv_nodename)
2019                                            && (strcasecmp(nodename, serv_info.serv_fqdn)))))
2020                                         ) {
2021                                                 strcat(WC->summ[nummsgs-1].from, " @ ");
2022                                                 strcat(WC->summ[nummsgs-1].from, nodename);
2023                                         }
2024                                 }
2025
2026                                 WC->summ[nummsgs-1].date = datestamp;
2027         
2028 #ifdef HAVE_ICONV
2029                                 /** Handle senders with RFC2047 encoding */
2030                                 utf8ify_rfc822_string(WC->summ[nummsgs-1].from);
2031 #endif
2032                                 if (strlen(WC->summ[nummsgs-1].from) > 25) {
2033                                         strcpy(&WC->summ[nummsgs-1].from[22], "...");
2034                                 }
2035                         }
2036                 }
2037         }
2038         return (nummsgs);
2039 }
2040
2041 /**
2042  * \brief qsort() compatible function to compare two longs in descending order.
2043  *
2044  * \param s1 first number to compare 
2045  * \param s2 second number to compare
2046  */
2047 int longcmp_r(const void *s1, const void *s2) {
2048         long l1;
2049         long l2;
2050
2051         l1 = *(long *)s1;
2052         l2 = *(long *)s2;
2053
2054         if (l1 > l2) return(-1);
2055         if (l1 < l2) return(+1);
2056         return(0);
2057 }
2058
2059  
2060 /**
2061  * \brief qsort() compatible function to compare two message summary structs by ascending subject.
2062  *
2063  * \param s1 first item to compare 
2064  * \param s2 second item to compare
2065  */
2066 int summcmp_subj(const void *s1, const void *s2) {
2067         struct message_summary *summ1;
2068         struct message_summary *summ2;
2069         
2070         summ1 = (struct message_summary *)s1;
2071         summ2 = (struct message_summary *)s2;
2072         return strcasecmp(summ1->subj, summ2->subj);
2073 }
2074
2075 /**
2076  * \brief qsort() compatible function to compare two message summary structs by descending subject.
2077  *
2078  * \param s1 first item to compare 
2079  * \param s2 second item to compare
2080  */
2081 int summcmp_rsubj(const void *s1, const void *s2) {
2082         struct message_summary *summ1;
2083         struct message_summary *summ2;
2084         
2085         summ1 = (struct message_summary *)s1;
2086         summ2 = (struct message_summary *)s2;
2087         return strcasecmp(summ2->subj, summ1->subj);
2088 }
2089
2090 /**
2091  * \brief qsort() compatible function to compare two message summary structs by ascending sender.
2092  *
2093  * \param s1 first item to compare 
2094  * \param s2 second item to compare
2095  */
2096 int summcmp_sender(const void *s1, const void *s2) {
2097         struct message_summary *summ1;
2098         struct message_summary *summ2;
2099         
2100         summ1 = (struct message_summary *)s1;
2101         summ2 = (struct message_summary *)s2;
2102         return strcasecmp(summ1->from, summ2->from);
2103 }
2104
2105 /**
2106  * \brief qsort() compatible function to compare two message summary structs by descending sender.
2107  *
2108  * \param s1 first item to compare 
2109  * \param s2 second item to compare
2110  */
2111 int summcmp_rsender(const void *s1, const void *s2) {
2112         struct message_summary *summ1;
2113         struct message_summary *summ2;
2114         
2115         summ1 = (struct message_summary *)s1;
2116         summ2 = (struct message_summary *)s2;
2117         return strcasecmp(summ2->from, summ1->from);
2118 }
2119
2120 /**
2121  * \brief qsort() compatible function to compare two message summary structs by ascending date.
2122  *
2123  * \param s1 first item to compare 
2124  * \param s2 second item to compare
2125  */
2126 int summcmp_date(const void *s1, const void *s2) {
2127         struct message_summary *summ1;
2128         struct message_summary *summ2;
2129         
2130         summ1 = (struct message_summary *)s1;
2131         summ2 = (struct message_summary *)s2;
2132
2133         if (summ1->date < summ2->date) return -1;
2134         else if (summ1->date > summ2->date) return +1;
2135         else return 0;
2136 }
2137
2138 /**
2139  * \brief qsort() compatible function to compare two message summary structs by descending date.
2140  *
2141  * \param s1 first item to compare 
2142  * \param s2 second item to compare
2143  */
2144 int summcmp_rdate(const void *s1, const void *s2) {
2145         struct message_summary *summ1;
2146         struct message_summary *summ2;
2147         
2148         summ1 = (struct message_summary *)s1;
2149         summ2 = (struct message_summary *)s2;
2150
2151         if (summ1->date < summ2->date) return +1;
2152         else if (summ1->date > summ2->date) return -1;
2153         else return 0;
2154 }
2155
2156
2157
2158 /**
2159  * \brief command loop for reading messages
2160  *
2161  * \param oper Set to "readnew" or "readold" or "readfwd" or "headers"
2162  */
2163 void readloop(char *oper)
2164 {
2165         char cmd[256];
2166         char buf[SIZ];
2167         char old_msgs[SIZ];
2168         int a, b;
2169         int nummsgs;
2170         long startmsg;
2171         int maxmsgs;
2172         long *displayed_msgs = NULL;
2173         int num_displayed = 0;
2174         int is_summary = 0;
2175         int is_addressbook = 0;
2176         int is_singlecard = 0;
2177         int is_calendar = 0;
2178         int is_tasks = 0;
2179         int is_notes = 0;
2180         int is_bbview = 0;
2181         int lo, hi;
2182         int lowest_displayed = (-1);
2183         int highest_displayed = 0;
2184         struct addrbookent *addrbook = NULL;
2185         int num_ab = 0;
2186         char *sortby = NULL;
2187         char sortpref_name[128];
2188         char sortpref_value[128];
2189         char *subjsort_button;
2190         char *sendsort_button;
2191         char *datesort_button;
2192         int bbs_reverse = 0;
2193
2194         if (WC->wc_view == VIEW_WIKI) {
2195                 sprintf(buf, "wiki?room=%s?page=home", WC->wc_roomname);
2196                 http_redirect(buf);
2197                 return;
2198         }
2199
2200         startmsg = atol(bstr("startmsg"));
2201         maxmsgs = atoi(bstr("maxmsgs"));
2202         is_summary = atoi(bstr("summary"));
2203         if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
2204
2205         snprintf(sortpref_name, sizeof sortpref_name, "sort %s", WC->wc_roomname);
2206         get_preference(sortpref_name, sortpref_value, sizeof sortpref_value);
2207
2208         sortby = bstr("sortby");
2209         if ( (!IsEmptyStr(sortby)) && (strcasecmp(sortby, sortpref_value)) ) {
2210                 set_preference(sortpref_name, sortby, 1);
2211         }
2212         if (IsEmptyStr(sortby)) sortby = sortpref_value;
2213
2214         /** mailbox sort */
2215         if (IsEmptyStr(sortby)) sortby = "rdate";
2216
2217         /** message board sort */
2218         if (!strcasecmp(sortby, "reverse")) {
2219                 bbs_reverse = 1;
2220         }
2221         else {
2222                 bbs_reverse = 0;
2223         }
2224
2225         output_headers(1, 1, 1, 0, 0, 0);
2226
2227         /**
2228          * When in summary mode, always show ALL messages instead of just
2229          * new or old.  Otherwise, show what the user asked for.
2230          */
2231         if (!strcmp(oper, "readnew")) {
2232                 strcpy(cmd, "MSGS NEW");
2233         }
2234         else if (!strcmp(oper, "readold")) {
2235                 strcpy(cmd, "MSGS OLD");
2236         }
2237         else if (!strcmp(oper, "do_search")) {
2238                 sprintf(cmd, "MSGS SEARCH|%s", bstr("query"));
2239         }
2240         else {
2241                 strcpy(cmd, "MSGS ALL");
2242         }
2243
2244         if ((WC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1)) {
2245                 is_summary = 1;
2246                 if (!strcmp(oper, "do_search")) {
2247                         sprintf(cmd, "MSGS SEARCH|%s", bstr("query"));
2248                 }
2249                 else {
2250                         strcpy(cmd, "MSGS ALL");
2251                 }
2252         }
2253
2254         if ((WC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) {
2255                 is_addressbook = 1;
2256                 if (!strcmp(oper, "do_search")) {
2257                         sprintf(cmd, "MSGS SEARCH|%s", bstr("query"));
2258                 }
2259                 else {
2260                         strcpy(cmd, "MSGS ALL");
2261                 }
2262                 maxmsgs = 9999999;
2263         }
2264
2265         if (is_summary) {                       /**< fetch header summary */
2266                 snprintf(cmd, sizeof cmd, "MSGS %s|%s||1",
2267                         (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"),
2268                         (!strcmp(oper, "do_search") ? bstr("query") : "")
2269                 );
2270                 startmsg = 1;
2271                 maxmsgs = 9999999;
2272         }
2273
2274         /**
2275          * Are we doing a summary view?  If so, we need to know old messages
2276          * and new messages, so we can do that pretty boldface thing for the
2277          * new messages.
2278          */
2279         strcpy(old_msgs, "");
2280         if (is_summary) {
2281                 serv_puts("GTSN");
2282                 serv_getln(buf, sizeof buf);
2283                 if (buf[0] == '2') {
2284                         strcpy(old_msgs, &buf[4]);
2285                 }
2286         }
2287
2288         is_singlecard = atoi(bstr("is_singlecard"));
2289
2290         if (WC->wc_default_view == VIEW_CALENDAR) {             /**< calendar */
2291                 is_calendar = 1;
2292                 strcpy(cmd, "MSGS ALL");
2293                 maxmsgs = 32767;
2294         }
2295         if (WC->wc_default_view == VIEW_TASKS) {                /**< tasks */
2296                 is_tasks = 1;
2297                 strcpy(cmd, "MSGS ALL");
2298                 maxmsgs = 32767;
2299         }
2300         if (WC->wc_default_view == VIEW_NOTES) {                /**< notes */
2301                 is_notes = 1;
2302                 strcpy(cmd, "MSGS ALL");
2303                 maxmsgs = 32767;
2304         }
2305
2306         if (is_notes) {
2307                 wprintf("<div align=center>%s</div>\n", _("Click on any note to edit it."));
2308                 wprintf("<div id=\"new_notes_here\"></div>\n");
2309         }
2310
2311         nummsgs = load_msg_ptrs(cmd, is_summary);
2312         if (nummsgs == 0) {
2313
2314                 if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) {
2315                         wprintf("<div align=\"center\"><br /><em>");
2316                         if (!strcmp(oper, "readnew")) {
2317                                 wprintf(_("No new messages."));
2318                         } else if (!strcmp(oper, "readold")) {
2319                                 wprintf(_("No old messages."));
2320                         } else {
2321                                 wprintf(_("No messages here."));
2322                         }
2323                         wprintf("</em><br /></div>\n");
2324                 }
2325
2326                 goto DONE;
2327         }
2328
2329         if (is_summary) {
2330                 for (a = 0; a < nummsgs; ++a) {
2331                         /** Are you a new message, or an old message? */
2332                         if (is_summary) {
2333                                 if (is_msg_in_mset(old_msgs, WC->msgarr[a])) {
2334                                         WC->summ[a].is_new = 0;
2335                                 }
2336                                 else {
2337                                         WC->summ[a].is_new = 1;
2338                                 }
2339                         }
2340                 }
2341         }
2342
2343         if (startmsg == 0L) {
2344                 if (bbs_reverse) {
2345                         startmsg = WC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0];
2346                 }
2347                 else {
2348                         startmsg = WC->msgarr[0];
2349                 }
2350         }
2351
2352         if (is_summary) {
2353                 if (!strcasecmp(sortby, "subject")) {
2354                         qsort(WC->summ, WC->num_summ,
2355                                 sizeof(struct message_summary), summcmp_subj);
2356                 }
2357                 else if (!strcasecmp(sortby, "rsubject")) {
2358                         qsort(WC->summ, WC->num_summ,
2359                                 sizeof(struct message_summary), summcmp_rsubj);
2360                 }
2361                 else if (!strcasecmp(sortby, "sender")) {
2362                         qsort(WC->summ, WC->num_summ,
2363                                 sizeof(struct message_summary), summcmp_sender);
2364                 }
2365                 else if (!strcasecmp(sortby, "rsender")) {
2366                         qsort(WC->summ, WC->num_summ,
2367                                 sizeof(struct message_summary), summcmp_rsender);
2368                 }
2369                 else if (!strcasecmp(sortby, "date")) {
2370                         qsort(WC->summ, WC->num_summ,
2371                                 sizeof(struct message_summary), summcmp_date);
2372                 }
2373                 else if (!strcasecmp(sortby, "rdate")) {
2374                         qsort(WC->summ, WC->num_summ,
2375                                 sizeof(struct message_summary), summcmp_rdate);
2376                 }
2377         }
2378
2379         if (!strcasecmp(sortby, "subject")) {
2380                 subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsubject\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
2381         }
2382         else if (!strcasecmp(sortby, "rsubject")) {
2383                 subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
2384         }
2385         else {
2386                 subjsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=subject\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
2387         }
2388
2389         if (!strcasecmp(sortby, "sender")) {
2390                 sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rsender\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
2391         }
2392         else if (!strcasecmp(sortby, "rsender")) {
2393                 sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
2394         }
2395         else {
2396                 sendsort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=sender\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
2397         }
2398
2399         if (!strcasecmp(sortby, "date")) {
2400                 datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/down_pointer.gif\" /></a>" ;
2401         }
2402         else if (!strcasecmp(sortby, "rdate")) {
2403                 datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=date\"><img border=\"0\" src=\"static/up_pointer.gif\" /></a>" ;
2404         }
2405         else {
2406                 datesort_button = "<a href=\"readfwd?startmsg=1?maxmsgs=9999999?summary=1?sortby=rdate\"><img border=\"0\" src=\"static/sort_none.gif\" /></a>" ;
2407         }
2408
2409         if (is_summary) {
2410
2411                 wprintf("<script language=\"javascript\" type=\"text/javascript\">"
2412                         " document.onkeydown = CtdlMsgListKeyPress;     "
2413                         " if (document.layers) {                        "
2414                         "       document.captureEvents(Event.KEYPRESS); "
2415                         " }                                             "
2416                         "</script>\n"
2417                 );
2418
2419                 /** note that Date and Delete are now in the same column */
2420                 wprintf("<div id=\"message_list_hdr\">"
2421                         "<div class=\"fix_scrollbar_bug\">"
2422                         "<table cellspacing=0 style=\"width:100%%\">"
2423                         "<tr>"
2424                 );
2425                 wprintf("<th width=%d%%>%s %s</th>"
2426                         "<th width=%d%%>%s %s</th>"
2427                         "<th width=%d%%>%s %s"
2428                         "&nbsp;"
2429                         "<input type=\"submit\" name=\"delete_button\" id=\"delbutton\" "
2430                         " onClick=\"CtdlDeleteSelectedMessages(event)\" "
2431                         " value=\"%s\">"
2432                         "</th>"
2433                         "</tr>\n"
2434                         ,
2435                         SUBJ_COL_WIDTH_PCT,
2436                         _("Subject"),   subjsort_button,
2437                         SENDER_COL_WIDTH_PCT,
2438                         _("Sender"),    sendsort_button,
2439                         DATE_PLUS_BUTTONS_WIDTH_PCT,
2440                         _("Date"),      datesort_button,
2441                         _("Delete")
2442                 );
2443                 wprintf("</table></div></div>\n");
2444
2445                 wprintf("<div id=\"message_list\">"
2446
2447                         "<div class=\"fix_scrollbar_bug\">\n"
2448
2449                         "<table class=\"mailbox_summary\" id=\"summary_headers\" "
2450                         "cellspacing=0 style=\"width:100%%;-moz-user-select:none;\">"
2451                 );
2452         }
2453
2454         for (a = 0; a < nummsgs; ++a) {
2455                 if ((WC->msgarr[a] >= startmsg) && (num_displayed < maxmsgs)) {
2456
2457                         /** Display the message */
2458                         if (is_summary) {
2459                                 display_summarized(a);
2460                         }
2461                         else if (is_addressbook) {
2462                                 fetch_ab_name(WC->msgarr[a], buf);
2463                                 ++num_ab;
2464                                 addrbook = realloc(addrbook,
2465                                         (sizeof(struct addrbookent) * num_ab) );
2466                                 safestrncpy(addrbook[num_ab-1].ab_name, buf,
2467                                         sizeof(addrbook[num_ab-1].ab_name));
2468                                 addrbook[num_ab-1].ab_msgnum = WC->msgarr[a];
2469                         }
2470                         else if (is_calendar) {
2471                                 display_calendar(WC->msgarr[a]);
2472                         }
2473                         else if (is_tasks) {
2474                                 display_task(WC->msgarr[a]);
2475                         }
2476                         else if (is_notes) {
2477                                 display_note(WC->msgarr[a]);
2478                         }
2479                         else {
2480                                 if (displayed_msgs == NULL) {
2481                                         displayed_msgs = malloc(sizeof(long) *
2482                                                                 (maxmsgs<nummsgs ? maxmsgs : nummsgs));
2483                                 }
2484                                 displayed_msgs[num_displayed] = WC->msgarr[a];
2485                         }
2486
2487                         if (lowest_displayed < 0) lowest_displayed = a;
2488                         highest_displayed = a;
2489
2490                         ++num_displayed;
2491                 }
2492         }
2493
2494         /**
2495          * Set the "is_bbview" variable if it appears that we are looking at
2496          * a classic bulletin board view.
2497          */
2498         if ((!is_tasks) && (!is_calendar) && (!is_addressbook)
2499               && (!is_notes) && (!is_singlecard) && (!is_summary)) {
2500                 is_bbview = 1;
2501         }
2502
2503         /** Output loop */
2504         if (displayed_msgs != NULL) {
2505                 if (bbs_reverse) {
2506                         qsort(displayed_msgs, num_displayed, sizeof(long), longcmp_r);
2507                 }
2508
2509                 /** if we do a split bbview in the future, begin messages div here */
2510
2511                 for (a=0; a<num_displayed; ++a) {
2512                         read_message(displayed_msgs[a], 0, "");
2513                 }
2514
2515                 /** if we do a split bbview in the future, end messages div here */
2516
2517                 free(displayed_msgs);
2518                 displayed_msgs = NULL;
2519         }
2520
2521         if (is_summary) {
2522                 wprintf("</table>"
2523                         "</div>\n");                    /**< end of 'fix_scrollbar_bug' div */
2524                 wprintf("</div>");                      /**< end of 'message_list' div */
2525
2526                 /** Here's the grab-it-to-resize-the-message-list widget */
2527                 wprintf("<div id=\"resize_msglist\" "
2528                         "onMouseDown=\"CtdlResizeMsgListMouseDown(event)\">"
2529                         "<div class=\"fix_scrollbar_bug\"> <hr>"
2530                         "</div></div>\n"
2531                 );
2532
2533                 wprintf("<div id=\"preview_pane\">");   /**< The preview pane will initially be empty */
2534         }
2535
2536         /**
2537          * Bump these because although we're thinking in zero base, the user
2538          * is a drooling idiot and is thinking in one base.
2539          */
2540         ++lowest_displayed;
2541         ++highest_displayed;
2542
2543         /**
2544          * If we're not currently looking at ALL requested
2545          * messages, then display the selector bar
2546          */
2547         if (is_bbview) {
2548                 /** begin bbview scroller */
2549                 wprintf("<form name=\"msgomatic\">");
2550                 wprintf(_("Reading #"), lowest_displayed, highest_displayed);
2551
2552                 wprintf("<select name=\"whichones\" size=\"1\" "
2553                         "OnChange=\"location.href=msgomatic.whichones.options"
2554                         "[selectedIndex].value\">\n");
2555
2556                 if (bbs_reverse) {
2557                         for (b=nummsgs-1; b>=0; b = b - maxmsgs) {
2558                                 hi = b + 1;
2559                                 lo = b - maxmsgs + 2;
2560                                 if (lo < 1) lo = 1;
2561                                 wprintf("<option %s value="
2562                                         "\"%s"
2563                                         "?startmsg=%ld"
2564                                         "?maxmsgs=%d"
2565                                         "?summary=%d\">"
2566                                         "%d-%d</option> \n",
2567                                         ((WC->msgarr[lo-1] == startmsg) ? "selected" : ""),
2568                                         oper,
2569                                         WC->msgarr[lo-1],
2570                                         maxmsgs,
2571                                         is_summary,
2572                                         hi, lo);
2573                         }
2574                 }
2575                 else {
2576                         for (b=0; b<nummsgs; b = b + maxmsgs) {
2577                                 lo = b + 1;
2578                                 hi = b + maxmsgs + 1;
2579                                 if (hi > nummsgs) hi = nummsgs;
2580                                 wprintf("<option %s value="
2581                                         "\"%s"
2582                                         "?startmsg=%ld"
2583                                         "?maxmsgs=%d"
2584                                         "?summary=%d\">"
2585                                         "%d-%d</option> \n",
2586                                         ((WC->msgarr[b] == startmsg) ? "selected" : ""),
2587                                         oper,
2588                                         WC->msgarr[lo-1],
2589                                         maxmsgs,
2590                                         is_summary,
2591                                         lo, hi);
2592                         }
2593                 }
2594
2595                 wprintf("<option value=\"%s?startmsg=%ld"
2596                         "?maxmsgs=9999999?summary=%d\">"
2597                         "ALL"
2598                         "</option> ",
2599                         oper,
2600                         WC->msgarr[0], is_summary);
2601
2602                 wprintf("</select> ");
2603                 wprintf(_("of %d messages."), nummsgs);
2604
2605                 /** forward/reverse */
2606                 wprintf("&nbsp;<select name=\"direction\" size=\"1\" "
2607                         "OnChange=\"location.href=msgomatic.direction.options"
2608                         "[selectedIndex].value\">\n"
2609                 );
2610
2611                 wprintf("<option %s value=\"%s?sortby=forward\">oldest to newest</option>\n",
2612                         (bbs_reverse ? "" : "selected"),
2613                         oper
2614                 );
2615         
2616                 wprintf("<option %s value=\"%s?sortby=reverse\">newest to oldest</option>\n",
2617                         (bbs_reverse ? "selected" : ""),
2618                         oper
2619                 );
2620         
2621                 wprintf("</select></form>\n");
2622                 /** end bbview scroller */
2623         }
2624
2625 DONE:
2626         if (is_tasks) {
2627                 do_tasks_view();        /** Render the task list */
2628         }
2629
2630         if (is_calendar) {
2631                 do_calendar_view();     /** Render the calendar */
2632         }
2633
2634         if (is_addressbook) {
2635                 do_addrbook_view(addrbook, num_ab);     /** Render the address book */
2636         }
2637
2638         /** Note: wDumpContent() will output one additional </div> tag. */
2639         wprintf("</div>\n");            /** end of 'content' div */
2640         wDumpContent(1);
2641
2642         /** free the summary */
2643         if (WC->summ != NULL) {
2644                 free(WC->summ);
2645                 WC->num_summ = 0;
2646                 WC->summ = NULL;
2647         }
2648         if (addrbook != NULL) free(addrbook);
2649 }
2650
2651
2652 /**
2653  * \brief Back end for post_message()
2654  * ... this is where the actual message gets transmitted to the server.
2655  */
2656 void post_mime_to_server(void) {
2657         char boundary[SIZ];
2658         int is_multipart = 0;
2659         static int seq = 0;
2660         struct wc_attachment *att;
2661         char *encoded;
2662         size_t encoded_length;
2663
2664         /** RFC2045 requires this, and some clients look for it... */
2665         serv_puts("MIME-Version: 1.0");
2666         serv_puts("X-Mailer: " SERVER);
2667
2668         /** If there are attachments, we have to do multipart/mixed */
2669         if (WC->first_attachment != NULL) {
2670                 is_multipart = 1;
2671         }
2672
2673         if (is_multipart) {
2674                 sprintf(boundary, "Citadel--Multipart--%s--%04x--%04x",
2675                         serv_info.serv_fqdn,
2676                         getpid(),
2677                         ++seq
2678                 );
2679
2680                 /** Remember, serv_printf() appends an extra newline */
2681                 serv_printf("Content-type: multipart/mixed; "
2682                         "boundary=\"%s\"\n", boundary);
2683                 serv_printf("This is a multipart message in MIME format.\n");
2684                 serv_printf("--%s", boundary);
2685         }
2686
2687         serv_puts("Content-type: text/html; charset=utf-8");
2688         serv_puts("Content-Transfer-Encoding: quoted-printable");
2689         serv_puts("");
2690         serv_puts("<html><body>\r\n");
2691         text_to_server_qp(bstr("msgtext"));     /** Transmit message in quoted-printable encoding */
2692         serv_puts("</body></html>\r\n");
2693         
2694         if (is_multipart) {
2695
2696                 /** Add in the attachments */
2697                 for (att = WC->first_attachment; att!=NULL; att=att->next) {
2698
2699                         encoded_length = ((att->length * 150) / 100);
2700                         encoded = malloc(encoded_length);
2701                         if (encoded == NULL) break;
2702                         CtdlEncodeBase64(encoded, att->data, att->length, 1);
2703
2704                         serv_printf("--%s", boundary);
2705                         serv_printf("Content-type: %s", att->content_type);
2706                         serv_printf("Content-disposition: attachment; "
2707                                 "filename=\"%s\"", att->filename);
2708                         serv_puts("Content-transfer-encoding: base64");
2709                         serv_puts("");
2710                         serv_write(encoded, strlen(encoded));
2711                         serv_puts("");
2712                         serv_puts("");
2713                         free(encoded);
2714                 }
2715                 serv_printf("--%s--", boundary);
2716         }
2717
2718         serv_puts("000");
2719 }
2720
2721
2722 /**
2723  * \brief Post message (or don't post message)
2724  *
2725  * Note regarding the "dont_post" variable:
2726  * A random value (actually, it's just a timestamp) is inserted as a hidden
2727  * field called "postseq" when the display_enter page is generated.  This
2728  * value is checked when posting, using the static variable dont_post.  If a
2729  * user attempts to post twice using the same dont_post value, the message is
2730  * discarded.  This prevents the accidental double-saving of the same message
2731  * if the user happens to click the browser "back" button.
2732  */
2733 void post_message(void)
2734 {
2735         char buf[1024];
2736         char encoded_subject[1024];
2737         static long dont_post = (-1L);
2738         struct wc_attachment *att, *aptr;
2739         int is_anonymous = 0;
2740         char *display_name;
2741
2742         display_name = bstr("display_name");
2743         if (!strcmp(display_name, "__ANONYMOUS__")) {
2744                 display_name = "";
2745                 is_anonymous = 1;
2746         }
2747
2748         if (WC->upload_length > 0) {
2749
2750                 lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WC->upload_length);
2751                 /** There's an attachment.  Save it to this struct... */
2752                 att = malloc(sizeof(struct wc_attachment));
2753                 memset(att, 0, sizeof(struct wc_attachment));
2754                 att->length = WC->upload_length;
2755                 strcpy(att->content_type, WC->upload_content_type);
2756                 strcpy(att->filename, WC->upload_filename);
2757                 att->next = NULL;
2758
2759                 /** And add it to the list. */
2760                 if (WC->first_attachment == NULL) {
2761                         WC->first_attachment = att;
2762                 }
2763                 else {
2764                         aptr = WC->first_attachment;
2765                         while (aptr->next != NULL) aptr = aptr->next;
2766                         aptr->next = att;
2767                 }
2768
2769                 /**
2770                  * Mozilla sends a simple filename, which is what we want,
2771                  * but Satan's Browser sends an entire pathname.  Reduce
2772                  * the path to just a filename if we need to.
2773                  */
2774                 while (num_tokens(att->filename, '/') > 1) {
2775                         remove_token(att->filename, 0, '/');
2776                 }
2777                 while (num_tokens(att->filename, '\\') > 1) {
2778                         remove_token(att->filename, 0, '\\');
2779                 }
2780
2781                 /**
2782                  * Transfer control of this memory from the upload struct
2783                  * to the attachment struct.
2784                  */
2785                 att->data = WC->upload;
2786                 WC->upload_length = 0;
2787                 WC->upload = NULL;
2788                 display_enter();
2789                 return;
2790         }
2791
2792         if (!IsEmptyStr(bstr("cancel_button"))) {
2793                 sprintf(WC->ImportantMessage, 
2794                         _("Cancelled.  Message was not posted."));
2795         } else if (!IsEmptyStr(bstr("attach_button"))) {
2796                 display_enter();
2797                 return;
2798         } else if (atol(bstr("postseq")) == dont_post) {
2799                 sprintf(WC->ImportantMessage, 
2800                         _("Automatically cancelled because you have already "
2801                         "saved this message."));
2802         } else {
2803                 rfc2047encode(encoded_subject, sizeof encoded_subject, bstr("subject"));
2804                 sprintf(buf, "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s",
2805                         bstr("recp"),
2806                         is_anonymous,
2807                         encoded_subject,
2808                         display_name,
2809                         bstr("cc"),
2810                         bstr("bcc"),
2811                         bstr("wikipage"),
2812                         bstr("my_email_addr")
2813                 );
2814                 serv_puts(buf);
2815                 serv_getln(buf, sizeof buf);
2816                 if (buf[0] == '4') {
2817                         post_mime_to_server();
2818                         if (  (!IsEmptyStr(bstr("recp")))
2819                            || (!IsEmptyStr(bstr("cc"  )))
2820                            || (!IsEmptyStr(bstr("bcc" )))
2821                         ) {
2822                                 sprintf(WC->ImportantMessage, _("Message has been sent.\n"));
2823                         }
2824                         else {
2825                                 sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
2826                         }
2827                         dont_post = atol(bstr("postseq"));
2828                 } else {
2829                         lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, buf);
2830                         sprintf(WC->ImportantMessage, "%s", &buf[4]);
2831                         display_enter();
2832                         return;
2833                 }
2834         }
2835
2836         free_attachments(WC);
2837
2838         /**
2839          *  We may have been supplied with instructions regarding the location
2840          *  to which we must return after posting.  If found, go there.
2841          */
2842         if (!IsEmptyStr(bstr("return_to"))) {
2843                 http_redirect(bstr("return_to"));
2844         }
2845         /**
2846          *  If we were editing a page in a wiki room, go to that page now.
2847          */
2848         else if (!IsEmptyStr(bstr("wikipage"))) {
2849                 snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
2850                 http_redirect(buf);
2851         }
2852         /**
2853          *  Otherwise, just go to the "read messages" loop.
2854          */
2855         else {
2856                 readloop("readnew");
2857         }
2858 }
2859
2860
2861
2862
2863 /**
2864  * \brief display the message entry screen
2865  */
2866 void display_enter(void)
2867 {
2868         char buf[SIZ];
2869         char ebuf[SIZ];
2870         long now;
2871         char *display_name;
2872         struct wc_attachment *att;
2873         int recipient_required = 0;
2874         int subject_required = 0;
2875         int recipient_bad = 0;
2876         int i;
2877         int is_anonymous = 0;
2878         long existing_page = (-1L);
2879
2880         now = time(NULL);
2881
2882         if (!IsEmptyStr(bstr("force_room"))) {
2883                 gotoroom(bstr("force_room"));
2884         }
2885
2886         display_name = bstr("display_name");
2887         if (!strcmp(display_name, "__ANONYMOUS__")) {
2888                 display_name = "";
2889                 is_anonymous = 1;
2890         }
2891
2892         /** First test to see whether this is a room that requires recipients to be entered */
2893         serv_puts("ENT0 0");
2894         serv_getln(buf, sizeof buf);
2895
2896         if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
2897                 recipient_required = 1;
2898         }
2899         else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
2900                 sprintf(WC->ImportantMessage, "%s", &buf[4]);
2901                 readloop("readnew");
2902                 return;
2903         }
2904
2905         /* Is the server strongly recommending that the user enter a message subject? */
2906         if ((buf[3] != '\0') && (buf[4] != '\0')) {
2907                 subject_required = extract_int(&buf[4], 1);
2908         }
2909
2910         /**
2911          * Are we perhaps in an address book view?  If so, then an "enter
2912          * message" command really means "add new entry."
2913          */
2914         if (WC->wc_default_view == VIEW_ADDRESSBOOK) {
2915                 do_edit_vcard(-1, "", "");
2916                 return;
2917         }
2918
2919 #ifdef WEBCIT_WITH_CALENDAR_SERVICE
2920         /**
2921          * Are we perhaps in a calendar room?  If so, then an "enter
2922          * message" command really means "add new calendar item."
2923          */
2924         if (WC->wc_default_view == VIEW_CALENDAR) {
2925                 display_edit_event();
2926                 return;
2927         }
2928
2929         /**
2930          * Are we perhaps in a tasks view?  If so, then an "enter
2931          * message" command really means "add new task."
2932          */
2933         if (WC->wc_default_view == VIEW_TASKS) {
2934                 display_edit_task();
2935                 return;
2936         }
2937 #endif
2938
2939         /**
2940          * Otherwise proceed normally.
2941          * Do a custom room banner with no navbar...
2942          */
2943         output_headers(1, 1, 2, 0, 0, 0);
2944         wprintf("<div id=\"banner\">\n");
2945         embed_room_banner(NULL, navbar_none);
2946         wprintf("</div>\n");
2947         wprintf("<div id=\"content\">\n"
2948                 "<div class=\"fix_scrollbar_bug message \">");
2949
2950         /** Now check our actual recipients if there are any */
2951         if (recipient_required) {
2952                 sprintf(buf, "ENT0 0|%s|%d|0||%s||%s|%s|%s",
2953                         bstr("recp"),
2954                         is_anonymous,
2955                         display_name,
2956                         bstr("cc"), bstr("bcc"), bstr("wikipage"));
2957                 serv_puts(buf);
2958                 serv_getln(buf, sizeof buf);
2959
2960                 if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
2961                         if (!IsEmptyStr(bstr("recp")) && 
2962                             !IsEmptyStr(bstr("cc"  )) && 
2963                             !IsEmptyStr(bstr("bcc" ))) {
2964                                 recipient_bad = 1;
2965                         }
2966                 }
2967                 else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
2968                         wprintf("<em>%s</em><br />\n", &buf[4]);
2969                         goto DONE;
2970                 }
2971         }
2972
2973         /** If we got this far, we can display the message entry screen. */
2974
2975         /** begin message entry screen */
2976         wprintf("<form "
2977                 "enctype=\"multipart/form-data\" "
2978                 "method=\"POST\" "
2979                 "accept-charset=\"UTF-8\" "
2980                 "action=\"post\" "
2981                 "name=\"enterform\""
2982                 ">\n");
2983         wprintf("<input type=\"hidden\" name=\"postseq\" value=\"%ld\">\n", now);
2984         if (WC->wc_view == VIEW_WIKI) {
2985                 wprintf("<input type=\"hidden\" name=\"wikipage\" value=\"%s\">\n", bstr("wikipage"));
2986         }
2987         wprintf("<input type=\"hidden\" name=\"return_to\" value=\"%s\">\n", bstr("return_to"));
2988         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%ld\">\n", WC->nonce);
2989
2990         /** submit or cancel buttons */
2991         wprintf("<p class=\"send_edit_msg\">");
2992         wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
2993         if (recipient_required) {
2994                 wprintf(_("Send message"));
2995         } else {
2996                 wprintf(_("Post message"));
2997         }
2998         wprintf("\">&nbsp;"
2999                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
3000         wprintf("</p>");
3001
3002         /** header bar */
3003
3004         wprintf("<img src=\"static/newmess3_24x.gif\" class=\"imgedit\">");
3005         wprintf("  ");  /** header bar */
3006         fmt_date(buf, now, 0);
3007         wprintf("%s", buf);
3008         wprintf("\n");  /** header bar */
3009
3010         wprintf("<div>");
3011         wprintf("<label for=\"from_id\" > ");
3012         wprintf(_(" <I>from</I> "));
3013         wprintf("</label>");
3014
3015         /* Allow the user to select any of his valid screen names */
3016
3017         wprintf("<select name=\"display_name\" size=1 id=\"from_id\">\n");
3018
3019         serv_puts("GVSN");
3020         serv_getln(buf, sizeof buf);
3021         if (buf[0] == '1') {
3022                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
3023                         wprintf("<option %s value=\"",
3024                                 ((!strcasecmp(bstr("display_name"), buf)) ? "selected" : "")
3025                         );
3026                         escputs(buf);
3027                         wprintf("\">");
3028                         escputs(buf);
3029                         wprintf("</option>\n");
3030                 }
3031         }
3032
3033         if (WC->room_flags & QR_ANONOPT) {
3034                 wprintf("<option %s value=\"__ANONYMOUS__\">%s</option>\n",
3035                         ((!strcasecmp(bstr("__ANONYMOUS__"), WC->wc_fullname)) ? "selected" : ""),
3036                         _("Anonymous")
3037                 );
3038         }
3039
3040         wprintf("</select>\n");
3041
3042         /* If this is an email (not a post), allow the user to select any of his
3043          * valid email addresses.
3044          */
3045         if (recipient_required) {
3046                 serv_puts("GVEA");
3047                 serv_getln(buf, sizeof buf);
3048                 if (buf[0] == '1') {
3049                         wprintf("<select name=\"my_email_addr\" size=1>\n");
3050                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
3051                                 wprintf("<option value=\"");
3052                                 escputs(buf);
3053                                 wprintf("\">&lt;");
3054                                 escputs(buf);
3055                                 wprintf("&gt;</option>\n");
3056                         }
3057                         wprintf("</select>\n");
3058                 }
3059         }
3060
3061         wprintf(_(" <I>in</I> "));
3062         escputs(WC->wc_roomname);
3063         wprintf("</div>");
3064
3065         if (recipient_required) {
3066
3067                 wprintf("<div style=\"float: left;\"><label for=\"recp_id\"> ");
3068                 wprintf(_("To:"));
3069                 wprintf("</label>"
3070                         "<input autocomplete=\"off\" type=\"text\" name=\"recp\" id=\"recp_id\" value=\"");
3071                 escputs(bstr("recp"));
3072                 wprintf("\" size=45 maxlength=1000 />");
3073                 wprintf("<div class=\"auto_complete\" id=\"recp_name_choices\"></div>");
3074
3075
3076                 wprintf("<br/><label for=\"cc_id\"> ");
3077                 wprintf(_("CC:"));
3078                 wprintf("</label>"
3079                         "<input autocomplete=\"off\" type=\"text\" name=\"cc\" id=\"cc_id\" value=\"");
3080                 escputs(bstr("cc"));
3081                 wprintf("\" size=45 maxlength=1000 />");
3082                 wprintf("<div class=\"auto_complete\" id=\"cc_name_choices\"></div>");
3083                 wprintf("<br/><label for=\"bcc_id\"> ");
3084                 wprintf(_("BCC:"));
3085                 wprintf("</label>"
3086                         "<input autocomplete=\"off\" type=\"text\" name=\"bcc\" id=\"bcc_id\" value=\"");
3087                 escputs(bstr("bcc"));
3088                 wprintf("\" size=45 maxlength=1000 />");
3089                 wprintf("<div class=\"auto_complete\" id=\"bcc_name_choices\"></div>");
3090
3091                 /** Initialize the autocomplete ajax helpers (found in wclib.js) */
3092                 wprintf("<script type=\"text/javascript\">      \n"
3093                         " activate_entmsg_autocompleters();     \n"
3094                         "</script>                              \n"
3095                 );
3096                 wprintf("</div>");
3097
3098                 /** Pop open an address book -- begin **/
3099                 wprintf(
3100                         "<a href=\"javascript:PopOpenAddressBook('recp_id|%s|cc_id|%s|bcc_id|%s');\" "
3101                         "title=\"%s\">"
3102                         "<img align=middle border=0 width=24 height=24 src=\"static/viewcontacts_24x.gif\">"
3103                         "&nbsp;%s</a>",
3104                         _("To:"), _("CC:"), _("BCC:"),
3105                         _("Contacts"), _("Contacts")
3106                 );
3107                 /** Pop open an address book -- end **/
3108         }
3109
3110         wprintf("<div style=\"clear: both;\"><label for=\"subject_id\" > ");
3111         if (recipient_required || subject_required) {
3112                 wprintf(_("Subject:"));
3113         }
3114         else {
3115                 wprintf(_("Subject (optional):"));
3116         }
3117         wprintf("</label>"
3118                 "<input type=\"text\" name=\"subject\" id=\"subject_id\" value=\" ");
3119         escputs(bstr("subject"));
3120         wprintf("\" size=45 maxlength=70>\n");
3121
3122         wprintf("</div>\n");
3123
3124         wprintf("<textarea name=\"msgtext\" cols=\"80\" rows=\"15\">");
3125
3126         /** If we're continuing from a previous edit, put our partially-composed message back... */
3127         msgescputs(bstr("msgtext"));
3128
3129         /* If we're forwarding a message, insert it here... */
3130         if (atol(bstr("fwdquote")) > 0L) {
3131                 wprintf("<br><div align=center><i>");
3132                 wprintf(_("--- forwarded message ---"));
3133                 wprintf("</i></div><br>");
3134                 pullquote_message(atol(bstr("fwdquote")), 1, 1);
3135         }
3136
3137         /** If we're replying quoted, insert the quote here... */
3138         else if (atol(bstr("replyquote")) > 0L) {
3139                 wprintf("<br>"
3140                         "<blockquote>");
3141                 pullquote_message(atol(bstr("replyquote")), 0, 1);
3142                 wprintf("</blockquote><br>");
3143         }
3144
3145         /** If we're editing a wiki page, insert the existing page here... */
3146         else if (WC->wc_view == VIEW_WIKI) {
3147                 safestrncpy(buf, bstr("wikipage"), sizeof buf);
3148                 str_wiki_index(buf);
3149                 existing_page = locate_message_by_uid(buf);
3150                 if (existing_page >= 0L) {
3151                         pullquote_message(existing_page, 1, 0);
3152                 }
3153         }
3154
3155         /** Insert our signature if appropriate... */
3156         if ( (WC->is_mailbox) && (strcmp(bstr("sig_inserted"), "yes")) ) {
3157                 get_preference("use_sig", buf, sizeof buf);
3158                 if (!strcasecmp(buf, "yes")) {
3159                         int len;
3160                         get_preference("signature", ebuf, sizeof ebuf);
3161                         euid_unescapize(buf, ebuf);
3162                         wprintf("<br>--<br>");
3163                         len = strlen(buf);
3164                         for (i=0; i<len; ++i) {
3165                                 if (buf[i] == '\n') {
3166                                         wprintf("<br>");
3167                                 }
3168                                 else if (buf[i] == '<') {
3169                                         wprintf("&lt;");
3170                                 }
3171                                 else if (buf[i] == '>') {
3172                                         wprintf("&gt;");
3173                                 }
3174                                 else if (buf[i] == '&') {
3175                                         wprintf("&amp;");
3176                                 }
3177                                 else if (buf[i] == '\"') {
3178                                         wprintf("&quot;");
3179                                 }
3180                                 else if (buf[i] == '\'') {
3181                                         wprintf("&#39;");
3182                                 }
3183                                 else if (isprint(buf[i])) {
3184                                         wprintf("%c", buf[i]);
3185                                 }
3186                         }
3187                 }
3188         }
3189
3190         wprintf("</textarea>");
3191
3192         /**
3193          * The following template embeds the TinyMCE richedit control, and automatically
3194          * transforms the textarea into a richedit textarea.
3195          */
3196         do_template("richedit");
3197
3198         /** Enumerate any attachments which are already in place... */
3199         wprintf("<div><img src=\"static/diskette_24x.gif\" class=\"imgedit\" ");
3200         wprintf(_("Attachments:"));
3201         wprintf(" ");
3202         wprintf("<select name=\"which_attachment\" size=1>");
3203         for (att = WC->first_attachment; att != NULL; att = att->next) {
3204                 wprintf("<option value=\"");
3205                 urlescputs(att->filename);
3206                 wprintf("\">");
3207                 escputs(att->filename);
3208                 /* wprintf(" (%s, %d bytes)",att->content_type,att->length); */
3209                 wprintf("</option>\n");
3210         }
3211         wprintf("</select>");
3212
3213         /** Now offer the ability to attach additional files... */
3214         wprintf("&nbsp;&nbsp;&nbsp;");
3215         wprintf(_("Attach file:"));
3216         wprintf(" <input name=\"attachfile\" "
3217                 "size=16 type=\"file\">\n&nbsp;&nbsp;"
3218                 "<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
3219         wprintf("</div>");
3220
3221
3222         /** Make sure we only insert our signature once */
3223         if (strcmp(bstr("sig_inserted"), "yes")) {
3224                 wprintf("<input type=\"hidden\" name=\"sig_inserted\" value=\"yes\">\n");
3225         }
3226
3227         wprintf("</form>\n");
3228         wprintf("</div></div>\n");
3229
3230 DONE:   address_book_popup();
3231         wDumpContent(1);
3232 }
3233
3234
3235 /**
3236  * \brief delete a message
3237  */
3238 void delete_msg(void)
3239 {
3240         long msgid;
3241         char buf[SIZ];
3242
3243         msgid = atol(bstr("msgid"));
3244
3245         if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
3246                 serv_printf("DELE %ld", msgid); 
3247         }
3248         else {                  /** Otherwise move it to Trash */
3249                 serv_printf("MOVE %ld|_TRASH_|0", msgid);
3250         }
3251
3252         serv_getln(buf, sizeof buf);
3253         sprintf(WC->ImportantMessage, "%s", &buf[4]);
3254
3255         readloop("readnew");
3256 }
3257
3258
3259 /**
3260  * \brief move a message to another folder
3261  */
3262 void move_msg(void)
3263 {
3264         long msgid;
3265         char buf[SIZ];
3266
3267         msgid = atol(bstr("msgid"));
3268
3269         if (!IsEmptyStr(bstr("move_button"))) {
3270                 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
3271                 serv_puts(buf);
3272                 serv_getln(buf, sizeof buf);
3273                 sprintf(WC->ImportantMessage, "%s", &buf[4]);
3274         } else {
3275                 sprintf(WC->ImportantMessage, (_("The message was not moved.")));
3276         }
3277
3278         readloop("readnew");
3279 }
3280
3281
3282
3283
3284
3285 /**
3286  * \brief Confirm move of a message
3287  */
3288 void confirm_move_msg(void)
3289 {
3290         long msgid;
3291         char buf[SIZ];
3292         char targ[SIZ];
3293
3294         msgid = atol(bstr("msgid"));
3295
3296
3297         output_headers(1, 1, 2, 0, 0, 0);
3298         wprintf("<div id=\"banner\">\n");
3299         wprintf("<TABLE WIDTH=100%% BORDER=0><TR><TD>");
3300         wprintf("<SPAN CLASS=\"titlebar\">");
3301         wprintf(_("Confirm move of message"));
3302         wprintf("</SPAN>\n");
3303         wprintf("</TD></TR></TABLE>\n");
3304         wprintf("</div>\n<div id=\"content\">\n");
3305
3306         wprintf("<CENTER>");
3307
3308         wprintf(_("Move this message to:"));
3309         wprintf("<br />\n");
3310
3311         wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
3312         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%ld\">\n", WC->nonce);
3313         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
3314
3315         wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
3316         serv_puts("LKRA");
3317         serv_getln(buf, sizeof buf);
3318         if (buf[0] == '1') {
3319                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
3320                         extract_token(targ, buf, 0, '|', sizeof targ);
3321                         wprintf("<OPTION>");
3322                         escputs(targ);
3323                         wprintf("\n");
3324                 }
3325         }
3326         wprintf("</SELECT>\n");
3327         wprintf("<br />\n");
3328
3329         wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
3330         wprintf("&nbsp;");
3331         wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
3332         wprintf("</form></CENTER>\n");
3333
3334         wprintf("</CENTER>\n");
3335         wDumpContent(1);
3336 }
3337
3338
3339 /*@}*/