* terminate the two mbox views the right way [tm]
[citadel.git] / webcit / msg_renderers.c
1 #include "webcit.h"
2 #include "webserver.h"
3 #include "groupdav.h"
4
5 /*
6  * message index functions
7  */
8
9
10 void DestroyMimeParts(wc_mime_attachment *Mime)
11 {
12         FreeStrBuf(&Mime->Name);
13         FreeStrBuf(&Mime->FileName);
14         FreeStrBuf(&Mime->PartNum);
15         FreeStrBuf(&Mime->Disposition);
16         FreeStrBuf(&Mime->ContentType);
17         FreeStrBuf(&Mime->Charset);
18         FreeStrBuf(&Mime->Data);
19 }
20
21 void DestroyMime(void *vMime)
22 {
23         wc_mime_attachment *Mime = (wc_mime_attachment*)vMime;
24         DestroyMimeParts(Mime);
25         free(Mime);
26 }
27
28 void DestroyMessageSummary(void *vMsg)
29 {
30         message_summary *Msg = (message_summary*) vMsg;
31
32         FreeStrBuf(&Msg->from);
33         FreeStrBuf(&Msg->to);
34         FreeStrBuf(&Msg->subj);
35         FreeStrBuf(&Msg->reply_inreplyto);
36         FreeStrBuf(&Msg->reply_references);
37         FreeStrBuf(&Msg->cccc);
38         FreeStrBuf(&Msg->hnod);
39         FreeStrBuf(&Msg->AllRcpt);
40         FreeStrBuf(&Msg->Room);
41         FreeStrBuf(&Msg->Rfca);
42         FreeStrBuf(&Msg->OtherNode);
43
44         FreeStrBuf(&Msg->reply_to);
45
46         DeleteHash(&Msg->Attachments);  /**< list of Accachments */
47         DeleteHash(&Msg->Submessages);
48         DeleteHash(&Msg->AttachLinks);
49         DeleteHash(&Msg->AllAttach);
50         free(Msg);
51 }
52
53
54
55 void RegisterMsgHdr(const char *HeaderName, long HdrNLen, ExamineMsgHeaderFunc evaluator, int type)
56 {
57         headereval *ev;
58         ev = (headereval*) malloc(sizeof(headereval));
59         ev->evaluator = evaluator;
60         ev->Type = type;
61         Put(MsgHeaderHandler, HeaderName, HdrNLen, ev, NULL);
62 }
63
64 void RegisterMimeRenderer(const char *HeaderName, long HdrNLen, 
65                           RenderMimeFunc MimeRenderer,
66                           int InlineRenderable,
67                           int Priority)
68 {
69         RenderMimeFuncStruct *f;
70
71         f = (RenderMimeFuncStruct*) malloc(sizeof(RenderMimeFuncStruct));
72         f->f = MimeRenderer;
73         Put(MimeRenderHandler, HeaderName, HdrNLen, f, NULL);
74         if (InlineRenderable)
75                 RegisterEmbeddableMimeType(HeaderName, HdrNLen, 10000 - Priority);
76 }
77
78 /*----------------------------------------------------------------------------*/
79
80 /*
81  *  comparator for two longs in descending order.
82  */
83 int longcmp_r(const void *s1, const void *s2) {
84         long l1;
85         long l2;
86
87         l1 = *(long *)GetSearchPayload(s1);
88         l2 = *(long *)GetSearchPayload(s2);
89
90         if (l1 > l2) return(-1);
91         if (l1 < l2) return(+1);
92         return(0);
93 }
94
95 /*
96  *  comparator for longs; descending order.
97  */
98 int qlongcmp_r(const void *s1, const void *s2) {
99         long l1 = (long) s1;
100         long l2 = (long) s2;
101
102         if (l1 > l2) return(-1);
103         if (l1 < l2) return(+1);
104         return(0);
105 }
106
107  
108 /*
109  * comparator for message summary structs by ascending subject.
110  */
111 int summcmp_subj(const void *s1, const void *s2) {
112         message_summary *summ1;
113         message_summary *summ2;
114         
115         summ1 = (message_summary *)GetSearchPayload(s1);
116         summ2 = (message_summary *)GetSearchPayload(s2);
117         return strcasecmp(ChrPtr(summ1->subj), ChrPtr(summ2->subj));
118 }
119
120 /*
121  * comparator for message summary structs by descending subject.
122  */
123 int summcmp_rsubj(const void *s1, const void *s2) {
124         message_summary *summ1;
125         message_summary *summ2;
126         
127         summ1 = (message_summary *)GetSearchPayload(s1);
128         summ2 = (message_summary *)GetSearchPayload(s2);
129         return strcasecmp(ChrPtr(summ2->subj), ChrPtr(summ1->subj));
130 }
131 /*
132  * comparator for message summary structs by descending subject.
133  */
134 int groupchange_subj(const void *s1, const void *s2) {
135         message_summary *summ1;
136         message_summary *summ2;
137         
138         summ1 = (message_summary *)s1;
139         summ2 = (message_summary *)s2;
140         return ChrPtr(summ2->subj)[0] != ChrPtr(summ1->subj)[0];
141 }
142
143 /*
144  * comparator for message summary structs by ascending sender.
145  */
146 int summcmp_sender(const void *s1, const void *s2) {
147         message_summary *summ1;
148         message_summary *summ2;
149         
150         summ1 = (message_summary *)GetSearchPayload(s1);
151         summ2 = (message_summary *)GetSearchPayload(s2);
152         return strcasecmp(ChrPtr(summ1->from), ChrPtr(summ2->from));
153 }
154
155 /*
156  * comparator for message summary structs by descending sender.
157  */
158 int summcmp_rsender(const void *s1, const void *s2) {
159         message_summary *summ1;
160         message_summary *summ2;
161         
162         summ1 = (message_summary *)GetSearchPayload(s1);
163         summ2 = (message_summary *)GetSearchPayload(s2);
164         return strcasecmp(ChrPtr(summ2->from), ChrPtr(summ1->from));
165 }
166 /*
167  * comparator for message summary structs by descending sender.
168  */
169 int groupchange_sender(const void *s1, const void *s2) {
170         message_summary *summ1;
171         message_summary *summ2;
172         
173         summ1 = (message_summary *)s1;
174         summ2 = (message_summary *)s2;
175         return strcasecmp(ChrPtr(summ2->from), ChrPtr(summ1->from)) != 0;
176
177 }
178
179 /*
180  * comparator for message summary structs by ascending date.
181  */
182 int summcmp_date(const void *s1, const void *s2) {
183         message_summary *summ1;
184         message_summary *summ2;
185         
186         summ1 = (message_summary *)GetSearchPayload(s1);
187         summ2 = (message_summary *)GetSearchPayload(s2);
188
189         if (summ1->date < summ2->date) return -1;
190         else if (summ1->date > summ2->date) return +1;
191         else return 0;
192 }
193
194 /*
195  * comparator for message summary structs by descending date.
196  */
197 int summcmp_rdate(const void *s1, const void *s2) {
198         message_summary *summ1;
199         message_summary *summ2;
200         
201         summ1 = (message_summary *)GetSearchPayload(s1);
202         summ2 = (message_summary *)GetSearchPayload(s2);
203
204         if (summ1->date < summ2->date) return +1;
205         else if (summ1->date > summ2->date) return -1;
206         else return 0;
207 }
208
209 /*
210  * comparator for message summary structs by descending date.
211  */
212 const long DAYSECONDS = 24 * 60 * 60;
213 int groupchange_date(const void *s1, const void *s2) {
214         message_summary *summ1;
215         message_summary *summ2;
216         
217         summ1 = (message_summary *)s1;
218         summ2 = (message_summary *)s2;
219
220         return (summ1->date % DAYSECONDS) != (summ2->date %DAYSECONDS);
221 }
222
223
224 /*----------------------------------------------------------------------------*/
225 /* Don't wanna know... or? */
226 void examine_pref(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset) {return;}
227 void examine_suff(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset) {return;}
228 void examine_path(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset) {return;}
229 void examine_content_encoding(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
230 {
231 /* TODO: do we care? */
232 }
233
234 void examine_nhdr(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
235 {
236         Msg->nhdr = 0;
237         if (!strncasecmp(ChrPtr(HdrLine), "yes", 8))
238                 Msg->nhdr = 1;
239 }
240 int Conditional_ANONYMOUS_MESSAGE(StrBuf *Target, WCTemplputParams *TP)
241 {
242         message_summary *Msg = (message_summary*) CTX;
243         return Msg->nhdr != 0;
244 }
245
246 void examine_type(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
247 {
248         Msg->format_type = StrToi(HdrLine);
249                         
250 }
251
252 void examine_from(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
253 {
254         FreeStrBuf(&Msg->from);
255         Msg->from = NewStrBufPlain(NULL, StrLength(HdrLine));
256         StrBuf_RFC822_to_Utf8(Msg->from, HdrLine, WC->DefaultCharset, FoundCharset);
257 }
258 void tmplput_MAIL_SUMM_FROM(StrBuf *Target, WCTemplputParams *TP)
259 {
260         message_summary *Msg = (message_summary*) CTX;
261         StrBufAppendTemplate(Target, TP, Msg->from, 0);
262 }
263
264
265
266 void examine_subj(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
267 {
268         FreeStrBuf(&Msg->subj);
269         Msg->subj = NewStrBufPlain(NULL, StrLength(HdrLine));
270         StrBuf_RFC822_to_Utf8(Msg->subj, HdrLine, WC->DefaultCharset, FoundCharset);
271 }
272 void tmplput_MAIL_SUMM_SUBJECT(StrBuf *Target, WCTemplputParams *TP)
273 {/*////TODO: Fwd: and RE: filter!!*/
274
275         message_summary *Msg = (message_summary*) CTX;
276         StrBufAppendTemplate(Target, TP, Msg->subj, 0);
277 }
278 int Conditional_MAIL_SUMM_SUBJECT(StrBuf *Target, WCTemplputParams *TP)
279 {
280         message_summary *Msg = (message_summary*) CTX;
281         return StrLength(Msg->subj) > 0;
282 }
283
284
285 void examine_msgn(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
286 {
287         FreeStrBuf(&Msg->reply_inreplyto);
288         Msg->reply_inreplyto = NewStrBufPlain(NULL, StrLength(HdrLine));
289         StrBuf_RFC822_to_Utf8(Msg->reply_inreplyto, HdrLine, WC->DefaultCharset, FoundCharset);
290 }
291 void tmplput_MAIL_SUMM_INREPLYTO(StrBuf *Target, WCTemplputParams *TP)
292 {
293         message_summary *Msg = (message_summary*) CTX;
294         StrBufAppendTemplate(Target, TP, Msg->reply_inreplyto, 0);
295 }
296
297 int Conditional_MAIL_SUMM_UNREAD(StrBuf *Target, WCTemplputParams *TP)
298 {
299         message_summary *Msg = (message_summary*) CTX;
300         return Msg->is_new != 0;
301 }
302
303 void examine_wefw(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
304 {
305         FreeStrBuf(&Msg->reply_references);
306         Msg->reply_references = NewStrBufPlain(NULL, StrLength(HdrLine));
307         StrBuf_RFC822_to_Utf8(Msg->reply_references, HdrLine, WC->DefaultCharset, FoundCharset);
308 }
309 void tmplput_MAIL_SUMM_REFIDS(StrBuf *Target, WCTemplputParams *TP)
310 {
311         message_summary *Msg = (message_summary*) CTX;
312         StrBufAppendTemplate(Target, TP, Msg->reply_references, 0);
313 }
314
315
316 void examine_cccc(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
317 {
318         FreeStrBuf(&Msg->cccc);
319         Msg->cccc = NewStrBufPlain(NULL, StrLength(HdrLine));
320         StrBuf_RFC822_to_Utf8(Msg->cccc, HdrLine, WC->DefaultCharset, FoundCharset);
321         if (Msg->AllRcpt == NULL)
322                 Msg->AllRcpt = NewStrBufPlain(NULL, StrLength(HdrLine));
323         if (StrLength(Msg->AllRcpt) > 0) {
324                 StrBufAppendBufPlain(Msg->AllRcpt, HKEY(", "), 0);
325         }
326         StrBufAppendBuf(Msg->AllRcpt, Msg->cccc, 0);
327 }
328 void tmplput_MAIL_SUMM_CCCC(StrBuf *Target, WCTemplputParams *TP)
329 {
330         message_summary *Msg = (message_summary*) CTX;
331         StrBufAppendTemplate(Target, TP, Msg->cccc, 0);
332 }
333
334
335 void examine_room(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
336 {
337         if ((StrLength(HdrLine) > 0) &&
338             (strcasecmp(ChrPtr(HdrLine), ChrPtr(WC->wc_roomname)))) {
339                 FreeStrBuf(&Msg->Room);
340                 Msg->Room = NewStrBufDup(HdrLine);              
341         }
342 }
343 void tmplput_MAIL_SUMM_ORGROOM(StrBuf *Target, WCTemplputParams *TP)
344 {
345         message_summary *Msg = (message_summary*) CTX;
346         StrBufAppendTemplate(Target, TP, Msg->Room, 0);
347 }
348
349
350 void examine_rfca(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
351 {
352         FreeStrBuf(&Msg->Rfca);
353         Msg->Rfca = NewStrBufDup(HdrLine);
354 }
355 void tmplput_MAIL_SUMM_RFCA(StrBuf *Target, WCTemplputParams *TP)
356 {
357         message_summary *Msg = (message_summary*) CTX;
358         StrBufAppendTemplate(Target, TP, Msg->Rfca, 0);
359 }
360 int Conditional_MAIL_SUMM_RFCA(StrBuf *Target, WCTemplputParams *TP)
361 {
362         message_summary *Msg = (message_summary*) CTX;
363         return StrLength(Msg->Rfca) > 0;
364 }
365 int Conditional_MAIL_SUMM_CCCC(StrBuf *Target, WCTemplputParams *TP)
366 {
367         message_summary *Msg = (message_summary*) CTX;
368         return StrLength(Msg->cccc) > 0;
369 }
370
371 void examine_node(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
372 {
373         wcsession *WCC = WC;
374
375         if ( (StrLength(HdrLine) > 0) &&
376              ((WC->room_flags & QR_NETWORK)
377               || ((strcasecmp(ChrPtr(HdrLine), ChrPtr(WCC->serv_info->serv_nodename))
378                    && (strcasecmp(ChrPtr(HdrLine), ChrPtr(WCC->serv_info->serv_fqdn))))))) {
379                 FreeStrBuf(&Msg->OtherNode);
380                 Msg->OtherNode = NewStrBufDup(HdrLine);
381         }
382 }
383 void tmplput_MAIL_SUMM_OTHERNODE(StrBuf *Target, WCTemplputParams *TP)
384 {
385         message_summary *Msg = (message_summary*) CTX;
386         StrBufAppendTemplate(Target, TP, Msg->OtherNode, 0);
387 }
388 int Conditional_MAIL_SUMM_OTHERNODE(StrBuf *Target, WCTemplputParams *TP)
389 {
390         message_summary *Msg = (message_summary*) CTX;
391         return StrLength(Msg->OtherNode) > 0;
392 }
393
394
395 void examine_rcpt(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
396 {
397         FreeStrBuf(&Msg->to);
398         Msg->to = NewStrBufPlain(NULL, StrLength(HdrLine));
399         StrBuf_RFC822_to_Utf8(Msg->to, HdrLine, WC->DefaultCharset, FoundCharset);
400         if (Msg->AllRcpt == NULL)
401                 Msg->AllRcpt = NewStrBufPlain(NULL, StrLength(HdrLine));
402         if (StrLength(Msg->AllRcpt) > 0) {
403                 StrBufAppendBufPlain(Msg->AllRcpt, HKEY(", "), 0);
404         }
405         StrBufAppendBuf(Msg->AllRcpt, Msg->to, 0);
406 }
407 void tmplput_MAIL_SUMM_TO(StrBuf *Target, WCTemplputParams *TP)
408 {
409         message_summary *Msg = (message_summary*) CTX;
410         StrBufAppendTemplate(Target, TP, Msg->to, 0);
411 }
412 int Conditional_MAIL_SUMM_TO(StrBuf *Target, WCTemplputParams *TP) 
413 {
414         message_summary *Msg = (message_summary*) CTX;
415         return StrLength(Msg->to) != 0;
416 }
417 int Conditional_MAIL_SUMM_SUBJ(StrBuf *Target, WCTemplputParams *TP) 
418 {
419         message_summary *Msg = (message_summary*) CTX;
420         return StrLength(Msg->subj) != 0;
421 }
422 void tmplput_MAIL_SUMM_ALLRCPT(StrBuf *Target, WCTemplputParams *TP)
423 {
424         message_summary *Msg = (message_summary*) CTX;
425         StrBufAppendTemplate(Target, TP, Msg->AllRcpt, 0);
426 }
427
428
429
430 void tmplput_SUMM_COUNT(StrBuf *Target, WCTemplputParams *TP)
431 {
432         StrBufAppendPrintf(Target, "%d", GetCount( WC->summ));
433 }
434
435 HashList *iterate_get_mailsumm_All(StrBuf *Target, WCTemplputParams *TP)
436 {
437         return WC->summ;
438 }
439 void examine_time(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
440 {
441         Msg->date = StrTol(HdrLine);
442 }
443
444 void tmplput_MAIL_SUMM_DATE_BRIEF(StrBuf *Target, WCTemplputParams *TP)
445 {
446         char datebuf[64];
447         message_summary *Msg = (message_summary*) CTX;
448         webcit_fmt_date(datebuf, 64, Msg->date, DATEFMT_BRIEF);
449         StrBufAppendBufPlain(Target, datebuf, -1, 0);
450 }
451
452 void tmplput_MAIL_SUMM_DATE_FULL(StrBuf *Target, WCTemplputParams *TP)
453 {
454         char datebuf[64];
455         message_summary *Msg = (message_summary*) CTX;
456         webcit_fmt_date(datebuf, 64, Msg->date, DATEFMT_FULL);
457         StrBufAppendBufPlain(Target, datebuf, -1, 0);
458 }
459 void tmplput_MAIL_SUMM_DATE_NO(StrBuf *Target, WCTemplputParams *TP)
460 {
461         message_summary *Msg = (message_summary*) CTX;
462         StrBufAppendPrintf(Target, "%ld", Msg->date, 0);
463 }
464
465
466
467 void render_MAIL(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
468 {
469         const StrBuf *TemplateMime;
470
471         Mime->Data = NewStrBufPlain(NULL, Mime->length);
472         read_message(Mime->Data, HKEY("view_submessage"), Mime->msgnum, Mime->PartNum, &TemplateMime);
473 /*
474         if ( (!IsEmptyStr(mime_submessages)) && (!section[0]) ) {
475                 for (i=0; i<num_tokens(mime_submessages, '|'); ++i) {
476                         extract_token(buf, mime_submessages, i, '|', sizeof buf);
477                         / ** use printable_view to suppress buttons * /
478                         wprintf("<blockquote>");
479                         read_message(Mime->msgnum, 1, ChrPtr(Mime->Section));
480                         wprintf("</blockquote>");
481                 }
482         }
483 */
484 }
485
486 void render_MIME_VCard(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
487 {
488         wcsession *WCC = WC;
489         MimeLoadData(Mime);
490         if (StrLength(Mime->Data) > 0) {
491                 StrBuf *Buf;
492                 Buf = NewStrBuf();
493                 /** If it's my vCard I can edit it */
494                 if (    (!strcasecmp(ChrPtr(WCC->wc_roomname), USERCONFIGROOM))
495                         || (!strcasecmp(&(ChrPtr(WCC->wc_roomname)[11]), USERCONFIGROOM))
496                         || (WC->wc_view == VIEW_ADDRESSBOOK)
497                         ) {
498                         StrBufAppendPrintf(Buf, "<a href=\"edit_vcard?msgnum=%ld&partnum=%s\">",
499                                 Mime->msgnum, ChrPtr(Mime->PartNum));
500                         StrBufAppendPrintf(Buf, "[%s]</a>", _("edit"));
501                 }
502
503                 /* In all cases, display the full card */
504                 display_vcard(Buf, Mime->Data, 0, 1, NULL, Mime->msgnum);
505                 FreeStrBuf(&Mime->Data);
506                 Mime->Data = Buf;
507         }
508
509 }
510
511 void render_MIME_VNote(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
512 {
513         MimeLoadData(Mime);
514         if (StrLength(Mime->Data) > 0) {
515                 struct vnote *v;
516                 StrBuf *Buf;
517                 char *vcard;
518
519                 Buf = NewStrBuf();
520                 vcard = SmashStrBuf(&Mime->Data);
521                 v = vnote_new_from_str(vcard);
522                 free (vcard);
523                 if (v) {
524                         WCTemplputParams TP;
525                         
526                         memset(&TP, 0, sizeof(WCTemplputParams));
527                         TP.Filter.ContextType = CTX_VNOTE;
528                         TP.Context = v;
529                         DoTemplate(HKEY("mail_vnoteitem"),
530                                    Buf, &TP);
531                         
532                         vnote_free(v);
533                         Mime->Data = Buf;
534                 }
535                 else
536                         Mime->Data = NewStrBuf();
537         }
538
539 }
540
541 void render_MIME_ICS(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
542 {
543         if (StrLength(Mime->Data) == 0) {
544                 MimeLoadData(Mime);
545         }
546         if (StrLength(Mime->Data) > 0) {
547                 cal_process_attachment(Mime);
548         }
549 }
550
551
552
553 void examine_mime_part(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
554 {
555         wc_mime_attachment *Mime;
556         StrBuf *Buf;
557         
558         Mime = (wc_mime_attachment*) malloc(sizeof(wc_mime_attachment));
559         memset(Mime, 0, sizeof(wc_mime_attachment));
560         Mime->msgnum = Msg->msgnum;
561         Buf = NewStrBuf();
562
563         Mime->Name = NewStrBuf();
564         StrBufExtract_token(Buf, HdrLine, 0, '|');
565         StrBuf_RFC822_to_Utf8(Mime->Name, Buf, WC->DefaultCharset, FoundCharset);
566         StrBufTrim(Mime->Name);
567
568         StrBufExtract_token(Buf, HdrLine, 1, '|');
569         Mime->FileName = NewStrBuf();
570         StrBuf_RFC822_to_Utf8(Mime->FileName, Buf, WC->DefaultCharset, FoundCharset);
571         StrBufTrim(Mime->FileName);
572
573         Mime->PartNum = NewStrBuf();
574         StrBufExtract_token(Mime->PartNum, HdrLine, 2, '|');
575         StrBufTrim(Mime->PartNum);
576         if (strchr(ChrPtr(Mime->PartNum), '.') != NULL) 
577                 Mime->level = 2;
578         else
579                 Mime->level = 1;
580
581         Mime->Disposition = NewStrBuf();
582         StrBufExtract_token(Mime->Disposition, HdrLine, 3, '|');
583
584         Mime->ContentType = NewStrBuf();
585         StrBufExtract_token(Mime->ContentType, HdrLine, 4, '|');
586         StrBufTrim(Mime->ContentType);
587         StrBufLowerCase(Mime->ContentType);
588
589         if (!strcmp(ChrPtr(Mime->ContentType), "application/octet-stream")) {
590                 StrBufPlain(Mime->ContentType, 
591                             GuessMimeByFilename(SKEY(Mime->FileName)), -1);
592         }
593         Mime->length = StrBufExtract_int(HdrLine, 5, '|');
594
595         if ( (StrLength(Mime->FileName) == 0) && (StrLength(Mime->Name) > 0) ) {
596                 StrBufAppendBuf(Mime->FileName, Mime->Name, 0);
597         }
598
599         if (StrLength(Msg->PartNum) > 0) {
600                 StrBuf *tmp;
601                 StrBufPrintf(Buf, "%s.%s", ChrPtr(Msg->PartNum), ChrPtr(Mime->PartNum));
602                 tmp = Mime->PartNum;
603                 Mime->PartNum = Buf;
604                 Buf = tmp;
605         }
606
607         if (Msg->AllAttach == NULL)
608                 Msg->AllAttach = NewHash(1,NULL);
609         Put(Msg->AllAttach, SKEY(Mime->PartNum), Mime, DestroyMime);
610         FreeStrBuf(&Buf);
611 }
612
613
614 void evaluate_mime_part(message_summary *Msg, wc_mime_attachment *Mime)
615 {
616         void *vMimeRenderer;
617
618         /* just print the root-node */
619         if ((Mime->level == 1) &&
620             GetHash(MimeRenderHandler, SKEY(Mime->ContentType), &vMimeRenderer) &&
621             vMimeRenderer != NULL)
622         {
623                 Mime->Renderer = (RenderMimeFuncStruct*) vMimeRenderer;
624                 if (Msg->Submessages == NULL)
625                         Msg->Submessages = NewHash(1,NULL);
626                 Put(Msg->Submessages, SKEY(Mime->PartNum), Mime, reference_free_handler);
627         }
628         else if ((Mime->level == 1) &&
629                  (!strcasecmp(ChrPtr(Mime->Disposition), "inline"))
630                  && (!strncasecmp(ChrPtr(Mime->ContentType), "image/", 6)) ){
631                 if (Msg->AttachLinks == NULL)
632                         Msg->AttachLinks = NewHash(1,NULL);
633                 Put(Msg->AttachLinks, SKEY(Mime->PartNum), Mime, reference_free_handler);
634         }
635         else if ((Mime->level == 1) &&
636                  (StrLength(Mime->ContentType) > 0) &&
637                   ( (!strcasecmp(ChrPtr(Mime->Disposition), "attachment")) 
638                     || (!strcasecmp(ChrPtr(Mime->Disposition), "inline"))
639                     || (!strcasecmp(ChrPtr(Mime->Disposition), ""))))
640         {               
641                 if (Msg->AttachLinks == NULL)
642                         Msg->AttachLinks = NewHash(1,NULL);
643                 Put(Msg->AttachLinks, SKEY(Mime->PartNum), Mime, reference_free_handler);
644                 if ((strcasecmp(ChrPtr(Mime->ContentType), "application/octet-stream") == 0) && 
645                     (StrLength(Mime->FileName) > 0)) {
646                         FlushStrBuf(Mime->ContentType);
647                         StrBufAppendBufPlain(Mime->ContentType,
648                                              GuessMimeByFilename(SKEY(Mime->FileName)),
649                                              -1, 0);
650                 }
651         }
652 }
653
654 void tmplput_MAIL_SUMM_NATTACH(StrBuf *Target, WCTemplputParams *TP)
655 {
656         message_summary *Msg = (message_summary*) CTX;
657         StrBufAppendPrintf(Target, "%ld", GetCount(Msg->Attachments));
658 }
659
660
661 void examine_hnod(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
662 {
663         FreeStrBuf(&Msg->hnod);
664         Msg->hnod = NewStrBufPlain(NULL, StrLength(HdrLine));
665         StrBuf_RFC822_to_Utf8(Msg->hnod, HdrLine, WC->DefaultCharset, FoundCharset);
666 }
667 void tmplput_MAIL_SUMM_H_NODE(StrBuf *Target, WCTemplputParams *TP)
668 {
669         message_summary *Msg = (message_summary*) CTX;
670         StrBufAppendTemplate(Target, TP, Msg->hnod, 0);
671 }
672 int Conditional_MAIL_SUMM_H_NODE(StrBuf *Target, WCTemplputParams *TP)
673 {
674         message_summary *Msg = (message_summary*) CTX;
675         return StrLength(Msg->hnod) > 0;
676 }
677
678
679
680 void examine_text(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
681 {
682         Msg->MsgBody->Data = NewStrBufPlain(NULL, SIZ);
683 }
684
685 void examine_msg4_partnum(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
686 {
687         Msg->MsgBody->PartNum = NewStrBufDup(HdrLine);
688         StrBufTrim(Msg->MsgBody->PartNum);
689 }
690
691 void examine_content_lengh(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
692 {
693         Msg->MsgBody->length = StrTol(HdrLine);
694         Msg->MsgBody->size_known = 1;
695 }
696
697 void examine_content_type(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
698 {
699         void *vHdr;
700         headereval *Hdr;
701         StrBuf *Token;
702         StrBuf *Value;
703         const char* sem;
704         const char *eq;
705         int len;
706         StrBufTrim(HdrLine);
707         Msg->MsgBody->ContentType = NewStrBufDup(HdrLine);
708         sem = strchr(ChrPtr(HdrLine), ';');
709
710         if (sem != NULL) {
711                 Token = NewStrBufPlain(NULL, StrLength(HdrLine));
712                 Value = NewStrBufPlain(NULL, StrLength(HdrLine));
713                 len = sem - ChrPtr(HdrLine);
714                 StrBufCutAt(Msg->MsgBody->ContentType, len, NULL);
715                 while (sem != NULL) {
716                         while (isspace(*(sem + 1)))
717                                 sem ++;
718                         StrBufCutLeft(HdrLine, sem - ChrPtr(HdrLine));
719                         sem = strchr(ChrPtr(HdrLine), ';');
720                         if (sem != NULL)
721                                 len = sem - ChrPtr(HdrLine);
722                         else
723                                 len = StrLength(HdrLine);
724                         FlushStrBuf(Token);
725                         FlushStrBuf(Value);
726                         StrBufAppendBufPlain(Token, ChrPtr(HdrLine), len, 0);
727                         eq = strchr(ChrPtr(Token), '=');
728                         if (eq != NULL) {
729                                 len = eq - ChrPtr(Token);
730                                 StrBufAppendBufPlain(Value, eq + 1, StrLength(Token) - len - 1, 0); 
731                                 StrBufCutAt(Token, len, NULL);
732                                 StrBufTrim(Value);
733                         }
734                         StrBufTrim(Token);
735
736                         if (GetHash(MsgHeaderHandler, SKEY(Token), &vHdr) &&
737                             (vHdr != NULL)) {
738                                 Hdr = (headereval*)vHdr;
739                                 Hdr->evaluator(Msg, Value, FoundCharset);
740                         }
741                         else lprintf(1, "don't know how to handle content type sub-header[%s]\n", ChrPtr(Token));
742                 }
743                 FreeStrBuf(&Token);
744                 FreeStrBuf(&Value);
745         }
746 }
747
748 void tmplput_MAIL_SUMM_N(StrBuf *Target, WCTemplputParams *TP)
749 {
750         message_summary *Msg = (message_summary*) CTX;
751         StrBufAppendPrintf(Target, "%ld", Msg->msgnum);
752 }
753
754
755
756 int Conditional_MAIL_MIME_ALL(StrBuf *Target, WCTemplputParams *TP)
757 {
758         message_summary *Msg = (message_summary*) CTX;
759         return GetCount(Msg->Attachments) > 0;
760 }
761
762 int Conditional_MAIL_MIME_SUBMESSAGES(StrBuf *Target, WCTemplputParams *TP)
763 {
764         message_summary *Msg = (message_summary*) CTX;
765         return GetCount(Msg->Submessages) > 0;
766 }
767
768 int Conditional_MAIL_MIME_ATTACHLINKS(StrBuf *Target, WCTemplputParams *TP)
769 {
770         message_summary *Msg = (message_summary*) CTX;
771         return GetCount(Msg->AttachLinks) > 0;
772 }
773
774 int Conditional_MAIL_MIME_ATTACH(StrBuf *Target, WCTemplputParams *TP)
775 {
776         message_summary *Msg = (message_summary*) CTX;
777         return GetCount(Msg->AllAttach) > 0;
778 }
779
780 void tmplput_QUOTED_MAIL_BODY(StrBuf *Target, WCTemplputParams *TP)
781 {
782         const StrBuf *Mime;
783         long MsgNum;
784         StrBuf *Buf;
785
786         MsgNum = LBstr(TKEY(0));
787         Buf = NewStrBuf();
788         read_message(Buf, HKEY("view_message_replyquote"), MsgNum, NULL, &Mime);
789         StrBufAppendTemplate(Target, TP, Buf, 1);
790         FreeStrBuf(&Buf);
791 }
792
793 void tmplput_EDIT_MAIL_BODY(StrBuf *Target, WCTemplputParams *TP)
794 {
795         const StrBuf *Mime;
796         long MsgNum;
797         StrBuf *Buf;
798
799         MsgNum = LBstr(TKEY(0));
800         Buf = NewStrBuf();
801         read_message(Buf, HKEY("view_message_edit"), MsgNum, NULL, &Mime);
802         StrBufAppendTemplate(Target, TP, Buf, 1);
803         FreeStrBuf(&Buf);
804 }
805
806 void tmplput_EDIT_WIKI_BODY(StrBuf *Target, WCTemplputParams *TP)       // FIXME
807 {
808         const StrBuf *Mime;
809         long msgnum;
810         StrBuf *Buf;
811
812         msgnum = locate_message_by_uid(BSTR("wikipage"));
813         if (msgnum >= 0L) {
814                 Buf = NewStrBuf();
815                 read_message(Buf, HKEY("view_message_wikiedit"), msgnum, NULL, &Mime);
816                 StrBufAppendTemplate(Target, TP, Buf, 1);
817                 FreeStrBuf(&Buf);
818         }
819 }
820
821 void tmplput_MAIL_BODY(StrBuf *Target, WCTemplputParams *TP)
822 {
823         message_summary *Msg = (message_summary*) CTX;
824         StrBufAppendTemplate(Target, TP, Msg->MsgBody->Data, 0);
825 }
826
827
828 void render_MAIL_variformat(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
829 {
830         /* Messages in legacy Citadel variformat get handled thusly... */
831         StrBuf *Target = NewStrBufPlain(NULL, StrLength(Mime->Data));
832         FmOut(Target, "JUSTIFY", Mime->Data);
833         FreeStrBuf(&Mime->Data);
834         Mime->Data = Target;
835 }
836
837 void render_MAIL_text_plain(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
838 {
839         const char *ptr, *pte;
840         const char *BufPtr = NULL;
841         StrBuf *Line;
842         StrBuf *Line1;
843         StrBuf *Line2;
844         StrBuf *Target;
845
846         int bn = 0;
847         int bq = 0;
848         int i;
849         long len;
850 #ifdef HAVE_ICONV
851         StrBuf *cs = NULL;
852         int ConvertIt = 1;
853         iconv_t ic = (iconv_t)(-1) ;
854 #endif
855
856         if ((StrLength(Mime->Data) == 0) && (Mime->length > 0)) {
857                 FreeStrBuf(&Mime->Data);
858                 MimeLoadData(Mime);
859         }
860
861 #ifdef HAVE_ICONV
862         if (ConvertIt) {
863                 if (StrLength(Mime->Charset) != 0)
864                         cs = Mime->Charset;
865                 else if (StrLength(FoundCharset) > 0)
866                         cs = FoundCharset;
867                 else if (StrLength(WC->DefaultCharset) > 0)
868                         cs = WC->DefaultCharset;
869                 if (cs == NULL) {
870                         ConvertIt = 0;
871                 }
872                 else if (!strcasecmp(ChrPtr(cs), "utf-8")) {
873                         ConvertIt = 0;
874                 }
875                 else if (!strcasecmp(ChrPtr(cs), "us-ascii")) {
876                         ConvertIt = 0;
877                 }
878                 else {
879                         ctdl_iconv_open("UTF-8", ChrPtr(cs), &ic);
880                         if (ic == (iconv_t)(-1) ) {
881                                 lprintf(5, "%s:%d iconv_open(UTF-8, %s) failed: %s\n",
882                                         __FILE__, __LINE__, ChrPtr(Mime->Charset), strerror(errno));
883                         }
884                 }
885         }
886 #endif
887         Line = NewStrBufPlain(NULL, SIZ);
888         Line1 = NewStrBufPlain(NULL, SIZ);
889         Line2 = NewStrBufPlain(NULL, SIZ);
890         Target = NewStrBufPlain(NULL, StrLength(Mime->Data));
891
892         if (StrLength(Mime->Data) > 0) 
893                 do 
894                 {
895                         StrBufSipLine(Line, Mime->Data, &BufPtr);
896                         bq = 0;
897                         i = 0;
898                         ptr = ChrPtr(Line);
899                         len = StrLength(Line);
900                         pte = ptr + len;
901                 
902                         while ((ptr < pte) &&
903                                ((*ptr == '>') ||
904                                 isspace(*ptr)))
905                         {
906                                 if (*ptr == '>')
907                                         bq++;
908                                 ptr ++;
909                                 i++;
910                         }
911                         if (i > 0) StrBufCutLeft(Line, i);
912                 
913                         if (StrLength(Line) == 0) {
914                                 StrBufAppendBufPlain(Target, HKEY("<tt></tt><br />\n"), 0);
915                                 continue;
916                         }
917
918                         for (i = bn; i < bq; i++)                               
919                                 StrBufAppendBufPlain(Target, HKEY("<blockquote>"), 0);
920                         for (i = bq; i < bn; i++)                               
921                                 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
922 #ifdef HAVE_ICONV
923                         if (ConvertIt) {
924                                 StrBufConvert(Line, Line1, &ic);
925                         }
926 #endif
927                         StrBufAppendBufPlain(Target, HKEY("<tt>"), 0);
928                         UrlizeText(Line1, Line, Line2);
929
930                         StrEscAppend(Target, Line1, NULL, 0, 0);
931                         StrBufAppendBufPlain(Target, HKEY("</tt><br />\n"), 0);
932                         bn = bq;
933                 }
934         while ((BufPtr != StrBufNOTNULL) &&
935                (BufPtr != NULL));
936
937         for (i = 0; i < bn; i++)                                
938                 StrBufAppendBufPlain(Target, HKEY("</blockquote>"), 0);
939
940         StrBufAppendBufPlain(Target, HKEY("</i><br />"), 0);
941 #ifdef HAVE_ICONV
942         if (ic != (iconv_t)(-1) ) {
943                 iconv_close(ic);
944         }
945 #endif
946
947         FreeStrBuf(&Mime->Data);
948         Mime->Data = Target;
949         FlushStrBuf(Mime->ContentType);
950         StrBufAppendBufPlain(Mime->ContentType, HKEY("text/html"), 0);
951         FlushStrBuf(Mime->Charset);
952         StrBufAppendBufPlain(Mime->Charset, HKEY("UTF-8"), 0);
953         FreeStrBuf(&Line);
954         FreeStrBuf(&Line1);
955         FreeStrBuf(&Line2);
956 }
957
958 void render_MAIL_html(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
959 {
960         StrBuf *Buf;
961
962         if (StrLength(Mime->Data) == 0)
963                 return;
964
965         Buf = NewStrBufPlain(NULL, StrLength(Mime->Data));
966
967         /* HTML is fun, but we've got to strip it first */
968         output_html(ChrPtr(Mime->Charset), 
969                     (WC->wc_view == VIEW_WIKI ? 1 : 0), 
970                     Mime->msgnum,
971                     Mime->Data, Buf);
972         FreeStrBuf(&Mime->Data);
973         Mime->Data = Buf;
974 }
975
976 void render_MAIL_UNKNOWN(wc_mime_attachment *Mime, StrBuf *RawData, StrBuf *FoundCharset)
977 {
978         /* Unknown weirdness */
979         FlushStrBuf(Mime->Data);
980         StrBufAppendBufPlain(Mime->Data, _("I don't know how to display "), -1, 0);
981         StrBufAppendBuf(Mime->Data, Mime->ContentType, 0);
982         StrBufAppendBufPlain(Mime->Data, HKEY("<br />\n"), 0);
983 }
984
985
986 HashList *iterate_get_mime_All(StrBuf *Target, WCTemplputParams *TP)
987 {
988         message_summary *Msg = (message_summary*) CTX;
989         return Msg->Attachments;
990 }
991 HashList *iterate_get_mime_Submessages(StrBuf *Target, WCTemplputParams *TP)
992 {
993         message_summary *Msg = (message_summary*) CTX;
994         return Msg->Submessages;
995 }
996 HashList *iterate_get_mime_AttachLinks(StrBuf *Target, WCTemplputParams *TP)
997 {
998         message_summary *Msg = (message_summary*) CTX;
999         return Msg->AttachLinks;
1000 }
1001 HashList *iterate_get_mime_Attachments(StrBuf *Target, WCTemplputParams *TP)
1002 {
1003         message_summary *Msg = (message_summary*) CTX;
1004         return Msg->AllAttach;
1005 }
1006
1007 void tmplput_MIME_Name(StrBuf *Target, WCTemplputParams *TP)
1008 {
1009         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1010         StrBufAppendTemplate(Target, TP, mime->Name, 0);
1011 }
1012
1013 void tmplput_MIME_FileName(StrBuf *Target, WCTemplputParams *TP)
1014 {
1015         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1016         StrBufAppendTemplate(Target, TP, mime->FileName, 0);
1017 }
1018
1019 void tmplput_MIME_PartNum(StrBuf *Target, WCTemplputParams *TP)
1020 {
1021         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1022         StrBufAppendTemplate(Target, TP, mime->PartNum, 0);
1023 }
1024
1025 void tmplput_MIME_MsgNum(StrBuf *Target, WCTemplputParams *TP)
1026 {
1027         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1028         StrBufAppendPrintf(Target, "%ld", mime->msgnum);
1029 }
1030
1031 void tmplput_MIME_Disposition(StrBuf *Target, WCTemplputParams *TP)
1032 {
1033         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1034         StrBufAppendTemplate(Target, TP, mime->Disposition, 0);
1035 }
1036
1037 void tmplput_MIME_ContentType(StrBuf *Target, WCTemplputParams *TP)
1038 {
1039         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1040         StrBufAppendTemplate(Target, TP, mime->ContentType, 0);
1041 }
1042
1043 void examine_charset(message_summary *Msg, StrBuf *HdrLine, StrBuf *FoundCharset)
1044 {
1045         Msg->MsgBody->Charset = NewStrBufDup(HdrLine);
1046 }
1047
1048 void tmplput_MIME_Charset(StrBuf *Target, WCTemplputParams *TP)
1049 {
1050         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1051         StrBufAppendTemplate(Target, TP, mime->Charset, 0);
1052 }
1053
1054 void tmplput_MIME_Data(StrBuf *Target, WCTemplputParams *TP)
1055 {
1056         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1057         if (mime->Renderer != NULL)
1058                 mime->Renderer->f(mime, NULL, NULL);
1059         StrBufAppendTemplate(Target, TP, mime->Data, 0);
1060         /* TODO: check whether we need to load it now? */
1061 }
1062
1063 void tmplput_MIME_LoadData(StrBuf *Target, WCTemplputParams *TP)
1064 {
1065         wcsession *WCC = WC;    
1066         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1067         wc_mime_attachment *att;
1068         
1069         if ( (!strcasecmp(ChrPtr(mime->Disposition), "inline"))||
1070              (!strcasecmp(ChrPtr(mime->Disposition), "attachment")) ) 
1071         {
1072                 
1073                 int n;
1074                 char N[64];
1075                 /* steal this mime part... */
1076                 att = malloc(sizeof(wc_mime_attachment));
1077                 memcpy(att, mime, sizeof(wc_mime_attachment));
1078                 memset(mime, 0, sizeof(wc_mime_attachment));
1079
1080                 if (att->Data == NULL) 
1081                         MimeLoadData(att);
1082
1083                 if (WCC->attachments == NULL)
1084                         WCC->attachments = NewHash(1, NULL);
1085                 /* And add it to the list. */
1086                 n = snprintf(N, sizeof N, "%d", GetCount(WCC->attachments) + 1);
1087                 Put(WCC->attachments, N, n, att, DestroyMime);
1088         }
1089 }
1090
1091 void tmplput_MIME_Length(StrBuf *Target, WCTemplputParams *TP)
1092 {
1093         wc_mime_attachment *mime = (wc_mime_attachment*) CTX;
1094         StrBufAppendPrintf(Target, "%ld", mime->length);
1095 }
1096
1097 /* startmsg is an index within the message list.
1098  * starting_from is the Citadel message number to be supplied to a "MSGS GT" operation
1099  */
1100 long DrawMessageDropdown(StrBuf *Selector, long maxmsgs, long startmsg, int nMessages, long starting_from)
1101 {
1102         StrBuf *TmpBuf;
1103         wcsession *WCC = WC;
1104         void *vMsg;
1105         int lo, hi;
1106         long ret;
1107         long hklen;
1108         const char *key;
1109         int nItems;
1110         HashPos *At;
1111         long vector[16];
1112         WCTemplputParams SubTP;
1113         int wantmore = 1;
1114
1115         memset(&SubTP, 0, sizeof(WCTemplputParams));
1116         SubTP.Filter.ContextType = CTX_LONGVECTOR;
1117         SubTP.Context = &vector;
1118         TmpBuf = NewStrBufPlain(NULL, SIZ);
1119         At = GetNewHashPos(WCC->summ, nMessages);
1120         nItems = GetCount(WCC->summ);
1121         ret = nMessages;
1122         vector[0] = 7;
1123         vector[2] = 1;
1124         vector[1] = startmsg;
1125         vector[3] = 0;
1126         vector[7] = starting_from;
1127
1128         while (wantmore)
1129         {
1130         
1131                 vector[3] = abs(nMessages);
1132                 lo = GetHashPosCounter(WCC->summ, At);
1133                 wantmore = GetNextHashPos(WCC->summ, At, &hklen, &key, &vMsg);
1134                 if (!wantmore)
1135                         break;
1136                 if (nMessages > 0) {
1137                         if (lo + nMessages >= nItems) {
1138                                 hi = nItems - 1;
1139                                 vector[3] = nItems - lo;
1140                                 if (startmsg == lo) 
1141                                         ret = vector[3];
1142                         }
1143                         else {
1144                                 hi = lo + nMessages - 1;
1145                         }
1146                 } else {
1147                         if (lo + nMessages < -1) {
1148                                 hi = 0;
1149                         }
1150                         else {
1151                                 if ((lo % abs(nMessages)) != 0) {
1152                                         int offset = (lo % abs(nMessages) *
1153                                                       (nMessages / abs(nMessages)));
1154                                         hi = lo + offset;
1155                                         vector[3] = abs(offset);
1156                                         if (startmsg == lo)
1157                                                  ret = offset;
1158                                 }
1159                                 else
1160                                         hi = lo + nMessages;
1161                         }
1162                 }
1163                 
1164                 /*
1165                  * Bump these because although we're thinking in zero base, the user
1166                  * is a drooling idiot and is thinking in one base.
1167                  */
1168                 vector[4] = lo + 1;
1169                 vector[5] = hi + 1;
1170                 vector[6] = lo;
1171                 FlushStrBuf(TmpBuf);
1172                 dbg_print_longvector(vector);
1173                 DoTemplate(HKEY("select_messageindex"), TmpBuf, &SubTP);
1174                 StrBufAppendBuf(Selector, TmpBuf, 0);
1175         }
1176         vector[6] = 0;
1177         FlushStrBuf(TmpBuf);
1178         if (maxmsgs == 9999999) {
1179                 vector[1] = 1;
1180                 ret = maxmsgs;
1181         }
1182         else
1183                 vector[1] = 0;          
1184         vector[2] = 0;
1185         dbg_print_longvector(vector);
1186         DoTemplate(HKEY("select_messageindex_all"), TmpBuf, &SubTP);
1187         StrBufAppendBuf(Selector, TmpBuf, 0);
1188         FreeStrBuf(&TmpBuf);
1189         DeleteHashPos(&At);
1190         return ret;
1191 }
1192
1193 HashList *iterate_get_registered_Attachments(StrBuf *Target, WCTemplputParams *TP)
1194 {
1195         return WC->attachments;
1196 }
1197
1198 void servcmd_do_search(char *buf, long bufsize)
1199 {
1200         snprintf(buf, bufsize, "MSGS SEARCH|%s", bstr("query"));
1201 }
1202
1203 void servcmd_headers(char *buf, long bufsize)
1204 {
1205         snprintf(buf, bufsize, "MSGS ALL");
1206 }
1207
1208 void servcmd_readfwd(char *buf, long bufsize)
1209 {
1210         snprintf(buf, bufsize, "MSGS ALL");
1211 }
1212
1213 void servcmd_readgt(char *buf, long bufsize)
1214 {
1215         snprintf(buf, bufsize, "MSGS GT|%s", bstr("gt"));
1216 }
1217
1218 void servcmd_readnew(char *buf, long bufsize)
1219 {
1220         snprintf(buf, bufsize, "MSGS NEW");
1221 }
1222
1223 void servcmd_readold(char *buf, long bufsize)
1224 {
1225         snprintf(buf, bufsize, "MSGS OLD");
1226 }
1227
1228
1229 readloop_struct rlid[] = {
1230         { {HKEY("do_search")}, servcmd_do_search},
1231         { {HKEY("headers")},   servcmd_headers},
1232         { {HKEY("readfwd")},   servcmd_readfwd},
1233         { {HKEY("readnew")},   servcmd_readnew},
1234         { {HKEY("readold")},   servcmd_readold},
1235         { {HKEY("readgt")},    servcmd_readgt}
1236 };
1237
1238 /* Spit out the new summary view. This is basically a static page, so clients can cache the layout, all the dirty work is javascript :) */
1239 void new_summary_view(void) {
1240         DoTemplate(HKEY("msg_listview"),NULL,&NoCtx);
1241 }
1242
1243
1244 int mailview_GetParamsGetServerCall(SharedMessageStatus *Stat, 
1245                                     void **ViewSpecific, 
1246                                     long oper, 
1247                                     char *cmd, 
1248                                     long len)
1249 {
1250         if (!WC->is_ajax) {
1251                 new_summary_view();
1252                 return 200;
1253         } else {
1254                 Stat->defaultsortorder = 2;
1255                 Stat->sortit = 1;
1256                 Stat->load_seen = 1;
1257                 /* Generally using maxmsgs|startmsg is not required
1258                    in mailbox view, but we have a 'safemode' for clients
1259                    (*cough* Exploder) that simply can't handle too many */
1260                 if (havebstr("maxmsgs"))  Stat->maxmsgs  = ibstr("maxmsgs");
1261                 else                      Stat->maxmsgs  = 9999999;
1262                 if (havebstr("startmsg")) Stat->startmsg = lbstr("startmsg");
1263                 snprintf(cmd, len, "MSGS %s|%s||1",
1264                          (oper == do_search) ? "SEARCH" : "ALL",
1265                          (oper == do_search) ? bstr("query") : ""
1266                         );
1267         }
1268         return 200;
1269 }
1270
1271 int mailview_RenderView_or_Tail(SharedMessageStatus *Stat, 
1272                                 void **ViewSpecific, 
1273                                 long oper)
1274 {
1275         WCTemplputParams SubTP;
1276
1277         if (WC->is_ajax)
1278                 DoTemplate(HKEY("mailsummary_json"),NULL, &SubTP);
1279         
1280         return 0;
1281 }
1282
1283 int mailview_Cleanup(void **ViewSpecific)
1284 {
1285         /* Note: wDumpContent() will output one additional </div> tag. */
1286         /* We ought to move this out into template */
1287         if (WC->is_ajax)
1288                 end_burst();
1289         else
1290                 wDumpContent(1);
1291
1292         return 0;
1293 }
1294
1295
1296
1297 typedef struct _bbsview_stuct {
1298         StrBuf *BBViewToolBar;
1299         StrBuf *MessageDropdown;
1300         long *displayed_msgs;
1301         int a;
1302 }bbsview_struct;
1303
1304 int bbsview_GetParamsGetServerCall(SharedMessageStatus *Stat, 
1305                                    void **ViewSpecific, 
1306                                    long oper, 
1307                                    char *cmd, 
1308                                    long len)
1309 {
1310         bbsview_struct *VS;
1311
1312         VS = (bbsview_struct*) malloc(sizeof(bbsview_struct));
1313         memset(VS, 0, sizeof(bbsview_struct));
1314         *ViewSpecific = (void*)VS;
1315         Stat->defaultsortorder = 1;
1316         Stat->startmsg = -1;
1317         Stat->sortit = 1;
1318         
1319         rlid[oper].cmd(cmd, len);
1320         
1321         if (havebstr("maxmsgs"))
1322                 Stat->maxmsgs = ibstr("maxmsgs");
1323         if (Stat->maxmsgs == 0) Stat->maxmsgs = DEFAULT_MAXMSGS;
1324         
1325         if (havebstr("startmsg")) {
1326                 Stat->startmsg = lbstr("startmsg");
1327         }
1328         if (lbstr("SortOrder") == 2) {
1329                 Stat->reverse = 1;
1330                 Stat->num_displayed = -DEFAULT_MAXMSGS;
1331         }
1332         else {
1333                 Stat->reverse = 0;
1334                 Stat->num_displayed = DEFAULT_MAXMSGS;
1335         }
1336
1337         return 200;
1338 }
1339
1340 int bbsview_PrintViewHeader(SharedMessageStatus *Stat, void **ViewSpecific)
1341 {
1342         bbsview_struct *VS;
1343         WCTemplputParams SubTP;
1344
1345         VS = (bbsview_struct*)*ViewSpecific;
1346
1347         VS->BBViewToolBar = NewStrBufPlain(NULL, SIZ);
1348         VS->MessageDropdown = NewStrBufPlain(NULL, SIZ);
1349
1350         /*** startmsg->maxmsgs = **/DrawMessageDropdown(VS->MessageDropdown, 
1351                                                         Stat->maxmsgs, 
1352                                                         Stat->startmsg,
1353                                                         Stat->num_displayed, 
1354                                                         Stat->lowest_found-1);
1355         if (Stat->num_displayed < 0) {
1356                 Stat->startmsg += Stat->maxmsgs;
1357                 if (Stat->num_displayed != Stat->maxmsgs)                               
1358                         Stat->maxmsgs = abs(Stat->maxmsgs) + 1;
1359                 else
1360                         Stat->maxmsgs = abs(Stat->maxmsgs);
1361
1362         }
1363         if (Stat->nummsgs > 0) {
1364                 memset(&SubTP, 0, sizeof(WCTemplputParams));
1365                 SubTP.Filter.ContextType = CTX_STRBUF;
1366                 SubTP.Context = VS->MessageDropdown;
1367                 DoTemplate(HKEY("msg_listselector_top"), VS->BBViewToolBar, &SubTP);
1368                 StrBufAppendBuf(WC->WBuf, VS->BBViewToolBar, 0);
1369                 FlushStrBuf(VS->BBViewToolBar);
1370         }
1371         return 200;
1372 }
1373
1374 int bbsview_LoadMsgFromServer(SharedMessageStatus *Stat, 
1375                               void **ViewSpecific, 
1376                               message_summary* Msg, 
1377                               int is_new, 
1378                               int i)
1379 {
1380         bbsview_struct *VS;
1381
1382         VS = (bbsview_struct*)*ViewSpecific;
1383         if (VS->displayed_msgs == NULL) {
1384                 VS->displayed_msgs = malloc(sizeof(long) *
1385                                             ((Stat->maxmsgs < Stat->nummsgs) ? 
1386                                              Stat->maxmsgs + 1 : 
1387                                              Stat->nummsgs + 1));
1388         }
1389         if ((i >= Stat->startmsg) && (i < Stat->startmsg + Stat->maxmsgs)) {
1390                 VS->displayed_msgs[Stat->num_displayed] = Msg->msgnum;
1391                 Stat->num_displayed++;
1392         }
1393         return 200;
1394 }
1395
1396
1397 int bbsview_RenderView_or_Tail(SharedMessageStatus *Stat, 
1398                                void **ViewSpecific, 
1399                                long oper)
1400 {
1401         wcsession *WCC = WC;
1402         bbsview_struct *VS;
1403         WCTemplputParams SubTP;
1404         const StrBuf *Mime;
1405
1406         VS = (bbsview_struct*)*ViewSpecific;
1407         if (Stat->nummsgs == 0) {
1408                 wprintf("<div class=\"nomsgs\"><br><em>");
1409                 switch (oper) {
1410                 case readnew:
1411                         wprintf(_("No new messages."));
1412                         break;
1413                 case readold:
1414                         wprintf(_("No old messages."));
1415                         break;
1416                 default:
1417                         wprintf(_("No messages here."));
1418                 }
1419                 wprintf("</em><br></div>\n");
1420         }
1421         else 
1422         {
1423                 if (VS->displayed_msgs != NULL) {
1424                         /* if we do a split bbview in the future, begin messages div here */
1425                         int a;/// todo  
1426                         for (a=0; a < Stat->num_displayed; ++a) {
1427                                 read_message(WCC->WBuf, HKEY("view_message"), VS->displayed_msgs[a], NULL, &Mime);
1428                         }
1429                         
1430                         /* if we do a split bbview in the future, end messages div here */
1431                         
1432                         free(VS->displayed_msgs);
1433                         VS->displayed_msgs = NULL;
1434                 }
1435                 memset(&SubTP, 0, sizeof(WCTemplputParams));
1436                 SubTP.Filter.ContextType = CTX_STRBUF;
1437                 SubTP.Context = VS->MessageDropdown;
1438                 DoTemplate(HKEY("msg_listselector_bottom"), VS->BBViewToolBar, &SubTP);
1439                 StrBufAppendBuf(WCC->WBuf, VS->BBViewToolBar, 0);
1440         }
1441         return 0;
1442
1443 }
1444
1445
1446 int bbsview_Cleanup(void **ViewSpecific)
1447 {
1448         bbsview_struct *VS;
1449
1450         VS = (bbsview_struct*)*ViewSpecific;
1451         end_burst();
1452         FreeStrBuf(&VS->BBViewToolBar);
1453         FreeStrBuf(&VS->MessageDropdown);
1454         free(VS);
1455         return 0;
1456 }
1457
1458 void 
1459 InitModule_MSGRENDERERS
1460 (void)
1461 {
1462         RegisterReadLoopHandlerset(
1463                 VIEW_MAILBOX,
1464                 mailview_GetParamsGetServerCall,
1465                 NULL, /// TODO: is this right?
1466                 NULL, //// ""
1467                 mailview_RenderView_or_Tail,
1468                 mailview_Cleanup);
1469
1470         RegisterReadLoopHandlerset(
1471                 VIEW_BBS,
1472                 bbsview_GetParamsGetServerCall,
1473                 bbsview_PrintViewHeader,
1474                 bbsview_LoadMsgFromServer,
1475                 bbsview_RenderView_or_Tail,
1476                 bbsview_Cleanup);
1477
1478         RegisterSortFunc(HKEY("date"), 
1479                          NULL, 0,
1480                          summcmp_date,
1481                          summcmp_rdate,
1482                          groupchange_date,
1483                          CTX_MAILSUM);
1484         RegisterSortFunc(HKEY("subject"), 
1485                          NULL, 0,
1486                          summcmp_subj,
1487                          summcmp_rsubj,
1488                          groupchange_subj,
1489                          CTX_MAILSUM);
1490         RegisterSortFunc(HKEY("sender"),
1491                          NULL, 0,
1492                          summcmp_sender,
1493                          summcmp_rsender,
1494                          groupchange_sender,
1495                          CTX_MAILSUM);
1496
1497         RegisterNamespace("SUMM:COUNT", 0, 0, tmplput_SUMM_COUNT, CTX_NONE);
1498         /* iterate over all known mails in WC->summ */
1499         RegisterIterator("MAIL:SUMM:MSGS", 0, NULL, iterate_get_mailsumm_All,
1500                          NULL,NULL, CTX_MAILSUM, CTX_NONE, IT_NOFLAG);
1501
1502         RegisterNamespace("MAIL:SUMM:DATEBRIEF", 0, 0, tmplput_MAIL_SUMM_DATE_BRIEF, CTX_MAILSUM);
1503         RegisterNamespace("MAIL:SUMM:DATEFULL", 0, 0, tmplput_MAIL_SUMM_DATE_FULL, CTX_MAILSUM);
1504         RegisterNamespace("MAIL:SUMM:DATENO",  0, 0, tmplput_MAIL_SUMM_DATE_NO,  CTX_MAILSUM);
1505         RegisterNamespace("MAIL:SUMM:N",       0, 0, tmplput_MAIL_SUMM_N,        CTX_MAILSUM);
1506         RegisterNamespace("MAIL:SUMM:FROM",    0, 2, tmplput_MAIL_SUMM_FROM,     CTX_MAILSUM);
1507         RegisterNamespace("MAIL:SUMM:TO",      0, 2, tmplput_MAIL_SUMM_TO,       CTX_MAILSUM);
1508         RegisterNamespace("MAIL:SUMM:SUBJECT", 0, 4, tmplput_MAIL_SUMM_SUBJECT,  CTX_MAILSUM);
1509         RegisterNamespace("MAIL:SUMM:NTATACH", 0, 0, tmplput_MAIL_SUMM_NATTACH,  CTX_MAILSUM);
1510         RegisterNamespace("MAIL:SUMM:CCCC", 0, 2, tmplput_MAIL_SUMM_CCCC,  CTX_MAILSUM);
1511         RegisterNamespace("MAIL:SUMM:H_NODE", 0, 2, tmplput_MAIL_SUMM_H_NODE,  CTX_MAILSUM);
1512         RegisterNamespace("MAIL:SUMM:ALLRCPT", 0, 2, tmplput_MAIL_SUMM_ALLRCPT,  CTX_MAILSUM);
1513         RegisterNamespace("MAIL:SUMM:ORGROOM", 0, 2, tmplput_MAIL_SUMM_ORGROOM,  CTX_MAILSUM);
1514         RegisterNamespace("MAIL:SUMM:RFCA", 0, 2, tmplput_MAIL_SUMM_RFCA,  CTX_MAILSUM);
1515         RegisterNamespace("MAIL:SUMM:OTHERNODE", 2, 0, tmplput_MAIL_SUMM_OTHERNODE,  CTX_MAILSUM);
1516         RegisterNamespace("MAIL:SUMM:REFIDS", 0, 1, tmplput_MAIL_SUMM_REFIDS,  CTX_MAILSUM);
1517         RegisterNamespace("MAIL:SUMM:INREPLYTO", 0, 2, tmplput_MAIL_SUMM_INREPLYTO,  CTX_MAILSUM);
1518         RegisterNamespace("MAIL:BODY", 0, 2, tmplput_MAIL_BODY,  CTX_MAILSUM);
1519         RegisterNamespace("MAIL:QUOTETEXT", 1, 2, tmplput_QUOTED_MAIL_BODY,  CTX_NONE);
1520         RegisterNamespace("MAIL:EDITTEXT", 1, 2, tmplput_EDIT_MAIL_BODY,  CTX_NONE);
1521         RegisterNamespace("MAIL:EDITWIKI", 1, 2, tmplput_EDIT_WIKI_BODY,  CTX_NONE);
1522         RegisterConditional(HKEY("COND:MAIL:SUMM:RFCA"), 0, Conditional_MAIL_SUMM_RFCA,  CTX_MAILSUM);
1523         RegisterConditional(HKEY("COND:MAIL:SUMM:CCCC"), 0, Conditional_MAIL_SUMM_CCCC,  CTX_MAILSUM);
1524         RegisterConditional(HKEY("COND:MAIL:SUMM:UNREAD"), 0, Conditional_MAIL_SUMM_UNREAD, CTX_MAILSUM);
1525         RegisterConditional(HKEY("COND:MAIL:SUMM:H_NODE"), 0, Conditional_MAIL_SUMM_H_NODE, CTX_MAILSUM);
1526         RegisterConditional(HKEY("COND:MAIL:SUMM:OTHERNODE"), 0, Conditional_MAIL_SUMM_OTHERNODE, CTX_MAILSUM);
1527         RegisterConditional(HKEY("COND:MAIL:SUMM:SUBJECT"), 0, Conditional_MAIL_SUMM_SUBJECT, CTX_MAILSUM);
1528         RegisterConditional(HKEY("COND:MAIL:ANON"), 0, Conditional_ANONYMOUS_MESSAGE, CTX_MAILSUM);
1529         RegisterConditional(HKEY("COND:MAIL:TO"), 0, Conditional_MAIL_SUMM_TO, CTX_MAILSUM);    
1530         RegisterConditional(HKEY("COND:MAIL:SUBJ"), 0, Conditional_MAIL_SUMM_SUBJ, CTX_MAILSUM);        
1531
1532         /* do we have mimetypes to iterate over? */
1533         RegisterConditional(HKEY("COND:MAIL:MIME:ATTACH"), 0, Conditional_MAIL_MIME_ALL, CTX_MAILSUM);
1534         RegisterConditional(HKEY("COND:MAIL:MIME:ATTACH:SUBMESSAGES"), 0, Conditional_MAIL_MIME_SUBMESSAGES, CTX_MAILSUM);
1535         RegisterConditional(HKEY("COND:MAIL:MIME:ATTACH:LINKS"), 0, Conditional_MAIL_MIME_ATTACHLINKS, CTX_MAILSUM);
1536         RegisterConditional(HKEY("COND:MAIL:MIME:ATTACH:ATT"), 0, Conditional_MAIL_MIME_ATTACH, CTX_MAILSUM);
1537         RegisterIterator("MAIL:MIME:ATTACH", 0, NULL, iterate_get_mime_All, 
1538                          NULL, NULL, CTX_MIME_ATACH, CTX_MAILSUM, IT_NOFLAG);
1539         RegisterIterator("MAIL:MIME:ATTACH:SUBMESSAGES", 0, NULL, iterate_get_mime_Submessages, 
1540                          NULL, NULL, CTX_MIME_ATACH, CTX_MAILSUM, IT_NOFLAG);
1541         RegisterIterator("MAIL:MIME:ATTACH:LINKS", 0, NULL, iterate_get_mime_AttachLinks, 
1542                          NULL, NULL, CTX_MIME_ATACH, CTX_MAILSUM, IT_NOFLAG);
1543         RegisterIterator("MAIL:MIME:ATTACH:ATT", 0, NULL, iterate_get_mime_Attachments, 
1544                          NULL, NULL, CTX_MIME_ATACH, CTX_MAILSUM, IT_NOFLAG);
1545
1546         /* Parts of a mime attachent */
1547         RegisterNamespace("MAIL:MIME:NAME", 0, 2, tmplput_MIME_Name, CTX_MIME_ATACH);
1548         RegisterNamespace("MAIL:MIME:FILENAME", 0, 2, tmplput_MIME_FileName, CTX_MIME_ATACH);
1549         RegisterNamespace("MAIL:MIME:PARTNUM", 0, 2, tmplput_MIME_PartNum, CTX_MIME_ATACH);
1550         RegisterNamespace("MAIL:MIME:MSGNUM", 0, 2, tmplput_MIME_MsgNum, CTX_MIME_ATACH);
1551         RegisterNamespace("MAIL:MIME:DISPOSITION", 0, 2, tmplput_MIME_Disposition, CTX_MIME_ATACH);
1552         RegisterNamespace("MAIL:MIME:CONTENTTYPE", 0, 2, tmplput_MIME_ContentType, CTX_MIME_ATACH);
1553         RegisterNamespace("MAIL:MIME:CHARSET", 0, 2, tmplput_MIME_Charset, CTX_MIME_ATACH);
1554         RegisterNamespace("MAIL:MIME:LENGTH", 0, 2, tmplput_MIME_Length, CTX_MIME_ATACH);
1555         RegisterNamespace("MAIL:MIME:DATA", 0, 2, tmplput_MIME_Data, CTX_MIME_ATACH);
1556         /* load the actual attachment into WC->attachments; no output!!! */
1557         RegisterNamespace("MAIL:MIME:LOADDATA", 0, 0, tmplput_MIME_LoadData, CTX_MIME_ATACH);
1558
1559         /* iterate the WC->attachments; use the above tokens for their contents */
1560         RegisterIterator("MSG:ATTACHNAMES", 0, NULL, iterate_get_registered_Attachments, 
1561                          NULL, NULL, CTX_MIME_ATACH, CTX_NONE, IT_NOFLAG);
1562
1563         /* mime renderers translate an attachment into webcit viewable html text */
1564         RegisterMimeRenderer(HKEY("message/rfc822"), render_MAIL, 1, 150);
1565         RegisterMimeRenderer(HKEY("text/vnote"), render_MIME_VNote, 1, 300);
1566         RegisterMimeRenderer(HKEY("text/x-vcard"), render_MIME_VCard, 1, 201);
1567         RegisterMimeRenderer(HKEY("text/vcard"), render_MIME_VCard, 1, 200);
1568         RegisterMimeRenderer(HKEY("text/calendar"), render_MIME_ICS, 1, 501);
1569         RegisterMimeRenderer(HKEY("application/ics"), render_MIME_ICS, 1, 500);
1570         RegisterMimeRenderer(HKEY("text/x-citadel-variformat"), render_MAIL_variformat, 1, 2);
1571         RegisterMimeRenderer(HKEY("text/plain"), render_MAIL_text_plain, 1, 3);
1572         RegisterMimeRenderer(HKEY("text"), render_MAIL_text_plain, 1, 1);
1573         RegisterMimeRenderer(HKEY("text/html"), render_MAIL_html, 1, 100);
1574         RegisterMimeRenderer(HKEY(""), render_MAIL_UNKNOWN, 0, 0);
1575         /* and finalize the anouncement to the server... */
1576         CreateMimeStr();
1577
1578         /* these headers are citserver replies to MSG4 and friends. one evaluator for each */
1579         RegisterMsgHdr(HKEY("nhdr"), examine_nhdr, 0);
1580         RegisterMsgHdr(HKEY("type"), examine_type, 0);
1581         RegisterMsgHdr(HKEY("from"), examine_from, 0);
1582         RegisterMsgHdr(HKEY("subj"), examine_subj, 0);
1583         RegisterMsgHdr(HKEY("msgn"), examine_msgn, 0);
1584         RegisterMsgHdr(HKEY("wefw"), examine_wefw, 0);
1585         RegisterMsgHdr(HKEY("cccc"), examine_cccc, 0);
1586         RegisterMsgHdr(HKEY("hnod"), examine_hnod, 0);
1587         RegisterMsgHdr(HKEY("room"), examine_room, 0);
1588         RegisterMsgHdr(HKEY("rfca"), examine_rfca, 0);
1589         RegisterMsgHdr(HKEY("node"), examine_node, 0);
1590         RegisterMsgHdr(HKEY("rcpt"), examine_rcpt, 0);
1591         RegisterMsgHdr(HKEY("time"), examine_time, 0);
1592         RegisterMsgHdr(HKEY("part"), examine_mime_part, 0);
1593         RegisterMsgHdr(HKEY("text"), examine_text, 1);
1594         /* these are the content-type headers we get infront of a message; put it into the same hash since it doesn't clash. */
1595         RegisterMsgHdr(HKEY("X-Citadel-MSG4-Partnum"), examine_msg4_partnum, 0);
1596         RegisterMsgHdr(HKEY("Content-type"), examine_content_type, 0);
1597         RegisterMsgHdr(HKEY("Content-length"), examine_content_lengh, 0);
1598         RegisterMsgHdr(HKEY("Content-transfer-encoding"), examine_content_encoding, 0); /* do we care? */
1599         RegisterMsgHdr(HKEY("charset"), examine_charset, 0);
1600
1601         /* Don't care about these... */
1602         RegisterMsgHdr(HKEY("pref"), examine_pref, 0);
1603         RegisterMsgHdr(HKEY("suff"), examine_suff, 0);
1604         RegisterMsgHdr(HKEY("path"), examine_path, 0);
1605 }
1606
1607 void 
1608 ServerStartModule_MSGRENDERERS
1609 (void)
1610 {
1611         MsgHeaderHandler = NewHash(1, NULL);
1612         MimeRenderHandler = NewHash(1, NULL);
1613         ReadLoopHandler = NewHash(1, NULL);
1614 }
1615
1616 void 
1617 ServerShutdownModule_MSGRENDERERS
1618 (void)
1619 {
1620         DeleteHash(&MsgHeaderHandler);
1621         DeleteHash(&MimeRenderHandler);
1622         DeleteHash(&ReadLoopHandler);
1623 }
1624
1625
1626
1627 void 
1628 SessionDestroyModule_MSGRENDERERS
1629 (wcsession *sess)
1630 {
1631         DeleteHash(&sess->attachments);
1632 }