4 * Functions which deal with the fetching and displaying of messages.
12 HashList
*MsgHeaderHandler
= NULL
;
13 HashList
*MsgEvaluators
= NULL
;
14 HashList
*MimeRenderHandler
= NULL
;
15 int dbg_analyze_msg
= 0;
17 #define SUBJ_COL_WIDTH_PCT 50 /**< Mailbox view column width */
18 #define SENDER_COL_WIDTH_PCT 30 /**< Mailbox view column width */
19 #define DATE_PLUS_BUTTONS_WIDTH_PCT 20 /**< Mailbox view column width */
21 void jsonMessageListHdr(void);
23 void display_enter(void);
25 /*----------------------------------------------------------------------------*/
28 typedef void (*MsgPartEvaluatorFunc
)(message_summary
*Sum
, StrBuf
*Buf
);
30 typedef struct _MsgPartEvaluatorStruct
{
31 MsgPartEvaluatorFunc f
;
32 }MsgPartEvaluatorStruct
;
35 /*----------------------------------------------------------------------------*/
40 * I wanna SEE that message!
42 * msgnum Message number to display
43 * printable_view Nonzero to display a printable view
44 * section Optional for encapsulated message/rfc822 submessage
46 int read_message(StrBuf
*Target
, const char *tmpl
, long tmpllen
, long msgnum
, int printable_view
, const StrBuf
*PartNum
) {
52 message_summary
*Msg
= NULL
;
60 WCTemplputParams SubTP
;
63 lprintf(1, "----------%s---------MSG4 %ld|%s--------------\n", tmpl
, msgnum
, ChrPtr(PartNum
));
64 serv_printf("MSG4 %ld|%s", msgnum
, ChrPtr(PartNum
));
65 StrBuf_ServGetln(Buf
);
66 if (GetServerStatus(Buf
, NULL
) != 1) {
67 StrBufAppendPrintf(Target
, "<strong>");
68 StrBufAppendPrintf(Target
, _("ERROR:"));
69 StrBufAppendPrintf(Target
, "</strong> %s<br />\n", &buf
[4]);
74 /** begin everythingamundo table */
77 HdrToken
= NewStrBuf();
78 Msg
= (message_summary
*)malloc(sizeof(message_summary
));
79 memset(Msg
, 0, sizeof(message_summary
));
81 Msg
->PartNum
= PartNum
;
82 Msg
->MsgBody
= (wc_mime_attachment
*) malloc(sizeof(wc_mime_attachment
));
83 memset(Msg
->MsgBody
, 0, sizeof(wc_mime_attachment
));
84 Msg
->MsgBody
->msgnum
= msgnum
;
85 FoundCharset
= NewStrBuf();
86 while ((StrBuf_ServGetln(Buf
)>=0) && !Done
) {
87 if ( (StrLength(Buf
)==3) &&
88 !strcmp(ChrPtr(Buf
), "000"))
92 lprintf(1, _("unexpected end of message"));
94 Msg
->MsgBody
->ContentType
= NewStrBufPlain(HKEY("text/html"));
95 StrBufAppendPrintf(Msg
->MsgBody
->Data
, "<div><i>");
96 StrBufAppendPrintf(Msg
->MsgBody
->Data
, _("unexpected end of message"));
97 StrBufAppendPrintf(Msg
->MsgBody
->Data
, " (1)</i><br /><br />\n");
98 StrBufAppendPrintf(Msg
->MsgBody
->Data
, "</div>\n");
103 case 0:/* Citadel Message Headers */
104 if (StrLength(Buf
) == 0) {
108 StrBufExtract_token(HdrToken
, Buf
, 0, '=');
109 StrBufCutLeft(Buf
, StrLength(HdrToken
) + 1);
112 if (dbg_analyze_msg
) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken
), ChrPtr(Buf
));
114 /* look up one of the examine_* functions to parse the content */
115 if (GetHash(MsgHeaderHandler
, SKEY(HdrToken
), &vHdr
) &&
117 Hdr
= (headereval
*)vHdr
;
118 Hdr
->evaluator(Msg
, Buf
, FoundCharset
);
119 if (Hdr
->Type
== 1) {
123 else lprintf(1, "don't know how to handle message header[%s]\n", ChrPtr(HdrToken
));
125 case 1:/* Message Mime Header */
126 if (StrLength(Buf
) == 0) {
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 */
135 StrBufExtract_token(HdrToken
, Buf
, 0, ':');
136 if (StrLength(HdrToken
) > 0) {
137 StrBufCutLeft(Buf
, StrLength(HdrToken
) + 1);
139 if (dbg_analyze_msg
) lprintf(1, ":: [%s] = [%s]\n", ChrPtr(HdrToken
), ChrPtr(Buf
));
141 /* the examine*'s know how to do with mime headers too... */
142 if (GetHash(MsgHeaderHandler
, SKEY(HdrToken
), &vHdr
) &&
144 Hdr
= (headereval
*)vHdr
;
145 Hdr
->evaluator(Msg
, Buf
, FoundCharset
);
150 case 2: /* Message Body */
152 if (Msg
->MsgBody
->size_known
> 0) {
153 StrBuf_ServGetBLOB(Msg
->MsgBody
->Data
, Msg
->MsgBody
->length
);
155 /*/ todo: check next line, if not 000, append following lines */
158 if (StrLength(Msg
->MsgBody
->Data
) > 0)
159 StrBufAppendBufPlain(Msg
->MsgBody
->Data
, "\n", 1, 0);
160 StrBufAppendBuf(Msg
->MsgBody
->Data
, Buf
, 0);
164 StrBufAppendBuf(Msg
->MsgBody
->Data
, Buf
, 0);
169 if (Msg
->AllAttach
== NULL
)
170 Msg
->AllAttach
= NewHash(1,NULL
);
171 /* now we put the body mimepart we read above into the mimelist */
172 Put(Msg
->AllAttach
, SKEY(Msg
->MsgBody
->PartNum
), Msg
->MsgBody
, DestroyMime
);
174 /* strip the bare contenttype, so we ommit charset etc. */
175 StrBufExtract_token(Buf
, Msg
->MsgBody
->ContentType
, 0, ';');
177 /* look up the renderer, that will convert this mimeitem into the htmlized form */
178 if (GetHash(MimeRenderHandler
, SKEY(Buf
), &vHdr
) &&
180 RenderMimeFuncStruct
*Render
;
181 Render
= (RenderMimeFuncStruct
*)vHdr
;
182 Render
->f(Msg
->MsgBody
, NULL
, FoundCharset
);
185 if (StrLength(Msg
->reply_references
)> 0) {
186 /* Trim down excessively long lists of thread references. We eliminate the
187 * second one in the list so that the thread root remains intact.
189 int rrtok
= num_tokens(ChrPtr(Msg
->reply_references
), '|');
190 int rrlen
= StrLength(Msg
->reply_references
);
191 if ( ((rrtok
>= 3) && (rrlen
> 900)) || (rrtok
> 10) ) {
192 StrBufRemove_token(Msg
->reply_references
, 1, '|');
196 /* Generate a reply-to address */
197 if (StrLength(Msg
->Rfca
) > 0) {
198 if (Msg
->reply_to
== NULL
)
199 Msg
->reply_to
= NewStrBuf();
200 if (StrLength(Msg
->from
) > 0) {
201 StrBufPrintf(Msg
->reply_to
, "%s <%s>", ChrPtr(Msg
->from
), ChrPtr(Msg
->Rfca
));
204 FlushStrBuf(Msg
->reply_to
);
205 StrBufAppendBuf(Msg
->reply_to
, Msg
->Rfca
, 0);
210 if ((StrLength(Msg
->OtherNode
)>0) &&
211 (strcasecmp(ChrPtr(Msg
->OtherNode
), ChrPtr(serv_info
.serv_nodename
))) &&
212 (strcasecmp(ChrPtr(Msg
->OtherNode
), ChrPtr(serv_info
.serv_humannode
)) ))
214 if (Msg
->reply_to
== NULL
)
215 Msg
->reply_to
= NewStrBuf();
216 StrBufPrintf(Msg
->reply_to
,
219 ChrPtr(Msg
->OtherNode
));
222 if (Msg
->reply_to
== NULL
)
223 Msg
->reply_to
= NewStrBuf();
224 FlushStrBuf(Msg
->reply_to
);
225 StrBufAppendBuf(Msg
->reply_to
, Msg
->from
, 0);
229 /* now check if we need to translate some mimeparts, and remove the duplicate */
230 it
= GetNewHashPos(Msg
->AllAttach
, 0);
231 while (GetNextHashPos(Msg
->AllAttach
, it
, &len
, &Key
, &vMime
) &&
233 wc_mime_attachment
*Mime
= (wc_mime_attachment
*) vMime
;
234 evaluate_mime_part(Msg
, Mime
);
237 memset(&SubTP
, 0, sizeof(WCTemplputParams
));
238 SubTP
.Filter
.ContextType
= CTX_MAILSUM
;
240 DoTemplate(tmpl
, tmpllen
, Target
, &SubTP
);
242 DestroyMessageSummary(Msg
);
243 FreeStrBuf(&FoundCharset
);
244 FreeStrBuf(&HdrToken
);
252 * Unadorned HTML output of an individual message, suitable
253 * for placing in a hidden iframe, for printing, or whatever
255 * msgnum_as_string == Message number, as a string instead of as a long int
257 void embed_message(void) {
259 const StrBuf
*Tmpl
= sbstr("template");
261 msgnum
= StrTol(WC
->UrlFragment2
);
262 if (StrLength(Tmpl
) > 0)
263 read_message(WC
->WBuf
, SKEY(Tmpl
), msgnum
, 0, NULL
);
265 read_message(WC
->WBuf
, HKEY("view_message"), msgnum
, 0, NULL
);
270 * Printable view of a message
272 * msgnum_as_string == Message number, as a string instead of as a long int
274 void print_message(void) {
277 msgnum
= StrTol(WC
->UrlFragment2
);
278 output_headers(0, 0, 0, 0, 0, 0);
280 hprintf("Content-type: text/html\r\n"
281 "Server: " PACKAGE_STRING
"\r\n"
282 "Connection: close\r\n");
286 read_message(WC
->WBuf
, HKEY("view_message_print"), msgnum
, 1, NULL
);
292 * Mobile browser view of message
294 * @param msg_num_as_string Message number as a string instead of as a long int
296 void mobile_message_view(void) {
298 msgnum
= StrTol(WC
->UrlFragment2
);
299 output_headers(1, 0, 0, 0, 0, 1);
301 do_template("msgcontrols", NULL
);
302 read_message(WC
->WBuf
, HKEY("view_message"), msgnum
,1, NULL
);
307 * \brief Display a message's headers
309 * \param msgnum_as_string Message number, as a string instead of as a long int
311 void display_headers(void) {
315 msgnum
= StrTol(WC
->UrlFragment2
);
316 output_headers(0, 0, 0, 0, 0, 0);
318 hprintf("Content-type: text/plain\r\n"
320 "Connection: close\r\n",
324 serv_printf("MSG2 %ld|3", msgnum
);
325 serv_getln(buf
, sizeof buf
);
327 while (serv_getln(buf
, sizeof buf
), strcmp(buf
, "000")) {
328 wprintf("%s\n", buf
);
336 message_summary
*ReadOneMessageSummary(StrBuf
*RawMessage
, const char *DefaultSubject
, long MsgNum
)
339 MsgPartEvaluatorStruct
*Eval
;
340 message_summary
*Msg
;
349 serv_printf("MSG0 %ld|1", MsgNum
); /* ask for headers only */
351 StrBuf_ServGetln(Buf
);
352 if (GetServerStatus(Buf
, NULL
) == 1) {
357 Msg
= (message_summary
*)malloc(sizeof(message_summary
));
358 memset(Msg
, 0, sizeof(message_summary
));
359 while (len
= StrBuf_ServGetln(Buf
),
361 strcmp(ChrPtr(Buf
), "000")== 0)){
363 ebuf
= strchr(ChrPtr(Buf
), '=');
365 if (GetHash(MsgEvaluators
, buf
, nBuf
, &vEval
) && vEval
!= NULL
) {
366 Eval
= (MsgPartEvaluatorStruct
*) vEval
;
367 StrBufCutLeft(Buf
, nBuf
+ 1);
370 else lprintf(1, "Don't know how to handle Message Headerline [%s]", ChrPtr(Buf
));
380 * load message pointers from the server for a "read messages" operation
382 * servcmd: the citadel command to send to the citserver
383 * with_headers: also include some of the headers with the message numbers (more expensive)
385 int load_msg_ptrs(char *servcmd
, int with_headers
)
387 StrBuf
* FoundCharset
= NULL
;
389 message_summary
*Msg
;
397 if (WCC
->summ
!= NULL
) {
398 DeleteHash(&WCC
->summ
);
400 WCC
->summ
= NewHash(1, Flathash
);
405 StrBuf_ServGetln(Buf
);
406 if (GetServerStatus(Buf
, NULL
) != 1) {
411 while (len
= StrBuf_ServGetln(Buf
),
413 strcmp(ChrPtr(Buf
), "000")!= 0))
415 if (nummsgs
< maxload
) {
417 Msg
= (message_summary
*)malloc(sizeof(message_summary
));
418 memset(Msg
, 0, sizeof(message_summary
));
420 Msg
->msgnum
= StrBufExtract_long(Buf
, 0, '|');
421 Msg
->date
= StrBufExtract_long(Buf
, 1, '|');
423 * as citserver probably gives us messages in forward date sorting
424 * nummsgs should be the same order as the message date.
426 if (Msg
->date
== 0) {
428 if (StrLength(Buf
) < 32)
432 Msg
->from
= NewStrBufPlain(NULL
, StrLength(Buf
));
433 StrBufExtract_token(Buf2
, Buf
, 2, '|');
434 if (StrLength(Buf2
) != 0) {
435 /** Handle senders with RFC2047 encoding */
436 StrBuf_RFC822_to_Utf8(Msg
->from
, Buf2
, WCC
->DefaultCharset
, FoundCharset
);
440 StrBufExtract_token(Buf2
, Buf
, 3, '|');
441 if ((StrLength(Buf2
) !=0 ) &&
442 ( ((WCC
->room_flags
& QR_NETWORK
)
443 || ((strcasecmp(ChrPtr(Buf2
), ChrPtr(serv_info
.serv_nodename
))
444 && (strcasecmp(ChrPtr(Buf2
), ChrPtr(serv_info
.serv_fqdn
))))))))
446 StrBufAppendBufPlain(Msg
->from
, HKEY(" @ "), 0);
447 StrBufAppendBuf(Msg
->from
, Buf2
, 0);
451 StrBufExtract_token(Msg->inetaddr, Buf, 4, '|');
454 Msg
->subj
= NewStrBufPlain(NULL
, StrLength(Buf
));
455 StrBufExtract_token(Buf2
, Buf
, 5, '|');
456 if (StrLength(Buf2
) == 0)
457 StrBufAppendBufPlain(Msg
->subj
, _("(no subject)"), -1,0);
459 StrBuf_RFC822_to_Utf8(Msg
->subj
, Buf2
, WCC
->DefaultCharset
, FoundCharset
);
460 if ((StrLength(Msg
->subj
) > 75) &&
461 (StrBuf_Utf8StrLen(Msg
->subj
) > 75)) {
462 StrBuf_Utf8StrCut(Msg
->subj
, 72);
463 StrBufAppendBufPlain(Msg
->subj
, HKEY("..."), 0);
468 if ((StrLength(Msg
->from
) > 25) &&
469 (StrBuf_Utf8StrLen(Msg
->from
) > 25)) {
470 StrBuf_Utf8StrCut(Msg
->from
, 23);
471 StrBufAppendBufPlain(Msg
->from
, HKEY("..."), 0);
475 Put(WCC
->summ
, (const char *)&n
, sizeof(n
), Msg
, DestroyMessageSummary
);
485 inline message_summary
* GetMessagePtrAt(int n
, HashList
*Summ
)
493 GetHashAt(Summ
, n
, &HKLen
, &Key
, &vMsg
);
494 return (message_summary
*) vMsg
;
498 long DrawMessageDropdown(StrBuf
*Selector
, long maxmsgs
, long startmsg
, int nMessages
)
511 WCTemplputParams SubTP
;
513 memset(&SubTP
, 0, sizeof(WCTemplputParams
));
514 SubTP
.Filter
.ContextType
= CTX_LONGVECTOR
;
515 SubTP
.Context
= &vector
;
516 TmpBuf
= NewStrBuf();
517 At
= GetNewHashPos(WCC
->summ
, nMessages
);
518 nItems
= GetCount(WCC
->summ
);
522 vector
[1] = startmsg
;
526 vector
[3] = abs(nMessages
);
527 lo
= GetHashPosCounter(At
);
529 if (lo
+ nMessages
>= nItems
) {
531 vector
[3] = nItems
- lo
;
536 hi
= lo
+ nMessages
- 1;
539 if (lo
+ nMessages
< -1) {
543 if ((lo
% abs(nMessages
)) != 0) {
544 int offset
= (lo
% abs(nMessages
) *
545 (nMessages
/ abs(nMessages
)));
547 vector
[3] = abs(offset
);
555 done
= !GetNextHashPos(WCC
->summ
, At
, &hklen
, &key
, &vMsg
);
558 * Bump these because although we're thinking in zero base, the user
559 * is a drooling idiot and is thinking in one base.
565 dbg_print_longvector(vector
);
566 DoTemplate(HKEY("select_messageindex"), TmpBuf
, &SubTP
);
567 StrBufAppendBuf(Selector
, TmpBuf
, 0);
571 if (maxmsgs
== 9999999) {
578 dbg_print_longvector(vector
);
579 DoTemplate(HKEY("select_messageindex_all"), TmpBuf
, &SubTP
);
580 StrBufAppendBuf(Selector
, TmpBuf
, 0);
586 void load_seen_flags(void)
588 message_summary
*Msg
;
596 OldMsg
= NewStrBuf();
598 StrBuf_ServGetln(OldMsg
);
599 if (GetServerStatus(OldMsg
, NULL
) == 2) {
600 StrBufCutLeft(OldMsg
, 4);
606 at
= GetNewHashPos(WCC
->summ
, 0);
607 while (GetNextHashPos(WCC
->summ
, at
, &HKLen
, &HashKey
, &vMsg
)) {
608 /** Are you a new message, or an old message? */
609 Msg
= (message_summary
*) vMsg
;
610 if (is_msg_in_mset(ChrPtr(OldMsg
), Msg
->msgnum
)) {
621 extern readloop_struct rlid
[];
624 * command loop for reading messages
626 * Set oper to "readnew" or "readold" or "readfwd" or "headers"
628 void readloop(long oper
)
630 StrBuf
*MessageDropdown
= NULL
;
631 StrBuf
*BBViewToolBar
= NULL
;
633 message_summary
*Msg
;
637 int with_headers
= 0;
641 long *displayed_msgs
= NULL
;
642 int num_displayed
= 0;
643 int is_singlecard
= 0;
646 int lowest_displayed
= (-1);
647 int highest_displayed
= 0;
648 addrbookent
*addrbook
= NULL
;
655 int care_for_empty_list
= 0;
658 int defaultsortorder
= 0;
659 WCTemplputParams SubTP
;
661 if (havebstr("is_summary") && (1 == (ibstr("is_summary"))))
662 WCC
->wc_view
= VIEW_MAILBOX
;
665 output_headers(1, 1, 1, 0, 0, 0);
666 } else if (WCC
->wc_view
== VIEW_MAILBOX
) {
667 jsonMessageListHdr();
670 switch (WCC
->wc_view
) {
672 sprintf(buf
, "wiki?room=%s&page=home", ChrPtr(WCC
->wc_roomname
));
678 strcpy(cmd
, "MSGS ALL|||1");
680 parse_calendar_view_request(&calv
);
683 strcpy(cmd
, "MSGS ALL");
687 strcpy(cmd
, "MSGS ALL");
689 wprintf("<div id=\"new_notes_here\"></div>\n");
691 case VIEW_ADDRESSBOOK
:
692 is_singlecard
= ibstr("is_singlecard");
693 if (is_singlecard
!= 1) {
694 if (oper
== do_search
) {
695 snprintf(cmd
, sizeof(cmd
), "MSGS SEARCH|%s", bstr("query"));
698 strcpy(cmd
, "MSGS ALL");
709 defaultsortorder
= 2;
712 care_for_empty_list
= 0;
714 /* Generally using maxmsgs|startmsg is not required
715 in mailbox view, but we have a 'safemode' for clients
716 (*cough* Exploder) that simply can't handle too many */
717 if (havebstr("maxmsgs")) maxmsgs
= ibstr("maxmsgs");
718 else maxmsgs
= 9999999;
719 if (havebstr("startmsg")) startmsg
= lbstr("startmsg");
720 snprintf(cmd
, sizeof(cmd
), "MSGS %s|%s||1",
721 (oper
== do_search
) ? "SEARCH" : "ALL",
722 (oper
== do_search
) ? bstr("query") : ""
728 defaultsortorder
= 1;
731 care_for_empty_list
= 1;
733 rlid
[oper
].cmd(cmd
, sizeof(cmd
));
734 SetAccessCommand(oper
);
736 if (havebstr("maxmsgs"))
737 maxmsgs
= ibstr("maxmsgs");
738 if (maxmsgs
== 0) maxmsgs
= DEFAULT_MAXMSGS
;
740 if (havebstr("startmsg")) {
741 startmsg
= lbstr("startmsg");
746 nummsgs
= load_msg_ptrs(cmd
, with_headers
);
748 if (care_for_empty_list
) {
749 wprintf("<div class=\"nomsgs\"><br><em>");
752 wprintf(_("No new messages."));
755 wprintf(_("No old messages."));
758 wprintf(_("No messages here."));
760 wprintf("</em><br></div>\n");
768 memset(&SubTP
, 0, sizeof(WCTemplputParams
));
769 SubTP
.Filter
.ContextType
= CTX_NONE
;
770 SubTP
.Context
= NULL
;
771 SortIt
= RetrieveSort(&SubTP
, NULL
, 0,
772 HKEY("date"), defaultsortorder
);
774 SortByPayload(WCC
->summ
, SortIt
);
775 if (WCC
->wc_view
== VIEW_BBS
) {
776 if (lbstr("SortOrder") == 2) {
778 num_displayed
= -DEFAULT_MAXMSGS
;
782 num_displayed
= DEFAULT_MAXMSGS
;
786 if (startmsg
< 0) startmsg
= (bbs_reverse
) ? nummsgs
- 1 : 0;
788 if (load_seen
) load_seen_flags();
791 * If we're to print s.th. above the message list...
793 switch (WCC
->wc_view
) {
795 BBViewToolBar
= NewStrBuf();
796 MessageDropdown
= NewStrBuf();
798 maxmsgs
= DrawMessageDropdown(MessageDropdown
, maxmsgs
, startmsg
, num_displayed
);
799 if (num_displayed
< 0) {
801 if (num_displayed
!= maxmsgs
)
802 maxmsgs
= abs(maxmsgs
) + 1;
804 maxmsgs
= abs(maxmsgs
);
807 memset(&SubTP
, 0, sizeof(WCTemplputParams
));
808 SubTP
.Filter
.ContextType
= CTX_STRBUF
;
809 SubTP
.Context
= MessageDropdown
;
810 DoTemplate(HKEY("msg_listselector_top"), BBViewToolBar
, &SubTP
);
811 StrBufAppendBuf(WCC
->WBuf
, BBViewToolBar
, 0);
812 FlushStrBuf(BBViewToolBar
);
815 WCC
->startmsg
= startmsg
;
816 WCC
->maxmsgs
= maxmsgs
;
817 WCC
->num_displayed
= 0;
819 /* Put some helpful data in vars for mailsummary_json */
820 svputlong("READLOOP:TOTALMSGS", nummsgs
);
821 svputlong("READLOOP:STARTMSG", startmsg
);
822 svputlong("WCVIEW", WCC
->wc_view
);
824 * iterate over each message. if we need to load an attachment, do it here.
826 if (WCC
->wc_view
== VIEW_MAILBOX
) goto NO_MSG_LOOP
;
828 * iterate over each message. if we need to load an attachment, do it here.
830 at
= GetNewHashPos(WCC
->summ
, 0);
831 num_displayed
= i
= 0;
832 while (GetNextHashPos(WCC
->summ
, at
, &HKLen
, &HashKey
, &vMsg
)) {
833 Msg
= (message_summary
*) vMsg
;
834 if ((Msg
->msgnum
>= startmsg
) && (num_displayed
<= maxmsgs
)) {
835 switch (WCC
->wc_view
) {
838 case VIEW_CALBRIEF
: /* load the mime attachments for special tasks... */
840 load_calendar_item(Msg
, Msg
->is_new
, &calv
);
843 display_task(Msg
, Msg
->is_new
);
846 display_note(Msg
, Msg
->is_new
);
848 case VIEW_ADDRESSBOOK
:
849 fetch_ab_name(Msg
, buf
);
851 addrbook
= realloc(addrbook
,
852 (sizeof(addrbookent
) * num_ab
) );
853 safestrncpy(addrbook
[num_ab
-1].ab_name
, buf
,
854 sizeof(addrbook
[num_ab
-1].ab_name
));
855 addrbook
[num_ab
-1].ab_msgnum
= Msg
->msgnum
;
857 case VIEW_BBS
: /* Tag the mails we want to show in bbview... */
859 if (displayed_msgs
== NULL
) {
860 displayed_msgs
= malloc(sizeof(long) *
861 (maxmsgs
<nummsgs
? maxmsgs
+ 1 : nummsgs
+ 1));
863 if ((i
>= startmsg
) && (i
< startmsg
+ maxmsgs
)) {
864 displayed_msgs
[num_displayed
] = Msg
->msgnum
;
865 if (lowest_displayed
< 0) lowest_displayed
= a
;
866 highest_displayed
= a
;
878 * Done iterating the message list. now tasks we want to do after.
880 switch (WCC
->wc_view
) {
882 DoTemplate(HKEY("mailsummary_json"),NULL
, &SubTP
);
885 if (displayed_msgs
!= NULL
) {
886 /** if we do a split bbview in the future, begin messages div here */
888 for (a
=0; a
<num_displayed
; ++a
) {
889 read_message(WCC
->WBuf
, HKEY("view_message"), displayed_msgs
[a
], 0, NULL
);
892 /** if we do a split bbview in the future, end messages div here */
894 free(displayed_msgs
);
895 displayed_msgs
= NULL
;
897 memset(&SubTP
, 0, sizeof(WCTemplputParams
));
898 SubTP
.Filter
.ContextType
= CTX_STRBUF
;
899 SubTP
.Context
= MessageDropdown
;
900 DoTemplate(HKEY("msg_listselector_bottom"), BBViewToolBar
, &SubTP
);
901 StrBufAppendBuf(WCC
->WBuf
, BBViewToolBar
, 0);
903 FreeStrBuf(&BBViewToolBar
);
904 FreeStrBuf(&MessageDropdown
);
909 switch (WCC
->wc_view
) {
914 render_calendar_view(&calv
);
917 do_tasks_view(); /** Render the task list */
921 case VIEW_ADDRESSBOOK
:
923 read_message(WC
->WBuf
, HKEY("view_message"), lbstr("startmsg"), 0, NULL
);
925 do_addrbook_view(addrbook
, num_ab
); /** Render the address book */
932 /** Note: wDumpContent() will output one additional </div> tag. */
933 if (WCC
->wc_view
!= VIEW_MAILBOX
) {
934 /* We ought to move this out into template */
941 if (WCC
->summ
!= NULL
) {
942 DeleteHash(&WCC
->summ
);
944 if (addrbook
!= NULL
) free(addrbook
);
945 FreeStrBuf(&BBViewToolBar
);
950 * Back end for post_message()
951 * ... this is where the actual message gets transmitted to the server.
953 void post_mime_to_server(void) {
955 char top_boundary
[SIZ
];
956 char alt_boundary
[SIZ
];
957 int is_multipart
= 0;
959 wc_mime_attachment
*att
;
961 size_t encoded_length
;
962 size_t encoded_strlen
;
963 char *txtmail
= NULL
;
965 sprintf(top_boundary
, "Citadel--Multipart--%s--%04x--%04x",
966 ChrPtr(serv_info
.serv_fqdn
),
970 sprintf(alt_boundary
, "Citadel--Multipart--%s--%04x--%04x",
971 ChrPtr(serv_info
.serv_fqdn
),
976 /* RFC2045 requires this, and some clients look for it... */
977 serv_puts("MIME-Version: 1.0");
978 serv_puts("X-Mailer: " PACKAGE_STRING
);
980 /* If there are attachments, we have to do multipart/mixed */
981 if (GetCount(WCC
->attachments
) > 0) {
986 /* Remember, serv_printf() appends an extra newline */
987 serv_printf("Content-type: multipart/mixed; boundary=\"%s\"\n", top_boundary
);
988 serv_printf("This is a multipart message in MIME format.\n");
989 serv_printf("--%s", top_boundary
);
992 /* Remember, serv_printf() appends an extra newline */
993 serv_printf("Content-type: multipart/alternative; "
994 "boundary=\"%s\"\n", alt_boundary
);
995 serv_printf("This is a multipart message in MIME format.\n");
996 serv_printf("--%s", alt_boundary
);
998 serv_puts("Content-type: text/plain; charset=utf-8");
999 serv_puts("Content-Transfer-Encoding: quoted-printable");
1001 txtmail
= html_to_ascii(bstr("msgtext"), 0, 80, 0);
1002 text_to_server_qp(txtmail
); /* Transmit message in quoted-printable encoding */
1005 serv_printf("--%s", alt_boundary
);
1007 serv_puts("Content-type: text/html; charset=utf-8");
1008 serv_puts("Content-Transfer-Encoding: quoted-printable");
1010 serv_puts("<html><body>\r\n");
1011 text_to_server_qp(bstr("msgtext")); /* Transmit message in quoted-printable encoding */
1012 serv_puts("</body></html>\r\n");
1014 serv_printf("--%s--", alt_boundary
);
1022 /* Add in the attachments */
1023 it
= GetNewHashPos(WCC
->attachments
, 0);
1024 while (GetNextHashPos(WCC
->attachments
, it
, &len
, &Key
, &vAtt
)) {
1025 att
= (wc_mime_attachment
*)vAtt
;
1026 encoded_length
= ((att
->length
* 150) / 100);
1027 encoded
= malloc(encoded_length
);
1028 if (encoded
== NULL
) break;
1029 encoded_strlen
= CtdlEncodeBase64(encoded
, ChrPtr(att
->Data
), StrLength(att
->Data
), 1);
1031 serv_printf("--%s", top_boundary
);
1032 serv_printf("Content-type: %s", ChrPtr(att
->ContentType
));
1033 serv_printf("Content-disposition: attachment; filename=\"%s\"", ChrPtr(att
->FileName
));
1034 serv_puts("Content-transfer-encoding: base64");
1036 serv_write(encoded
, encoded_strlen
);
1041 serv_printf("--%s--", top_boundary
);
1050 * Post message (or don't post message)
1052 * Note regarding the "dont_post" variable:
1053 * A random value (actually, it's just a timestamp) is inserted as a hidden
1054 * field called "postseq" when the display_enter page is generated. This
1055 * value is checked when posting, using the static variable dont_post. If a
1056 * user attempts to post twice using the same dont_post value, the message is
1057 * discarded. This prevents the accidental double-saving of the same message
1058 * if the user happens to click the browser "back" button.
1060 void post_message(void)
1063 StrBuf
*encoded_subject
= NULL
;
1064 static long dont_post
= (-1L);
1065 wc_mime_attachment
*att
;
1066 int is_anonymous
= 0;
1067 const StrBuf
*display_name
= NULL
;
1068 wcsession
*WCC
= WC
;
1070 if (havebstr("force_room")) {
1071 gotoroom(sbstr("force_room"));
1074 if (havebstr("display_name")) {
1075 display_name
= sbstr("display_name");
1076 if (!strcmp(ChrPtr(display_name
), "__ANONYMOUS__")) {
1077 display_name
= NULL
;
1082 if (WCC
->upload_length
> 0) {
1087 lprintf(9, "%s:%d: we are uploading %d bytes\n", __FILE__
, __LINE__
, WCC
->upload_length
);
1088 /** There's an attachment. Save it to this struct... */
1089 att
= malloc(sizeof(wc_mime_attachment
));
1090 memset(att
, 0, sizeof(wc_mime_attachment
));
1091 att
->length
= WCC
->upload_length
;
1092 att
->ContentType
= NewStrBufPlain(WCC
->upload_content_type
, -1);
1093 att
->FileName
= NewStrBufPlain(WCC
->upload_filename
, -1);
1096 if (WCC
->attachments
== NULL
)
1097 WCC
->attachments
= NewHash(1, NULL
);
1098 /* And add it to the list. */
1099 n
= snprintf(N
, sizeof N
, "%d", GetCount(WCC
->attachments
) + 1);
1100 Put(WCC
->attachments
, N
, n
, att
, DestroyMime
);
1103 * Mozilla sends a simple filename, which is what we want,
1104 * but Satan's Browser sends an entire pathname. Reduce
1105 * the path to just a filename if we need to.
1107 pch
= strrchr(ChrPtr(att
->FileName
), '/');
1109 StrBufCutLeft(att
->FileName
, pch
- ChrPtr(att
->FileName
));
1111 pch
= strrchr(ChrPtr(att
->FileName
), '\\');
1113 StrBufCutLeft(att
->FileName
, pch
- ChrPtr(att
->FileName
));
1117 * Transfer control of this memory from the upload struct
1118 * to the attachment struct.
1120 att
->Data
= NewStrBufPlain(WCC
->upload
, WCC
->upload_length
);
1122 WCC
->upload_length
= 0;
1128 if (havebstr("cancel_button")) {
1129 sprintf(WCC
->ImportantMessage
,
1130 _("Cancelled. Message was not posted."));
1131 } else if (havebstr("attach_button")) {
1134 } else if (lbstr("postseq") == dont_post
) {
1135 sprintf(WCC
->ImportantMessage
,
1136 _("Automatically cancelled because you have already "
1137 "saved this message."));
1139 const char CMD
[] = "ENT0 1|%s|%d|4|%s|%s||%s|%s|%s|%s|%s";
1140 const StrBuf
*Recp
= NULL
;
1141 const StrBuf
*Cc
= NULL
;
1142 const StrBuf
*Bcc
= NULL
;
1143 const StrBuf
*Wikipage
= NULL
;
1144 const StrBuf
*my_email_addr
= NULL
;
1145 StrBuf
*CmdBuf
= NULL
;
1146 StrBuf
*references
= NULL
;
1148 if (havebstr("references"))
1150 const StrBuf
*ref
= sbstr("references");
1151 references
= NewStrBufPlain(ChrPtr(ref
), StrLength(ref
));
1152 lprintf(9, "Converting: %s\n", ChrPtr(references
));
1153 StrBufReplaceChars(references
, '|', '!');
1154 lprintf(9, "Converted: %s\n", ChrPtr(references
));
1156 if (havebstr("subject")) {
1159 * make enough room for the encoded string;
1160 * plus the QP header
1162 Subj
= sbstr("subject");
1164 StrBufRFC2047encode(&encoded_subject
, Subj
);
1166 Recp
= sbstr("recp");
1169 Wikipage
= sbstr("wikipage");
1170 my_email_addr
= sbstr("my_email_addr");
1172 CmdBuf
= NewStrBufPlain(NULL
,
1175 StrLength(encoded_subject
) +
1178 StrLength(Wikipage
) +
1179 StrLength(my_email_addr
) +
1180 StrLength(references
));
1182 StrBufPrintf(CmdBuf
,
1186 ChrPtr(encoded_subject
),
1187 ChrPtr(display_name
),
1191 ChrPtr(my_email_addr
),
1192 ChrPtr(references
));
1193 FreeStrBuf(&references
);
1195 lprintf(9, "%s\n", ChrPtr(CmdBuf
));
1196 serv_puts(ChrPtr(CmdBuf
));
1197 serv_getln(buf
, sizeof buf
);
1198 FreeStrBuf(&CmdBuf
);
1199 FreeStrBuf(&encoded_subject
);
1200 if (buf
[0] == '4') {
1201 post_mime_to_server();
1202 if ( (havebstr("recp"))
1203 || (havebstr("cc" ))
1204 || (havebstr("bcc" ))
1206 sprintf(WCC
->ImportantMessage
, _("Message has been sent.\n"));
1209 sprintf(WC
->ImportantMessage
, _("Message has been posted.\n"));
1211 dont_post
= lbstr("postseq");
1213 lprintf(9, "%s:%d: server post error: %s\n", __FILE__
, __LINE__
, buf
);
1214 sprintf(WC
->ImportantMessage
, "%s", &buf
[4]);
1220 DeleteHash(&WCC
->attachments
);
1223 * We may have been supplied with instructions regarding the location
1224 * to which we must return after posting. If found, go there.
1226 if (havebstr("return_to")) {
1227 http_redirect(bstr("return_to"));
1230 * If we were editing a page in a wiki room, go to that page now.
1232 else if (havebstr("wikipage")) {
1233 snprintf(buf
, sizeof buf
, "wiki?page=%s", bstr("wikipage"));
1237 * Otherwise, just go to the "read messages" loop.
1248 * \brief display the message entry screen
1250 void display_enter(void)
1254 const StrBuf
*display_name
= NULL
;
1255 int recipient_required
= 0;
1256 int subject_required
= 0;
1257 int recipient_bad
= 0;
1258 int is_anonymous
= 0;
1259 wcsession
*WCC
= WC
;
1263 if (havebstr("force_room")) {
1264 gotoroom(sbstr("force_room"));
1267 display_name
= sbstr("display_name");
1268 if (!strcmp(ChrPtr(display_name
), "__ANONYMOUS__")) {
1269 display_name
= NULL
;
1273 /** First test to see whether this is a room that requires recipients to be entered */
1274 serv_puts("ENT0 0");
1275 serv_getln(buf
, sizeof buf
);
1277 if (!strncmp(buf
, "570", 3)) { /** 570 means that we need a recipient here */
1278 recipient_required
= 1;
1280 else if (buf
[0] != '2') { /** Any other error means that we cannot continue */
1281 sprintf(WCC
->ImportantMessage
, "%s", &buf
[4]);
1286 /* Is the server strongly recommending that the user enter a message subject? */
1287 if ((buf
[3] != '\0') && (buf
[4] != '\0')) {
1288 subject_required
= extract_int(&buf
[4], 1);
1292 * Are we perhaps in an address book view? If so, then an "enter
1293 * message" command really means "add new entry."
1295 if (WCC
->wc_default_view
== VIEW_ADDRESSBOOK
) {
1296 do_edit_vcard(-1, "", "", ChrPtr(WCC
->wc_roomname
));
1301 * Are we perhaps in a calendar room? If so, then an "enter
1302 * message" command really means "add new calendar item."
1304 if (WCC
->wc_default_view
== VIEW_CALENDAR
) {
1305 display_edit_event();
1310 * Are we perhaps in a tasks view? If so, then an "enter
1311 * message" command really means "add new task."
1313 if (WCC
->wc_default_view
== VIEW_TASKS
) {
1314 display_edit_task();
1319 * Otherwise proceed normally.
1320 * Do a custom room banner with no navbar...
1323 if (recipient_required
) {
1324 const StrBuf
*Recp
= NULL
;
1325 const StrBuf
*Cc
= NULL
;
1326 const StrBuf
*Bcc
= NULL
;
1327 const StrBuf
*Wikipage
= NULL
;
1328 StrBuf
*CmdBuf
= NULL
;
1329 const char CMD
[] = "ENT0 0|%s|%d|0||%s||%s|%s|%s";
1331 Recp
= sbstr("recp");
1334 Wikipage
= sbstr("wikipage");
1336 CmdBuf
= NewStrBufPlain(NULL
,
1339 StrLength(display_name
) +
1342 StrLength(Wikipage
));
1344 StrBufPrintf(CmdBuf
,
1348 ChrPtr(display_name
),
1352 serv_puts(ChrPtr(CmdBuf
));
1353 serv_getln(buf
, sizeof buf
);
1354 FreeStrBuf(&CmdBuf
);
1356 if (!strncmp(buf
, "570", 3)) { /** 570 means we have an invalid recipient listed */
1357 if (havebstr("recp") &&
1363 else if (buf
[0] != '2') { /** Any other error means that we cannot continue */
1364 wprintf("<em>%s</em><br />\n", &buf
[4]);/*TODO -> important message */
1368 svputlong("RCPTREQUIRED", recipient_required
);
1369 svputlong("SUBJREQUIRED", recipient_required
|| subject_required
);
1372 output_headers(1, 0, 0, 0, 1, 0);
1373 DoTemplate(HKEY("edit_message"), NULL
, &NoCtx
);
1380 * \brief delete a message
1382 void delete_msg(void)
1387 msgid
= lbstr("msgid");
1389 if (WC
->wc_is_trash
) { /** Delete from Trash is a real delete */
1390 serv_printf("DELE %ld", msgid
);
1392 else { /** Otherwise move it to Trash */
1393 serv_printf("MOVE %ld|_TRASH_|0", msgid
);
1396 serv_getln(buf
, sizeof buf
);
1397 sprintf(WC
->ImportantMessage
, "%s", &buf
[4]);
1404 * \brief move a message to another folder
1411 msgid
= lbstr("msgid");
1413 if (havebstr("move_button")) {
1414 sprintf(buf
, "MOVE %ld|%s", msgid
, bstr("target_room"));
1416 serv_getln(buf
, sizeof buf
);
1417 sprintf(WC
->ImportantMessage
, "%s", &buf
[4]);
1419 sprintf(WC
->ImportantMessage
, (_("The message was not moved.")));
1430 * Confirm move of a message
1432 void confirm_move_msg(void)
1438 msgid
= lbstr("msgid");
1441 output_headers(1, 1, 2, 0, 0, 0);
1442 wprintf("<div id=\"banner\">\n");
1444 wprintf(_("Confirm move of message"));
1446 wprintf("</div>\n");
1448 wprintf("<div id=\"content\" class=\"service\">\n");
1450 wprintf("<CENTER>");
1452 wprintf(_("Move this message to:"));
1453 wprintf("<br />\n");
1455 wprintf("<form METHOD=\"POST\" action=\"move_msg\">\n");
1456 wprintf("<input type=\"hidden\" name=\"nonce\" value=\"%d\">\n", WC
->nonce
);
1457 wprintf("<INPUT TYPE=\"hidden\" NAME=\"msgid\" VALUE=\"%s\">\n", bstr("msgid"));
1459 wprintf("<SELECT NAME=\"target_room\" SIZE=5>\n");
1461 serv_getln(buf
, sizeof buf
);
1462 if (buf
[0] == '1') {
1463 while (serv_getln(buf
, sizeof buf
), strcmp(buf
, "000")) {
1464 extract_token(targ
, buf
, 0, '|', sizeof targ
);
1465 wprintf("<OPTION>");
1470 wprintf("</SELECT>\n");
1471 wprintf("<br />\n");
1473 wprintf("<INPUT TYPE=\"submit\" NAME=\"move_button\" VALUE=\"%s\">", _("Move"));
1475 wprintf("<INPUT TYPE=\"submit\" NAME=\"cancel_button\" VALUE=\"%s\">", _("Cancel"));
1476 wprintf("</form></CENTER>\n");
1478 wprintf("</CENTER>\n");
1484 * Generic function to output an arbitrary MIME attachment from
1485 * message being composed
1487 * partnum The MIME part to be output
1488 * filename Fake filename to give
1489 * force_download Nonzero to force set the Content-Type: header to "application/octet-stream"
1491 void postpart(StrBuf
*partnum
, StrBuf
*filename
, int force_download
)
1494 StrBuf
*content_type
;
1495 wc_mime_attachment
*part
;
1497 if (GetHash(WC
->attachments
, SKEY(partnum
), &vPart
) &&
1499 part
= (wc_mime_attachment
*) vPart
;
1500 if (force_download
) {
1501 content_type
= NewStrBufPlain(HKEY("application/octet-stream"));
1504 content_type
= NewStrBufDup(part
->ContentType
);
1506 output_headers(0, 0, 0, 0, 0, 0);
1507 StrBufAppendBuf(WC
->WBuf
, part
->Data
, 0);
1508 http_transmit_thing(ChrPtr(content_type
), 0);
1510 hprintf("HTTP/1.1 404 %s\n", ChrPtr(partnum
));
1511 output_headers(0, 0, 0, 0, 0, 0);
1512 hprintf("Content-Type: text/plain\r\n");
1513 wprintf(_("An error occurred while retrieving this part: %s/%s\n"),
1514 ChrPtr(partnum
), ChrPtr(filename
));
1517 FreeStrBuf(&content_type
);
1522 * Generic function to output an arbitrary MIME part from an arbitrary
1523 * message number on the server.
1525 * msgnum Number of the item on the citadel server
1526 * partnum The MIME part to be output
1527 * force_download Nonzero to force set the Content-Type: header to "application/octet-stream"
1529 void mimepart(const char *msgnum
, const char *partnum
, int force_download
)
1533 char content_type
[256];
1535 serv_printf("OPNA %s|%s", msgnum
, partnum
);
1536 serv_getln(buf
, sizeof buf
);
1537 if (buf
[0] == '2') {
1538 bytes
= extract_long(&buf
[4], 0);
1539 if (force_download
) {
1540 strcpy(content_type
, "application/octet-stream");
1543 extract_token(content_type
, &buf
[4], 3, '|', sizeof content_type
);
1545 output_headers(0, 0, 0, 0, 0, 0);
1547 read_server_binary(WC
->WBuf
, bytes
);
1549 serv_getln(buf
, sizeof buf
);
1550 http_transmit_thing(content_type
, 0);
1552 hprintf("HTTP/1.1 404 %s\n", &buf
[4]);
1553 output_headers(0, 0, 0, 0, 0, 0);
1554 hprintf("Content-Type: text/plain\r\n");
1555 wprintf(_("An error occurred while retrieving this part: %s\n"), &buf
[4]);
1562 * Read any MIME part of a message, from the server, into memory.
1564 char *load_mimepart(long msgnum
, char *partnum
)
1568 char content_type
[SIZ
];
1571 serv_printf("DLAT %ld|%s", msgnum
, partnum
);
1572 serv_getln(buf
, sizeof buf
);
1573 if (buf
[0] == '6') {
1574 bytes
= extract_long(&buf
[4], 0);
1575 extract_token(content_type
, &buf
[4], 3, '|', sizeof content_type
);
1577 content
= malloc(bytes
+ 2);
1578 serv_read(content
, bytes
);
1580 content
[bytes
] = 0; /* null terminate for good measure */
1589 * Read any MIME part of a message, from the server, into memory.
1591 void MimeLoadData(wc_mime_attachment
*Mime
)
1595 /* TODO: is there a chance the contenttype is different to the one we know? */
1596 serv_printf("DLAT %ld|%s", Mime
->msgnum
, ChrPtr(Mime
->PartNum
));
1597 serv_getln(buf
, sizeof buf
);
1598 if (buf
[0] == '6') {
1599 bytes
= extract_long(&buf
[4], 0);
1601 if (Mime
->Data
== NULL
)
1602 Mime
->Data
= NewStrBufPlain(NULL
, bytes
);
1603 StrBuf_ServGetBLOB(Mime
->Data
, bytes
);
1607 FlushStrBuf(Mime
->Data
);
1608 /* TODO XImportant message */
1615 void view_mimepart(void) {
1616 mimepart(ChrPtr(WC
->UrlFragment2
),
1617 ChrPtr(WC
->UrlFragment3
),
1621 void download_mimepart(void) {
1622 mimepart(ChrPtr(WC
->UrlFragment2
),
1623 ChrPtr(WC
->UrlFragment3
),
1627 void view_postpart(void) {
1628 postpart(WC
->UrlFragment2
,
1633 void download_postpart(void) {
1634 postpart(WC
->UrlFragment2
,
1639 void h_readnew(void) { readloop(readnew
);}
1640 void h_readold(void) { readloop(readold
);}
1641 void h_readfwd(void) { readloop(readfwd
);}
1642 void h_headers(void) { readloop(headers
);}
1643 void h_do_search(void) { readloop(do_search
);}
1645 void jsonMessageListHdr(void)
1647 /* TODO: make a generic function */
1648 hprintf("HTTP/1.1 200 OK\r\n");
1649 hprintf("Content-type: application/json; charset=utf-8\r\n");
1650 hprintf("Server: %s / %s\r\n", PACKAGE_STRING
, ChrPtr(serv_info
.serv_software
));
1651 hprintf("Connection: close\r\n");
1652 hprintf("Pragma: no-cache\r\nCache-Control: no-store\r\nExpires:-1\r\n");
1655 /* Spit out the new summary view. This is basically a static page, so clients can cache the layout, all the dirty work is javascript :) */
1656 void new_summary_view(void) {
1658 DoTemplate(HKEY("msg_listview"),NULL
,&NoCtx
);
1659 DoTemplate(HKEY("trailing"),NULL
,&NoCtx
);
1662 /** Output message list in JSON-format */
1663 void jsonMessageList(void) {
1664 const StrBuf
*room
= sbstr("room");
1666 long oper
= (havebstr("query")) ? do_search
: readnew
;
1676 RegisterPreference(HKEY("use_sig"),
1677 _("Attach signature to email messages?"),
1680 RegisterPreference(HKEY("signature"), _("Use this signature:"), PRF_QP_STRING
, NULL
);
1681 RegisterPreference(HKEY("default_header_charset"),
1682 _("Default character set for email headers:"),
1685 RegisterPreference(HKEY("defaultfrom"), _("Preferred email address"), PRF_STRING
, NULL
);
1686 RegisterPreference(HKEY("defaultname"),
1687 _("Preferred display name for email messages"),
1690 RegisterPreference(HKEY("defaulthandle"),
1691 _("Preferred display name for bulletin board posts"),
1694 RegisterPreference(HKEY("mailbox"),_("Mailbox view mode"), PRF_STRING
, NULL
);
1696 WebcitAddUrlHandler(HKEY("readnew"), h_readnew
, NEED_URL
);
1697 WebcitAddUrlHandler(HKEY("readold"), h_readold
, NEED_URL
);
1698 WebcitAddUrlHandler(HKEY("readfwd"), h_readfwd
, NEED_URL
);
1699 WebcitAddUrlHandler(HKEY("headers"), h_headers
, NEED_URL
);
1700 WebcitAddUrlHandler(HKEY("do_search"), h_do_search
, 0);
1701 WebcitAddUrlHandler(HKEY("display_enter"), display_enter
, 0);
1702 WebcitAddUrlHandler(HKEY("post"), post_message
, 0);
1703 WebcitAddUrlHandler(HKEY("move_msg"), move_msg
, 0);
1704 WebcitAddUrlHandler(HKEY("delete_msg"), delete_msg
, 0);
1705 WebcitAddUrlHandler(HKEY("confirm_move_msg"), confirm_move_msg
, 0);
1706 WebcitAddUrlHandler(HKEY("msg"), embed_message
, NEED_URL
|AJAX
);
1707 WebcitAddUrlHandler(HKEY("printmsg"), print_message
, NEED_URL
);
1708 WebcitAddUrlHandler(HKEY("mobilemsg"), mobile_message_view
, NEED_URL
);
1709 WebcitAddUrlHandler(HKEY("msgheaders"), display_headers
, NEED_URL
);
1711 WebcitAddUrlHandler(HKEY("mimepart"), view_mimepart
, NEED_URL
);
1712 WebcitAddUrlHandler(HKEY("mimepart_download"), download_mimepart
, NEED_URL
);
1713 WebcitAddUrlHandler(HKEY("postpart"), view_postpart
, NEED_URL
);
1714 WebcitAddUrlHandler(HKEY("postpart_download"), download_postpart
, NEED_URL
);
1717 WebcitAddUrlHandler(HKEY("roommsgs"), jsonMessageList
,0);
1719 WebcitAddUrlHandler(HKEY("mimepart"), view_mimepart
, NEED_URL
);
1720 WebcitAddUrlHandler(HKEY("mimepart_download"), download_mimepart
, NEED_URL
);
1721 WebcitAddUrlHandler(HKEY("postpart"), view_postpart
, NEED_URL
);
1722 WebcitAddUrlHandler(HKEY("postpart_download"), download_postpart
, NEED_URL
);