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