* migrate message creation to templates (citing still missing)
[citadel.git] / webcit / messages.c
1 /*
2  * $Id$
3  *
4  * Functions which deal with the fetching and displaying of messages.
5  *
6  */
7
8 #include "webcit.h"
9 #include "webserver.h"
10 #include "groupdav.h"
11
12 HashList *MsgHeaderHandler = NULL;
13 HashList *MsgEvaluators = NULL;
14 HashList *MimeRenderHandler = NULL;
15
16 #define SUBJ_COL_WIDTH_PCT              50      /**< Mailbox view column width */
17 #define SENDER_COL_WIDTH_PCT            30      /**< Mailbox view column width */
18 #define DATE_PLUS_BUTTONS_WIDTH_PCT     20      /**< Mailbox view column width */
19
20
21
22 void display_enter(void);
23 int longcmp_r(const void *s1, const void *s2);
24 int summcmp_subj(const void *s1, const void *s2);
25 int summcmp_rsubj(const void *s1, const void *s2);
26 int summcmp_sender(const void *s1, const void *s2);
27 int summcmp_rsender(const void *s1, const void *s2);
28 int summcmp_date(const void *s1, const void *s2);
29 int summcmp_rdate(const void *s1, const void *s2);
30
31 /*----------------------------------------------------------------------------*/
32
33
34 typedef void (*MsgPartEvaluatorFunc)(message_summary *Sum, StrBuf *Buf);
35
36
37
38 const char* SortIcons[3] = {
39         "static/up_pointer.gif",
40         "static/down_pointer.gif",
41         "static/sort_none.gif"
42 };
43
44 enum  {/// SortByEnum
45         eDate,
46         eRDate,
47         eSubject,
48         eRSubject,
49         eSender,
50         eRSender,
51         eReverse,
52         eUnSet
53 }; 
54
55 /* SortEnum to plain string representation */
56 static const char* SortByStrings[] = {
57         "date",
58         "rdate",
59         "subject", 
60         "rsubject", 
61         "sender",
62         "rsender",
63         "reverse",
64         "unset"
65 };
66
67 /* SortEnum to sort-Function Table */
68 const CompareFunc SortFuncs[eUnSet] = {
69         summcmp_date,
70         summcmp_rdate,
71         summcmp_subj,
72         summcmp_rsubj,
73         summcmp_sender,
74         summcmp_rsender,
75         summcmp_rdate
76 };
77
78 /* given a SortEnum, which icon should we choose? */
79 const int SortDateToIcon[eUnSet] = { eUp, eDown, eNone, eNone, eNone, eNone, eNone};
80 const int SortSubjectToIcon[eUnSet] = { eNone, eNone, eUp, eDown, eNone, eNone, eNone};
81 const int SortSenderToIcon[eUnSet] = { eNone, eNone, eNone, eNone, eUp, eDown, eNone};
82
83 /* given a SortEnum, which would be the "opposite" search option? */
84 const int DateInvertSortString[eUnSet] =  { eRDate, eDate, eDate, eDate, eDate, eDate, eDate};
85 const int SubjectInvertSortString[eUnSet] =  { eSubject, eSubject, eRSubject, eUnSet, eSubject, eSubject, eSubject};
86 const int SenderInvertSortString[eUnSet] =  { eSender, eSender, eSender, eSender, eRSender, eUnSet, eSender};
87
88
89
90 /*----------------------------------------------------------------------------*/
91
92 /*
93  * Translates sortoption String to its SortEnum representation 
94  * returns the enum matching the string; defaults to RDate
95  */
96 //SortByEnum 
97 int StrToESort (const StrBuf *sortby)
98 {////todoo: hash
99         int result = eDate;
100
101         if (!IsEmptyStr(ChrPtr(sortby))) while (result < eUnSet){
102                         if (!strcasecmp(ChrPtr(sortby), 
103                                         SortByStrings[result])) 
104                                 return result;
105                         result ++;
106                 }
107         return eRDate;
108 }
109
110
111 typedef int (*QSortFunction) (const void*, const void*);
112
113 /*
114  * qsort() compatible function to compare two longs in descending order.
115  */
116 int longcmp_r(const void *s1, const void *s2) {
117         long l1;
118         long l2;
119
120         l1 = *(long *)GetSearchPayload(s1);
121         l2 = *(long *)GetSearchPayload(s2);
122
123         if (l1 > l2) return(-1);
124         if (l1 < l2) return(+1);
125         return(0);
126 }
127
128 /*
129  * qsort() compatible function to compare two longs in descending order.
130  */
131 int qlongcmp_r(const void *s1, const void *s2) {
132         long l1 = (long) s1;
133         long l2 = (long) s2;
134
135         if (l1 > l2) return(-1);
136         if (l1 < l2) return(+1);
137         return(0);
138 }
139
140  
141 /*
142  * qsort() compatible function to compare two message summary structs by ascending subject.
143  */
144 int summcmp_subj(const void *s1, const void *s2) {
145         message_summary *summ1;
146         message_summary *summ2;
147         
148         summ1 = (message_summary *)GetSearchPayload(s1);
149         summ2 = (message_summary *)GetSearchPayload(s2);
150         return strcasecmp(ChrPtr(summ1->subj), ChrPtr(summ2->subj));
151 }
152
153 /*
154  * qsort() compatible function to compare two message summary structs by descending subject.
155  */
156 int summcmp_rsubj(const void *s1, const void *s2) {
157         message_summary *summ1;
158         message_summary *summ2;
159         
160         summ1 = (message_summary *)GetSearchPayload(s1);
161         summ2 = (message_summary *)GetSearchPayload(s2);
162         return strcasecmp(ChrPtr(summ2->subj), ChrPtr(summ1->subj));
163 }
164
165 /*
166  * qsort() compatible function to compare two message summary structs by ascending sender.
167  */
168 int summcmp_sender(const void *s1, const void *s2) {
169         message_summary *summ1;
170         message_summary *summ2;
171         
172         summ1 = (message_summary *)GetSearchPayload(s1);
173         summ2 = (message_summary *)GetSearchPayload(s2);
174         return strcasecmp(ChrPtr(summ1->from), ChrPtr(summ2->from));
175 }
176
177 /*
178  * qsort() compatible function to compare two message summary structs by descending sender.
179  */
180 int summcmp_rsender(const void *s1, const void *s2) {
181         message_summary *summ1;
182         message_summary *summ2;
183         
184         summ1 = (message_summary *)GetSearchPayload(s1);
185         summ2 = (message_summary *)GetSearchPayload(s2);
186         return strcasecmp(ChrPtr(summ2->from), ChrPtr(summ1->from));
187 }
188
189 /*
190  * qsort() compatible function to compare two message summary structs by ascending date.
191  */
192 int summcmp_date(const void *s1, const void *s2) {
193         message_summary *summ1;
194         message_summary *summ2;
195         
196         summ1 = (message_summary *)GetSearchPayload(s1);
197         summ2 = (message_summary *)GetSearchPayload(s2);
198
199         if (summ1->date < summ2->date) return -1;
200         else if (summ1->date > summ2->date) return +1;
201         else return 0;
202 }
203
204 /*
205  * qsort() compatible function to compare two message summary structs by descending date.
206  */
207 int summcmp_rdate(const void *s1, const void *s2) {
208         message_summary *summ1;
209         message_summary *summ2;
210         
211         summ1 = (message_summary *)GetSearchPayload(s1);
212         summ2 = (message_summary *)GetSearchPayload(s2);
213
214         if (summ1->date < summ2->date) return +1;
215         else if (summ1->date > summ2->date) return -1;
216         else return 0;
217 }
218
219
220
221
222
223
224 /*
225  * I wanna SEE that message!
226  *
227  * msgnum               Message number to display
228  * printable_view       Nonzero to display a printable view
229  * section              Optional for encapsulated message/rfc822 submessage
230  */
231 void read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, int printable_view, char *section) {
232         StrBuf *Buf;
233         StrBuf *Token;
234         StrBuf *FoundCharset;
235         message_summary *Msg = NULL;
236         headereval *Hdr;
237         void *vHdr;
238         char buf[SIZ];
239         struct attach_link *attach_links = NULL;
240         int num_attach_links = 0;
241 //      char mime_submessages[256] = "";
242         char reply_references[1024] = "";
243         int i = 0;
244         int Done = 0;
245         int state=0;
246         char vcard_partnum[256] = "";
247         char cal_partnum[256] = "";
248         char *part_source = NULL;
249         char msg4_partnum[32] = "";
250
251 ////    strcpy(mime_submessages, "");
252
253         Buf = NewStrBuf();
254         serv_printf("MSG4 %ld|%s", msgnum, section);
255         StrBuf_ServGetln(Buf);
256         if (GetServerStatus(Buf, NULL) != 1) {
257                 StrBufAppendPrintf(Target, "<strong>");
258                 StrBufAppendPrintf(Target, _("ERROR:"));
259                 StrBufAppendPrintf(Target, "</strong> %s<br />\n", &buf[4]);
260                 FreeStrBuf(&Buf);
261                 return;
262         }
263         svputlong("MsgPrintable", printable_view);
264         /** begin everythingamundo table */
265
266
267         Token = NewStrBuf();
268         Msg = (message_summary *)malloc(sizeof(message_summary));
269         memset(Msg, 0, sizeof(message_summary));
270         Msg->msgnum = msgnum;
271         FoundCharset = NewStrBuf();
272         while ((StrBuf_ServGetln(Buf)>=0) && !Done) {
273                 if ( (StrLength(Buf)==3) && 
274                     !strcmp(ChrPtr(Buf), "000")) 
275                 {
276                         Done = 1;
277                         if (state < 2) {
278                                 StrBufAppendPrintf(Target, "<i>");
279                                 StrBufAppendPrintf(Target, _("unexpected end of message"));
280                                 StrBufAppendPrintf(Target, " (1)</i><br /><br />\n");
281                                 StrBufAppendPrintf(Target, "</div>\n");
282                                 FreeStrBuf(&Buf);
283                                 FreeStrBuf(&Token);
284                                 DestroyMessageSummary(Msg);
285                                 FreeStrBuf(&FoundCharset);
286                                 return;
287                         }
288                         else {
289                                 break;
290                         }
291                 }
292                 switch (state) {
293                 case 0:/* Citadel Message Headers */
294                         if (StrLength(Buf) == 0) {
295                                 state ++;
296                                 break;
297                         }
298                         StrBufExtract_token(Token, Buf, 0, '=');
299                         StrBufCutLeft(Buf, StrLength(Token) + 1);
300                         
301                         lprintf(1, ":: [%s] = [%s]\n", ChrPtr(Token), ChrPtr(Buf));
302                         if (GetHash(MsgHeaderHandler, SKEY(Token), &vHdr) &&
303                             (vHdr != NULL)) {
304                                 Hdr = (headereval*)vHdr;
305                                 Hdr->evaluator(Msg, Buf, FoundCharset);
306                                 if (Hdr->Type == 1) {
307                                         state++;
308                                 }
309                         }
310                         else lprintf(1, "don't know how to handle message header[%s]\n", ChrPtr(Token));
311                         break;
312                 case 1:/* Message Mime Header */
313                         if (StrLength(Buf) == 0) {
314                                 state++;
315                                 if (Msg->MsgBody.ContentType == NULL)
316                                         /* end of header or no header? */
317                                         Msg->MsgBody.ContentType = NewStrBufPlain(HKEY("text/plain"));
318                                  /* usual end of mime header */
319                         }
320                         else
321                         {
322                                 StrBufExtract_token(Token, Buf, 0, ':');
323                                 if (StrLength(Token) > 0) {
324                                         StrBufCutLeft(Buf, StrLength(Token) + 1);
325                                         lprintf(1, ":: [%s] = [%s]\n", ChrPtr(Token), ChrPtr(Buf));
326                                         if (GetHash(MsgHeaderHandler, SKEY(Token), &vHdr) &&
327                                             (vHdr != NULL)) {
328                                                 Hdr = (headereval*)vHdr;
329                                                 Hdr->evaluator(Msg, Buf, FoundCharset);
330                                         }
331                                         break;
332                                 }
333                         }
334                 case 2: /* Message Body */
335                         
336                         if (Msg->MsgBody.size_known > 0) {
337                                 StrBuf_ServGetBLOB(Msg->MsgBody.Data, Msg->MsgBody.length);
338                                 state ++;
339                                         /// todo: check next line, if not 000, append following lines
340                         }
341                         else if (1){
342                                 if (StrLength(Msg->MsgBody.Data) > 0)
343                                         StrBufAppendBufPlain(Msg->MsgBody.Data, "\n", 1, 0);
344                                 StrBufAppendBuf(Msg->MsgBody.Data, Buf, 0);
345                         }
346                         break;
347                 case 3:
348                         StrBufAppendBuf(Msg->MsgBody.Data, Buf, 0);
349                         break;
350                 }
351         }
352         
353         /* strip the bare contenttype, so we ommit charset etc. */
354         StrBufExtract_token(Buf, Msg->MsgBody.ContentType, 0, ';');
355         StrBufTrim(Buf);
356         if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) &&
357             (vHdr != NULL)) {
358                 RenderMimeFunc Render;
359                 Render = (RenderMimeFunc)vHdr;
360                 Render(&Msg->MsgBody, NULL, FoundCharset);
361         }
362
363
364         if (StrLength(Msg->reply_references)> 0) {
365                 /* Trim down excessively long lists of thread references.  We eliminate the
366                  * second one in the list so that the thread root remains intact.
367                  */
368                 int rrtok = num_tokens(ChrPtr(Msg->reply_references), '|');
369                 int rrlen = StrLength(Msg->reply_references);
370                 if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
371                         remove_token(reply_references, 1, '|');////todo
372                 }
373         }
374
375         /* Generate a reply-to address */
376         if (StrLength(Msg->Rfca) > 0) {
377                 if (Msg->reply_to == NULL)
378                         Msg->reply_to = NewStrBuf();
379                 if (StrLength(Msg->from) > 0) {
380                         StrBufPrintf(Msg->reply_to, "%s <%s>", ChrPtr(Msg->from), ChrPtr(Msg->Rfca));
381                 }
382                 else {
383                         FlushStrBuf(Msg->reply_to);
384                         StrBufAppendBuf(Msg->reply_to, Msg->Rfca, 0);
385                 }
386         }
387         else 
388         {
389                 if ((StrLength(Msg->OtherNode)>0) && 
390                     (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_nodename)) &&
391                     (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_humannode)) ) 
392                 {
393                         if (Msg->reply_to == NULL)
394                                 Msg->reply_to = NewStrBuf();
395                         StrBufPrintf(Msg->reply_to, 
396                                      "%s @ %s",
397                                      ChrPtr(Msg->from), 
398                                      ChrPtr(Msg->OtherNode));
399                 }
400                 else {
401                         if (Msg->reply_to == NULL)
402                                 Msg->reply_to = NewStrBuf();
403                         FlushStrBuf(Msg->reply_to);
404                         StrBufAppendBuf(Msg->reply_to, Msg->from, 0);
405                 }
406         }
407         DoTemplate(tmpl, tmpllen, Target, Msg, CTX_MAILSUM);
408
409
410
411 //// put message renderer lookup here.
412 ///ENDBODY:     /* If there are attached submessages, display them now... */
413 ///
414 ///     if ( (!IsEmptyStr(mime_submessages)) && (!section[0]) ) {
415 ///             for (i=0; i<num_tokens(mime_submessages, '|'); ++i) {
416 ///                     extract_token(buf, mime_submessages, i, '|', sizeof buf);
417 ///                     /** use printable_view to suppress buttons */
418 ///                     wprintf("<blockquote>");
419 ///                     read_message(msgnum, 1, buf);
420 ///                     wprintf("</blockquote>");
421 ///             }
422 ///     }
423
424
425         /* Afterwards, offer links to download attachments 'n' such */
426         if ( (num_attach_links > 0) && (!section[0]) ) {
427                 for (i=0; i<num_attach_links; ++i) {
428                         if (strcasecmp(attach_links[i].partnum, msg4_partnum)) {
429                                 wprintf("%s", attach_links[i].html);
430                         }
431                 }
432         }
433
434         /* Handler for vCard parts */
435         if (!IsEmptyStr(vcard_partnum)) {
436                 part_source = load_mimepart(msgnum, vcard_partnum);
437                 if (part_source != NULL) {
438
439                         /** If it's my vCard I can edit it */
440                         if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
441                                 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
442                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
443                         ) {
444                                 wprintf("<a href=\"edit_vcard?msgnum=%ld&partnum=%s\">",
445                                         msgnum, vcard_partnum);
446                                 wprintf("[%s]</a>", _("edit"));
447                         }
448
449                         /* In all cases, display the full card */
450                         display_vcard(WC->WBuf, part_source, 0, 1, NULL,msgnum);
451                 }
452         }
453
454         /* Handler for calendar parts */
455         if (!IsEmptyStr(cal_partnum)) {
456         }
457
458         if (part_source) {
459                 free(part_source);
460                 part_source = NULL;
461         }
462
463         wprintf("</div>\n");
464
465         /* end everythingamundo table */
466         if (!printable_view) {
467                 wprintf("</div>\n");
468         }
469
470         if (num_attach_links > 0) {
471                 free(attach_links);
472         }
473         DestroyMessageSummary(Msg);
474         FreeStrBuf(&FoundCharset);
475         FreeStrBuf(&Token);
476         FreeStrBuf(&Buf);
477 }
478
479
480
481 /*
482  * Unadorned HTML output of an individual message, suitable
483  * for placing in a hidden iframe, for printing, or whatever
484  *
485  * msgnum_as_string == Message number, as a string instead of as a long int
486  */
487 void embed_message(void) {
488         long msgnum = 0L;
489         const StrBuf *Tmpl = sbstr("template");
490
491         msgnum = StrTol(WC->UrlFragment1);
492         if (StrLength(Tmpl) > 0) 
493                 read_message(WC->WBuf, SKEY(Tmpl), msgnum, 0, "");
494         else 
495                 read_message(WC->WBuf, HKEY("view_message"), msgnum, 0, "");
496 }
497
498
499 /*
500  * Printable view of a message
501  *
502  * msgnum_as_string == Message number, as a string instead of as a long int
503  */
504 void print_message(void) {
505         long msgnum = 0L;
506
507         msgnum = StrTol(WC->UrlFragment1);
508         output_headers(0, 0, 0, 0, 0, 0);
509
510         hprintf("Content-type: text/html\r\n"
511                 "Server: " PACKAGE_STRING "\r\n"
512                 "Connection: close\r\n");
513
514         begin_burst();
515
516         read_message(WC->WBuf, HKEY("view_message_print"), msgnum, 1, "");
517
518         wDumpContent(0);
519 }
520
521 /* 
522  * Mobile browser view of message
523  *
524  * @param msg_num_as_string Message number as a string instead of as a long int 
525  */
526 void mobile_message_view(void) {
527   long msgnum = 0L;
528   msgnum = StrTol(WC->UrlFragment1);
529   output_headers(1, 0, 0, 0, 0, 1);
530   begin_burst();
531   do_template("msgcontrols", NULL);
532   read_message(WC->WBuf, HKEY("view_message"), msgnum,1, "");
533   wDumpContent(0);
534 }
535
536 /**
537  * \brief Display a message's headers
538  *
539  * \param msgnum_as_string Message number, as a string instead of as a long int
540  */
541 void display_headers(void) {
542         long msgnum = 0L;
543         char buf[1024];
544
545         msgnum = StrTol(WC->UrlFragment1);
546         output_headers(0, 0, 0, 0, 0, 0);
547
548         hprintf("Content-type: text/plain\r\n"
549                 "Server: %s\r\n"
550                 "Connection: close\r\n",
551                 PACKAGE_STRING);
552         begin_burst();
553
554         serv_printf("MSG2 %ld|3", msgnum);
555         serv_getln(buf, sizeof buf);
556         if (buf[0] == '1') {
557                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
558                         wprintf("%s\n", buf);
559                 }
560         }
561
562         wDumpContent(0);
563 }
564
565
566
567 /**
568  * \brief Read message in simple, JavaScript-embeddable form for 'forward'
569  *      or 'reply quoted' operations.
570  *
571  * NOTE: it is VITALLY IMPORTANT that we output no single-quotes or linebreaks
572  *       in this function.  Doing so would throw a JavaScript error in the
573  *       'supplied text' argument to the editor.
574  *
575  * \param msgnum Message number of the message we want to quote
576  * \param forward_attachments Nonzero if we want attachments to be forwarded
577  */
578 void pullquote_message(long msgnum, int forward_attachments, int include_headers) {
579         struct wcsession *WCC = WC;
580         char buf[SIZ];
581         char mime_partnum[256];
582         char mime_filename[256];
583         char mime_content_type[256];
584         char mime_charset[256];
585         char mime_disposition[256];
586         int mime_length;
587         char *attachments = NULL;
588         char *ptr = NULL;
589         int num_attachments = 0;
590         wc_attachment *att;
591         char m_subject[1024];
592         char from[256];
593         char node[256];
594         char rfca[256];
595         char to[256];
596         char reply_to[512];
597         char now[256];
598         int format_type = 0;
599         int nhdr = 0;
600         int bq = 0;
601         int i = 0;
602 #ifdef HAVE_ICONV
603         iconv_t ic = (iconv_t)(-1) ;
604         char *ibuf;                /**< Buffer of characters to be converted */
605         char *obuf;                /**< Buffer for converted characters      */
606         size_t ibuflen;    /**< Length of input buffer         */
607         size_t obuflen;    /**< Length of output buffer       */
608         char *osav;                /**< Saved pointer to output buffer       */
609 #endif
610
611         strcpy(from, "");
612         strcpy(node, "");
613         strcpy(rfca, "");
614         strcpy(reply_to, "");
615         strcpy(mime_content_type, "text/plain");
616         strcpy(mime_charset, "us-ascii");
617
618         serv_printf("MSG4 %ld", msgnum);
619         serv_getln(buf, sizeof buf);
620         if (buf[0] != '1') {
621                 wprintf(_("ERROR:"));
622                 wprintf("%s<br />", &buf[4]);
623                 return;
624         }
625
626         strcpy(m_subject, "");
627
628         while (serv_getln(buf, sizeof buf), strcasecmp(buf, "text")) {
629                 if (!strcmp(buf, "000")) {
630                         wprintf("%s (3)", _("unexpected end of message"));
631                         return;
632                 }
633                 if (include_headers) {
634                         if (!strncasecmp(buf, "nhdr=yes", 8))
635                                 nhdr = 1;
636                         if (nhdr == 1)
637                                 buf[0] = '_';
638                         if (!strncasecmp(buf, "type=", 5))
639                                 format_type = atoi(&buf[5]);
640                         if (!strncasecmp(buf, "from=", 5)) {
641                                 strcpy(from, &buf[5]);
642                                 wprintf(_("from "));
643                                 utf8ify_rfc822_string(from);
644                                 msgescputs(from);
645                         }
646                         if (!strncasecmp(buf, "subj=", 5)) {
647                                 strcpy(m_subject, &buf[5]);
648                         }
649                         if ((!strncasecmp(buf, "hnod=", 5))
650                             && (strcasecmp(&buf[5], serv_info.serv_humannode))) {
651                                 wprintf("(%s) ", &buf[5]);
652                         }
653                         if ((!strncasecmp(buf, "room=", 5))
654                             && (strcasecmp(&buf[5], WC->wc_roomname))
655                             && (!IsEmptyStr(&buf[5])) ) {
656                                 wprintf(_("in "));
657                                 wprintf("%s&gt; ", &buf[5]);
658                         }
659                         if (!strncasecmp(buf, "rfca=", 5)) {
660                                 strcpy(rfca, &buf[5]);
661                                 wprintf("&lt;");
662                                 msgescputs(rfca);
663                                 wprintf("&gt; ");
664                         }
665                         if (!strncasecmp(buf, "node=", 5)) {
666                                 strcpy(node, &buf[5]);
667                                 if ( ((WC->room_flags & QR_NETWORK)
668                                 || ((strcasecmp(&buf[5], serv_info.serv_nodename)
669                                 && (strcasecmp(&buf[5], serv_info.serv_fqdn)))))
670                                 && (IsEmptyStr(rfca))
671                                 ) {
672                                         wprintf("@%s ", &buf[5]);
673                                 }
674                         }
675                         if (!strncasecmp(buf, "rcpt=", 5)) {
676                                 wprintf(_("to "));
677                                 strcpy(to, &buf[5]);
678                                 utf8ify_rfc822_string(to);
679                                 wprintf("%s ", to);
680                         }
681                         if (!strncasecmp(buf, "time=", 5)) {
682                                 webcit_fmt_date(now, atol(&buf[5]), 0);
683                                 wprintf("%s ", now);
684                         }
685                 }
686
687                 /**
688                  * Save attachment info for later.  We can't start downloading them
689                  * yet because we're in the middle of a server transaction.
690                  */
691                 if (!strncasecmp(buf, "part=", 5)) {
692                         ptr = malloc( (strlen(buf) + ((attachments != NULL) ? strlen(attachments) : 0)) ) ;
693                         if (ptr != NULL) {
694                                 ++num_attachments;
695                                 sprintf(ptr, "%s%s\n",
696                                         ((attachments != NULL) ? attachments : ""),
697                                         &buf[5]
698                                 );
699                                 free(attachments);
700                                 attachments = ptr;
701                                 lprintf(9, "attachments=<%s>\n", attachments);
702                         }
703                 }
704
705         }
706
707         if (include_headers) {
708                 wprintf("<br>");
709
710                 utf8ify_rfc822_string(m_subject);
711                 if (!IsEmptyStr(m_subject)) {
712                         wprintf(_("Subject:"));
713                         wprintf(" ");
714                         msgescputs(m_subject);
715                         wprintf("<br />");
716                 }
717
718                 /**
719                  * Begin body
720                  */
721                 wprintf("<br />");
722         }
723
724         /**
725          * Learn the content type
726          */
727         strcpy(mime_content_type, "text/plain");
728         while (serv_getln(buf, sizeof buf), (!IsEmptyStr(buf))) {
729                 if (!strcmp(buf, "000")) {
730                         wprintf("%s (4)", _("unexpected end of message"));
731                         goto ENDBODY;
732                 }
733                 if (!strncasecmp(buf, "Content-type: ", 14)) {
734                         int len;
735                         safestrncpy(mime_content_type, &buf[14],
736                                 sizeof(mime_content_type));
737                         for (i=0; i<strlen(mime_content_type); ++i) {
738                                 if (!strncasecmp(&mime_content_type[i], "charset=", 8)) {
739                                         safestrncpy(mime_charset, &mime_content_type[i+8],
740                                                 sizeof mime_charset);
741                                 }
742                         }
743                         len = strlen(mime_content_type);
744                         for (i=0; i<len; ++i) {
745                                 if (mime_content_type[i] == ';') {
746                                         mime_content_type[i] = 0;
747                                         len = i - 1;
748                                 }
749                         }
750                         len = strlen(mime_charset);
751                         for (i=0; i<len; ++i) {
752                                 if (mime_charset[i] == ';') {
753                                         mime_charset[i] = 0;
754                                         len = i - 1;
755                                 }
756                         }
757                 }
758         }
759
760         /** Set up a character set conversion if we need to (and if we can) */
761 #ifdef HAVE_ICONV
762         if ( (strcasecmp(mime_charset, "us-ascii"))
763            && (strcasecmp(mime_charset, "UTF-8"))
764            && (strcasecmp(mime_charset, ""))
765         ) {
766                 ctdl_iconv_open("UTF-8", mime_charset, &ic);
767                 if (ic == (iconv_t)(-1) ) {
768                         lprintf(5, "%s:%d iconv_open(%s, %s) failed: %s\n",
769                                 __FILE__, __LINE__, "UTF-8", mime_charset, strerror(errno));
770                 }
771         }
772 #endif
773
774         /** Messages in legacy Citadel variformat get handled thusly... */
775         if (!strcasecmp(mime_content_type, "text/x-citadel-variformat")) {
776                 pullquote_fmout();
777         }
778
779         /* Boring old 80-column fixed format text gets handled this way... */
780         else if (!strcasecmp(mime_content_type, "text/plain")) {
781                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
782                         int len;
783                         len = strlen(buf);
784                         if ((len > 0) && (buf[len-1] == '\n')) buf[--len] = 0;
785                         if ((len > 0) && (buf[len-1] == '\r')) buf[--len] = 0;
786
787 #ifdef HAVE_ICONV
788                         if (ic != (iconv_t)(-1) ) {
789                                 ibuf = buf;
790                                 ibuflen = len;
791                                 obuflen = SIZ;
792                                 obuf = (char *) malloc(obuflen);
793                                 osav = obuf;
794                                 iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
795                                 osav[SIZ-obuflen] = 0;
796                                 safestrncpy(buf, osav, sizeof buf);
797                                 free(osav);
798                         }
799 #endif
800
801                         len = strlen(buf);
802                         while ((!IsEmptyStr(buf)) && (isspace(buf[len - 1]))) 
803                                 buf[--len] = 0;
804                         if ((bq == 0) &&
805                         ((!strncmp(buf, ">", 1)) || (!strncmp(buf, " >", 2)) )) {
806                                 wprintf("<blockquote>");
807                                 bq = 1;
808                         } else if ((bq == 1) &&
809                                 (strncmp(buf, ">", 1)) && (strncmp(buf, " >", 2)) ) {
810                                 wprintf("</blockquote>");
811                                 bq = 0;
812                         }
813                         wprintf("<tt>");
814                         url(buf, sizeof(buf));
815                         msgescputs1(buf);
816                         wprintf("</tt><br />");
817                 }
818                 wprintf("</i><br />");
819         }
820
821         /** HTML just gets escaped and stuffed back into the editor */
822         else if (!strcasecmp(mime_content_type, "text/html")) {
823                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
824                         strcat(buf, "\n");
825                         msgescputs(buf);
826                 }
827         }//// TODO: charset? utf8?
828
829         /** Unknown weirdness ... don't know how to handle this content type */
830         else {
831                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) { }
832         }
833
834 ENDBODY:
835         /** end of body handler */
836
837         /*
838          * If there were attachments, we have to download them and insert them
839          * into the attachment chain for the forwarded message we are composing.
840          */
841         if ( (forward_attachments) && (num_attachments) ) {
842                 for (i=0; i<num_attachments; ++i) {
843                         extract_token(buf, attachments, i, '\n', sizeof buf);
844                         extract_token(mime_filename, buf, 1, '|', sizeof mime_filename);
845                         extract_token(mime_partnum, buf, 2, '|', sizeof mime_partnum);
846                         extract_token(mime_disposition, buf, 3, '|', sizeof mime_disposition);
847                         extract_token(mime_content_type, buf, 4, '|', sizeof mime_content_type);
848                         mime_length = extract_int(buf, 5);
849
850                         /*
851                          * tracing  ... uncomment if necessary
852                          *
853                          */
854                         lprintf(9, "fwd filename: %s\n", mime_filename);
855                         lprintf(9, "fwd partnum : %s\n", mime_partnum);
856                         lprintf(9, "fwd conttype: %s\n", mime_content_type);
857                         lprintf(9, "fwd dispose : %s\n", mime_disposition);
858                         lprintf(9, "fwd length  : %d\n", mime_length);
859
860                         if ( (!strcasecmp(mime_disposition, "inline"))
861                            || (!strcasecmp(mime_disposition, "attachment")) ) {
862                 
863                                 int n;
864                                 char N[64];
865                                 /* Create an attachment struct from this mime part... */
866                                 att = malloc(sizeof(wc_attachment));
867                                 memset(att, 0, sizeof(wc_attachment));
868                                 att->length = mime_length;
869                                 att->content_type = NewStrBufPlain(mime_content_type, -1);
870                                 att->filename = NewStrBufPlain(mime_filename, -1);
871                                 att->data = load_mimepart(msgnum, mime_partnum);
872                 
873                                 if (WCC->attachments == NULL)
874                                         WCC->attachments = NewHash(1, NULL);
875                                 /* And add it to the list. */
876                                 n = snprintf(N, sizeof N, "%d", GetCount(WCC->attachments) + 1);
877                                 Put(WCC->attachments, N, n, att, free_attachment);
878                         }
879
880                 }
881         }
882
883 #ifdef HAVE_ICONV
884         if (ic != (iconv_t)(-1) ) {
885                 iconv_close(ic);
886         }
887 #endif
888
889         if (attachments != NULL) {
890                 free(attachments);
891         }
892 }
893
894
895
896
897 void EvaluateMimePart(message_summary *Sum, StrBuf *Buf)
898 {//// paert=; TODO
899 /*
900         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
901         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
902         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
903         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
904         mime_length = extract_int(&buf[5], 5);
905         
906         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
907               || (!strcasecmp(mime_content_type, "text/vcard")) ) {
908                 strcpy(vcard_partnum, mime_partnum);
909         }
910 */
911 }
912
913 message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) 
914 {
915         void                 *vEval;
916         MsgPartEvaluatorFunc  Eval;
917         message_summary      *Msg;
918         StrBuf *Buf;
919         const char *buf;
920         const char *ebuf;
921         int nBuf;
922         long len;
923         
924         Buf = NewStrBuf();
925
926         serv_printf("MSG0 %ld|1", MsgNum);      /* ask for headers only */
927         
928         StrBuf_ServGetln(Buf);
929         if (GetServerStatus(Buf, NULL) == 1) {
930                 FreeStrBuf(&Buf);
931                 return NULL;
932         }
933
934         Msg = (message_summary*)malloc(sizeof(message_summary));
935         memset(Msg, 0, sizeof(message_summary));
936         while (len = StrBuf_ServGetln(Buf),
937                ((len != 3)  ||
938                 strcmp(ChrPtr(Buf), "000")== 0)){
939                 buf = ChrPtr(Buf);
940                 ebuf = strchr(ChrPtr(Buf), '=');
941                 nBuf = ebuf - buf;
942                 if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) {
943                         Eval = (MsgPartEvaluatorFunc) vEval;
944                         StrBufCutLeft(Buf, nBuf + 1);
945                         Eval(Msg, Buf);
946                 }
947                 else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
948         }
949         return Msg;
950 }
951
952
953 /**
954  * \brief display the adressbook overview
955  * \param msgnum the citadel message number
956  * \param alpha what????
957  */
958 void display_addressbook(long msgnum, char alpha) {
959         //char buf[SIZ];
960         /* char mime_partnum[SIZ]; */
961 /*      char mime_filename[SIZ]; */
962 /*      char mime_content_type[SIZ]; */
963         ///char mime_disposition[SIZ];
964         //int mime_length;
965         char vcard_partnum[SIZ];
966         char *vcard_source = NULL;
967         message_summary summ;////TODO: this will leak
968
969         memset(&summ, 0, sizeof(summ));
970         ///safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
971 ///Load Message headers
972 //      Msg = 
973         if (!IsEmptyStr(vcard_partnum)) {
974                 vcard_source = load_mimepart(msgnum, vcard_partnum);
975                 if (vcard_source != NULL) {
976
977                         /** Display the summary line */
978                         display_vcard(WC->WBuf, vcard_source, alpha, 0, NULL,msgnum);
979
980                         /** If it's my vCard I can edit it */
981                         if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
982                                 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
983                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
984                         ) {
985                                 wprintf("<a href=\"edit_vcard?"
986                                         "msgnum=%ld&partnum=%s\">",
987                                         msgnum, vcard_partnum);
988                                 wprintf("[%s]</a>", _("edit"));
989                         }
990
991                         free(vcard_source);
992                 }
993         }
994
995 }
996
997
998
999 /**
1000  * \brief  If it's an old "Firstname Lastname" style record, try to convert it.
1001  * \param namebuf name to analyze, reverse if nescessary
1002  */
1003 void lastfirst_firstlast(char *namebuf) {
1004         char firstname[SIZ];
1005         char lastname[SIZ];
1006         int i;
1007
1008         if (namebuf == NULL) return;
1009         if (strchr(namebuf, ';') != NULL) return;
1010
1011         i = num_tokens(namebuf, ' ');
1012         if (i < 2) return;
1013
1014         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
1015         remove_token(namebuf, i-1, ' ');
1016         strcpy(firstname, namebuf);
1017         sprintf(namebuf, "%s; %s", lastname, firstname);
1018 }
1019
1020 /**
1021  * \brief fetch what??? name
1022  * \param msgnum the citadel message number
1023  * \param namebuf where to put the name in???
1024  */
1025 void fetch_ab_name(message_summary *Msg, char *namebuf) {
1026         char buf[SIZ];
1027         char mime_partnum[SIZ];
1028         char mime_filename[SIZ];
1029         char mime_content_type[SIZ];
1030         char mime_disposition[SIZ];
1031         int mime_length;
1032         char vcard_partnum[SIZ];
1033         char *vcard_source = NULL;
1034         int i, len;
1035         message_summary summ;/// TODO this will lak
1036
1037         if (namebuf == NULL) return;
1038         strcpy(namebuf, "");
1039
1040         memset(&summ, 0, sizeof(summ));
1041         //////safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
1042
1043         sprintf(buf, "MSG0 %ld|0", Msg->msgnum);        /** unfortunately we need the mime info now */
1044         serv_puts(buf);
1045         serv_getln(buf, sizeof buf);
1046         if (buf[0] != '1') return;
1047
1048         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1049                 if (!strncasecmp(buf, "part=", 5)) {
1050                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
1051                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
1052                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
1053                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
1054                         mime_length = extract_int(&buf[5], 5);
1055
1056                         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
1057                            || (!strcasecmp(mime_content_type, "text/vcard")) ) {
1058                                 strcpy(vcard_partnum, mime_partnum);
1059                         }
1060
1061                 }
1062         }
1063
1064         if (!IsEmptyStr(vcard_partnum)) {
1065                 vcard_source = load_mimepart(Msg->msgnum, vcard_partnum);
1066                 if (vcard_source != NULL) {
1067
1068                         /* Grab the name off the card */
1069                         display_vcard(WC->WBuf, vcard_source, 0, 0, namebuf, Msg->msgnum);
1070
1071                         free(vcard_source);
1072                 }
1073         }
1074
1075         lastfirst_firstlast(namebuf);
1076         striplt(namebuf);
1077         len = strlen(namebuf);
1078         for (i=0; i<len; ++i) {
1079                 if (namebuf[i] != ';') return;
1080         }
1081         strcpy(namebuf, _("(no name)"));
1082 }
1083
1084
1085
1086
1087
1088 /*
1089  * load message pointers from the server for a "read messages" operation
1090  *
1091  * servcmd:             the citadel command to send to the citserver
1092  * with_headers:        also include some of the headers with the message numbers (more expensive)
1093  */
1094 int load_msg_ptrs(char *servcmd, int with_headers)
1095 {
1096         StrBuf* FoundCharset = NULL;
1097         struct wcsession *WCC = WC;
1098         message_summary *Msg;
1099         StrBuf *Buf, *Buf2;
1100         ///char buf[1024];
1101         ///time_t datestamp;
1102         //char fullname[128];
1103         //char nodename[128];
1104         //char inetaddr[128];
1105         //char subject[1024];
1106         ///char *ptr;
1107         int nummsgs;
1108         ////int sbjlen;
1109         int maxload = 0;
1110         long len;
1111
1112         ////int num_summ_alloc = 0;
1113
1114         if (WCC->summ != NULL) {
1115                 if (WCC->summ != NULL)
1116                         DeleteHash(&WCC->summ);
1117         }
1118         WCC->summ = NewHash(1, Flathash);
1119         nummsgs = 0;
1120         maxload = 1000;/// TODO
1121         
1122         Buf = NewStrBuf();
1123         serv_puts(servcmd);
1124         StrBuf_ServGetln(Buf);
1125         if (GetServerStatus(Buf, NULL) != 1) {
1126                 FreeStrBuf(&Buf);
1127                 return (nummsgs);
1128         }
1129 // TODO                         if (with_headers) { //// TODO: Have Attachments?
1130         Buf2 = NewStrBuf();
1131         while (len = StrBuf_ServGetln(Buf),
1132                ((len != 3)  ||
1133                 strcmp(ChrPtr(Buf), "000")!= 0))
1134         {
1135                 if (nummsgs < maxload) {
1136                         Msg = (message_summary*)malloc(sizeof(message_summary));
1137                         memset(Msg, 0, sizeof(message_summary));
1138
1139                         Msg->msgnum = StrBufExtract_long(Buf, 0, '|');
1140                         Msg->date = StrBufExtract_long(Buf, 1, '|');
1141
1142                         Msg->from = NewStrBufPlain(NULL, StrLength(Buf));
1143                         StrBufExtract_token(Buf2, Buf, 2, '|');
1144                         if (StrLength(Buf2) != 0) {
1145                                 /** Handle senders with RFC2047 encoding */
1146                                 StrBuf_RFC822_to_Utf8(Msg->from, Buf2, WCC->DefaultCharset, FoundCharset);
1147                         }
1148                         
1149                         /** Nodename */
1150                         StrBufExtract_token(Buf2, Buf, 3, '|');
1151                         if ((StrLength(Buf2) !=0 ) &&
1152                             ( ((WCC->room_flags & QR_NETWORK)
1153                                || ((strcasecmp(ChrPtr(Buf2), serv_info.serv_nodename)
1154                                     && (strcasecmp(ChrPtr(Buf2), serv_info.serv_fqdn)))))))
1155                         {
1156                                 StrBufAppendBufPlain(Msg->from, HKEY(" @ "), 0);
1157                                 StrBufAppendBuf(Msg->from, Buf2, 0);
1158                         }
1159
1160                         /** Not used:
1161                         StrBufExtract_token(Msg->inetaddr, Buf, 4, '|');
1162                         */
1163
1164                         Msg->subj = NewStrBufPlain(NULL, StrLength(Buf));
1165                         StrBufExtract_token(Buf2,  Buf, 5, '|');
1166                         if (StrLength(Buf2) == 0)
1167                                 StrBufAppendBufPlain(Msg->subj, _("(no subj)"), 0, -1);
1168                         else {
1169                                 StrBuf_RFC822_to_Utf8(Msg->subj, Buf2, WCC->DefaultCharset, FoundCharset);
1170                                 if ((StrLength(Msg->subj) > 75) && 
1171                                     (StrBuf_Utf8StrLen(Msg->subj) > 75)) {
1172                                         StrBuf_Utf8StrCut(Msg->subj, 72);
1173                                         StrBufAppendBufPlain(Msg->subj, HKEY("..."), 0);
1174                                 }
1175                         }
1176
1177
1178                         if ((StrLength(Msg->from) > 25) && 
1179                             (StrBuf_Utf8StrLen(Msg->from) > 25)) {
1180                                 StrBuf_Utf8StrCut(Msg->from, 23);
1181                                 StrBufAppendBufPlain(Msg->from, HKEY("..."), 0);
1182                         }
1183                         Put(WCC->summ, (const char*)&Msg->msgnum, sizeof(Msg->msgnum), Msg, DestroyMessageSummary);
1184                 }
1185                 nummsgs++;
1186         }
1187         FreeStrBuf(&Buf2);
1188         FreeStrBuf(&Buf);
1189         return (nummsgs);
1190 }
1191
1192
1193
1194
1195
1196
1197 /*
1198  * command loop for reading messages
1199  *
1200  * Set oper to "readnew" or "readold" or "readfwd" or "headers"
1201  */
1202 void readloop(char *oper)
1203 {
1204         void *vMsg;
1205         message_summary *Msg;
1206         char cmd[256] = "";
1207         char buf[SIZ];
1208         char old_msgs[SIZ];
1209         int a = 0;
1210         int b = 0;
1211         int nummsgs;
1212         long startmsg;
1213         int maxmsgs;
1214         long *displayed_msgs = NULL;
1215         int num_displayed = 0;
1216         int is_summary = 0;
1217         int is_addressbook = 0;
1218         int is_singlecard = 0;
1219         int is_calendar = 0;
1220         struct calview calv;
1221         int is_tasks = 0;
1222         int is_notes = 0;
1223         int is_bbview = 0;
1224         int lo, hi;
1225         int lowest_displayed = (-1);
1226         int highest_displayed = 0;
1227         addrbookent *addrbook = NULL;
1228         int num_ab = 0;
1229         const StrBuf *sortby = NULL;
1230         //SortByEnum 
1231         int SortBy = eRDate;
1232         const StrBuf *sortpref_value;
1233         int bbs_reverse = 0;
1234         struct wcsession *WCC = WC;     /* This is done to make it run faster; WC is a function */
1235         HashPos *at;
1236         const char *HashKey;
1237         long HKLen;
1238
1239         if (WCC->wc_view == VIEW_WIKI) {
1240                 sprintf(buf, "wiki?room=%s&page=home", WCC->wc_roomname);
1241                 http_redirect(buf);
1242                 return;
1243         }
1244
1245         startmsg = lbstr("startmsg");
1246         maxmsgs = ibstr("maxmsgs");
1247         is_summary = (ibstr("is_summary") && !WCC->is_mobile);
1248         if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
1249
1250         sortpref_value = get_room_pref("sort");
1251
1252         sortby = sbstr("sortby");
1253         if ( (!IsEmptyStr(ChrPtr(sortby))) && 
1254              (strcasecmp(ChrPtr(sortby), ChrPtr(sortpref_value)) != 0)) {
1255                 set_room_pref("sort", NewStrBufDup(sortby), 1);
1256                 sortpref_value = NULL;
1257                 sortpref_value = sortby;
1258         }
1259
1260         SortBy = StrToESort(sortpref_value);
1261         /* message board sort */
1262         if (SortBy == eReverse) {
1263                 bbs_reverse = 1;
1264         }
1265         else {
1266                 bbs_reverse = 0;
1267         }
1268
1269         output_headers(1, 1, 1, 0, 0, 0);
1270
1271         /*
1272          * When in summary mode, always show ALL messages instead of just
1273          * new or old.  Otherwise, show what the user asked for.
1274          */
1275         if (!strcmp(oper, "readnew")) {
1276                 strcpy(cmd, "MSGS NEW");
1277         }
1278         else if (!strcmp(oper, "readold")) {
1279                 strcpy(cmd, "MSGS OLD");
1280         }
1281         else if (!strcmp(oper, "do_search")) {
1282                 snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query"));
1283         }
1284         else {
1285                 strcpy(cmd, "MSGS ALL");
1286         }
1287
1288         if ((WCC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1) && !WCC->is_mobile) {
1289                 is_summary = 1;
1290                 if (!strcmp(oper, "do_search")) {
1291                         snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query"));
1292                 }
1293                 else {
1294                         strcpy(cmd, "MSGS ALL");
1295                 }
1296         }
1297
1298         if ((WCC->wc_view == VIEW_ADDRESSBOOK) && (maxmsgs > 1)) {
1299                 is_addressbook = 1;
1300                 if (!strcmp(oper, "do_search")) {
1301                         snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query"));
1302                 }
1303                 else {
1304                         strcpy(cmd, "MSGS ALL");
1305                 }
1306                 maxmsgs = 9999999;
1307         }
1308
1309         if (is_summary) {                       /**< fetch header summary */
1310                 snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1",
1311                         (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"),
1312                         (!strcmp(oper, "do_search") ? bstr("query") : "")
1313                 );
1314                 startmsg = 1;
1315                 maxmsgs = 9999999;
1316         } 
1317         if (WCC->is_mobile) {
1318                 maxmsgs = 20;
1319                 snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1",
1320                         (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"),
1321                         (!strcmp(oper, "do_search") ? bstr("query") : "")
1322                 );
1323                 SortBy =  eRDate;
1324         }
1325
1326         /*
1327          * Are we doing a summary view?  If so, we need to know old messages
1328          * and new messages, so we can do that pretty boldface thing for the
1329          * new messages.
1330          */
1331         strcpy(old_msgs, "");
1332         if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){
1333                 serv_puts("GTSN");
1334                 serv_getln(buf, sizeof buf);
1335                 if (buf[0] == '2') {
1336                         strcpy(old_msgs, &buf[4]);
1337                 }
1338         }
1339
1340         is_singlecard = ibstr("is_singlecard");
1341
1342         if (WCC->wc_default_view == VIEW_CALENDAR) {            /**< calendar */
1343                 is_calendar = 1;
1344                 strcpy(cmd, "MSGS ALL|||1");
1345                 maxmsgs = 32767;
1346                 parse_calendar_view_request(&calv);
1347         }
1348         if (WCC->wc_default_view == VIEW_TASKS) {               /**< tasks */
1349                 is_tasks = 1;
1350                 strcpy(cmd, "MSGS ALL");
1351                 maxmsgs = 32767;
1352         }
1353         if (WCC->wc_default_view == VIEW_NOTES) {               /**< notes */
1354                 is_notes = 1;
1355                 strcpy(cmd, "MSGS ALL");
1356                 maxmsgs = 32767;
1357         }
1358
1359         if (is_notes) {
1360                 wprintf("<div id=\"new_notes_here\"></div>\n");
1361         }
1362
1363         nummsgs = load_msg_ptrs(cmd, (is_summary || WCC->is_mobile));
1364         if (nummsgs == 0) {
1365
1366                 if ((!is_tasks) && (!is_calendar) && (!is_notes) && (!is_addressbook)) {
1367                         wprintf("<div align=\"center\"><br /><em>");
1368                         if (!strcmp(oper, "readnew")) {
1369                                 wprintf(_("No new messages."));
1370                         } else if (!strcmp(oper, "readold")) {
1371                                 wprintf(_("No old messages."));
1372                         } else {
1373                                 wprintf(_("No messages here."));
1374                         }
1375                         wprintf("</em><br /></div>\n");
1376                 }
1377
1378                 goto DONE;
1379         }
1380
1381         if ((is_summary) || (WCC->wc_default_view == VIEW_CALENDAR) || WCC->is_mobile){
1382                 void *vMsg;
1383                 message_summary *Msg;
1384
1385                 at = GetNewHashPos();
1386                 while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
1387                         /** Are you a new message, or an old message? */
1388                         Msg = (message_summary*) vMsg;
1389                         if (is_summary) {
1390                                 if (is_msg_in_mset(old_msgs, Msg->msgnum)) {
1391                                         Msg->is_new = 0;
1392                                 }
1393                                 else {
1394                                         Msg->is_new = 1;
1395                                 }
1396                         }
1397                 }
1398                 DeleteHashPos(&at);
1399         }
1400
1401         if (startmsg == 0L) {
1402                 if (bbs_reverse) {
1403                         startmsg = WCC->msgarr[(nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0];
1404                 }
1405                 else {
1406                         startmsg = WCC->msgarr[0];
1407                 }
1408         }
1409
1410         if (is_summary || WCC->is_mobile) {
1411                 SortByPayload(WCC->summ, SortFuncs[SortBy]);
1412         }
1413
1414         if (is_summary) {
1415
1416                 wprintf("<script language=\"javascript\" type=\"text/javascript\">"
1417                         " document.onkeydown = CtdlMsgListKeyPress;     "
1418                         " if (document.layers) {                        "
1419                         "       document.captureEvents(Event.KEYPRESS); "
1420                         " }                                             "
1421                         "</script>\n"
1422                 );
1423
1424                 /** note that Date and Delete are now in the same column */
1425                 wprintf("<div id=\"message_list_hdr\">"
1426                         "<div class=\"fix_scrollbar_bug\">"
1427                         "<table cellspacing=0 style=\"width:100%%\">"
1428                         "<tr>"
1429                 );
1430                 wprintf("<th width=%d%%>%s <a href=\"readfwd?startmsg=1?maxmsgs=9999999?is_summary=1?sortby=%s\"><img border=\"0\" src=\"%s\" /></a> </th>\n"
1431                         "<th width=%d%%>%s <a href=\"readfwd?startmsg=1?maxmsgs=9999999?is_summary=1?sortby=%s\"><img border=\"0\" src=\"%s\" /></a> </th>\n"
1432                         "<th width=%d%%>%s <a href=\"readfwd?startmsg=1?maxmsgs=9999999?is_summary=1?sortby=%s\"><img border=\"0\" src=\"%s\" /></a> \n"
1433                         "&nbsp;"
1434                         "<input type=\"submit\" name=\"delete_button\" id=\"delbutton\" "
1435                         " onClick=\"CtdlDeleteSelectedMessages(event)\" "
1436                         " value=\"%s\">"
1437                         "</th>"
1438                         "</tr>\n"
1439                         ,
1440                         SUBJ_COL_WIDTH_PCT,
1441                         _("Subject"),
1442                         SortByStrings[SubjectInvertSortString[SortBy]],
1443                         SortIcons[SortSubjectToIcon[SortBy]],
1444                         SENDER_COL_WIDTH_PCT,
1445                         _("Sender"),
1446                         SortByStrings[SenderInvertSortString[SortBy]],
1447                         SortIcons[SortSenderToIcon[SortBy]],
1448                         DATE_PLUS_BUTTONS_WIDTH_PCT,
1449                         _("Date"),
1450                         SortByStrings[DateInvertSortString[SortBy]],
1451                         SortIcons[SortDateToIcon[SortBy]],
1452                         _("Delete")
1453                 );
1454                 wprintf("</table></div></div>\n");
1455                 wprintf("<div id=\"message_list\">"
1456
1457                         "<div class=\"fix_scrollbar_bug\">\n"
1458                         "<table class=\"mailbox_summary\" id=\"summary_headers\" "
1459                         "cellspacing=0 style=\"width:100%%;-moz-user-select:none;\">"
1460                 );
1461         } else if (WCC->is_mobile) {
1462                 wprintf("<div id=\"message_list\">");
1463         }
1464
1465
1466         /**
1467          * Set the "is_bbview" variable if it appears that we are looking at
1468          * a classic bulletin board view.
1469          */
1470         if ((!is_tasks) && (!is_calendar) && (!is_addressbook)
1471               && (!is_notes) && (!is_singlecard) && (!is_summary)) {
1472                 is_bbview = 1;
1473         }
1474
1475         /**
1476          * If we're not currently looking at ALL requested
1477          * messages, then display the selector bar
1478          */
1479         if (is_bbview) {
1480                 /** begin bbview scroller */
1481                 wprintf("<form name=\"msgomatictop\" class=\"selector_top\" > \n <p>");
1482                 wprintf(_("Reading #"));//// TODO this isn't used, should it? : , lowest_displayed, highest_displayed);
1483
1484                 wprintf("<select name=\"whichones\" size=\"1\" "
1485                         "OnChange=\"location.href=msgomatictop.whichones.options"
1486                         "[selectedIndex].value\">\n");
1487
1488                 if (bbs_reverse) {
1489                         for (b=nummsgs-1; b>=0; b = b - maxmsgs) {
1490                                 hi = b + 1;
1491                                 lo = b - maxmsgs + 2;
1492                                 if (lo < 1) lo = 1;
1493                                 wprintf("<option %s value="
1494                                         "\"%s"
1495                                         "&startmsg=%ld"
1496                                         "&maxmsgs=%d"
1497                                         "&is_summary=%d\">"
1498                                         "%d-%d</option> \n",
1499                                         ((WCC->msgarr[lo-1] == startmsg) ? "selected" : ""),
1500                                         oper,
1501                                         WCC->msgarr[lo-1],
1502                                         maxmsgs,
1503                                         is_summary,
1504                                         hi, lo);
1505                         }
1506                 }
1507                 else {
1508                         for (b=0; b<nummsgs; b = b + maxmsgs) {
1509                                 lo = b + 1;
1510                                 hi = b + maxmsgs + 1;
1511                                 if (hi > nummsgs) hi = nummsgs;
1512                                 wprintf("<option %s value="
1513                                         "\"%s"
1514                                         "&startmsg=%ld"
1515                                         "&maxmsgs=%d"
1516                                         "&is_summary=%d\">"
1517                                         "%d-%d</option> \n",
1518                                         ((WCC->msgarr[b] == startmsg) ? "selected" : ""),
1519                                         oper,
1520                                         WCC->msgarr[lo-1],
1521                                         maxmsgs,
1522                                         is_summary,
1523                                         lo, hi);
1524                         }
1525                 }
1526
1527                 wprintf("<option value=\"%s?startmsg=%ld"
1528                         "&maxmsgs=9999999&is_summary=%d\">",
1529                         oper,
1530                         WCC->msgarr[0], is_summary);
1531                 wprintf(_("All"));
1532                 wprintf("</option>");
1533                 wprintf("</select> ");
1534                 wprintf(_("of %d messages."), nummsgs);
1535
1536                 /** forward/reverse */
1537                 wprintf("<input type=\"radio\" %s name=\"direction\" value=\"\""
1538                         "OnChange=\"location.href='%s?sortby=forward'\"",  
1539                         (bbs_reverse ? "" : "checked"),
1540                         oper
1541                 );
1542                 wprintf(">");
1543                 wprintf(_("oldest to newest"));
1544                 wprintf("&nbsp;&nbsp;&nbsp;&nbsp;");
1545
1546                 wprintf("<input type=\"radio\" %s name=\"direction\" value=\"\""
1547                         "OnChange=\"location.href='%s?sortby=reverse'\"", 
1548                         (bbs_reverse ? "checked" : ""),
1549                         oper
1550                 );
1551                 wprintf(">");
1552                 wprintf(_("newest to oldest"));
1553                 wprintf("\n");
1554         
1555                 wprintf("</p></form>\n");
1556                 /** end bbview scroller */
1557         }
1558                         
1559         at = GetNewHashPos();
1560         while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
1561                 Msg = (message_summary*) vMsg;          
1562                 if ((Msg->msgnum >= startmsg) && (num_displayed < maxmsgs)) {
1563                                 
1564                         /** Display the message */
1565                         if (is_summary) {
1566                                 DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM);
1567                         }
1568                         else if (is_addressbook) {
1569                                 fetch_ab_name(Msg, buf);
1570                                 ++num_ab;
1571                                 addrbook = realloc(addrbook,
1572                                                    (sizeof(addrbookent) * num_ab) );
1573                                 safestrncpy(addrbook[num_ab-1].ab_name, buf,
1574                                             sizeof(addrbook[num_ab-1].ab_name));
1575                                 addrbook[num_ab-1].ab_msgnum = Msg->msgnum;
1576                         }
1577                         else if (is_calendar) {
1578                                 load_calendar_item(Msg, Msg->is_new, &calv);
1579                         }
1580                         else if (is_tasks) {
1581                                 display_task(Msg, Msg->is_new);
1582                         }
1583                         else if (is_notes) {
1584                                 display_note(Msg, Msg->is_new);
1585                         } else if (WCC->is_mobile) {
1586                                 DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM);
1587                         }
1588                         else {
1589                                 if (displayed_msgs == NULL) {
1590                                         displayed_msgs = malloc(sizeof(long) *
1591                                                                 (maxmsgs<nummsgs ? maxmsgs : nummsgs));
1592                                 }
1593                                 displayed_msgs[num_displayed] = Msg->msgnum;
1594                         }
1595                         
1596                         if (lowest_displayed < 0) lowest_displayed = a;
1597                         highest_displayed = a;
1598                         
1599                         ++num_displayed;
1600                 }
1601         }
1602         DeleteHashPos(&at);
1603
1604         /** Output loop */
1605         if (displayed_msgs != NULL) {
1606                 if (bbs_reverse) {
1607                         qsort(displayed_msgs, num_displayed, sizeof(long), qlongcmp_r);
1608                 }
1609
1610                 /** if we do a split bbview in the future, begin messages div here */
1611
1612                 for (a=0; a<num_displayed; ++a) {
1613                         read_message(WC->WBuf, HKEY("view_message"), displayed_msgs[a], 0, "");
1614                 }
1615
1616                 /** if we do a split bbview in the future, end messages div here */
1617
1618                 free(displayed_msgs);
1619                 displayed_msgs = NULL;
1620         }
1621
1622         if (is_summary) {
1623                 wprintf("</table>"
1624                         "</div>\n");                    /**< end of 'fix_scrollbar_bug' div */
1625                 wprintf("</div>");                      /**< end of 'message_list' div */
1626                 
1627                 /** Here's the grab-it-to-resize-the-message-list widget */
1628                 wprintf("<div id=\"resize_msglist\" "
1629                         "onMouseDown=\"CtdlResizeMsgListMouseDown(event)\">"
1630                         "<div class=\"fix_scrollbar_bug\"> <hr>"
1631                         "</div></div>\n"
1632                 );
1633
1634                 wprintf("<div id=\"preview_pane\">");   /**< The preview pane will initially be empty */
1635         } else if (WCC->is_mobile) {
1636                 wprintf("</div>");
1637         }
1638
1639         /**
1640          * Bump these because although we're thinking in zero base, the user
1641          * is a drooling idiot and is thinking in one base.
1642          */
1643         ++lowest_displayed;
1644         ++highest_displayed;
1645
1646         /**
1647          * If we're not currently looking at ALL requested
1648          * messages, then display the selector bar
1649          */
1650         if (is_bbview) {
1651                 /** begin bbview scroller */
1652                 wprintf("<form name=\"msgomatic\" class=\"selector_bottom\" > \n <p>");
1653                 wprintf(_("Reading #")); /// TODO: this isn't used: , lowest_displayed, highest_displayed);
1654
1655                 wprintf("<select name=\"whichones\" size=\"1\" "
1656                         "OnChange=\"location.href=msgomatic.whichones.options"
1657                         "[selectedIndex].value\">\n");
1658
1659                 if (bbs_reverse) {
1660                         for (b=nummsgs-1; b>=0; b = b - maxmsgs) {
1661                                 hi = b + 1;
1662                                 lo = b - maxmsgs + 2;
1663                                 if (lo < 1) lo = 1;
1664                                 wprintf("<option %s value="
1665                                         "\"%s"
1666                                         "&startmsg=%ld"
1667                                         "&maxmsgs=%d"
1668                                         "&is_summary=%d\">"
1669                                         "%d-%d</option> \n",
1670                                         ((WCC->msgarr[lo-1] == startmsg) ? "selected" : ""),
1671                                         oper,
1672                                         WCC->msgarr[lo-1],
1673                                         maxmsgs,
1674                                         is_summary,
1675                                         hi, lo);
1676                         }
1677                 }
1678                 else {
1679                         for (b=0; b<nummsgs; b = b + maxmsgs) {
1680                                 lo = b + 1;
1681                                 hi = b + maxmsgs + 1;
1682                                 if (hi > nummsgs) hi = nummsgs;
1683                                 wprintf("<option %s value="
1684                                         "\"%s"
1685                                         "&startmsg=%ld"
1686                                         "&maxmsgs=%d"
1687                                         "&is_summary=%d\">"
1688                                         "%d-%d</option> \n",
1689                                         ((WCC->msgarr[b] == startmsg) ? "selected" : ""),
1690                                         oper,
1691                                         WCC->msgarr[lo-1],
1692                                         maxmsgs,
1693                                         is_summary,
1694                                         lo, hi);
1695                         }
1696                 }
1697
1698                 wprintf("<option value=\"%s&startmsg=%ld"
1699                         "&maxmsgs=9999999&is_summary=%d\">",
1700                         oper,
1701                         WCC->msgarr[0], is_summary);
1702                 wprintf(_("All"));
1703                 wprintf("</option>");
1704                 wprintf("</select> ");
1705                 wprintf(_("of %d messages."), nummsgs);
1706
1707                 /** forward/reverse */
1708                 wprintf("<input type=\"radio\" %s name=\"direction\" value=\"\""
1709                         "OnChange=\"location.href='%s&sortby=forward'\"",  
1710                         (bbs_reverse ? "" : "checked"),
1711                         oper
1712                 );
1713                 wprintf(">");
1714                 wprintf(_("oldest to newest"));
1715                 wprintf("&nbsp;&nbsp;&nbsp;&nbsp;");
1716                 wprintf("<input type=\"radio\" %s name=\"direction\" value=\"\""
1717                         "OnChange=\"location.href='%s&sortby=reverse'\"", 
1718                         (bbs_reverse ? "checked" : ""),
1719                         oper
1720                 );
1721                 wprintf(">");
1722                 wprintf(_("newest to oldest"));
1723                 wprintf("\n");
1724
1725                 wprintf("</p></form>\n");
1726                 /** end bbview scroller */
1727         }
1728         
1729 DONE:
1730         if (is_tasks) {
1731                 do_tasks_view();        /** Render the task list */
1732         }
1733
1734         if (is_calendar) {
1735                 render_calendar_view(&calv);
1736         }
1737
1738         if (is_addressbook) {
1739                 do_addrbook_view(addrbook, num_ab);     /** Render the address book */
1740         }
1741
1742         /** Note: wDumpContent() will output one additional </div> tag. */
1743         wprintf("</div>\n");            /** end of 'content' div */
1744         wDumpContent(1);
1745
1746         /** free the summary */
1747         if (WCC->summ != NULL) {
1748                 DeleteHash(&WCC->summ);
1749         }
1750         if (addrbook != NULL) free(addrbook);
1751 }
1752
1753
1754 /*
1755  * Back end for post_message()
1756  * ... this is where the actual message gets transmitted to the server.
1757  */
1758 void post_mime_to_server(void) {
1759         struct wcsession *WCC = WC;
1760         char top_boundary[SIZ];
1761         char alt_boundary[SIZ];
1762         int is_multipart = 0;
1763         static int seq = 0;
1764         wc_attachment *att;
1765         char *encoded;
1766         size_t encoded_length;
1767         size_t encoded_strlen;
1768         char *txtmail = NULL;
1769
1770         sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x",
1771                 serv_info.serv_fqdn,
1772                 getpid(),
1773                 ++seq
1774         );
1775         sprintf(alt_boundary, "Citadel--Multipart--%s--%04x--%04x",
1776                 serv_info.serv_fqdn,
1777                 getpid(),
1778                 ++seq
1779         );
1780
1781         /* RFC2045 requires this, and some clients look for it... */
1782         serv_puts("MIME-Version: 1.0");
1783         serv_puts("X-Mailer: " PACKAGE_STRING);
1784
1785         /* If there are attachments, we have to do multipart/mixed */
1786         if (GetCount(WCC->attachments) > 0) {
1787                 is_multipart = 1;
1788         }
1789
1790         if (is_multipart) {
1791                 /* Remember, serv_printf() appends an extra newline */
1792                 serv_printf("Content-type: multipart/mixed; boundary=\"%s\"\n", top_boundary);
1793                 serv_printf("This is a multipart message in MIME format.\n");
1794                 serv_printf("--%s", top_boundary);
1795         }
1796
1797         /* Remember, serv_printf() appends an extra newline */
1798         serv_printf("Content-type: multipart/alternative; "
1799                 "boundary=\"%s\"\n", alt_boundary);
1800         serv_printf("This is a multipart message in MIME format.\n");
1801         serv_printf("--%s", alt_boundary);
1802
1803         serv_puts("Content-type: text/plain; charset=utf-8");
1804         serv_puts("Content-Transfer-Encoding: quoted-printable");
1805         serv_puts("");
1806         txtmail = html_to_ascii(bstr("msgtext"), 0, 80, 0);
1807         text_to_server_qp(txtmail);     /* Transmit message in quoted-printable encoding */
1808         free(txtmail);
1809
1810         serv_printf("--%s", alt_boundary);
1811
1812         serv_puts("Content-type: text/html; charset=utf-8");
1813         serv_puts("Content-Transfer-Encoding: quoted-printable");
1814         serv_puts("");
1815         serv_puts("<html><body>\r\n");
1816         text_to_server_qp(bstr("msgtext"));     /* Transmit message in quoted-printable encoding */
1817         serv_puts("</body></html>\r\n");
1818
1819         serv_printf("--%s--", alt_boundary);
1820         
1821         if (is_multipart) {
1822                 long len;
1823                 const char *Key; 
1824                 void *vAtt;
1825                 HashPos  *it;
1826
1827                 /* Add in the attachments */
1828                 it = GetNewHashPos();
1829                 while (GetNextHashPos(WCC->attachments, it, &len, &Key, &vAtt)) {
1830                         att = (wc_attachment*)vAtt;
1831                         encoded_length = ((att->length * 150) / 100);
1832                         encoded = malloc(encoded_length);
1833                         if (encoded == NULL) break;
1834                         encoded_strlen = CtdlEncodeBase64(encoded, att->data, att->length, 1);
1835
1836                         serv_printf("--%s", top_boundary);
1837                         serv_printf("Content-type: %s", ChrPtr(att->content_type));
1838                         serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att->filename));
1839                         serv_puts("Content-transfer-encoding: base64");
1840                         serv_puts("");
1841                         serv_write(encoded, encoded_strlen);
1842                         serv_puts("");
1843                         serv_puts("");
1844                         free(encoded);
1845                 }
1846                 serv_printf("--%s--", top_boundary);
1847                 DeleteHashPos(&it);
1848         }
1849
1850         serv_puts("000");
1851 }
1852
1853
1854 /*
1855  * Post message (or don't post message)
1856  *
1857  * Note regarding the "dont_post" variable:
1858  * A random value (actually, it's just a timestamp) is inserted as a hidden
1859  * field called "postseq" when the display_enter page is generated.  This
1860  * value is checked when posting, using the static variable dont_post.  If a
1861  * user attempts to post twice using the same dont_post value, the message is
1862  * discarded.  This prevents the accidental double-saving of the same message
1863  * if the user happens to click the browser "back" button.
1864  */
1865 void post_message(void)
1866 {
1867         char buf[1024];
1868         StrBuf *encoded_subject = NULL;
1869         static long dont_post = (-1L);
1870         wc_attachment *att;
1871         int is_anonymous = 0;
1872         const StrBuf *display_name = NULL;
1873         struct wcsession *WCC = WC;
1874         
1875         if (havebstr("force_room")) {
1876                 gotoroom(bstr("force_room"));
1877         }
1878
1879         if (havebstr("display_name")) {
1880                 display_name = sbstr("display_name");
1881                 if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
1882                         display_name = NULL;
1883                         is_anonymous = 1;
1884                 }
1885         }
1886
1887         if (WCC->upload_length > 0) {
1888                 const char *pch;
1889                 int n;
1890                 char N[64];
1891
1892                 lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length);
1893                 /** There's an attachment.  Save it to this struct... */
1894                 att = malloc(sizeof(wc_attachment));
1895                 memset(att, 0, sizeof(wc_attachment));
1896                 att->length = WCC->upload_length;
1897                 att->content_type = NewStrBufPlain(WCC->upload_content_type, -1);
1898                 att->filename = NewStrBufPlain(WCC->upload_filename, -1);
1899                 
1900                 
1901                 if (WCC->attachments == NULL)
1902                         WCC->attachments = NewHash(1, NULL);
1903                 /* And add it to the list. */
1904                 n = snprintf(N, sizeof N, "%d", GetCount(WCC->attachments) + 1);
1905                 Put(WCC->attachments, N, n, att, free_attachment);
1906
1907                 /**
1908                  * Mozilla sends a simple filename, which is what we want,
1909                  * but Satan's Browser sends an entire pathname.  Reduce
1910                  * the path to just a filename if we need to.
1911                  */
1912                 pch = strrchr(ChrPtr(att->filename), '/');
1913                 if (pch != NULL) {
1914                         StrBufCutLeft(att->filename, pch - ChrPtr(att->filename));
1915                 }
1916                 pch = strrchr(ChrPtr(att->filename), '\\');
1917                 if (pch != NULL) {
1918                         StrBufCutLeft(att->filename, pch - ChrPtr(att->filename));
1919                 }
1920
1921                 /**
1922                  * Transfer control of this memory from the upload struct
1923                  * to the attachment struct.
1924                  */
1925                 att->data = WCC->upload;
1926                 WCC->upload_length = 0;
1927                 WCC->upload = NULL;
1928                 display_enter();
1929                 return;
1930         }
1931
1932         if (havebstr("cancel_button")) {
1933                 sprintf(WCC->ImportantMessage, 
1934                         _("Cancelled.  Message was not posted."));
1935         } else if (havebstr("attach_button")) {
1936                 display_enter();
1937                 return;
1938         } else if (lbstr("postseq") == dont_post) {
1939                 sprintf(WCC->ImportantMessage, 
1940                         _("Automatically cancelled because you have already "
1941                         "saved this message."));
1942         } else {
1943                 const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
1944                 const StrBuf *Recp = NULL; 
1945                 const StrBuf *Cc = NULL;
1946                 const StrBuf *Bcc = NULL;
1947                 const StrBuf *Wikipage = NULL;
1948                 const StrBuf *my_email_addr = NULL;
1949                 StrBuf *CmdBuf = NULL;;
1950                 StrBuf *references = NULL;
1951
1952                 if (havebstr("references"))
1953                 {
1954                         const StrBuf *ref = sbstr("references");
1955                         references = NewStrBufPlain(ChrPtr(ref), StrLength(ref));
1956                         lprintf(9, "Converting: %s\n", ChrPtr(references));
1957                         StrBufReplaceChars(references, '|', '!');
1958                         lprintf(9, "Converted: %s\n", ChrPtr(references));
1959                 }
1960                 if (havebstr("subject")) {
1961                         const StrBuf *Subj;
1962                         /*
1963                          * make enough room for the encoded string; 
1964                          * plus the QP header 
1965                          */
1966                         Subj = sbstr("subject");
1967                         
1968                         StrBufRFC2047encode(&encoded_subject, Subj);
1969                 }
1970                 Recp = sbstr("recp");
1971                 Cc = sbstr("cc");
1972                 Bcc = sbstr("bcc");
1973                 Wikipage = sbstr("wikipage");
1974                 my_email_addr = sbstr("my_email_addr");
1975                 
1976                 CmdBuf = NewStrBufPlain(NULL, 
1977                                         sizeof (CMD) + 
1978                                         StrLength(Recp) + 
1979                                         StrLength(encoded_subject) +
1980                                         StrLength(Cc) +
1981                                         StrLength(Bcc) + 
1982                                         StrLength(Wikipage) +
1983                                         StrLength(my_email_addr) + 
1984                                         StrLength(references));
1985
1986                 StrBufPrintf(CmdBuf, 
1987                              CMD,
1988                              ChrPtr(Recp),
1989                              is_anonymous,
1990                              ChrPtr(encoded_subject),
1991                              ChrPtr(display_name),
1992                              ChrPtr(Cc),
1993                              ChrPtr(Bcc),
1994                              ChrPtr(Wikipage),
1995                              ChrPtr(my_email_addr),
1996                              ChrPtr(references));
1997                 FreeStrBuf(&references);
1998
1999                 lprintf(9, "%s\n", CmdBuf);
2000                 serv_puts(ChrPtr(CmdBuf));
2001                 serv_getln(buf, sizeof buf);
2002                 FreeStrBuf(&CmdBuf);
2003                 FreeStrBuf(&encoded_subject);
2004                 if (buf[0] == '4') {
2005                         post_mime_to_server();
2006                         if (  (havebstr("recp"))
2007                            || (havebstr("cc"  ))
2008                            || (havebstr("bcc" ))
2009                         ) {
2010                                 sprintf(WCC->ImportantMessage, _("Message has been sent.\n"));
2011                         }
2012                         else {
2013                                 sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
2014                         }
2015                         dont_post = lbstr("postseq");
2016                 } else {
2017                         lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, buf);
2018                         sprintf(WC->ImportantMessage, "%s", &buf[4]);
2019                         display_enter();
2020                         return;
2021                 }
2022         }
2023
2024         DeleteHash(&WCC->attachments);
2025
2026         /**
2027          *  We may have been supplied with instructions regarding the location
2028          *  to which we must return after posting.  If found, go there.
2029          */
2030         if (havebstr("return_to")) {
2031                 http_redirect(bstr("return_to"));
2032         }
2033         /**
2034          *  If we were editing a page in a wiki room, go to that page now.
2035          */
2036         else if (havebstr("wikipage")) {
2037                 snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
2038                 http_redirect(buf);
2039         }
2040         /**
2041          *  Otherwise, just go to the "read messages" loop.
2042          */
2043         else {
2044                 readloop("readnew");
2045         }
2046 }
2047
2048
2049
2050
2051 /**
2052  * \brief display the message entry screen
2053  */
2054 void display_enter(void)
2055 {
2056         char buf[SIZ];
2057         StrBuf *ebuf;
2058         long now;
2059         const StrBuf *display_name = NULL;
2060         /////wc_attachment *att;
2061         int recipient_required = 0;
2062         int subject_required = 0;
2063         int recipient_bad = 0;
2064         int is_anonymous = 0;
2065         long existing_page = (-1L);
2066         struct wcsession *WCC = WC;
2067
2068         now = time(NULL);
2069
2070         if (havebstr("force_room")) {
2071                 gotoroom(bstr("force_room"));
2072         }
2073
2074         display_name = sbstr("display_name");
2075         if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
2076                 display_name = NULL;
2077                 is_anonymous = 1;
2078         }
2079
2080         /** First test to see whether this is a room that requires recipients to be entered */
2081         serv_puts("ENT0 0");
2082         serv_getln(buf, sizeof buf);
2083
2084         if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
2085                 recipient_required = 1;
2086         }
2087         else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
2088                 sprintf(WCC->ImportantMessage, "%s", &buf[4]);
2089                 readloop("readnew");
2090                 return;
2091         }
2092
2093         /* Is the server strongly recommending that the user enter a message subject? */
2094         if ((buf[3] != '\0') && (buf[4] != '\0')) {
2095                 subject_required = extract_int(&buf[4], 1);
2096         }
2097
2098         /**
2099          * Are we perhaps in an address book view?  If so, then an "enter
2100          * message" command really means "add new entry."
2101          */
2102         if (WCC->wc_default_view == VIEW_ADDRESSBOOK) {
2103                 do_edit_vcard(-1, "", "", WCC->wc_roomname);
2104                 return;
2105         }
2106
2107         /*
2108          * Are we perhaps in a calendar room?  If so, then an "enter
2109          * message" command really means "add new calendar item."
2110          */
2111         if (WCC->wc_default_view == VIEW_CALENDAR) {
2112                 display_edit_event();
2113                 return;
2114         }
2115
2116         /*
2117          * Are we perhaps in a tasks view?  If so, then an "enter
2118          * message" command really means "add new task."
2119          */
2120         if (WCC->wc_default_view == VIEW_TASKS) {
2121                 display_edit_task();
2122                 return;
2123         }
2124
2125         /*
2126          * Otherwise proceed normally.
2127          * Do a custom room banner with no navbar...
2128          */
2129         output_headers(1, 1, 2, 0, 0, 0);
2130
2131 /*
2132         wprintf("<div id=\"banner\">\n");
2133         embed_room_banner(NULL, navbar_none);
2134         wprintf("</div>\n");
2135         wprintf("<div id=\"content\">\n"
2136                 "<div class=\"fix_scrollbar_bug message \">");
2137 */
2138         /* Now check our actual recipients if there are any */
2139         if (recipient_required) {
2140                 const StrBuf *Recp = NULL; 
2141                 const StrBuf *Cc = NULL;
2142                 const StrBuf *Bcc = NULL;
2143                 const StrBuf *Wikipage = NULL;
2144                 StrBuf *CmdBuf = NULL;;
2145                 const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s";
2146                 
2147                 Recp = sbstr("recp");
2148                 Cc = sbstr("cc");
2149                 Bcc = sbstr("bcc");
2150                 Wikipage = sbstr("wikipage");
2151                 
2152                 CmdBuf = NewStrBufPlain(NULL, 
2153                                         sizeof (CMD) + 
2154                                         StrLength(Recp) + 
2155                                         StrLength(display_name) +
2156                                         StrLength(Cc) +
2157                                         StrLength(Bcc) + 
2158                                         StrLength(Wikipage));
2159
2160                 StrBufPrintf(CmdBuf, 
2161                              CMD,
2162                              ChrPtr(Recp), 
2163                              is_anonymous,
2164                              ChrPtr(display_name),
2165                              ChrPtr(Cc), 
2166                              ChrPtr(Bcc), 
2167                              ChrPtr(Wikipage));
2168                 serv_puts(ChrPtr(CmdBuf));
2169                 serv_getln(buf, sizeof buf);
2170                 FreeStrBuf(&CmdBuf);
2171
2172                 if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
2173                         if (havebstr("recp") && 
2174                             havebstr("cc"  ) && 
2175                             havebstr("bcc" )) {
2176                                 recipient_bad = 1;
2177                         }
2178                 }
2179                 else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
2180                         wprintf("<em>%s</em><br />\n", &buf[4]);/// -> important message
2181                         goto DONE;
2182                 }
2183         }
2184         DoTemplate(HKEY("edit_message"), NULL, NULL, CTX_NONE);
2185         address_book_popup();
2186         wDumpContent(1);
2187
2188
2189         return;
2190
2191
2192         /** If we got this far, we can display the message entry screen. */
2193
2194         /* begin message entry screen */
2195         wprintf("<form "
2196                 "enctype=\"multipart/form-data\" "
2197                 "method=\"POST\" "
2198                 "accept-charset=\"UTF-8\" "
2199                 "action=\"post\" "
2200                 "name=\"enterform\""
2201                 ">\n");
2202         wprintf("<input type=\"hidden\" name=\"postseq\" value=\"%ld\">\n", now);
2203         if (WCC->wc_view == VIEW_WIKI) {
2204                 wprintf("<input type=\"hidden\" name=\"wikipage\" value=\"%s\">\n", bstr("wikipage"));
2205         }
2206         wprintf("<input type=\"hidden\" name=\"return_to\" value=\"%s\">\n", bstr("return_to"));
2207         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WCC->nonce);
2208         wprintf("<input type=\"hidden\" name=\"force_room\" value=\"");
2209         escputs(WCC->wc_roomname);
2210         wprintf("\">\n");
2211         wprintf("<input type=\"hidden\" name=\"references\" value=\"");
2212         escputs(bstr("references"));
2213         wprintf("\">\n");
2214
2215         /** submit or cancel buttons */
2216         wprintf("<p class=\"send_edit_msg\">");
2217         wprintf("<input type=\"submit\" name=\"send_button\" value=\"");
2218         if (recipient_required) {
2219                 wprintf(_("Send message"));
2220         } else {
2221                 wprintf(_("Post message"));
2222         }
2223         wprintf("\">&nbsp;"
2224                 "<input type=\"submit\" name=\"cancel_button\" value=\"%s\">\n", _("Cancel"));
2225         wprintf("</p>");
2226
2227         /** header bar */
2228
2229         wprintf("<img src=\"static/newmess3_24x.gif\" class=\"imgedit\">");
2230         wprintf("  ");  /** header bar */
2231         webcit_fmt_date(buf, now, 0);
2232         wprintf("%s", buf);
2233         wprintf("\n");  /** header bar */
2234
2235         wprintf("<table width=\"100%%\" class=\"edit_msg_table\">");
2236         wprintf("<tr>");
2237         wprintf("<th><label for=\"from_id\" > ");
2238         wprintf(_(" <I>from</I> "));
2239         wprintf("</label></th>");
2240
2241         wprintf("<td colspan=\"2\">");
2242
2243         /* Allow the user to select any of his valid screen names */
2244
2245         wprintf("<select name=\"display_name\" size=1 id=\"from_id\">\n");
2246
2247         serv_puts("GVSN");
2248         serv_getln(buf, sizeof buf);
2249         if (buf[0] == '1') {
2250                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
2251                         wprintf("<option %s value=\"",
2252                                 ((!strcasecmp(bstr("display_name"), buf)) ? "selected" : "")
2253                         );
2254                         escputs(buf);
2255                         wprintf("\">");
2256                         escputs(buf);
2257                         wprintf("</option>\n");
2258                 }
2259         }
2260
2261         if (WCC->room_flags & QR_ANONOPT) {
2262                 wprintf("<option %s value=\"__ANONYMOUS__\">%s</option>\n",
2263                         ((!strcasecmp(bstr("__ANONYMOUS__"), WCC->wc_fullname)) ? "selected" : ""),
2264                         _("Anonymous")
2265                 );
2266         }
2267
2268         wprintf("</select>\n");
2269
2270         /* If this is an email (not a post), allow the user to select any of his
2271          * valid email addresses.
2272          */
2273         if (recipient_required) {
2274                 serv_puts("GVEA");
2275                 serv_getln(buf, sizeof buf);
2276                 if (buf[0] == '1') {
2277                         wprintf("<select name=\"my_email_addr\" size=1>\n");
2278                         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
2279                                 wprintf("<option value=\"");
2280                                 escputs(buf);
2281                                 wprintf("\">&lt;");
2282                                 escputs(buf);
2283                                 wprintf("&gt;</option>\n");
2284                         }
2285                         wprintf("</select>\n");
2286                 }
2287         }
2288
2289         wprintf(_(" <I>in</I> "));
2290         escputs(WCC->wc_roomname);
2291
2292         wprintf("</td></tr>");
2293
2294         if (recipient_required) {
2295                 char *ccraw;
2296                 char *copy;
2297                 size_t len;
2298                 wprintf("<tr><th><label for=\"recp_id\"> ");
2299                 wprintf(_("To:"));
2300                 wprintf("</label></th>"
2301                         "<td><input autocomplete=\"off\" type=\"text\" name=\"recp\" id=\"recp_id\" value=\"");
2302                 ccraw = xbstr("recp", &len);
2303                 copy = (char*) malloc(len * 2 + 1);
2304                 memcpy(copy, ccraw, len + 1); 
2305                 utf8ify_rfc822_string(copy);
2306                 escputs(copy);
2307                 free(copy);
2308                 wprintf("\" size=45 maxlength=1000 />");
2309                 wprintf("<div class=\"auto_complete\" id=\"recp_name_choices\"></div>");
2310                 wprintf("</td><td rowspan=\"3\" align=\"left\" valign=\"top\">");
2311
2312                 /** Pop open an address book -- begin **/
2313                 wprintf(
2314                         "<a href=\"javascript:PopOpenAddressBook('recp_id|%s|cc_id|%s|bcc_id|%s');\" "
2315                         "title=\"%s\">"
2316                         "<img align=middle border=0 width=24 height=24 src=\"static/viewcontacts_24x.gif\">"
2317                         "&nbsp;%s</a>",
2318                         _("To:"), _("CC:"), _("BCC:"),
2319                         _("Contacts"), _("Contacts")
2320                 );
2321                 /** Pop open an address book -- end **/
2322
2323                 wprintf("</td></tr>");
2324
2325                 wprintf("<tr><th><label for=\"cc_id\"> ");
2326                 wprintf(_("CC:"));
2327                 wprintf("</label></th>"
2328                         "<td><input autocomplete=\"off\" type=\"text\" name=\"cc\" id=\"cc_id\" value=\"");
2329                 ccraw = xbstr("cc", &len);
2330                 copy = (char*) malloc(len * 2 + 1);
2331                 memcpy(copy, ccraw, len + 1); 
2332                 utf8ify_rfc822_string(copy);
2333                 escputs(copy);
2334                 free(copy);
2335                 wprintf("\" size=45 maxlength=1000 />");
2336                 wprintf("<div class=\"auto_complete\" id=\"cc_name_choices\"></div>");
2337                 wprintf("</td></tr>");
2338
2339                 wprintf("<tr><th><label for=\"bcc_id\"> ");
2340                 wprintf(_("BCC:"));
2341                 wprintf("</label></th>"
2342                         "<td><input autocomplete=\"off\" type=\"text\" name=\"bcc\" id=\"bcc_id\" value=\"");
2343                 ccraw = xbstr("bcc", &len);
2344                 copy = (char*) malloc(len * 2 + 1);
2345                 memcpy(copy, ccraw, len + 1); 
2346                 utf8ify_rfc822_string(copy);
2347                 escputs(copy);
2348                 free(copy);
2349                 wprintf("\" size=45 maxlength=1000 />");
2350                 wprintf("<div class=\"auto_complete\" id=\"bcc_name_choices\"></div>");
2351                 wprintf("</td></tr>");
2352
2353                 /** Initialize the autocomplete ajax helpers (found in wclib.js) */
2354                 wprintf("<script type=\"text/javascript\">      \n"
2355                         " activate_entmsg_autocompleters();     \n"
2356                         "</script>                              \n"
2357                 );
2358
2359         }
2360
2361         wprintf("<tr><th><label for=\"subject_id\" > ");
2362         if (recipient_required || subject_required) {
2363                 wprintf(_("Subject:"));
2364         }
2365         else {
2366                 wprintf(_("Subject (optional):"));
2367         }
2368         wprintf("</label></th>"
2369                 "<td colspan=\"2\">"
2370                 "<input type=\"text\" name=\"subject\" id=\"subject_id\" value=\"");
2371         escputs(bstr("subject"));
2372         wprintf("\" size=45 maxlength=70>\n");
2373         wprintf("</td></tr>");
2374
2375         wprintf("<tr><td colspan=\"3\">\n");
2376
2377         wprintf("<textarea name=\"msgtext\" cols=\"80\" rows=\"15\">");
2378
2379         /** If we're continuing from a previous edit, put our partially-composed message back... */
2380         msgescputs(bstr("msgtext"));
2381
2382         /* If we're forwarding a message, insert it here... */
2383         if (lbstr("fwdquote") > 0L) {
2384                 wprintf("<br><div align=center><i>");
2385                 wprintf(_("--- forwarded message ---"));
2386                 wprintf("</i></div><br>");
2387                 pullquote_message(lbstr("fwdquote"), 1, 1);
2388         }
2389
2390         /** If we're replying quoted, insert the quote here... */
2391         else if (lbstr("replyquote") > 0L) {
2392                 wprintf("<br>"
2393                         "<blockquote>");
2394                 pullquote_message(lbstr("replyquote"), 0, 1);
2395                 wprintf("</blockquote><br>");
2396         }
2397
2398         /** If we're editing a wiki page, insert the existing page here... */
2399         else if (WCC->wc_view == VIEW_WIKI) {
2400                 safestrncpy(buf, bstr("wikipage"), sizeof buf);
2401                 str_wiki_index(buf);
2402                 existing_page = locate_message_by_uid(buf);
2403                 if (existing_page >= 0L) {
2404                         pullquote_message(existing_page, 1, 0);
2405                 }
2406         }
2407
2408         /** Insert our signature if appropriate... */
2409         if ( (WCC->is_mailbox) && !yesbstr("sig_inserted") ) {
2410                 int UseSig;
2411                 get_pref_yesno("use_sig", &UseSig, 0);
2412                 if (UseSig) {
2413                         StrBuf *Sig;
2414                         const char *sig, *esig;
2415
2416                         get_preference("signature", &ebuf);
2417                         Sig = NewStrBuf();
2418                         StrBufEUid_unescapize(Sig, ebuf);
2419                         sig = ChrPtr(Sig);
2420                         esig = sig + StrLength(Sig);
2421                         wprintf("<br>--<br>");
2422                         while (sig <= esig) {
2423                                 if (*sig == '\n') {
2424                                         wprintf("<br>");
2425                                 }
2426                                 else if (*sig == '<') {
2427                                         wprintf("&lt;");
2428                                 }
2429                                 else if (*sig == '>') {
2430                                         wprintf("&gt;");
2431                                 }
2432                                 else if (*sig == '&') {
2433                                         wprintf("&amp;");
2434                                 }
2435                                 else if (*sig == '\"') {
2436                                         wprintf("&quot;");
2437                                 }
2438                                 else if (*sig == '\'') {
2439                                         wprintf("&#39;");
2440                                 }
2441                                 else /* since we're utf 8, is this a good idea? if (isprint(*sig))*/ {
2442                                         wprintf("%c", *sig);
2443                                 } 
2444                                 sig ++;
2445                         }
2446                         FreeStrBuf(&Sig);
2447                 }
2448         }
2449
2450         wprintf("</textarea>\n");
2451
2452         /** Make sure we only insert our signature once */
2453         /** We don't care if it was there or not before, it needs to be there now. */
2454         wprintf("<input type=\"hidden\" name=\"sig_inserted\" value=\"yes\">\n");
2455         
2456         /**
2457          * The following template embeds the TinyMCE richedit control, and automatically
2458          * transforms the textarea into a richedit textarea.
2459          */
2460         do_template("richedit", NULL);
2461
2462         /** Enumerate any attachments which are already in place... */
2463         wprintf("<div class=\"attachment buttons\"><img src=\"static/diskette_24x.gif\" class=\"imgedit\" > ");
2464         wprintf(_("Attachments:"));
2465         wprintf(" ");
2466         wprintf("<select name=\"which_attachment\" size=1>");
2467 /*
2468         for (att = WCC->first_attachment; att != NULL; att = att->next) {
2469                 wprintf("<option value=\"");
2470                 urlescputs(att->filename);
2471                 wprintf("\">");
2472                 escputs(att->filename);
2473                 / * wprintf(" (%s, %d bytes)",att->content_type,att->length); * /
2474                 wprintf("</option>\n");
2475         }
2476 */
2477         wprintf("</select>");
2478
2479         /** Now offer the ability to attach additional files... */
2480         wprintf("&nbsp;&nbsp;&nbsp;");
2481         wprintf(_("Attach file:"));
2482         wprintf(" <input name=\"attachfile\" class=\"attachfile\" "
2483                 "size=16 type=\"file\">\n&nbsp;&nbsp;"
2484                 "<input type=\"submit\" name=\"attach_button\" value=\"%s\">\n", _("Add"));
2485         wprintf("</div>");      /* End of "attachment buttons" div */
2486
2487
2488         wprintf("</td></tr></table>");
2489         
2490         wprintf("</form>\n");
2491         wprintf("</div>\n");    /* end of "fix_scrollbar_bug" div */
2492
2493         /* NOTE: address_book_popup() will close the "content" div.  Don't close it here. */
2494 DONE:   address_book_popup();
2495         wDumpContent(1);
2496 }
2497
2498
2499 /**
2500  * \brief delete a message
2501  */
2502 void delete_msg(void)
2503 {
2504         long msgid;
2505         char buf[SIZ];
2506
2507         msgid = lbstr("msgid");
2508
2509         if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
2510                 serv_printf("DELE %ld", msgid); 
2511         }
2512         else {                  /** Otherwise move it to Trash */
2513                 serv_printf("MOVE %ld|_TRASH_|0", msgid);
2514         }
2515
2516         serv_getln(buf, sizeof buf);
2517         sprintf(WC->ImportantMessage, "%s", &buf[4]);
2518
2519         readloop("readnew");
2520 }
2521
2522
2523 /**
2524  * \brief move a message to another folder
2525  */
2526 void move_msg(void)
2527 {
2528         long msgid;
2529         char buf[SIZ];
2530
2531         msgid = lbstr("msgid");
2532
2533         if (havebstr("move_button")) {
2534                 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
2535                 serv_puts(buf);
2536                 serv_getln(buf, sizeof buf);
2537                 sprintf(WC->ImportantMessage, "%s", &buf[4]);
2538         } else {
2539                 sprintf(WC->ImportantMessage, (_("The message was not moved.")));
2540         }
2541
2542         readloop("readnew");
2543 }
2544
2545
2546
2547
2548
2549 /*
2550  * Confirm move of a message
2551  */
2552 void confirm_move_msg(void)
2553 {
2554         long msgid;
2555         char buf[SIZ];
2556         char targ[SIZ];
2557
2558         msgid = lbstr("msgid");
2559
2560
2561         output_headers(1, 1, 2, 0, 0, 0);
2562         wprintf("<div id=\"banner\">\n");
2563         wprintf("<h1>");
2564         wprintf(_("Confirm move of message"));
2565         wprintf("</h1>");
2566         wprintf("</div>\n");
2567
2568         wprintf("<div id=\"content\" class=\"service\">\n");
2569
2570         wprintf("<CENTER>");
2571
2572         wprintf(_("Move this message to:"));
2573         wprintf("<br />\n");
2574
2575         wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
2576         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
2577         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
2578
2579         wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
2580         serv_puts("LKRA");
2581         serv_getln(buf, sizeof buf);
2582         if (buf[0] == '1') {
2583                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
2584                         extract_token(targ, buf, 0, '|', sizeof targ);
2585                         wprintf("<OPTION>");
2586                         escputs(targ);
2587                         wprintf("\n");
2588                 }
2589         }
2590         wprintf("</SELECT>\n");
2591         wprintf("<br />\n");
2592
2593         wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
2594         wprintf("&nbsp;");
2595         wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
2596         wprintf("</form></CENTER>\n");
2597
2598         wprintf("</CENTER>\n");
2599         wDumpContent(1);
2600 }
2601
2602 void readnew(void) { readloop("readnew");}
2603 void readold(void) { readloop("readold");}
2604 void readfwd(void) { readloop("readfwd");}
2605 void headers(void) { readloop("headers");}
2606 void do_search(void) { readloop("do_search");}
2607
2608
2609
2610
2611
2612
2613 void 
2614 InitModule_MSG
2615 (void)
2616 {
2617         WebcitAddUrlHandler(HKEY("readnew"), readnew, 0);
2618         WebcitAddUrlHandler(HKEY("readold"), readold, 0);
2619         WebcitAddUrlHandler(HKEY("readfwd"), readfwd, 0);
2620         WebcitAddUrlHandler(HKEY("headers"), headers, 0);
2621         WebcitAddUrlHandler(HKEY("do_search"), do_search, 0);
2622         WebcitAddUrlHandler(HKEY("display_enter"), display_enter, 0);
2623         WebcitAddUrlHandler(HKEY("post"), post_message, 0);
2624         WebcitAddUrlHandler(HKEY("move_msg"), move_msg, 0);
2625         WebcitAddUrlHandler(HKEY("delete_msg"), delete_msg, 0);
2626         WebcitAddUrlHandler(HKEY("confirm_move_msg"), confirm_move_msg, 0);
2627         WebcitAddUrlHandler(HKEY("msg"), embed_message, NEED_URL|AJAX);
2628         WebcitAddUrlHandler(HKEY("printmsg"), print_message, NEED_URL);
2629         WebcitAddUrlHandler(HKEY("mobilemsg"), mobile_message_view, NEED_URL);
2630         WebcitAddUrlHandler(HKEY("msgheaders"), display_headers, NEED_URL);
2631
2632         return ;
2633 }