9a1dc2e45aa1c26c3d463f96ca3bae42c8628c93
[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 /*----------------------------------------------------------------------------*/
39
40
41
42 /*
43  * I wanna SEE that message!
44  *
45  * msgnum               Message number to display
46  * printable_view       Nonzero to display a printable view
47  * section              Optional for encapsulated message/rfc822 submessage
48  */
49 int read_message(StrBuf *Target, const char *tmpl, long tmpllen, long msgnum, int printable_view, const StrBuf *PartNum) {
50         StrBuf *Buf;
51         StrBuf *HdrToken;
52         StrBuf *FoundCharset;
53         HashPos  *it;
54         void *vMime;
55         message_summary *Msg = NULL;
56         headereval *Hdr;
57         void *vHdr;
58         char buf[SIZ];
59 //      char mime_submessages[256] = "";
60         char reply_references[1024] = "";
61         int Done = 0;
62         int state=0;
63         long len;
64         const char *Key;
65
66         Buf = NewStrBuf();
67         lprintf(1, "----------%s---------MSG4 %ld|%s--------------\n", tmpl, msgnum, ChrPtr(PartNum));
68         serv_printf("MSG4 %ld|%s", msgnum, ChrPtr(PartNum));
69         StrBuf_ServGetln(Buf);
70         if (GetServerStatus(Buf, NULL) != 1) {
71                 StrBufAppendPrintf(Target, "<strong>");
72                 StrBufAppendPrintf(Target, _("ERROR:"));
73                 StrBufAppendPrintf(Target, "</strong> %s<br />\n", &buf[4]);
74                 FreeStrBuf(&Buf);
75                 return 0;
76         }
77
78         /** begin everythingamundo table */
79
80
81         HdrToken = NewStrBuf();
82         Msg = (message_summary *)malloc(sizeof(message_summary));
83         memset(Msg, 0, sizeof(message_summary));
84         Msg->msgnum = msgnum;
85         Msg->PartNum = PartNum;
86         Msg->MsgBody =  (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
87         memset(Msg->MsgBody, 0, sizeof(wc_mime_attachment));
88         FoundCharset = NewStrBuf();
89         while ((StrBuf_ServGetln(Buf)>=0) && !Done) {
90                 if ( (StrLength(Buf)==3) && 
91                     !strcmp(ChrPtr(Buf), "000")) 
92                 {
93                         Done = 1;
94                         if (state < 2) {
95                                 lprintf(1, _("unexpected end of message"));
96                                 
97                                 Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/html"));
98                                 StrBufAppendPrintf(Msg->MsgBody->Data, "<div><i>");
99                                 StrBufAppendPrintf(Msg->MsgBody->Data, _("unexpected end of message"));
100                                 StrBufAppendPrintf(Msg->MsgBody->Data, " (1)</i><br /><br />\n");
101                                 StrBufAppendPrintf(Msg->MsgBody->Data, "</div>\n");
102                         }
103                         break;
104                 }
105                 switch (state) {
106                 case 0:/* Citadel Message Headers */
107                         if (StrLength(Buf) == 0) {
108                                 state ++;
109                                 break;
110                         }
111                         StrBufExtract_token(HdrToken, Buf, 0, '=');
112                         StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
113                         
114                         lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf));
115                         if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
116                             (vHdr != NULL)) {
117                                 Hdr = (headereval*)vHdr;
118                                 Hdr->evaluator(Msg, Buf, FoundCharset);
119                                 if (Hdr->Type == 1) {
120                                         state++;
121                                 }
122                         }
123                         else lprintf(1, "don't know how to handle message header[%s]\n", ChrPtr(HdrToken));
124                         break;
125                 case 1:/* Message Mime Header */
126                         if (StrLength(Buf) == 0) {
127                                 state++;
128                                 if (Msg->MsgBody->ContentType == NULL)
129                                         /* end of header or no header? */
130                                         Msg->MsgBody->ContentType = NewStrBufPlain(HKEY("text/plain"));
131                                  /* usual end of mime header */
132                         }
133                         else
134                         {
135                                 StrBufExtract_token(HdrToken, Buf, 0, ':');
136                                 if (StrLength(HdrToken) > 0) {
137                                         StrBufCutLeft(Buf, StrLength(HdrToken) + 1);
138                                         lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken), ChrPtr(Buf));
139                                         if (GetHash(MsgHeaderHandler, SKEY(HdrToken), &vHdr) &&
140                                             (vHdr != NULL)) {
141                                                 Hdr = (headereval*)vHdr;
142                                                 Hdr->evaluator(Msg, Buf, FoundCharset);
143                                         }
144                                         break;
145                                 }
146                         }
147                 case 2: /* Message Body */
148                         
149                         if (Msg->MsgBody->size_known > 0) {
150                                 StrBuf_ServGetBLOB(Msg->MsgBody->Data, Msg->MsgBody->length);
151                                 state ++;
152                                         /// todo: check next line, if not 000, append following lines
153                         }
154                         else if (1){
155                                 if (StrLength(Msg->MsgBody->Data) > 0)
156                                         StrBufAppendBufPlain(Msg->MsgBody->Data, "\n", 1, 0);
157                                 StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0);
158                         }
159                         break;
160                 case 3:
161                         StrBufAppendBuf(Msg->MsgBody->Data, Buf, 0);
162                         break;
163                 }
164         }
165
166         if (Msg->AllAttach == NULL)
167                 Msg->AllAttach = NewHash(1,NULL);
168         Put(Msg->AllAttach, SKEY(Msg->MsgBody->PartNum), Msg->MsgBody, DestroyMime);
169         
170         /* strip the bare contenttype, so we ommit charset etc. */
171         StrBufExtract_token(Buf, Msg->MsgBody->ContentType, 0, ';');
172         StrBufTrim(Buf);
173         if (GetHash(MimeRenderHandler, SKEY(Buf), &vHdr) &&
174             (vHdr != NULL)) {
175                 RenderMimeFunc Render;
176                 Render = (RenderMimeFunc)vHdr;
177                 Render(Msg->MsgBody, NULL, FoundCharset);
178         }
179
180         if (StrLength(Msg->reply_references)> 0) {
181                 /* Trim down excessively long lists of thread references.  We eliminate the
182                  * second one in the list so that the thread root remains intact.
183                  */
184                 int rrtok = num_tokens(ChrPtr(Msg->reply_references), '|');
185                 int rrlen = StrLength(Msg->reply_references);
186                 if ( ((rrtok >= 3) && (rrlen > 900)) || (rrtok > 10) ) {
187                         remove_token(reply_references, 1, '|');////todo
188                 }
189         }
190
191         /* Generate a reply-to address */
192         if (StrLength(Msg->Rfca) > 0) {
193                 if (Msg->reply_to == NULL)
194                         Msg->reply_to = NewStrBuf();
195                 if (StrLength(Msg->from) > 0) {
196                         StrBufPrintf(Msg->reply_to, "%s <%s>", ChrPtr(Msg->from), ChrPtr(Msg->Rfca));
197                 }
198                 else {
199                         FlushStrBuf(Msg->reply_to);
200                         StrBufAppendBuf(Msg->reply_to, Msg->Rfca, 0);
201                 }
202         }
203         else 
204         {
205                 if ((StrLength(Msg->OtherNode)>0) && 
206                     (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_nodename)) &&
207                     (strcasecmp(ChrPtr(Msg->OtherNode), serv_info.serv_humannode)) ) 
208                 {
209                         if (Msg->reply_to == NULL)
210                                 Msg->reply_to = NewStrBuf();
211                         StrBufPrintf(Msg->reply_to, 
212                                      "%s @ %s",
213                                      ChrPtr(Msg->from), 
214                                      ChrPtr(Msg->OtherNode));
215                 }
216                 else {
217                         if (Msg->reply_to == NULL)
218                                 Msg->reply_to = NewStrBuf();
219                         FlushStrBuf(Msg->reply_to);
220                         StrBufAppendBuf(Msg->reply_to, Msg->from, 0);
221                 }
222         }
223         it = GetNewHashPos(Msg->AllAttach, 0);
224         while (GetNextHashPos(Msg->AllAttach, it, &len, &Key, &vMime) && 
225                (vMime != NULL)) {
226                 wc_mime_attachment *Mime = (wc_mime_attachment*) vMime;
227                 evaluate_mime_part(Msg, Mime);
228         }
229         DeleteHashPos(&it);
230
231         DoTemplate(tmpl, tmpllen, Target, Msg, CTX_MAILSUM);
232
233         DestroyMessageSummary(Msg);
234         FreeStrBuf(&FoundCharset);
235         FreeStrBuf(&HdrToken);
236         FreeStrBuf(&Buf);
237         return 1;
238 }
239
240
241
242 /*
243  * Unadorned HTML output of an individual message, suitable
244  * for placing in a hidden iframe, for printing, or whatever
245  *
246  * msgnum_as_string == Message number, as a string instead of as a long int
247  */
248 void embed_message(void) {
249         long msgnum = 0L;
250         const StrBuf *Tmpl = sbstr("template");
251
252         msgnum = StrTol(WC->UrlFragment2);
253         if (StrLength(Tmpl) > 0) 
254                 read_message(WC->WBuf, SKEY(Tmpl), msgnum, 0, NULL);
255         else 
256                 read_message(WC->WBuf, HKEY("view_message"), msgnum, 0, NULL);
257 }
258
259
260 /*
261  * Printable view of a message
262  *
263  * msgnum_as_string == Message number, as a string instead of as a long int
264  */
265 void print_message(void) {
266         long msgnum = 0L;
267
268         msgnum = StrTol(WC->UrlFragment2);
269         output_headers(0, 0, 0, 0, 0, 0);
270
271         hprintf("Content-type: text/html\r\n"
272                 "Server: " PACKAGE_STRING "\r\n"
273                 "Connection: close\r\n");
274
275         begin_burst();
276
277         read_message(WC->WBuf, HKEY("view_message_print"), msgnum, 1, NULL);
278
279         wDumpContent(0);
280 }
281
282 /* 
283  * Mobile browser view of message
284  *
285  * @param msg_num_as_string Message number as a string instead of as a long int 
286  */
287 void mobile_message_view(void) {
288   long msgnum = 0L;
289   msgnum = StrTol(WC->UrlFragment2);
290   output_headers(1, 0, 0, 0, 0, 1);
291   begin_burst();
292   do_template("msgcontrols", NULL);
293   read_message(WC->WBuf, HKEY("view_message"), msgnum,1, NULL);
294   wDumpContent(0);
295 }
296
297 /**
298  * \brief Display a message's headers
299  *
300  * \param msgnum_as_string Message number, as a string instead of as a long int
301  */
302 void display_headers(void) {
303         long msgnum = 0L;
304         char buf[1024];
305
306         msgnum = StrTol(WC->UrlFragment2);
307         output_headers(0, 0, 0, 0, 0, 0);
308
309         hprintf("Content-type: text/plain\r\n"
310                 "Server: %s\r\n"
311                 "Connection: close\r\n",
312                 PACKAGE_STRING);
313         begin_burst();
314
315         serv_printf("MSG2 %ld|3", msgnum);
316         serv_getln(buf, sizeof buf);
317         if (buf[0] == '1') {
318                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
319                         wprintf("%s\n", buf);
320                 }
321         }
322
323         wDumpContent(0);
324 }
325
326
327 message_summary *ReadOneMessageSummary(StrBuf *RawMessage, const char *DefaultSubject, long MsgNum) 
328 {
329         void                 *vEval;
330         MsgPartEvaluatorFunc  Eval;
331         message_summary      *Msg;
332         StrBuf *Buf;
333         const char *buf;
334         const char *ebuf;
335         int nBuf;
336         long len;
337         
338         Buf = NewStrBuf();
339
340         serv_printf("MSG0 %ld|1", MsgNum);      /* ask for headers only */
341         
342         StrBuf_ServGetln(Buf);
343         if (GetServerStatus(Buf, NULL) == 1) {
344                 FreeStrBuf(&Buf);
345                 return NULL;
346         }
347
348         Msg = (message_summary*)malloc(sizeof(message_summary));
349         memset(Msg, 0, sizeof(message_summary));
350         while (len = StrBuf_ServGetln(Buf),
351                ((len != 3)  ||
352                 strcmp(ChrPtr(Buf), "000")== 0)){
353                 buf = ChrPtr(Buf);
354                 ebuf = strchr(ChrPtr(Buf), '=');
355                 nBuf = ebuf - buf;
356                 if (GetHash(MsgEvaluators, buf, nBuf, &vEval) && vEval != NULL) {
357                         Eval = (MsgPartEvaluatorFunc) vEval;
358                         StrBufCutLeft(Buf, nBuf + 1);
359                         Eval(Msg, Buf);
360                 }
361                 else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf));
362         }
363         return Msg;
364 }
365
366
367 /**
368  * \brief display the adressbook overview
369  * \param msgnum the citadel message number
370  * \param alpha what????
371  */
372 void display_addressbook(long msgnum, char alpha) {
373         //char buf[SIZ];
374         /* char mime_partnum[SIZ]; */
375 /*      char mime_filename[SIZ]; */
376 /*      char mime_content_type[SIZ]; */
377         ///char mime_disposition[SIZ];
378         //int mime_length;
379         char vcard_partnum[SIZ];
380         char *vcard_source = NULL;
381         message_summary summ;////TODO: this will leak
382
383         memset(&summ, 0, sizeof(summ));
384         ///safestrncpy(summ.subj, _("(no subject)"), sizeof summ.subj);
385 ///Load Message headers
386 //      Msg = 
387         if (!IsEmptyStr(vcard_partnum)) {
388                 vcard_source = load_mimepart(msgnum, vcard_partnum);
389                 if (vcard_source != NULL) {
390
391                         /** Display the summary line */
392                         display_vcard(WC->WBuf, vcard_source, alpha, 0, NULL,msgnum);
393
394                         /** If it's my vCard I can edit it */
395                         if (    (!strcasecmp(WC->wc_roomname, USERCONFIGROOM))
396                                 || (!strcasecmp(&WC->wc_roomname[11], USERCONFIGROOM))
397                                 || (WC->wc_view == VIEW_ADDRESSBOOK)
398                         ) {
399                                 wprintf("<a href=\"edit_vcard?"
400                                         "msgnum=%ld&partnum=%s\">",
401                                         msgnum, vcard_partnum);
402                                 wprintf("[%s]</a>", _("edit"));
403                         }
404
405                         free(vcard_source);
406                 }
407         }
408
409 }
410
411
412
413 /**
414  * \brief  If it's an old "Firstname Lastname" style record, try to convert it.
415  * \param namebuf name to analyze, reverse if nescessary
416  */
417 void lastfirst_firstlast(char *namebuf) {
418         char firstname[SIZ];
419         char lastname[SIZ];
420         int i;
421
422         if (namebuf == NULL) return;
423         if (strchr(namebuf, ';') != NULL) return;
424
425         i = num_tokens(namebuf, ' ');
426         if (i < 2) return;
427
428         extract_token(lastname, namebuf, i-1, ' ', sizeof lastname);
429         remove_token(namebuf, i-1, ' ');
430         strcpy(firstname, namebuf);
431         sprintf(namebuf, "%s; %s", lastname, firstname);
432 }
433
434 /**
435  * \brief fetch what??? name
436  * \param msgnum the citadel message number
437  * \param namebuf where to put the name in???
438  */
439 void fetch_ab_name(message_summary *Msg, char *namebuf) {
440         char buf[SIZ];
441         char mime_partnum[SIZ];
442         char mime_filename[SIZ];
443         char mime_content_type[SIZ];
444         char mime_disposition[SIZ];
445         int mime_length;
446         char vcard_partnum[SIZ];
447         char *vcard_source = NULL;
448         int i, len;
449         message_summary summ;/// TODO this will lak
450
451         if (namebuf == NULL) return;
452         strcpy(namebuf, "");
453
454         memset(&summ, 0, sizeof(summ));
455         //////safestrncpy(summ.subj, "(no subject)", sizeof summ.subj);
456
457         sprintf(buf, "MSG0 %ld|0", Msg->msgnum);        /** unfortunately we need the mime info now */
458         serv_puts(buf);
459         serv_getln(buf, sizeof buf);
460         if (buf[0] != '1') return;
461
462         while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
463                 if (!strncasecmp(buf, "part=", 5)) {
464                         extract_token(mime_filename, &buf[5], 1, '|', sizeof mime_filename);
465                         extract_token(mime_partnum, &buf[5], 2, '|', sizeof mime_partnum);
466                         extract_token(mime_disposition, &buf[5], 3, '|', sizeof mime_disposition);
467                         extract_token(mime_content_type, &buf[5], 4, '|', sizeof mime_content_type);
468                         mime_length = extract_int(&buf[5], 5);
469
470                         if (  (!strcasecmp(mime_content_type, "text/x-vcard"))
471                            || (!strcasecmp(mime_content_type, "text/vcard")) ) {
472                                 strcpy(vcard_partnum, mime_partnum);
473                         }
474
475                 }
476         }
477
478         if (!IsEmptyStr(vcard_partnum)) {
479                 vcard_source = load_mimepart(Msg->msgnum, vcard_partnum);
480                 if (vcard_source != NULL) {
481
482                         /* Grab the name off the card */
483                         display_vcard(WC->WBuf, vcard_source, 0, 0, namebuf, Msg->msgnum);
484
485                         free(vcard_source);
486                 }
487         }
488
489         lastfirst_firstlast(namebuf);
490         striplt(namebuf);
491         len = strlen(namebuf);
492         for (i=0; i<len; ++i) {
493                 if (namebuf[i] != ';') return;
494         }
495         strcpy(namebuf, _("(no name)"));
496 }
497
498
499
500
501
502 /*
503  * load message pointers from the server for a "read messages" operation
504  *
505  * servcmd:             the citadel command to send to the citserver
506  * with_headers:        also include some of the headers with the message numbers (more expensive)
507  */
508 int load_msg_ptrs(char *servcmd, int with_headers)
509 {
510         StrBuf* FoundCharset = NULL;
511         struct wcsession *WCC = WC;
512         message_summary *Msg;
513         StrBuf *Buf, *Buf2;
514         ///char buf[1024];
515         ///time_t datestamp;
516         //char fullname[128];
517         //char nodename[128];
518         //char inetaddr[128];
519         //char subject[1024];
520         ///char *ptr;
521         int nummsgs;
522         ////int sbjlen;
523         int maxload = 0;
524         long len;
525         int n;
526         ////int num_summ_alloc = 0;
527
528         if (WCC->summ != NULL) {
529                 if (WCC->summ != NULL)
530                         DeleteHash(&WCC->summ);
531         }
532         WCC->summ = NewHash(1, Flathash);
533         nummsgs = 0;
534         maxload = 10000;
535         
536         Buf = NewStrBuf();
537         serv_puts(servcmd);
538         StrBuf_ServGetln(Buf);
539         if (GetServerStatus(Buf, NULL) != 1) {
540                 FreeStrBuf(&Buf);
541                 return (nummsgs);
542         }
543 // TODO                         if (with_headers) { //// TODO: Have Attachments?
544         Buf2 = NewStrBuf();
545         while (len = StrBuf_ServGetln(Buf),
546                ((len != 3)  ||
547                 strcmp(ChrPtr(Buf), "000")!= 0))
548         {
549                 if (nummsgs < maxload) {
550                         Msg = (message_summary*)malloc(sizeof(message_summary));
551                         memset(Msg, 0, sizeof(message_summary));
552
553                         Msg->msgnum = StrBufExtract_long(Buf, 0, '|');
554                         Msg->date = StrBufExtract_long(Buf, 1, '|');
555
556                         Msg->from = NewStrBufPlain(NULL, StrLength(Buf));
557                         StrBufExtract_token(Buf2, Buf, 2, '|');
558                         if (StrLength(Buf2) != 0) {
559                                 /** Handle senders with RFC2047 encoding */
560                                 StrBuf_RFC822_to_Utf8(Msg->from, Buf2, WCC->DefaultCharset, FoundCharset);
561                         }
562                         
563                         /** Nodename */
564                         StrBufExtract_token(Buf2, Buf, 3, '|');
565                         if ((StrLength(Buf2) !=0 ) &&
566                             ( ((WCC->room_flags & QR_NETWORK)
567                                || ((strcasecmp(ChrPtr(Buf2), serv_info.serv_nodename)
568                                     && (strcasecmp(ChrPtr(Buf2), serv_info.serv_fqdn)))))))
569                         {
570                                 StrBufAppendBufPlain(Msg->from, HKEY(" @ "), 0);
571                                 StrBufAppendBuf(Msg->from, Buf2, 0);
572                         }
573
574                         /** Not used:
575                         StrBufExtract_token(Msg->inetaddr, Buf, 4, '|');
576                         */
577
578                         Msg->subj = NewStrBufPlain(NULL, StrLength(Buf));
579                         StrBufExtract_token(Buf2,  Buf, 5, '|');
580                         if (StrLength(Buf2) == 0)
581                                 StrBufAppendBufPlain(Msg->subj, _("(no subj)"), 0, -1);
582                         else {
583                                 StrBuf_RFC822_to_Utf8(Msg->subj, Buf2, WCC->DefaultCharset, FoundCharset);
584                                 if ((StrLength(Msg->subj) > 75) && 
585                                     (StrBuf_Utf8StrLen(Msg->subj) > 75)) {
586                                         StrBuf_Utf8StrCut(Msg->subj, 72);
587                                         StrBufAppendBufPlain(Msg->subj, HKEY("..."), 0);
588                                 }
589                         }
590
591
592                         if ((StrLength(Msg->from) > 25) && 
593                             (StrBuf_Utf8StrLen(Msg->from) > 25)) {
594                                 StrBuf_Utf8StrCut(Msg->from, 23);
595                                 StrBufAppendBufPlain(Msg->from, HKEY("..."), 0);
596                         }
597                         n = Msg->msgnum;
598                         Put(WCC->summ, (const char *)&n, sizeof(n), Msg, DestroyMessageSummary);
599                 }
600                 nummsgs++;
601         }
602         FreeStrBuf(&Buf2);
603         FreeStrBuf(&Buf);
604         return (nummsgs);
605 }
606
607
608 inline message_summary* GetMessagePtrAt(int n, HashList *Summ)
609 {
610         const char *Key;
611         long HKLen;
612         void *vMsg;
613
614         if (Summ == NULL)
615                 return NULL;
616         GetHashAt(Summ, n, &HKLen, &Key, &vMsg);
617         return (message_summary*) vMsg;
618 }
619
620
621 void DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg)
622 {
623         StrBuf *TmpBuf;
624         struct wcsession *WCC = WC;
625         message_summary* Msg;
626         int lo, hi, n;
627         int i = 0;
628         long StartMsg;
629         void *vMsg;
630         long hklen;
631         const char *key;
632         int done = 0;
633         int nItems;
634         HashPos *At;
635         long vector[16];
636         int nMessages = DEFAULT_MAXMSGS;
637
638         TmpBuf = NewStrBuf();
639         At = GetNewHashPos(WCC->summ, (lbstr("SortOrder") == 1)? -nMessages : nMessages);
640         nItems = GetCount(WCC->summ);
641         
642         vector[0] = 7;
643         vector[1] = startmsg;
644         vector[2] = maxmsgs;
645         vector[3] = 0;
646         vector[4] = 1;
647
648         while (!done) {
649                 lo = GetHashPosCounter(At);
650                 if (lo + nMessages > nItems) {
651                         hi = nItems;
652                 }
653                 else {
654                         hi = lo + nMessages;
655                 }
656                 done = !GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg);
657                 Msg = (message_summary*) vMsg;
658                 n = (Msg==NULL)? 0 : Msg->msgnum;
659                 if (i == 0)
660                         StartMsg = n;
661                 vector[4] = lo;
662                 vector[5] = hi;
663                 vector[6] = n;
664                 FlushStrBuf(TmpBuf);
665                 DoTemplate(HKEY("select_messageindex"), TmpBuf, &vector, CTX_LONGVECTOR);
666                 StrBufAppendBuf(Selector, TmpBuf, 0);
667                 i++;
668         }
669         vector[6] = StartMsg;
670         FlushStrBuf(TmpBuf);
671         DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &vector, CTX_LONGVECTOR);
672         StrBufAppendBuf(Selector, TmpBuf, 0);
673         FreeStrBuf(&TmpBuf);
674         DeleteHashPos(&At);
675 }
676
677
678 extern readloop_struct rlid[];
679
680 /*
681  * command loop for reading messages
682  *
683  * Set oper to "readnew" or "readold" or "readfwd" or "headers"
684  */
685 void readloop(long oper)
686 {
687         StrBuf *MessageDropdown = NULL;
688         StrBuf *BBViewToolBar = NULL;
689         void *vMsg;
690         message_summary *Msg;
691         char cmd[256] = "";
692         char buf[SIZ];
693         char old_msgs[SIZ];
694         int a = 0;
695         ///int b = 0;
696         int nummsgs;
697         long startmsg = 0;
698         int maxmsgs = 0;
699         long *displayed_msgs = NULL;
700         int num_displayed = 0;
701         int is_summary = 0;
702         int is_addressbook = 0;
703         int is_singlecard = 0;
704         int is_calendar = 0;
705         struct calview calv;
706         int is_tasks = 0;
707         int is_notes = 0;
708         int is_bbview = 0;
709         int lowest_displayed = (-1);
710         int highest_displayed = 0;
711         addrbookent *addrbook = NULL;
712         int num_ab = 0;
713         int bbs_reverse = 0;
714         struct wcsession *WCC = WC;
715         HashPos *at;
716         const char *HashKey;
717         long HKLen;
718         int care_for_empty_list = 0;
719         int load_seen = 0;
720         int sortit = 0;
721
722         switch (WCC->wc_view) {
723         case VIEW_WIKI:
724                 sprintf(buf, "wiki?room=%s&page=home", WCC->wc_roomname);
725                 http_redirect(buf);
726                 return;
727         case VIEW_CALENDAR:
728                 load_seen = 1;
729                 is_calendar = 1;
730                 strcpy(cmd, "MSGS ALL|||1");
731                 maxmsgs = 32767;
732                 parse_calendar_view_request(&calv);
733                 break;
734         case VIEW_TASKS:
735                 is_tasks = 1;
736                 strcpy(cmd, "MSGS ALL");
737                 maxmsgs = 32767;
738                 break;
739         case VIEW_NOTES:
740                 is_notes = 1;
741                 strcpy(cmd, "MSGS ALL");
742                 maxmsgs = 32767;
743                 wprintf("<div id=\"new_notes_here\"></div>\n");
744                 break;
745         case VIEW_ADDRESSBOOK:
746                 is_singlecard = ibstr("is_singlecard");
747                 if (maxmsgs > 1) {
748                         is_addressbook = 1;
749                         if (oper == do_search) {
750                                 snprintf(cmd, sizeof(cmd), "MSGS SEARCH|%s", bstr("query"));
751                         }
752                         else {
753                                 strcpy(cmd, "MSGS ALL");
754                         }
755                         maxmsgs = 9999999;
756                         break;
757                 }
758
759         default:
760                 care_for_empty_list = 1;
761                 startmsg = lbstr("startmsg");
762                 if (havebstr("maxmsgs"))
763                         maxmsgs = ibstr("maxmsgs");
764                 is_summary = (ibstr("is_summary") && !WCC->is_mobile);
765                 if (maxmsgs == 0) maxmsgs = DEFAULT_MAXMSGS;
766                 
767
768                 
769                 /*
770                  * When in summary mode, always show ALL messages instead of just
771                  * new or old.  Otherwise, show what the user asked for.
772                  */
773                 rlid[oper].cmd(cmd, sizeof(cmd));
774                 
775                 if ((WCC->wc_view == VIEW_MAILBOX) && (maxmsgs > 1) && !WCC->is_mobile) {
776                         is_summary = 1;
777                         if (oper != do_search) {
778                                 strcpy(cmd, "MSGS ALL");
779                         }
780                 }
781
782                 is_bbview = !is_summary;
783                 if (is_summary) {                       /**< fetch header summary */
784                         load_seen = 1;
785                         snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1",
786                                  (oper == do_search) ? "SEARCH" : "ALL",
787                                  (oper == do_search) ? bstr("query") : ""
788                                 );
789                         startmsg = 1;
790                         maxmsgs = 9999999;
791                 } 
792
793                 bbs_reverse = is_bbview && (lbstr("SortOrder") == 2);
794
795                 if (is_bbview && (startmsg == 0L)) {
796                         if (bbs_reverse) {
797                                 Msg = GetMessagePtrAt((nummsgs >= maxmsgs) ? (nummsgs - maxmsgs) : 0, WCC->summ);
798                                 startmsg = (Msg==NULL)? 0 : Msg->msgnum;
799                         }
800                         else {
801                                 Msg = GetMessagePtrAt(0, WCC->summ);
802                                 startmsg = (Msg==NULL)? 0 : Msg->msgnum;
803                         }
804                 }
805                 sortit = is_summary || WCC->is_mobile;
806         }
807
808
809         output_headers(1, 1, 1, 0, 0, 0);
810
811         /*
812         if (WCC->is_mobile) {
813                 maxmsgs = 20;
814                 snprintf(cmd, sizeof(cmd), "MSGS %s|%s||1",
815                         (!strcmp(oper, "do_search") ? "SEARCH" : "ALL"),
816                         (!strcmp(oper, "do_search") ? bstr("query") : "")
817                 );
818                 SortBy =  eRDate;
819         }
820
821         /*
822          * Are we doing a summary view?  If so, we need to know old messages
823          * and new messages, so we can do that pretty boldface thing for the
824          * new messages.
825          */
826
827
828         nummsgs = load_msg_ptrs(cmd, (is_summary || WCC->is_mobile));
829         if (nummsgs == 0) {
830                 if (care_for_empty_list) {
831                         wprintf("<div align=\"center\"><br /><em>");
832                         switch (oper) {
833                         case readnew:
834                                 wprintf(_("No new messages."));
835                                 break;
836                         case readold:
837                                 wprintf(_("No old messages."));
838                                 break;
839                         default:
840                                 wprintf(_("No messages here."));
841                         }
842                         wprintf("</em><br /></div>\n");
843                 }
844
845                 goto DONE;
846         }
847
848         if (load_seen){
849                 void *vMsg;
850
851                 strcpy(old_msgs, "");
852                 serv_puts("GTSN");
853                 serv_getln(buf, sizeof buf);
854                 if (buf[0] == '2') {
855                         strcpy(old_msgs, &buf[4]);
856                 }
857                 at = GetNewHashPos(WCC->summ, 0);
858                 while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
859                         /** Are you a new message, or an old message? */
860                         Msg = (message_summary*) vMsg;
861                         if (is_msg_in_mset(old_msgs, Msg->msgnum)) {
862                                 Msg->is_new = 0;
863                         }
864                         else {
865                                 Msg->is_new = 1;
866                         }
867                 }
868                 DeleteHashPos(&at);
869         }
870
871         if (sortit) {
872                 CompareFunc SortIt;
873                 SortIt =  RetrieveSort(CTX_MAILSUM, NULL, 
874                                        HKEY("date"), 2);
875                 if (SortIt != NULL)
876                         SortByPayload(WCC->summ, SortIt);
877         }
878
879         if (is_summary) {
880                 do_template("summary_header", NULL);
881         } else if (WCC->is_mobile) {
882                 wprintf("<div id=\"message_list\">");
883         }
884
885
886         /**
887          * If we're not currently looking at ALL requested
888          * messages, then display the selector bar
889          */
890         if (is_bbview)  {
891                 BBViewToolBar = NewStrBuf();
892                 MessageDropdown = NewStrBuf();
893
894                 DrawMessageDropdown(MessageDropdown, maxmsgs, startmsg);
895                 
896                 DoTemplate(HKEY("msg_listselector_top"), BBViewToolBar, MessageDropdown, CTX_STRBUF);
897                 StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0);
898                 FlushStrBuf(BBViewToolBar);
899         }
900                         
901         at = GetNewHashPos(WCC->summ, 0);
902         while (GetNextHashPos(WCC->summ, at, &HKLen, &HashKey, &vMsg)) {
903                 Msg = (message_summary*) vMsg;          
904                 if ((Msg->msgnum >= startmsg) && (num_displayed < maxmsgs)) {
905                                 
906                         /** Display the message */
907                         if (is_summary) {
908                                 DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM);
909                         }
910                         else if (is_addressbook) {
911                                 fetch_ab_name(Msg, buf);
912                                 ++num_ab;
913                                 addrbook = realloc(addrbook,
914                                                    (sizeof(addrbookent) * num_ab) );
915                                 safestrncpy(addrbook[num_ab-1].ab_name, buf,
916                                             sizeof(addrbook[num_ab-1].ab_name));
917                                 addrbook[num_ab-1].ab_msgnum = Msg->msgnum;
918                         }
919                         else if (is_calendar) {
920                                 load_calendar_item(Msg, Msg->is_new, &calv);
921                         }
922                         else if (is_tasks) {
923                                 display_task(Msg, Msg->is_new);
924                         }
925                         else if (is_notes) {
926                                 display_note(Msg, Msg->is_new);
927                         } else if (WCC->is_mobile) {
928                                 DoTemplate(HKEY("section_mailsummary"), NULL, Msg, CTX_MAILSUM);
929                         }
930                         else {
931                                 if (displayed_msgs == NULL) {
932                                         displayed_msgs = malloc(sizeof(long) *
933                                                                 (maxmsgs<nummsgs ? maxmsgs : nummsgs));
934                                 }
935                                 displayed_msgs[num_displayed] = Msg->msgnum;
936                         }
937                         
938                         if (lowest_displayed < 0) lowest_displayed = a;
939                         highest_displayed = a;
940                         
941                         ++num_displayed;
942                 }
943         }
944         DeleteHashPos(&at);
945
946         /** Output loop */
947         if (displayed_msgs != NULL) {
948                 if (bbs_reverse) {
949                         ////TODOqsort(displayed_msgs, num_displayed, sizeof(long), qlongcmp_r);
950                 }
951
952                 /** if we do a split bbview in the future, begin messages div here */
953
954                 for (a=0; a<num_displayed; ++a) {
955                         read_message(WC->WBuf, HKEY("view_message"), displayed_msgs[a], 0, NULL);
956                 }
957
958                 /** if we do a split bbview in the future, end messages div here */
959
960                 free(displayed_msgs);
961                 displayed_msgs = NULL;
962         }
963
964         if (is_summary) {
965                 do_template("summary_trailer", NULL);
966         } else if (WCC->is_mobile) {
967                 wprintf("</div>");
968         }
969
970         /**
971          * Bump these because although we're thinking in zero base, the user
972          * is a drooling idiot and is thinking in one base.
973          */
974         ++lowest_displayed;
975         ++highest_displayed;
976
977         /**
978          * If we're not currently looking at ALL requested
979          * messages, then display the selector bar
980          */
981         if (is_bbview) {
982                 /** begin bbview scroller */
983
984                 DoTemplate(HKEY("msg_listselector_bottom"), BBViewToolBar, MessageDropdown, CTX_STRBUF);
985                 StrBufAppendBuf(WCC->WBuf, BBViewToolBar, 0);
986
987                 FreeStrBuf(&BBViewToolBar);
988                 FreeStrBuf(&MessageDropdown);
989         }
990         
991 DONE:
992         if (is_tasks) {
993                 do_tasks_view();        /** Render the task list */
994         }
995
996         if (is_calendar) {
997                 render_calendar_view(&calv);
998         }
999
1000         if (is_addressbook) {
1001                 do_addrbook_view(addrbook, num_ab);     /** Render the address book */
1002         }
1003
1004         /** Note: wDumpContent() will output one additional </div> tag. */
1005         wprintf("</div>\n");            /** end of 'content' div */
1006         wDumpContent(1);
1007
1008         /** free the summary */
1009         if (WCC->summ != NULL) {
1010                 DeleteHash(&WCC->summ);
1011         }
1012         if (addrbook != NULL) free(addrbook);
1013         FreeStrBuf(&BBViewToolBar);
1014 }
1015
1016
1017 /*
1018  * Back end for post_message()
1019  * ... this is where the actual message gets transmitted to the server.
1020  */
1021 void post_mime_to_server(void) {
1022         struct wcsession *WCC = WC;
1023         char top_boundary[SIZ];
1024         char alt_boundary[SIZ];
1025         int is_multipart = 0;
1026         static int seq = 0;
1027         wc_mime_attachment *att;
1028         char *encoded;
1029         size_t encoded_length;
1030         size_t encoded_strlen;
1031         char *txtmail = NULL;
1032
1033         sprintf(top_boundary, "Citadel--Multipart--%s--%04x--%04x",
1034                 serv_info.serv_fqdn,
1035                 getpid(),
1036                 ++seq
1037         );
1038         sprintf(alt_boundary, "Citadel--Multipart--%s--%04x--%04x",
1039                 serv_info.serv_fqdn,
1040                 getpid(),
1041                 ++seq
1042         );
1043
1044         /* RFC2045 requires this, and some clients look for it... */
1045         serv_puts("MIME-Version: 1.0");
1046         serv_puts("X-Mailer: " PACKAGE_STRING);
1047
1048         /* If there are attachments, we have to do multipart/mixed */
1049         if (GetCount(WCC->attachments) > 0) {
1050                 is_multipart = 1;
1051         }
1052
1053         if (is_multipart) {
1054                 /* Remember, serv_printf() appends an extra newline */
1055                 serv_printf("Content-type: multipart/mixed; boundary=\"%s\"\n", top_boundary);
1056                 serv_printf("This is a multipart message in MIME format.\n");
1057                 serv_printf("--%s", top_boundary);
1058         }
1059
1060         /* Remember, serv_printf() appends an extra newline */
1061         serv_printf("Content-type: multipart/alternative; "
1062                 "boundary=\"%s\"\n", alt_boundary);
1063         serv_printf("This is a multipart message in MIME format.\n");
1064         serv_printf("--%s", alt_boundary);
1065
1066         serv_puts("Content-type: text/plain; charset=utf-8");
1067         serv_puts("Content-Transfer-Encoding: quoted-printable");
1068         serv_puts("");
1069         txtmail = html_to_ascii(bstr("msgtext"), 0, 80, 0);
1070         text_to_server_qp(txtmail);     /* Transmit message in quoted-printable encoding */
1071         free(txtmail);
1072
1073         serv_printf("--%s", alt_boundary);
1074
1075         serv_puts("Content-type: text/html; charset=utf-8");
1076         serv_puts("Content-Transfer-Encoding: quoted-printable");
1077         serv_puts("");
1078         serv_puts("<html><body>\r\n");
1079         text_to_server_qp(bstr("msgtext"));     /* Transmit message in quoted-printable encoding */
1080         serv_puts("</body></html>\r\n");
1081
1082         serv_printf("--%s--", alt_boundary);
1083         
1084         if (is_multipart) {
1085                 long len;
1086                 const char *Key; 
1087                 void *vAtt;
1088                 HashPos  *it;
1089
1090                 /* Add in the attachments */
1091                 it = GetNewHashPos(WCC->attachments, 0);
1092                 while (GetNextHashPos(WCC->attachments, it, &len, &Key, &vAtt)) {
1093                         att = (wc_mime_attachment *)vAtt;
1094                         encoded_length = ((att->length * 150) / 100);
1095                         encoded = malloc(encoded_length);
1096                         if (encoded == NULL) break;
1097                         encoded_strlen = CtdlEncodeBase64(encoded, ChrPtr(att->Data), StrLength(att->Data), 1);
1098
1099                         serv_printf("--%s", top_boundary);
1100                         serv_printf("Content-type: %s", ChrPtr(att->ContentType));
1101                         serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att->FileName));
1102                         serv_puts("Content-transfer-encoding: base64");
1103                         serv_puts("");
1104                         serv_write(encoded, encoded_strlen);
1105                         serv_puts("");
1106                         serv_puts("");
1107                         free(encoded);
1108                 }
1109                 serv_printf("--%s--", top_boundary);
1110                 DeleteHashPos(&it);
1111         }
1112
1113         serv_puts("000");
1114 }
1115
1116
1117 /*
1118  * Post message (or don't post message)
1119  *
1120  * Note regarding the "dont_post" variable:
1121  * A random value (actually, it's just a timestamp) is inserted as a hidden
1122  * field called "postseq" when the display_enter page is generated.  This
1123  * value is checked when posting, using the static variable dont_post.  If a
1124  * user attempts to post twice using the same dont_post value, the message is
1125  * discarded.  This prevents the accidental double-saving of the same message
1126  * if the user happens to click the browser "back" button.
1127  */
1128 void post_message(void)
1129 {
1130         char buf[1024];
1131         StrBuf *encoded_subject = NULL;
1132         static long dont_post = (-1L);
1133         wc_mime_attachment  *att;
1134         int is_anonymous = 0;
1135         const StrBuf *display_name = NULL;
1136         struct wcsession *WCC = WC;
1137         
1138         if (havebstr("force_room")) {
1139                 gotoroom(bstr("force_room"));
1140         }
1141
1142         if (havebstr("display_name")) {
1143                 display_name = sbstr("display_name");
1144                 if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
1145                         display_name = NULL;
1146                         is_anonymous = 1;
1147                 }
1148         }
1149
1150         if (WCC->upload_length > 0) {
1151                 const char *pch;
1152                 int n;
1153                 char N[64];
1154
1155                 lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__, __LINE__, WCC->upload_length);
1156                 /** There's an attachment.  Save it to this struct... */
1157                 att = malloc(sizeof(wc_mime_attachment));
1158                 memset(att, 0, sizeof(wc_mime_attachment ));
1159                 att->length = WCC->upload_length;
1160                 att->ContentType = NewStrBufPlain(WCC->upload_content_type, -1);
1161                 att->FileName = NewStrBufPlain(WCC->upload_filename, -1);
1162                 
1163                 
1164                 if (WCC->attachments == NULL)
1165                         WCC->attachments = NewHash(1, NULL);
1166                 /* And add it to the list. */
1167                 n = snprintf(N, sizeof N, "%d", GetCount(WCC->attachments) + 1);
1168                 Put(WCC->attachments, N, n, att, DestroyMime);
1169
1170                 /**
1171                  * Mozilla sends a simple filename, which is what we want,
1172                  * but Satan's Browser sends an entire pathname.  Reduce
1173                  * the path to just a filename if we need to.
1174                  */
1175                 pch = strrchr(ChrPtr(att->FileName), '/');
1176                 if (pch != NULL) {
1177                         StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName));
1178                 }
1179                 pch = strrchr(ChrPtr(att->FileName), '\\');
1180                 if (pch != NULL) {
1181                         StrBufCutLeft(att->FileName, pch - ChrPtr(att->FileName));
1182                 }
1183
1184                 /**
1185                  * Transfer control of this memory from the upload struct
1186                  * to the attachment struct.
1187                  */
1188                 att->Data = NewStrBufPlain(WCC->upload, WCC->upload_length);
1189                 free(WCC->upload);
1190                 WCC->upload_length = 0;
1191                 WCC->upload = NULL;
1192                 display_enter();
1193                 return;
1194         }
1195
1196         if (havebstr("cancel_button")) {
1197                 sprintf(WCC->ImportantMessage, 
1198                         _("Cancelled.  Message was not posted."));
1199         } else if (havebstr("attach_button")) {
1200                 display_enter();
1201                 return;
1202         } else if (lbstr("postseq") == dont_post) {
1203                 sprintf(WCC->ImportantMessage, 
1204                         _("Automatically cancelled because you have already "
1205                         "saved this message."));
1206         } else {
1207                 const char CMD[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
1208                 const StrBuf *Recp = NULL; 
1209                 const StrBuf *Cc = NULL;
1210                 const StrBuf *Bcc = NULL;
1211                 const StrBuf *Wikipage = NULL;
1212                 const StrBuf *my_email_addr = NULL;
1213                 StrBuf *CmdBuf = NULL;;
1214                 StrBuf *references = NULL;
1215
1216                 if (havebstr("references"))
1217                 {
1218                         const StrBuf *ref = sbstr("references");
1219                         references = NewStrBufPlain(ChrPtr(ref), StrLength(ref));
1220                         lprintf(9, "Converting: %s\n", ChrPtr(references));
1221                         StrBufReplaceChars(references, '|', '!');
1222                         lprintf(9, "Converted: %s\n", ChrPtr(references));
1223                 }
1224                 if (havebstr("subject")) {
1225                         const StrBuf *Subj;
1226                         /*
1227                          * make enough room for the encoded string; 
1228                          * plus the QP header 
1229                          */
1230                         Subj = sbstr("subject");
1231                         
1232                         StrBufRFC2047encode(&encoded_subject, Subj);
1233                 }
1234                 Recp = sbstr("recp");
1235                 Cc = sbstr("cc");
1236                 Bcc = sbstr("bcc");
1237                 Wikipage = sbstr("wikipage");
1238                 my_email_addr = sbstr("my_email_addr");
1239                 
1240                 CmdBuf = NewStrBufPlain(NULL, 
1241                                         sizeof (CMD) + 
1242                                         StrLength(Recp) + 
1243                                         StrLength(encoded_subject) +
1244                                         StrLength(Cc) +
1245                                         StrLength(Bcc) + 
1246                                         StrLength(Wikipage) +
1247                                         StrLength(my_email_addr) + 
1248                                         StrLength(references));
1249
1250                 StrBufPrintf(CmdBuf, 
1251                              CMD,
1252                              ChrPtr(Recp),
1253                              is_anonymous,
1254                              ChrPtr(encoded_subject),
1255                              ChrPtr(display_name),
1256                              ChrPtr(Cc),
1257                              ChrPtr(Bcc),
1258                              ChrPtr(Wikipage),
1259                              ChrPtr(my_email_addr),
1260                              ChrPtr(references));
1261                 FreeStrBuf(&references);
1262
1263                 lprintf(9, "%s\n", ChrPtr(CmdBuf));
1264                 serv_puts(ChrPtr(CmdBuf));
1265                 serv_getln(buf, sizeof buf);
1266                 FreeStrBuf(&CmdBuf);
1267                 FreeStrBuf(&encoded_subject);
1268                 if (buf[0] == '4') {
1269                         post_mime_to_server();
1270                         if (  (havebstr("recp"))
1271                            || (havebstr("cc"  ))
1272                            || (havebstr("bcc" ))
1273                         ) {
1274                                 sprintf(WCC->ImportantMessage, _("Message has been sent.\n"));
1275                         }
1276                         else {
1277                                 sprintf(WC->ImportantMessage, _("Message has been posted.\n"));
1278                         }
1279                         dont_post = lbstr("postseq");
1280                 } else {
1281                         lprintf(9, "%s:%d: server post error: %s\n", __FILE__, __LINE__, buf);
1282                         sprintf(WC->ImportantMessage, "%s", &buf[4]);
1283                         display_enter();
1284                         return;
1285                 }
1286         }
1287
1288         DeleteHash(&WCC->attachments);
1289
1290         /**
1291          *  We may have been supplied with instructions regarding the location
1292          *  to which we must return after posting.  If found, go there.
1293          */
1294         if (havebstr("return_to")) {
1295                 http_redirect(bstr("return_to"));
1296         }
1297         /**
1298          *  If we were editing a page in a wiki room, go to that page now.
1299          */
1300         else if (havebstr("wikipage")) {
1301                 snprintf(buf, sizeof buf, "wiki?page=%s", bstr("wikipage"));
1302                 http_redirect(buf);
1303         }
1304         /**
1305          *  Otherwise, just go to the "read messages" loop.
1306          */
1307         else {
1308                 readloop(readnew);
1309         }
1310 }
1311
1312
1313
1314
1315 /**
1316  * \brief display the message entry screen
1317  */
1318 void display_enter(void)
1319 {
1320         char buf[SIZ];
1321         long now;
1322         const StrBuf *display_name = NULL;
1323         /////wc_attachment *att;
1324         int recipient_required = 0;
1325         int subject_required = 0;
1326         int recipient_bad = 0;
1327         int is_anonymous = 0;
1328
1329         struct wcsession *WCC = WC;
1330
1331         now = time(NULL);
1332
1333         if (havebstr("force_room")) {
1334                 gotoroom(bstr("force_room"));
1335         }
1336
1337         display_name = sbstr("display_name");
1338         if (!strcmp(ChrPtr(display_name), "__ANONYMOUS__")) {
1339                 display_name = NULL;
1340                 is_anonymous = 1;
1341         }
1342
1343         /** First test to see whether this is a room that requires recipients to be entered */
1344         serv_puts("ENT0 0");
1345         serv_getln(buf, sizeof buf);
1346
1347         if (!strncmp(buf, "570", 3)) {          /** 570 means that we need a recipient here */
1348                 recipient_required = 1;
1349         }
1350         else if (buf[0] != '2') {               /** Any other error means that we cannot continue */
1351                 sprintf(WCC->ImportantMessage, "%s", &buf[4]);
1352                 readloop(readnew);
1353                 return;
1354         }
1355
1356         /* Is the server strongly recommending that the user enter a message subject? */
1357         if ((buf[3] != '\0') && (buf[4] != '\0')) {
1358                 subject_required = extract_int(&buf[4], 1);
1359         }
1360
1361         /**
1362          * Are we perhaps in an address book view?  If so, then an "enter
1363          * message" command really means "add new entry."
1364          */
1365         if (WCC->wc_default_view == VIEW_ADDRESSBOOK) {
1366                 do_edit_vcard(-1, "", "", WCC->wc_roomname);
1367                 return;
1368         }
1369
1370         /*
1371          * Are we perhaps in a calendar room?  If so, then an "enter
1372          * message" command really means "add new calendar item."
1373          */
1374         if (WCC->wc_default_view == VIEW_CALENDAR) {
1375                 display_edit_event();
1376                 return;
1377         }
1378
1379         /*
1380          * Are we perhaps in a tasks view?  If so, then an "enter
1381          * message" command really means "add new task."
1382          */
1383         if (WCC->wc_default_view == VIEW_TASKS) {
1384                 display_edit_task();
1385                 return;
1386         }
1387
1388         /*
1389          * Otherwise proceed normally.
1390          * Do a custom room banner with no navbar...
1391          */
1392
1393         if (recipient_required) {
1394                 const StrBuf *Recp = NULL; 
1395                 const StrBuf *Cc = NULL;
1396                 const StrBuf *Bcc = NULL;
1397                 const StrBuf *Wikipage = NULL;
1398                 StrBuf *CmdBuf = NULL;;
1399                 const char CMD[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s";
1400                 
1401                 Recp = sbstr("recp");
1402                 Cc = sbstr("cc");
1403                 Bcc = sbstr("bcc");
1404                 Wikipage = sbstr("wikipage");
1405                 
1406                 CmdBuf = NewStrBufPlain(NULL, 
1407                                         sizeof (CMD) + 
1408                                         StrLength(Recp) + 
1409                                         StrLength(display_name) +
1410                                         StrLength(Cc) +
1411                                         StrLength(Bcc) + 
1412                                         StrLength(Wikipage));
1413
1414                 StrBufPrintf(CmdBuf, 
1415                              CMD,
1416                              ChrPtr(Recp), 
1417                              is_anonymous,
1418                              ChrPtr(display_name),
1419                              ChrPtr(Cc), 
1420                              ChrPtr(Bcc), 
1421                              ChrPtr(Wikipage));
1422                 serv_puts(ChrPtr(CmdBuf));
1423                 serv_getln(buf, sizeof buf);
1424                 FreeStrBuf(&CmdBuf);
1425
1426                 if (!strncmp(buf, "570", 3)) {  /** 570 means we have an invalid recipient listed */
1427                         if (havebstr("recp") && 
1428                             havebstr("cc"  ) && 
1429                             havebstr("bcc" )) {
1430                                 recipient_bad = 1;
1431                         }
1432                 }
1433                 else if (buf[0] != '2') {       /** Any other error means that we cannot continue */
1434                         wprintf("<em>%s</em><br />\n", &buf[4]);/// -> important message
1435                         return;
1436                 }
1437         }
1438         svputlong("RCPTREQUIRED", recipient_required);
1439         svputlong("SUBJREQUIRED", recipient_required || subject_required);
1440
1441         begin_burst();
1442         output_headers(1, 0, 0, 0, 1, 0);
1443         DoTemplate(HKEY("edit_message"), NULL, NULL, CTX_NONE);
1444         end_burst();
1445
1446         return;
1447 }
1448
1449 /**
1450  * \brief delete a message
1451  */
1452 void delete_msg(void)
1453 {
1454         long msgid;
1455         char buf[SIZ];
1456
1457         msgid = lbstr("msgid");
1458
1459         if (WC->wc_is_trash) {  /** Delete from Trash is a real delete */
1460                 serv_printf("DELE %ld", msgid); 
1461         }
1462         else {                  /** Otherwise move it to Trash */
1463                 serv_printf("MOVE %ld|_TRASH_|0", msgid);
1464         }
1465
1466         serv_getln(buf, sizeof buf);
1467         sprintf(WC->ImportantMessage, "%s", &buf[4]);
1468
1469         readloop(readnew);
1470 }
1471
1472
1473 /**
1474  * \brief move a message to another folder
1475  */
1476 void move_msg(void)
1477 {
1478         long msgid;
1479         char buf[SIZ];
1480
1481         msgid = lbstr("msgid");
1482
1483         if (havebstr("move_button")) {
1484                 sprintf(buf, "MOVE %ld|%s", msgid, bstr("target_room"));
1485                 serv_puts(buf);
1486                 serv_getln(buf, sizeof buf);
1487                 sprintf(WC->ImportantMessage, "%s", &buf[4]);
1488         } else {
1489                 sprintf(WC->ImportantMessage, (_("The message was not moved.")));
1490         }
1491
1492         readloop(readnew);
1493 }
1494
1495
1496
1497
1498
1499 /*
1500  * Confirm move of a message
1501  */
1502 void confirm_move_msg(void)
1503 {
1504         long msgid;
1505         char buf[SIZ];
1506         char targ[SIZ];
1507
1508         msgid = lbstr("msgid");
1509
1510
1511         output_headers(1, 1, 2, 0, 0, 0);
1512         wprintf("<div id=\"banner\">\n");
1513         wprintf("<h1>");
1514         wprintf(_("Confirm move of message"));
1515         wprintf("</h1>");
1516         wprintf("</div>\n");
1517
1518         wprintf("<div id=\"content\" class=\"service\">\n");
1519
1520         wprintf("<CENTER>");
1521
1522         wprintf(_("Move this message to:"));
1523         wprintf("<br />\n");
1524
1525         wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
1526         wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC->nonce);
1527         wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
1528
1529         wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
1530         serv_puts("LKRA");
1531         serv_getln(buf, sizeof buf);
1532         if (buf[0] == '1') {
1533                 while (serv_getln(buf, sizeof buf), strcmp(buf, "000")) {
1534                         extract_token(targ, buf, 0, '|', sizeof targ);
1535                         wprintf("<OPTION>");
1536                         escputs(targ);
1537                         wprintf("\n");
1538                 }
1539         }
1540         wprintf("</SELECT>\n");
1541         wprintf("<br />\n");
1542
1543         wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
1544         wprintf("&nbsp;");
1545         wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
1546         wprintf("</form></CENTER>\n");
1547
1548         wprintf("</CENTER>\n");
1549         wDumpContent(1);
1550 }
1551
1552 void h_readnew(void) { readloop(readnew);}
1553 void h_readold(void) { readloop(readold);}
1554 void h_readfwd(void) { readloop(readfwd);}
1555 void h_headers(void) { readloop(headers);}
1556 void h_do_search(void) { readloop(do_search);}
1557
1558
1559
1560
1561
1562
1563 void 
1564 InitModule_MSG
1565 (void)
1566 {
1567         WebcitAddUrlHandler(HKEY("readnew"), h_readnew, NEED_URL);
1568         WebcitAddUrlHandler(HKEY("readold"), h_readold, NEED_URL);
1569         WebcitAddUrlHandler(HKEY("readfwd"), h_readfwd, NEED_URL);
1570         WebcitAddUrlHandler(HKEY("headers"), h_headers, NEED_URL);
1571         WebcitAddUrlHandler(HKEY("do_search"), h_do_search, 0);
1572         WebcitAddUrlHandler(HKEY("display_enter"), display_enter, 0);
1573         WebcitAddUrlHandler(HKEY("post"), post_message, 0);
1574         WebcitAddUrlHandler(HKEY("move_msg"), move_msg, 0);
1575         WebcitAddUrlHandler(HKEY("delete_msg"), delete_msg, 0);
1576         WebcitAddUrlHandler(HKEY("confirm_move_msg"), confirm_move_msg, 0);
1577         WebcitAddUrlHandler(HKEY("msg"), embed_message, NEED_URL|AJAX);
1578         WebcitAddUrlHandler(HKEY("printmsg"), print_message, NEED_URL);
1579         WebcitAddUrlHandler(HKEY("mobilemsg"), mobile_message_view, NEED_URL);
1580         WebcitAddUrlHandler(HKEY("msgheaders"), display_headers, NEED_URL);
1581
1582
1583         return ;
1584 }