* If the calendar invitation is part of the body of a message instead
[alpine.git] / pith / mailview.c
blob984b560f65499348cc0c327792a6d2926cfbe940
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2017 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
21 mailview.c
22 Implements message data gathering and formatting
24 ====*/
27 #include "headers.h"
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
60 #define FBUF_LEN (50)
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
69 * set.
71 static struct envelope_s {
72 char *name;
73 long val;
74 } envelope_hdrs[] = {
75 {"from", FE_FROM},
76 {"sender", FE_SENDER},
77 {"date", FE_DATE},
78 {"to", FE_TO},
79 {"cc", FE_CC},
80 {"bcc", FE_BCC},
81 {"newsgroups", FE_NEWSGROUPS},
82 {"subject", FE_SUBJECT},
83 {"message-id", FE_MESSAGEID},
84 {"reply-to", FE_REPLYTO},
85 {"followup-to", FE_FOLLOWUPTO},
86 {"in-reply-to", FE_INREPLYTO},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES},
89 {NULL, 0}
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
108 fmt_env_t, gf_io_t, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S **, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S **, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S **, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
116 void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
117 gf_io_t, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM *, long, char *, char *,
120 ADDRESS *, int, char *, gf_io_t);
121 void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t);
123 int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
124 int format_env_puts(char *, gf_io_t);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR *, gf_io_t);
127 COLOR_PAIR *get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
141 prefix FM_
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
152 ----*/
154 format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
155 HANDLE_S **handlesp, int flgs, gf_io_t pc)
157 char *decode_err = NULL;
158 HEADER_S h;
159 int width;
161 clear_cur_embedded_color();
163 if(!(flgs & FM_DISPLAY))
164 flgs |= FM_NOINDENT;
166 width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs & FM_NOEDITORIAL)){
170 if(ps_global->full_header == 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
172 being shown. */
173 q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
174 else if(ps_global->full_header == 2)
175 q_status_message(SM_INFO, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
182 switch(format_header(ps_global->mail_stream, msgno, NULL,
183 env, &h, NULL, handlesp, flgs, NULL, pc)){
185 case -1 : /* write error */
186 goto write_error;
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc)
190 && !gf_puts(NEWLINE, pc)))
191 goto write_error;
193 break;
196 if(!(body == NULL
197 || (ps_global->full_header == 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global)))){
199 format_attachment_list(msgno, body, handlesp, flgs, width, pc);
200 format_calendar(msgno, body, handlesp, flgs, width, pc);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE, pc)
205 && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
206 return(1);
209 write_error:
211 if(!(flgs & FM_DISPLAY))
212 q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
213 decode_err ? decode_err : error_description(errno));
215 return(0);
218 void
219 format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags)
221 int avail, m1, m2, hwid, i, partwid, padwid;
222 int s1, s2, dwid;
223 int *margin;
224 char padding[1024];
225 VEVENT_SUMMARY_S *vesy; /* vevent summary */
227 vesy = ical_vevent_summary(vcal);
229 if(vesy == NULL) return;
231 if((cflags & FC_SUMMARY) && (cflags & FC_FULL))
232 cflags |= ~FC_FULL;
234 avail = width;
235 margin = (cflags & FC_FULL) ? NULL
236 : (flgs & FM_NOINDENT) ? NULL : format_view_margin();
238 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
239 avail -= m1;
241 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
242 avail -= m2;
244 hwid = MAX(avail, 0);
245 padwid = 0;
247 if(cflags & FC_SUMMARY){
248 i = utf8_width(_("Calendar Entry:"));
249 partwid = MIN(i, hwid);
250 if(m1 > 0){
251 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
252 if(!gf_puts(tmp_20k_buf, pc))
253 return;
256 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s",
257 partwid, partwid, _("Calendar Entry:"),
258 padwid, padwid, "");
260 if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc))
261 return;
263 else
264 partwid = 0;
266 if(m1 > 0){
267 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, "");
268 if(!gf_puts(tmp_20k_buf, pc))
269 return;
272 avail = width - m1 - m2;
274 s1 = MAX(MIN(1, avail), 0);
275 avail -= s1;
277 dwid = MAX(MIN(1, avail), 0);
278 avail -= dwid;
280 s2 = MAX(MIN(1, avail), 0);
281 avail -= s2;
283 if(cflags & FC_SUMMARY)
284 utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s",
285 s1, s1, "", dwid, dwid, "", s2, s2, "");
286 else
287 padding[0] = '\0';
289 if(vesy->cancel){
290 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
291 padding, _("This event was cancelled!"));
292 if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){
293 gf_puts(tmp_20k_buf, pc);
294 gf_puts(NEWLINE, pc);
295 (*pc)(TAG_EMBED);
296 (*pc)(TAG_BOLDOFF);
300 if(vesy->organizer){
301 if(vesy->sender != NULL){
302 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
303 padding, _("Sent-by: "), vesy->sender);
304 gf_puts(tmp_20k_buf, pc);
305 gf_puts(NEWLINE, pc);
308 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
309 padding, _("Organizer: "), vesy->organizer);
310 gf_puts(tmp_20k_buf, pc);
311 gf_puts(NEWLINE, pc);
312 } /* end of if(organizer) */
314 if(vesy->location){
315 ical_remove_escapes(&vesy->location);
316 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
317 padding, _("Location: "), vesy->location);
318 gf_puts(tmp_20k_buf, pc);
319 gf_puts(NEWLINE, pc);
320 } /* end of if location */
322 if(vesy->evstart){
323 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
324 padding, _("Start Date: "), vesy->evstart);
325 gf_puts(tmp_20k_buf, pc);
326 gf_puts(NEWLINE, pc);
327 } /* end of if dtstart */
329 if(vesy->duration){
330 int i;
332 for(i = 0; vesy->duration[i] != NULL; i++){
333 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
334 padding, _("Duration: "), vesy->duration[i]);
335 gf_puts(tmp_20k_buf, pc);
336 gf_puts(NEWLINE, pc);
338 } /* end of DURATION */
339 else if(vesy->evend){
340 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
341 padding, _("End Date: "), vesy->evend);
342 gf_puts(tmp_20k_buf, pc);
343 gf_puts(NEWLINE, pc);
344 } else { /* end of if dtend */
345 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s",
346 padding, _("No duration nor end time found for this event"));
347 gf_puts(tmp_20k_buf, pc);
348 gf_puts(NEWLINE, pc);
349 } /* end of else for if (duration) and if (dtend) */
351 if(vesy->attendee){
352 #define MAX_DISPLAYED 3
353 int i;
355 for(i = 0; vesy->attendee[i] != NULL; i++){
356 if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED)
357 break;
359 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s",
360 padding,
361 _("Attendee: "), vesy->attendee[i]);
362 gf_puts(tmp_20k_buf, pc);
363 gf_puts(NEWLINE, pc);
365 if(cflags & FC_SUMMARY){
366 COLOR_PAIR *lastc = NULL;
367 char numbuf[50];
368 int thisdescwid;
369 COLOR_PAIR *hdrcolor = NULL;
371 if((flgs & FM_DISPLAY)
372 && !(flgs & FM_NOCOLOR)
373 && pico_usingcolor()
374 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
375 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
376 && ps_global->VAR_NORM_FORE_COLOR
377 && ps_global->VAR_NORM_BACK_COLOR
378 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
379 ps_global->VAR_NORM_FORE_COLOR)
380 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
381 ps_global->VAR_NORM_BACK_COLOR))){
383 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
384 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
385 if(!pico_is_good_colorpair(hdrcolor))
386 free_color_pair(&hdrcolor);
390 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
391 return;
393 gf_puts(padding, pc);
394 utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details"));
396 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
397 char buf[16], color[64];
398 int l;
399 HANDLE_S *h;
401 h = new_handle(handlesp);
402 h->type = Attach;
403 h->h.attach = a;
405 snprintf(buf, sizeof(buf), "%d", h->key);
406 buf[sizeof(buf)-1] = '\0';
408 if(!(flgs & FM_NOCOLOR)
409 && handle_start_color(color, sizeof(color), &l, 1)){
410 lastc = get_cur_embedded_color();
411 if(!gf_nputs(color, (long) l, pc))
412 return;
414 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
415 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
416 return;
418 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
419 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
420 return;
421 } else
422 tmp_20k_buf[0] = '\0';
424 if(!format_env_puts(tmp_20k_buf, pc))
425 return;
427 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
428 if(lastc){
429 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
430 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
431 return;
434 if(!embed_color(lastc, pc))
435 return;
437 free_color_pair(&lastc);
439 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
440 return;
442 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
443 return;
446 if(padwid > 0){
447 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, "");
448 if(!gf_puts(tmp_20k_buf, pc))
449 return;
452 if(!gf_puts(NEWLINE, pc))
453 return;
455 } /* end of ATTENDEES */
457 free_vevent_summary(&vesy);
459 avail = width - m1 -2;
461 dwid = MAX(MIN(40, avail), 0);
462 avail -= dwid;
464 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "",
465 repeat_char(dwid, '-'));
466 gf_puts(tmp_20k_buf, pc);
467 // gf_puts(NEWLINE, pc);
471 format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
473 char *rawtext, *caltext;
474 unsigned long callen;
475 VCALENDAR_S *vcal = NULL;
476 ATTACH_S *a;
477 BODY *b;
479 if(flgs & FM_NEW_MESS) {
480 zero_atmts(ps_global->atmts);
481 describe_mime(body, "", 1, 1, 0, flgs);
484 for(a = ps_global->atmts; a->description != NULL; a++){
485 if(MIME_VCALENDAR(a->body->type, a->body->subtype)){
486 b = mail_body (ps_global->mail_stream, msgno, a->number);
487 if(b == NULL){
488 gf_puts(_("Error fetching calendar body part"), pc);
489 gf_puts(NEWLINE, pc);
490 continue;
492 if(b->sparep == NULL){
493 rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0);
494 if(rawtext == NULL){
495 gf_puts(_("Error fetching calendar text"), pc);
496 gf_puts(NEWLINE, pc);
497 continue;
499 rawtext[callen] = '\0'; /* chop off cookie */
500 switch(b->encoding){
501 case ENCBASE64:
502 caltext = rfc822_base64(rawtext, strlen(rawtext), &callen);
503 if(caltext == NULL){
504 gf_puts(_("Error in calendar base64 encoding"), pc);
505 gf_puts(NEWLINE, pc);
506 continue;
508 break;
510 case ENCQUOTEDPRINTABLE:
511 caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen);
512 if(caltext == NULL){
513 gf_puts(_("Error in calendar quoted printable encoding"), pc);
514 gf_puts(NEWLINE, pc);
515 continue;
517 break;
519 vcal = ical_parse_text(caltext);
520 if(vcal != NULL) vcal->encoding = b->encoding;
521 b->sparep = create_body_sparep(iCalType, (void *) vcal);
523 else if(get_body_sparep_type(b->sparep) == iCalType)
524 vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep);
525 if(vcal != NULL && vcal->comp != NULL){
526 if(vcal->comp[VEvent] != NULL){
527 format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY);
528 } /* else another type of entry in the calendar */
530 gf_puts(NEWLINE, pc);
533 return 0;
537 char *
538 format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
540 int filt_only_c0 = 0, wrapflags, error_found = 0;
541 int is_in_sig = OUT_SIG_BLOCK;
542 char *charset, *decode_err = NULL, *tmp1, *description;
543 ATTACH_S *a;
544 URL_HILITE_S uh;
545 gf_io_t gc;
547 if(body == NULL
548 || (ps_global->full_header == 2
549 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
551 /*--- Server is not an IMAP2bis, It can't parse MIME
552 so we just show the text here. Hopefully the
553 message isn't a MIME message
554 ---*/
555 void *text2;
557 if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
558 msgno, NULL, NULL, NIL)) != NULL){
560 if(!gf_puts(NEWLINE, pc)) /* write delimiter */
561 return("Write Error");
563 gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
564 gf_filter_init();
567 * We need to translate the message
568 * into UTF-8, but that's trouble in the full header case
569 * because we don't know what to translate from. We'll just
570 * take a guess by looking for the first text part and
571 * using its charset.
573 if(body && body->type == TYPETEXT)
574 charset = parameter_val(body->parameter, "charset");
575 else if(body && body->type == TYPEMULTIPART && body->nested.part
576 && body->nested.part->body.type == TYPETEXT)
577 charset = parameter_val(body->nested.part->body.parameter, "charset");
578 else
579 charset = ps_global->display_charmap;
581 if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
582 /* transliterate message text to UTF-8 */
583 gf_link_filter(gf_utf8, gf_utf8_opt(charset));
586 /* link in filters, similar to what is done in decode_text() */
587 if(!ps_global->pass_ctrl_chars){
588 gf_link_filter(gf_escape_filter, NULL);
589 filt_only_c0 = 1;
590 gf_link_filter(gf_control_filter,
591 gf_control_filter_opt(&filt_only_c0));
594 gf_link_filter(gf_tag_filter, NULL);
596 if((F_ON(F_VIEW_SEL_URL, ps_global)
597 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
598 || F_ON(F_SCAN_ADDR, ps_global))
599 && handlesp){
600 gf_link_filter(gf_line_test,
601 gf_line_test_opt(url_hilite,
602 gf_url_hilite_opt(&uh,handlesp,0)));
605 if((flgs & FM_DISPLAY)
606 && !(flgs & FM_NOCOLOR)
607 && pico_usingcolor()
608 && ps_global->VAR_SIGNATURE_FORE_COLOR
609 && ps_global->VAR_SIGNATURE_BACK_COLOR){
610 gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
613 if((flgs & FM_DISPLAY)
614 && !(flgs & FM_NOCOLOR)
615 && pico_usingcolor()
616 && ps_global->VAR_QUOTE1_FORE_COLOR
617 && ps_global->VAR_QUOTE1_BACK_COLOR){
618 gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
621 if(!(flgs & FM_NOWRAP)){
622 wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
623 if(flgs & FM_DISPLAY
624 && !(flgs & FM_NOCOLOR)
625 && pico_usingcolor())
626 wrapflags |= GFW_USECOLOR;
627 gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
628 (flgs & FM_NOINDENT)
629 ? NULL : format_view_margin(),
631 wrapflags));
634 gf_link_filter(gf_nvtnl_local, NULL);
635 if((decode_err = gf_pipe(gc, pc)) != NULL){
636 /* TRANSLATORS: There was an error putting together a message for
637 viewing. The arg is the description of the error. */
638 snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
639 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
640 if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
641 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
642 && gf_puts(NEWLINE, pc))
643 decode_err = NULL;
644 else
645 return(decode_err);
649 if(!text2){
650 if(!gf_puts(NEWLINE, pc)
651 || !gf_puts(_(" [ERROR fetching text of message]"), pc)
652 || !gf_puts(NEWLINE, pc)
653 || !gf_puts(NEWLINE, pc))
654 return("Write Error");
657 else{
658 int show_parts = 0;
660 /*======== Now loop through formatting all the parts =======*/
661 for(a = ps_global->atmts; a->description != NULL; a++) {
662 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
663 continue;
665 if(a->body->type == TYPEMULTIPART){
666 #ifdef SMIME
667 if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
668 if(a->description){
669 if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
670 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
671 return("Write Error");
674 #endif /* SMIME */
675 continue;
678 if(!a->shown) {
679 if(a->suppress_editorial)
680 continue;
682 if(!(flgs & FM_NOEDITORIAL)
683 && (!gf_puts(NEWLINE, pc)
684 || (decode_err = part_desc(a->number, a->body,
685 (flgs & FM_DISPLAY)
686 ? (a->can_display != MCD_NONE)
687 ? 1 : 2
688 : 3, width, flgs, pc))))
689 return("Write Error");
691 continue;
694 switch(a->body->type){
696 case TYPETEXT:
698 * If a message is multipart *and* the first part of it
699 * is text *and that text is empty, there is a good chance that
700 * there was actually something there that c-client was
701 * unable to parse. Here we report the empty message body
702 * and insert the raw RFC822.TEXT (if full-headers are
703 * on).
705 if(body->type == TYPEMULTIPART
706 && a == ps_global->atmts
707 && a->body->size.bytes == 0
708 && F_ON(F_ENABLE_FULL_HDR, ps_global)){
709 char *err = NULL;
711 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
712 "Empty or malformed message%s.",
713 ps_global->full_header == 2
714 ? ". Displaying raw text"
715 : ". Use \"H\" to see raw text");
716 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
718 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
719 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
720 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
721 return("Write Error");
723 if(ps_global->full_header == 2
724 && (err = detach_raw(ps_global->mail_stream, msgno,
725 a->number, pc, flgs))){
726 snprintf(tmp_20k_buf, SIZEOF_20KBUF,
727 "%s%s [ Formatting error: %s ]%s%s",
728 NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
729 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
730 if(!gf_puts(tmp_20k_buf, pc))
731 return("Write Error");
734 break;
738 * Don't write our delimiter if this text part is
739 * the first part of a message/rfc822 segment...
741 if(show_parts && a != ps_global->atmts
742 && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
743 #ifdef SMIME
744 || (a[-1].body->type == TYPEMULTIPART
745 && a[-1].body->subtype
746 && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
747 && &a[-1] != ps_global->atmts
748 && a[-2].body && a[-2].body->type == TYPEMESSAGE)
749 #endif /* SMIME */
751 && !(flgs & FM_NOEDITORIAL)){
752 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
753 tmp1 = "Calendar entry";
754 else
755 tmp1 = a->body->description ? a->body->description
756 : "Attached Text";
757 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
758 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
760 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
761 description);
762 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
763 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
764 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
765 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
766 return("Write Error");
768 /* skip calendar */
769 if(!MIME_VCALENDAR(a->body->type, a->body->subtype))
770 error_found += decode_text(a, msgno, pc, handlesp,
771 (flgs & FM_DISPLAY) ? InLine : QStatus,
772 flgs);
773 break;
775 case TYPEMESSAGE:
776 tmp1 = a->body->description ? a->body->description
777 : (strucmp(a->body->subtype, "delivery-status") == 0)
778 ? "Delivery Status"
779 : "Included Message";
780 description = iutf8ncpy((char *)(tmp_20k_buf+10000),
781 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
783 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
784 description);
785 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
787 if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
788 && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
789 && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
790 return("Write Error");
792 if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
793 /* imapenvonly, we may not have all the headers we need */
794 if(a->body->nested.msg->env->imapenvonly)
795 mail_fetch_header(ps_global->mail_stream, msgno,
796 a->number, NULL, NULL, FT_PEEK);
797 switch(format_header(ps_global->mail_stream, msgno, a->number,
798 a->body->nested.msg->env, hp,
799 NULL, handlesp, flgs, NULL, pc)){
800 case -1 : /* write error */
801 return("Write Error");
803 case 1 : /* fetch error */
804 if(!(gf_puts("[ Error fetching header ]", pc)
805 && !gf_puts(NEWLINE, pc)))
806 return("Write Error");
808 break;
811 else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
812 int *margin, avail, m1, m2;
814 avail = width;
815 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
817 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
818 avail -= m1;
820 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
821 avail -= m2;
823 if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
824 || !gf_puts(NEWLINE, pc)
825 || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
826 return("Write Error");
828 else
829 error_found += decode_text(a, msgno, pc, handlesp,
830 (flgs&FM_DISPLAY) ? InLine : QStatus,
831 flgs);
833 if(!gf_puts(NEWLINE, pc))
834 return("Write Error");
836 break;
838 default:
839 if((decode_err = part_desc(a->number, a->body,
840 (flgs & FM_DISPLAY) ? 1 : 3,
841 width, flgs, pc)) != NULL)
842 return("Write Error");
845 show_parts++;
848 if(!(!error_found
849 && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
850 && format_blip_seen(msgno)))
851 return("Cannot format body.");
854 return(NULL);
859 format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
861 ATTACH_S *a;
863 if(flgs & FM_NEW_MESS) {
864 zero_atmts(ps_global->atmts);
865 describe_mime(body, "", 1, 1, 0, flgs);
868 /*----- First do the list of parts/attachments if needed ----*/
869 if((flgs & FM_DISPLAY)
870 && (ps_global->atmts[1].description
871 || (ps_global->atmts[0].body
872 && ps_global->atmts[0].body->type != TYPETEXT))){
873 char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
874 int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
875 int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
876 int sizewid, descwid, dashwid, partwid, padwid;
877 COLOR_PAIR *hdrcolor = NULL;
879 if((flgs & FM_DISPLAY)
880 && !(flgs & FM_NOCOLOR)
881 && pico_usingcolor()
882 && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
883 && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
884 && ps_global->VAR_NORM_FORE_COLOR
885 && ps_global->VAR_NORM_BACK_COLOR
886 && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
887 ps_global->VAR_NORM_FORE_COLOR)
888 || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
889 ps_global->VAR_NORM_BACK_COLOR))){
891 if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
892 ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
893 if(!pico_is_good_colorpair(hdrcolor))
894 free_color_pair(&hdrcolor);
898 margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
901 * Attachment list header
904 avail = width;
906 m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
907 avail -= m1;
909 m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
910 avail -= m2;
912 hwid = MAX(avail, 0);
914 i = utf8_width(_("Parts/Attachments:"));
915 partwid = MIN(i, hwid);
916 padwid = hdrcolor ? (hwid-partwid) : 0;
918 if(m1 > 0){
919 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
920 if(!gf_puts(tmp, pc))
921 return(0);
924 utf8_snprintf(tmp, sizeof(tmp),
925 "%-*.*w%*.*s",
926 /* TRANSLATORS: A label */
927 partwid, partwid, _("Parts/Attachments:"),
928 padwid, padwid, "");
930 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
931 return(0);
934 /*----- Figure max display widths -----*/
935 for(a = ps_global->atmts; a->description != NULL; a++){
936 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
937 continue;
939 if((n = utf8_width(a->number)) > maxnumwid)
940 maxnumwid = n;
942 if((n = utf8_width(a->size)) > maxsizewid)
943 maxsizewid = n;
947 * ----- adjust max lengths for nice display -----
949 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
953 avail = width - m1 - m2;
955 s1 = MAX(MIN(1, avail), 0);
956 avail -= s1;
958 dwid = MAX(MIN(1, avail), 0);
959 avail -= dwid;
961 s2 = MAX(MIN(1, avail), 0);
962 avail -= s2;
964 maxnumwid = MIN(maxnumwid, width/3);
965 maxnumwid = MAX(MIN(maxnumwid, avail), 0);
966 avail -= maxnumwid;
968 s3 = MAX(MIN(1, avail), 0);
969 avail -= s3;
971 shownwid = MAX(MIN(5, avail), 0);
972 avail -= shownwid;
974 s4 = MAX(MIN(3, avail), 0);
975 avail -= s4;
977 sizewid = MAX(MIN(maxsizewid, avail), 0);
978 avail -= sizewid;
980 s5 = MAX(MIN(2, avail), 0);
981 avail -= s5;
983 descwid = MAX(0, avail);
985 /*----- Format the list of attachments -----*/
986 for(a = ps_global->atmts; a->description != NULL; a++){
987 COLOR_PAIR *lastc = NULL;
988 char numbuf[50];
989 int thisdescwid, padwid;
991 if(MIME_VCALENDAR(a->body->type, a->body->subtype))
992 continue;
993 #ifdef SMIME
994 if(a->body->type == TYPEMULTIPART
995 && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
996 continue;
997 #endif /* SMIME */
999 i = utf8_width((descwid > 2 && a->description) ? a->description : "");
1000 thisdescwid = MIN(i, descwid);
1001 padwid = hdrcolor ? (descwid-thisdescwid) : 0;
1003 if(m1 > 0){
1004 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1005 if(!gf_puts(tmp, pc))
1006 return(0);
1009 utf8_snprintf(tmp, sizeof(tmp),
1010 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1011 s1, s1, "",
1012 dwid, dwid,
1013 msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
1014 s2, s2, "",
1015 maxnumwid, maxnumwid,
1016 a->number
1017 ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
1018 : "",
1019 s3, s3, "",
1020 shownwid, shownwid,
1021 a->shown ? "Shown" :
1022 (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
1023 ? "OK " : "",
1024 s4, s4, "",
1025 sizewid, sizewid,
1026 a->size ? a->size : "",
1027 s5, s5, "",
1028 thisdescwid, thisdescwid,
1029 (descwid > 2 && a->description) ? a->description : "");
1031 if(!(!hdrcolor || embed_color(hdrcolor, pc)))
1032 return(0);
1034 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1035 char buf[16], color[64];
1036 int l;
1037 HANDLE_S *h;
1039 for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
1040 if(!(*pc)(' '))
1041 return(0);
1043 h = new_handle(handlesp);
1044 h->type = Attach;
1045 h->h.attach = a;
1047 snprintf(buf, sizeof(buf), "%d", h->key);
1048 buf[sizeof(buf)-1] = '\0';
1050 if(!(flgs & FM_NOCOLOR)
1051 && handle_start_color(color, sizeof(color), &l, 1)){
1052 lastc = get_cur_embedded_color();
1053 if(!gf_nputs(color, (long) l, pc))
1054 return(0);
1056 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
1057 && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
1058 return(0);
1060 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
1061 && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
1062 return(0);
1064 else
1065 tmpp = tmp;
1067 if(!format_env_puts(tmpp, pc))
1068 return(0);
1070 if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
1071 if(lastc){
1072 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1073 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1074 return(0);
1077 if(!embed_color(lastc, pc))
1078 return(0);
1080 free_color_pair(&lastc);
1082 else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
1083 return(0);
1085 if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
1086 return(0);
1089 if(padwid > 0){
1090 snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
1091 if(!gf_puts(tmp, pc))
1092 return(0);
1095 if(!gf_puts(NEWLINE, pc))
1096 return(0);
1100 * Dashed line after list
1103 if(hdrcolor){
1104 avail = width - m1 - m2;
1105 hwid = MAX(avail, 0);
1107 dashwid = MAX(MIN(40, hwid-2), 0);
1108 padwid = hwid - dashwid;
1109 if(m1 > 0){
1110 snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
1111 if(!gf_puts(tmp, pc))
1112 return(0);
1115 snprintf(tmp, sizeof(tmp),
1116 "%s%*.*s",
1117 repeat_char(dashwid, '-'),
1118 padwid, padwid, "");
1120 else{
1121 avail = width - m1 -2;
1123 dashwid = MAX(MIN(40, avail), 0);
1124 avail -= dashwid;
1126 snprintf(tmp, sizeof(tmp),
1127 "%*.*s%s",
1128 m1, m1, "",
1129 repeat_char(dashwid, '-'));
1132 if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
1133 return(0);
1135 if(hdrcolor)
1136 free_color_pair(&hdrcolor);
1139 return(1);
1145 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1146 * of body part fetches as we're formatting) for the
1147 * given message isn't set (likely because there
1148 * weren't any parts suitable for display), then make
1149 * sure to set it here.
1152 format_blip_seen(long int msgno)
1154 MESSAGECACHE *mc;
1156 if(msgno > 0L && ps_global->mail_stream
1157 && msgno <= ps_global->mail_stream->nmsgs
1158 && (mc = mail_elt(ps_global->mail_stream, msgno))
1159 && !mc->seen
1160 && !ps_global->mail_stream->rdonly)
1161 mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
1163 return(1);
1168 * is_an_env_hdr - is this name a header in the envelope structure?
1170 * name - the header name to check
1173 is_an_env_hdr(char *name)
1175 register int i;
1177 for(i = 0; envelope_hdrs[i].name; i++)
1178 if(!strucmp(name, envelope_hdrs[i].name))
1179 return(1);
1181 return(0);
1188 * is_an_addr_hdr - is this an address header?
1190 * name - the header name to check
1193 is_an_addr_hdr(char *fieldname)
1195 char fbuf[FBUF_LEN+1];
1196 char *colon, *fname;
1197 static char *addr_headers[] = {
1198 "from",
1199 "reply-to",
1200 "to",
1201 "cc",
1202 "bcc",
1203 "return-path",
1204 "sender",
1205 "x-sender",
1206 "x-x-sender",
1207 "resent-from",
1208 "resent-to",
1209 "resent-cc",
1210 NULL
1213 /* so it is pointing to NULL */
1214 char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
1216 if((colon = strindex(fieldname, ':')) != NULL){
1217 strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
1218 fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
1219 fname = fbuf;
1221 else
1222 fname = fieldname;
1224 if(fname && *fname){
1225 for(p = addr_headers; *p; p++)
1226 if(!strucmp(fname, *p))
1227 break;
1230 return((*p) ? 1 : 0);
1235 * Format a single field from the envelope
1237 void
1238 format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
1239 fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
1241 register int i;
1243 if(!fmt_env)
1244 fmt_env = format_envelope;
1246 for(i = 0; envelope_hdrs[i].name; i++)
1247 if(!strucmp(field, envelope_hdrs[i].name)){
1248 (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
1249 return;
1255 * Look through header string beginning with "begin", for the next
1256 * occurrence of header "field". Set "start" to that. Set "end" to point one
1257 * position past all of the continuation lines that go with "field".
1258 * That is, if "end" is converted to a null
1259 * character then the string "start" will be the next occurence of header
1260 * "field" including all of its continuation lines. Assume we
1261 * have CRLF's as end of lines.
1263 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1264 * make "end" the end of that header.
1266 * Returns 1 if found, 0 if not.
1269 delineate_this_header(char *field, char *begin, char **start, char **end)
1271 char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
1272 char *p;
1273 char *begin_srch;
1275 if(field == NULL){
1276 if(!begin || !*begin || isspace((unsigned char)*begin))
1277 return 0;
1278 else
1279 *start = begin;
1281 else{
1282 strncpy(tmpfield, field, sizeof(tmpfield)-2);
1283 tmpfield[sizeof(tmpfield)-2] = '\0';
1284 strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
1285 tmpfield[sizeof(tmpfield)-1] = '\0';
1288 * We require that start is at the beginning of a line, so
1289 * either it equals begin (which we assume is the beginning of a
1290 * line) or it is preceded by a CRLF.
1292 begin_srch = begin;
1293 *start = srchstr(begin_srch, tmpfield);
1294 while(*start && *start != begin
1295 && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
1296 begin_srch = *start + 1;
1297 *start = srchstr(begin_srch, tmpfield);
1300 if(!*start)
1301 return 0;
1304 for(p = *start; *p; p++){
1305 if(ISRFCEOL(p)
1306 && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
1308 * The final 015 in the test above is to test for the end
1309 * of the headers.
1311 *end = p+2;
1312 break;
1316 if(!*p)
1317 *end = p;
1319 return 1;
1325 handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
1327 *len = 0;
1329 if(pico_usingcolor()){
1330 char *fg = NULL, *bg = NULL, *s;
1331 char *basefg = NULL, *basebg = NULL;
1333 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
1334 : ps_global->VAR_NORM_FORE_COLOR;
1335 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
1336 : ps_global->VAR_NORM_BACK_COLOR;
1338 if(ps_global->VAR_SLCTBL_FORE_COLOR
1339 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
1340 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
1342 if(ps_global->VAR_SLCTBL_BACK_COLOR
1343 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
1344 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
1346 if(bg || fg){
1347 COLOR_PAIR *tmp;
1350 * The blacks are just known good colors for
1351 * testing whether the other color is good.
1353 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
1354 bg ? bg : colorx(COL_BLACK))) != NULL){
1355 if(pico_is_good_colorpair(tmp))
1356 for(s = color_embed(fg, bg);
1357 (*len) < buflen && (colorstring[*len] = *s);
1358 s++, (*len)++)
1361 free_color_pair(&tmp);
1365 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1366 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
1367 *len += 2;
1371 colorstring[buflen-1] = '\0';
1373 return(*len != 0);
1378 handle_end_color(char *colorstring, size_t buflen, int *len)
1380 *len = 0;
1381 if(pico_usingcolor()){
1382 char *fg = NULL, *bg = NULL, *s;
1385 * We need to change the fg and bg colors back even if they
1386 * are the same as the Normal Colors so that color_a_quote
1387 * will have a chance to pick up those colors as the ones to
1388 * switch to. We don't do this before the handle above so that
1389 * the quote color will flow into the selectable item when
1390 * the selectable item color is partly the same as the
1391 * normal color. That is, suppose the normal color was black on
1392 * cyan and the selectable color was blue on cyan, only a fg color
1393 * change. We preserve the only-a-fg-color-change in a quote by
1394 * letting the quote background color flow into the selectable text.
1396 if(ps_global->VAR_SLCTBL_FORE_COLOR)
1397 fg = ps_global->VAR_NORM_FORE_COLOR;
1399 if(ps_global->VAR_SLCTBL_BACK_COLOR)
1400 bg = ps_global->VAR_NORM_BACK_COLOR;
1402 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
1403 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
1404 *len = 2;
1407 if(fg || bg)
1408 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
1412 colorstring[buflen-1] = '\0';
1414 return(*len != 0);
1418 char *
1419 url_embed(int embed)
1421 static char buf[3] = {TAG_EMBED};
1422 buf[1] = embed;
1423 buf[2] = '\0';
1424 return(buf);
1429 * Paint the signature.
1432 color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
1434 struct variable *vars = ps_global->vars;
1435 int *in_sig_block;
1436 COLOR_PAIR *col = NULL;
1438 if(is_in_sig == NULL)
1439 return 0;
1441 in_sig_block = (int *) is_in_sig;
1443 if(!strcmp(line, SIGDASHES))
1444 *in_sig_block = START_SIG_BLOCK;
1445 else if(*line == '\0')
1447 * Suggested by Eduardo: allow for a blank line right after
1448 * the sigdashes.
1450 *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
1451 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1452 else
1453 *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
1454 ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
1456 if(*in_sig_block != OUT_SIG_BLOCK
1457 && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
1458 && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
1459 VAR_SIGNATURE_BACK_COLOR))){
1460 if(!pico_is_good_colorpair(col))
1461 free_color_pair(&col);
1464 if(col){
1465 char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1467 ins = gf_line_test_new_ins(ins, line,
1468 color_embed(col->fg, col->bg),
1469 (2 * RGBLEN) + 4);
1471 strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
1472 fg[sizeof(fg)-1] = '\0';
1473 strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
1474 bg[sizeof(bg)-1] = '\0';
1477 * Loop watching colors, and override with
1478 * signature color whenever the normal foreground and background
1479 * colors are in force.
1482 for(p = line; *p; )
1483 if(*p++ == TAG_EMBED){
1485 switch(*p++){
1486 case TAG_HANDLE :
1487 p += *p + 1; /* skip handle key */
1488 break;
1490 case TAG_FGCOLOR :
1491 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1492 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1493 p += RGBLEN; /* advance past color value */
1495 if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
1496 && !colorcmp(bg, VAR_NORM_BACK_COLOR))
1497 ins = gf_line_test_new_ins(ins, p,
1498 color_embed(col->fg,NULL),
1499 RGBLEN + 2);
1500 break;
1502 case TAG_BGCOLOR :
1503 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1504 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1505 p += RGBLEN; /* advance past color value */
1507 if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
1508 && !colorcmp(fg, VAR_NORM_FORE_COLOR))
1509 ins = gf_line_test_new_ins(ins, p,
1510 color_embed(NULL,col->bg),
1511 RGBLEN + 2);
1513 break;
1515 default :
1516 break;
1520 ins = gf_line_test_new_ins(ins, line + strlen(line),
1521 color_embed(VAR_NORM_FORE_COLOR,
1522 VAR_NORM_BACK_COLOR),
1523 (2 * RGBLEN) + 4);
1524 free_color_pair(&col);
1527 return 0;
1532 * Line filter to add color to displayed headers.
1535 color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
1537 static char field[FBUF_LEN + 1];
1538 char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
1539 char *p, *q, *value, *beg;
1540 COLOR_PAIR *color;
1541 int in_quote = 0, in_comment = 0, did_color = 0;
1542 struct variable *vars = ps_global->vars;
1544 field[FBUF_LEN] = '\0';
1546 if(isspace((unsigned char)*line)) /* continuation line */
1547 value = line;
1548 else{
1549 if(!(value = strindex(line, ':')))
1550 return(0);
1552 memset(field, 0, sizeof(field));
1553 strncpy(field, line, MIN(value-line, sizeof(field)-1));
1556 for(value++; isspace((unsigned char)*value); value++)
1559 strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
1560 fg[sizeof(fg)-1] = '\0';
1561 strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
1562 bg[sizeof(bg)-1] = '\0';
1565 * Split into two cases depending on whether this is a header which
1566 * contains addresses or not. We may color addresses separately.
1568 if(is_an_addr_hdr(field)){
1571 * If none of the patterns are for this header, don't bother parsing
1572 * and checking each address.
1574 if(!any_hdr_color(field))
1575 return(0);
1578 * First check for patternless patterns which color whole line.
1580 if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
1581 if(pico_is_good_colorpair(color)){
1582 ins = gf_line_test_new_ins(ins, value,
1583 color_embed(color->fg, color->bg),
1584 (2 * RGBLEN) + 4);
1585 strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
1586 fg[sizeof(fg)-1] = '\0';
1587 strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
1588 bg[sizeof(bg)-1] = '\0';
1589 did_color++;
1591 else
1592 free_color_pair(&color);
1596 * Then go through checking address by address.
1597 * Keep track of quotes and watch for color changes, and override
1598 * with most recent header color whenever the normal foreground
1599 * and background colors are in force.
1601 beg = p = value;
1602 while(*p){
1603 switch(*p){
1604 case '\\':
1605 /* skip next character */
1606 if(*(p+1) && (in_comment || in_quote))
1607 p += 2;
1608 else
1609 p++;
1611 break;
1613 case '"':
1614 if(!in_comment)
1615 in_quote = 1 - in_quote;
1617 p++;
1618 break;
1620 case '(':
1621 in_comment++;
1622 p++;
1623 break;
1625 case ')':
1626 if(in_comment > 0)
1627 in_comment--;
1629 p++;
1630 break;
1632 case ',':
1633 if(!(in_quote || in_comment)){
1634 /* we reached the end of this address */
1635 *p = '\0';
1636 if(color)
1637 free_color_pair(&color);
1639 if((color = hdr_color(field, beg,
1640 ps_global->hdr_colors)) != NULL){
1641 if(pico_is_good_colorpair(color)){
1642 did_color++;
1643 ins = gf_line_test_new_ins(ins, beg,
1644 color_embed(color->fg,
1645 color->bg),
1646 (2 * RGBLEN) + 4);
1647 *p = ',';
1648 for(q = p; q > beg &&
1649 isspace((unsigned char)*(q-1)); q--)
1652 ins = gf_line_test_new_ins(ins, q,
1653 color_embed(fg, bg),
1654 (2 * RGBLEN) + 4);
1656 else
1657 free_color_pair(&color);
1659 else
1660 *p = ',';
1662 for(p++; isspace((unsigned char)*p); p++)
1665 beg = p;
1667 else
1668 p++;
1670 break;
1672 case TAG_EMBED:
1673 switch(*(++p)){
1674 case TAG_HANDLE:
1675 p++;
1676 p += *p + 1; /* skip handle key */
1677 break;
1679 case TAG_FGCOLOR:
1680 p++;
1681 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1682 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1683 p += RGBLEN; /* advance past color value */
1685 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
1686 ins = gf_line_test_new_ins(ins, p,
1687 color_embed(color->fg,NULL),
1688 RGBLEN + 2);
1689 break;
1691 case TAG_BGCOLOR:
1692 p++;
1693 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1694 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1695 p += RGBLEN; /* advance past color value */
1697 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
1698 ins = gf_line_test_new_ins(ins, p,
1699 color_embed(NULL,color->bg),
1700 RGBLEN + 2);
1702 break;
1704 default:
1705 break;
1708 break;
1710 default:
1711 p++;
1712 break;
1716 for(q = beg; *q && isspace((unsigned char)*q); q++)
1719 if(*q && !(in_quote || in_comment)){
1720 /* we reached the end of this address */
1721 if(color)
1722 free_color_pair(&color);
1724 if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
1725 if(pico_is_good_colorpair(color)){
1726 did_color++;
1727 ins = gf_line_test_new_ins(ins, beg,
1728 color_embed(color->fg,
1729 color->bg),
1730 (2 * RGBLEN) + 4);
1731 for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
1734 ins = gf_line_test_new_ins(ins, q,
1735 color_embed(fg, bg),
1736 (2 * RGBLEN) + 4);
1738 else
1739 free_color_pair(&color);
1743 if(color)
1744 free_color_pair(&color);
1746 if(did_color)
1747 ins = gf_line_test_new_ins(ins, line + strlen(line),
1748 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1749 VAR_HEADER_GENERAL_BACK_COLOR),
1750 (2 * RGBLEN) + 4);
1752 else{
1754 color = hdr_color(field, value, ps_global->hdr_colors);
1756 if(color){
1757 if(pico_is_good_colorpair(color)){
1758 ins = gf_line_test_new_ins(ins, value,
1759 color_embed(color->fg, color->bg),
1760 (2 * RGBLEN) + 4);
1763 * Loop watching colors, and override with header
1764 * color whenever the normal foreground and background
1765 * colors are in force.
1767 p = value;
1768 while(*p)
1769 if(*p++ == TAG_EMBED){
1771 switch(*p++){
1772 case TAG_HANDLE:
1773 p += *p + 1; /* skip handle key */
1774 break;
1776 case TAG_FGCOLOR:
1777 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1778 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1779 p += RGBLEN; /* advance past color value */
1781 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
1782 && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
1783 ins = gf_line_test_new_ins(ins, p,
1784 color_embed(color->fg,NULL),
1785 RGBLEN + 2);
1786 break;
1788 case TAG_BGCOLOR:
1789 snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
1790 rgbbuf[sizeof(rgbbuf)-1] = '\0';
1791 p += RGBLEN; /* advance past color value */
1793 if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
1794 && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
1795 ins = gf_line_test_new_ins(ins, p,
1796 color_embed(NULL,color->bg),
1797 RGBLEN + 2);
1799 break;
1801 default:
1802 break;
1806 ins = gf_line_test_new_ins(ins, line + strlen(line),
1807 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
1808 VAR_HEADER_GENERAL_BACK_COLOR),
1809 (2 * RGBLEN) + 4);
1812 free_color_pair(&color);
1816 return(0);
1821 url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
1823 register char *lp, *up = NULL, *urlp = NULL,
1824 *weburlp = NULL, *mailurlp = NULL;
1825 int n, n1, n2, n3, l;
1826 char buf[256], color[256];
1827 HANDLE_S *h;
1828 URL_HILITE_S *uh;
1830 for(lp = line; ; lp = up + n){
1831 /* scan for all of them so we can choose the first */
1832 if(F_ON(F_VIEW_SEL_URL,ps_global))
1833 urlp = rfc1738_scan(lp, &n1);
1834 if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
1835 weburlp = web_host_scan(lp, &n2);
1836 if(F_ON(F_SCAN_ADDR,ps_global))
1837 mailurlp = mail_addr_scan(lp, &n3);
1839 if(urlp || weburlp || mailurlp){
1840 up = urlp ? urlp :
1841 weburlp ? weburlp : mailurlp;
1842 if(up == urlp && weburlp && weburlp < up)
1843 up = weburlp;
1844 if(mailurlp && mailurlp < up)
1845 up = mailurlp;
1847 if(up == urlp){
1848 n = n1;
1849 weburlp = mailurlp = NULL;
1851 else if(up == weburlp){
1852 n = n2;
1853 mailurlp = NULL;
1855 else{
1856 n = n3;
1857 weburlp = NULL;
1860 else
1861 break;
1863 uh = (URL_HILITE_S *) local;
1865 h = new_handle(uh->handlesp);
1866 h->type = URL;
1867 h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
1868 snprintf(h->h.url.path, n+10, "%s%.*s",
1869 weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
1870 h->h.url.path[n+10-1] = '\0';
1872 if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
1873 ins = gf_line_test_new_ins(ins, up, color, l);
1874 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
1875 ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
1877 buf[0] = TAG_EMBED;
1878 buf[1] = TAG_HANDLE;
1879 snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
1880 buf[sizeof(buf)-1] = '\0';
1881 buf[2] = strlen(&buf[3]);
1882 ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
1884 /* in case it was the current selection */
1885 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
1887 if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
1888 ins = gf_line_test_new_ins(ins, up + n, color, l);
1889 else
1890 ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
1892 urlp = weburlp = mailurlp = NULL;
1895 return(0);
1900 url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
1902 static int check_for_urls = 0;
1903 register char *lp;
1905 if(isspace((unsigned char)*line)) /* continuation, check or not
1906 depending on last line */
1907 lp = line;
1908 else{
1909 check_for_urls = 0;
1910 if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
1911 FieldType ft;
1913 *lp = '\0';
1915 if(((ft = pine_header_standard(line)) == FreeText
1916 || ft == Subject
1917 || ft == TypeUnknown)
1918 && strucmp(line, "message-id")
1919 && strucmp(line, "newsgroups")
1920 && strucmp(line, "references")
1921 && strucmp(line, "in-reply-to")
1922 && strucmp(line, "received")
1923 && strucmp(line, "date")){
1924 check_for_urls = 1;
1927 *lp = ':';
1931 if(check_for_urls)
1932 (void) url_hilite(linenum, lp + 1, ins, local);
1934 return(0);
1939 pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
1941 char *p;
1942 int wid = 0;
1943 int total_wid;
1944 struct variable *vars = ps_global->vars;
1946 if(!line[0])
1947 return 0;
1949 total_wid = *((int *) local);
1951 /* calculate width of line */
1952 p = line;
1953 while(*p){
1955 switch(*p){
1956 case TAG_EMBED:
1957 p++;
1958 switch(*p){
1959 case TAG_HANDLE:
1960 p++;
1961 p += *p + 1; /* skip handle key */
1962 break;
1964 case TAG_FGCOLOR :
1965 case TAG_BGCOLOR :
1966 p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
1967 break;
1969 case TAG_INVON:
1970 case TAG_INVOFF:
1971 case TAG_BOLDON:
1972 case TAG_BOLDOFF:
1973 case TAG_ULINEON:
1974 case TAG_ULINEOFF:
1975 p++;
1976 break;
1978 default: /* literal embed char */
1979 break;
1982 break;
1984 case TAB:
1985 p++;
1986 while(((++wid) & 0x07) != 0) /* add tab's spaces */
1989 break;
1991 default:
1992 wid += width_at_this_position((unsigned char *) p, strlen(p));
1993 p++;
1994 break;
1998 if(total_wid > wid){
1999 ins = gf_line_test_new_ins(ins, line + strlen(line),
2000 color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
2001 VAR_HEADER_GENERAL_BACK_COLOR),
2002 (2 * RGBLEN) + 4);
2003 ins = gf_line_test_new_ins(ins, line+strlen(line),
2004 repeat_char(total_wid-wid, ' '), total_wid-wid);
2005 ins = gf_line_test_new_ins(ins, line + strlen(line),
2006 color_embed(VAR_NORM_FORE_COLOR,
2007 VAR_NORM_BACK_COLOR),
2008 (2 * RGBLEN) + 4);
2011 return(0);
2016 #define UES_LEN 12
2017 #define UES_MAX 32
2019 url_external_specific_handler(char *url, int len)
2021 static char list[UES_LEN * UES_MAX];
2023 if(url){
2024 char *p;
2025 int i;
2027 for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
2028 if(strlen(p) == len && !struncmp(p, url, len))
2029 return(1);
2031 else{ /* initialize! */
2032 char **l, *test, *cmd, *p, *p2;
2033 int i = 0, n;
2035 memset(list, 0, sizeof(list));
2036 for(l = ps_global->VAR_BROWSER ; l && *l; l++){
2037 get_pair(*l, &test, &cmd, 1, 1);
2039 if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
2040 *p2 = '\0';
2042 for(p += 8; *p && i < UES_MAX; p += n)
2043 if((p2 = strchr(p, ',')) != NULL){
2044 if((n = p2 - p) < UES_LEN){
2045 strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
2046 i++;
2048 else
2049 dprint((1,
2050 "* * * HANLDER TOO LONG: %.*s\n", n,
2051 p ? p : "?"));
2053 n++;
2055 else{
2056 if(strlen(p) <= UES_LEN){
2057 strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
2058 i++;
2061 break;
2065 if(test)
2066 fs_give((void **) &test);
2068 if(cmd)
2069 fs_give((void **) &cmd);
2073 return(0);
2078 url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
2079 imapuid_t *uid, char **search, int silent)
2081 char *url, *scheme, *p, *cmd, *server = NULL,
2082 *user = NULL, *auth = NULL, *mailbox = NULL,
2083 *section = NULL;
2084 size_t l;
2085 int rv = URL_IMAP_ERROR;
2088 * Since we're planting nulls, operate on a temporary copy...
2090 scheme = silent ? NULL : "IMAP";
2091 url = cpystr(true_url + 7);
2093 /* Try to pick apart the "iserver" portion */
2094 if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
2095 *cmd++ = '\0';
2097 else{
2098 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2099 url ? url : "?"));
2100 cmd = &url[strlen(url)-1]; /* assume only iserver */
2103 if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
2104 *p++ = '\0';
2105 server = rfc1738_str(p);
2107 /* only ";auth=*" supported (and also ";auth=anonymous") */
2108 if((p = srchstr(url, ";auth=")) != NULL){
2109 *p = '\0';
2110 auth = rfc1738_str(p + 6);
2113 if(*url)
2114 user = rfc1738_str(url);
2116 else
2117 server = rfc1738_str(url);
2119 if(!*server)
2120 return(url_bogus_imap(&url, scheme, "No server specified"));
2123 * "iserver" in hand, pick apart the "icommand"...
2125 p = NULL;
2126 if(!*cmd || (p = srchstr(cmd, ";type="))){
2127 char *criteria;
2130 * No "icommand" (all top-level folders) or "imailboxlist"...
2132 if(p){
2133 *p = '\0'; /* tie off criteria */
2134 criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
2135 if(!strucmp(p = rfc1738_str(p+6), "lsub"))
2136 rv |= URL_IMAP_IMBXLSTLSUB;
2137 else if(strucmp(p, "list"))
2138 return(url_bogus_imap(&url, scheme,
2139 "Invalid list type specified"));
2141 else{
2142 rv |= URL_IMAP_ISERVERONLY;
2143 criteria = "";
2146 /* build folder list from specified server/criteria/list-method */
2147 l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
2148 *folder = (char *) fs_get((l+1) * sizeof(char));
2149 snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
2150 user ? "user=\"" : "Anonymous",
2151 user ? user : "",
2152 user ? "\"" : "",
2153 *criteria ? "[" : "", criteria, *criteria ? "[" : "");
2154 (*folder)[l] = '\0';
2155 rv |= URL_IMAP_IMAILBOXLIST;
2157 else{
2158 if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
2159 *p = '\0'; /* tie off mailbox [uidvalidity] */
2160 if((section = srchstr(p += 6, "/;section=")) != NULL){
2161 *section = '\0'; /* tie off UID */
2162 section = rfc1738_str(section + 10);
2163 /* BUG: verify valid section spec ala rfc 2060 */
2164 dprint((2,
2165 "-- URL IMAP FOLDER: section not used: %s\n",
2166 section ? section : "?"));
2169 if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
2170 return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
2172 /* optional "uidvalidity"? */
2173 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2174 *p = '\0';
2175 p += 13;
2176 if(!(*uid_val = rfc1738_num(&p)) || *p)
2177 return(url_bogus_imap(&url, scheme,
2178 "Invalid UIDVALIDITY"));
2181 mailbox = rfc1738_str(cmd);
2182 rv = URL_IMAP_IMESSAGEPART;
2184 else{ /* "imessagelist" */
2185 /* optional "uidvalidity"? */
2186 if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
2187 *p = '\0';
2188 p += 13;
2189 if(!(*uid_val = rfc1738_num(&p)) || *p)
2190 return(url_bogus_imap(&url, scheme,
2191 "Invalid UIDVALIDITY"));
2194 /* optional "enc_search"? */
2195 if((p = strchr(cmd, '?')) != NULL){
2196 *p = '\0';
2197 if(search)
2198 *search = cpystr(rfc1738_str(p + 1));
2199 /* BUG: verify valid search spec ala rfc 2060 */
2202 mailbox = rfc1738_str(cmd);
2203 rv = URL_IMAP_IMESSAGELIST;
2206 if(auth && *auth != '*' && strucmp(auth, "anonymous"))
2207 q_status_message(SM_ORDER, 3, 3,
2208 "Unsupported authentication method. Using standard login.");
2211 * At this point our structure should contain the
2212 * digested url. Now put it together for c-client...
2214 l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
2215 + (user ? (strlen(user)+2) : 9);
2216 *folder = (char *) fs_get((l+1) * sizeof(char));
2217 snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
2218 (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
2219 user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
2220 user ? user : "",
2221 user ? "\"" : "",
2222 mailbox);
2223 (*folder)[l] = '\0';
2226 fs_give((void **) &url);
2227 return(rv);
2232 url_bogus_imap(char **freeme, char *url, char *problem)
2234 fs_give((void **) freeme);
2235 (void) url_bogus(url, problem);
2236 return(URL_IMAP_ERROR);
2241 * url_bogus - report url syntax errors and such
2244 url_bogus(char *url, char *reason)
2246 dprint((2, "-- bogus url \"%s\": %s\n",
2247 url ? url : "<NULL URL>", reason ? reason : "?"));
2248 if(url)
2249 q_status_message3(SM_ORDER|SM_DING, 2, 3,
2250 "Malformed \"%.*s\" URL: %.200s",
2251 (void *) (strchr(url, ':') - url), url, reason);
2253 return(0);
2258 /*----------------------------------------------------------------------
2259 Format header text suitable for display
2261 Args: stream -- mail stream for various header text fetches
2262 msgno -- sequence number in stream of message we're interested in
2263 section -- which section of message
2264 env -- pointer to msg's envelope
2265 hdrs -- struct containing what's to get formatted
2266 prefix -- prefix to append to each output line
2267 handlesp -- address of pointer to the message's handles
2268 flags -- FM_ flags, see pith/mailview.h
2269 final_pc -- function to write header text with
2271 Result: 0 if all's well, -1 if write error, 1 if fetch error
2273 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2274 in the local convention.
2276 ----*/
2277 #define FHT_OK 0
2278 #define FHT_WRTERR -1
2279 #define FHT_FTCHERR 1
2281 format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
2282 HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
2283 fmt_env_t fmt_env, gf_io_t final_pc)
2285 int rv = FHT_OK;
2286 int nfields, i;
2287 char *h = NULL, **fields = NULL, **v, *q, *start,
2288 *finish, *current;
2289 STORE_S *tmp_store;
2290 URL_HILITE_S uh;
2291 gf_io_t tmp_pc, tmp_gc;
2292 struct variable *vars = ps_global->vars;
2294 if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
2295 gf_set_so_writec(&tmp_pc, tmp_store);
2296 else
2297 return(FHT_WRTERR);
2299 if(!fmt_env)
2300 fmt_env = format_envelope;
2302 if(ps_global->full_header == 2){
2303 rv = format_raw_header(stream, msgno, section, tmp_pc);
2305 else{
2307 * First, calculate how big a fields array we need.
2310 /* Custom header viewing list specified */
2311 if(hdrs->type == HD_LIST){
2312 /* view all these headers */
2313 if(!hdrs->except){
2314 for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2315 if(!is_an_env_hdr(q))
2316 nfields++;
2318 /* view all except these headers */
2319 else{
2320 for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
2321 nfields++;
2323 if(nfields > 1)
2324 nfields--; /* subtract one for ALL_EXCEPT field */
2327 else
2328 nfields = 6; /* default view */
2330 /* allocate pointer space */
2331 if(nfields){
2332 fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
2333 memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
2336 if(hdrs->type == HD_LIST){
2337 /* view all these headers */
2338 if(!hdrs->except){
2339 /* put the non-envelope headers in fields */
2340 if(nfields)
2341 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2342 if(!is_an_env_hdr(q))
2343 fields[i++] = q;
2345 /* view all except these headers */
2346 else{
2347 /* put the list of headers not to view in fields */
2348 if(nfields)
2349 for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
2350 if(strucmp(ALL_EXCEPT, q))
2351 fields[i++] = q;
2354 v = hdrs->h.l;
2356 else{
2357 if(nfields){
2358 fields[i = 0] = "Resent-Date";
2359 fields[++i] = "Resent-From";
2360 fields[++i] = "Resent-To";
2361 fields[++i] = "Resent-cc";
2362 fields[++i] = "Resent-Subject";
2365 v = fields;
2368 /* custom view with exception list */
2369 if(hdrs->type == HD_LIST && hdrs->except){
2371 * Go through each header in h and print it.
2372 * First we check to see if it is an envelope header so we
2373 * can print our envelope version of it instead of the raw version.
2376 /* fetch all the other headers */
2377 if(nfields)
2378 h = pine_fetchheader_lines_not(stream, msgno, section, fields);
2380 for(current = h;
2381 h && delineate_this_header(NULL, current, &start, &finish);
2382 current = finish){
2383 char tmp[MAILTMPLEN+1];
2384 char *colon_loc;
2386 colon_loc = strindex(start, ':');
2387 if(colon_loc && colon_loc < finish){
2388 strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
2389 tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
2391 else
2392 colon_loc = NULL;
2394 if(colon_loc && is_an_env_hdr(tmp)){
2395 char *dummystart, *dummyfinish;
2398 * Pretty format for env hdrs.
2399 * If the same header appears more than once, only
2400 * print the last to avoid duplicates.
2401 * They should have been combined in the env when parsed.
2403 if(!delineate_this_header(tmp, current+1, &dummystart,
2404 &dummyfinish))
2405 format_env_hdr(stream, msgno, section, env,
2406 fmt_env, tmp_pc, tmp, hdrs->charset, flags);
2408 else{
2409 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2410 hdrs->charset, flags)) != 0)
2411 goto write_error;
2412 else
2413 start = finish;
2417 /* custom view or default */
2418 else{
2419 /* fetch the non-envelope headers */
2420 if(nfields)
2421 h = pine_fetchheader_lines(stream, msgno, section, fields);
2423 /* default envelope for default view */
2424 if(hdrs->type == HD_BFIELD)
2425 (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
2427 /* go through each header in list, v initialized above */
2428 for(; (q = *v) != NULL; v++){
2429 if(is_an_env_hdr(q)){
2430 /* pretty format for env hdrs */
2431 format_env_hdr(stream, msgno, section, env,
2432 fmt_env, tmp_pc, q, hdrs->charset, flags);
2434 else{
2436 * Go through h finding all occurences of this header
2437 * and all continuation lines, and output.
2439 for(current = h;
2440 h && delineate_this_header(q,current,&start,&finish);
2441 current = finish){
2442 if((rv = format_raw_hdr_string(start, finish, tmp_pc,
2443 hdrs->charset, flags)) != 0)
2444 goto write_error;
2445 else
2446 start = finish;
2454 write_error:
2456 gf_clear_so_writec(tmp_store);
2458 if(!rv){ /* valid data? Do wrapping and filtering... */
2459 int column;
2460 char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
2461 STORE_S *df_store = NULL;
2463 so_seek(tmp_store, 0L, 0);
2465 column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
2468 * Test for and act on any display filter
2469 * This barely makes sense. The display filter is going
2470 * to be getting UTF-8'ized headers here. In pre-alpine
2471 * pine the display filter was being fed already decoded
2472 * headers in whatever character set they were in.
2473 * The good news is that that didn't make much
2474 * sense either, so this shouldn't break anything.
2475 * It seems unlikely that anybody is doing anything useful
2476 * with the header part of display filters.
2478 if(ps_global->tools.display_filter
2479 && ps_global->tools.display_filter_trigger
2480 && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
2481 if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
2483 gf_set_so_writec(&tmp_pc, df_store);
2484 gf_set_so_readc(&tmp_gc, df_store);
2485 if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
2486 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2487 _("Formatting error: %s"), errstr);
2488 rv = FHT_WRTERR;
2490 else
2491 so_seek(df_store, 0L, 0);
2493 gf_clear_so_writec(df_store);
2495 else{
2496 q_status_message(SM_ORDER | SM_DING, 3, 3,
2497 "No space for filtered text.");
2498 rv = FHT_WRTERR;
2501 else{
2502 so_seek(tmp_store, 0L, 0);
2503 gf_set_so_readc(&tmp_gc, tmp_store);
2506 if(!rv){
2507 int *margin, wrapflags = GFW_ONCOMMA;
2509 gf_filter_init();
2510 gf_link_filter(gf_local_nvtnl, NULL);
2512 if((F_ON(F_VIEW_SEL_URL, ps_global)
2513 || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
2514 || F_ON(F_SCAN_ADDR, ps_global))
2515 && handlesp){
2516 gf_link_filter(gf_line_test,
2517 gf_line_test_opt(url_hilite_hdr,
2518 gf_url_hilite_opt(&uh,handlesp,1)));
2519 wrapflags |= GFW_HANDLES;
2522 if((flags & FM_DISPLAY)
2523 && !(flags & FM_NOCOLOR)
2524 && pico_usingcolor()
2525 && ((VAR_NORM_FORE_COLOR
2526 && VAR_HEADER_GENERAL_FORE_COLOR
2527 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2529 (VAR_NORM_BACK_COLOR
2530 && VAR_HEADER_GENERAL_BACK_COLOR
2531 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
2532 wrapflags |= GFW_HDRCOLOR;
2534 if((flags & FM_DISPLAY)
2535 && !(flags & FM_NOCOLOR)
2536 && pico_usingcolor()
2537 && ps_global->hdr_colors){
2538 gf_link_filter(gf_line_test,
2539 gf_line_test_opt(color_headers, NULL));
2540 wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
2543 if(prefix && *prefix)
2544 column = MAX(column-strlen(prefix), 50);
2546 margin = format_view_margin();
2548 if(!(flags & FM_NOWRAP))
2549 gf_link_filter(gf_wrap,
2550 gf_wrap_filter_opt(column, column,
2551 (flags & FM_NOINDENT) ? NULL : margin,
2552 4, wrapflags));
2554 if(prefix && *prefix)
2555 gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
2557 if((flags & FM_DISPLAY)
2558 && !(flags & FM_NOCOLOR)
2559 && pico_usingcolor()
2560 && ((VAR_NORM_FORE_COLOR
2561 && VAR_HEADER_GENERAL_FORE_COLOR
2562 && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
2564 (VAR_NORM_BACK_COLOR
2565 && VAR_HEADER_GENERAL_BACK_COLOR
2566 && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
2567 int right_margin;
2568 int total_wid;
2570 right_margin = margin ? margin[1] : 0;
2571 total_wid = column - right_margin;
2573 gf_link_filter(gf_line_test,
2574 gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
2575 wrapflags |= GFW_HANDLES;
2578 gf_link_filter(gf_nvtnl_local, NULL);
2580 if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
2581 rv = FHT_WRTERR;
2582 q_status_message1(SM_ORDER | SM_DING, 3, 3,
2583 "Can't build header : %.200s", errstr);
2587 if(df_store){
2588 gf_clear_so_readc(df_store);
2589 so_give(&df_store);
2591 else
2592 gf_clear_so_readc(tmp_store);
2595 so_give(&tmp_store);
2597 if(h)
2598 fs_give((void **)&h);
2600 if(fields)
2601 fs_give((void **)&fields);
2603 return(rv);
2607 /*----------------------------------------------------------------------
2608 Format RAW header text for display
2610 Args: stream -- mail stream for various header text fetches
2611 rawno -- sequence number in stream of message we're interested in
2612 section -- which section of message
2613 pc -- function to write header text with
2615 Result: 0 if all's well, -1 if write error, 1 if fetch error
2617 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2618 in the local convention.
2620 ----*/
2622 format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
2624 char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
2625 unsigned char c;
2627 if(h){
2628 while(*h){
2629 if(ISRFCEOL(h)){
2630 h += 2;
2631 if(!gf_puts(NEWLINE, pc))
2632 return(FHT_WRTERR);
2634 if(ISRFCEOL(h)) /* all done! */
2635 return(FHT_OK);
2637 else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
2638 !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
2639 c = (unsigned char) *h++;
2640 if(!((*pc)(c >= 0x80 ? '~' : '^')
2641 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
2642 return(FHT_WRTERR);
2644 else if(!(*pc)(*h++))
2645 return(FHT_WRTERR);
2648 else
2649 return(FHT_FTCHERR);
2651 return(FHT_OK);
2656 /*----------------------------------------------------------------------
2657 Format c-client envelope data suitable for display
2659 Args: s -- mail stream for various header text fetches
2660 n -- raw sequence number in stream of message we're interested in
2661 sect -- which section of message
2662 e -- pointer to msg's envelope
2663 pc -- function to write header text with
2664 which -- which header lines to write
2665 oacs --
2666 flags -- FM_ flags, see pith/mailview.h
2668 Result: 0 if all's well, -1 if write error, 1 if fetch error
2670 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2671 in the local convention.
2673 ----*/
2674 void
2675 format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
2676 long int which, char *oacs, int flags)
2678 char *q, *p2, buftmp[MAILTMPLEN];
2680 if(!e)
2681 return;
2683 if((which & FE_DATE) && e->date) {
2684 q = "Date: ";
2685 snprintf(buftmp, sizeof(buftmp), "%s",
2686 F_ON(F_DATES_TO_LOCAL,ps_global)
2687 ? convert_date_to_local((char *) e->date) : (char *) e->date);
2688 buftmp[sizeof(buftmp)-1] = '\0';
2689 p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
2690 SIZEOF_20KBUF, buftmp);
2691 gf_puts(q, pc);
2692 format_env_puts(p2, pc);
2693 gf_puts(NEWLINE, pc);
2696 if((which & FE_FROM) && e->from)
2697 format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
2699 if((which & FE_REPLYTO) && e->reply_to
2700 && (!e->from || !address_is_same(e->reply_to, e->from)))
2701 format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
2703 if((which & FE_TO) && e->to)
2704 format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
2706 if((which & FE_CC) && e->cc)
2707 format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
2709 if((which & FE_BCC) && e->bcc)
2710 format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
2712 if((which & FE_RETURNPATH) && e->return_path)
2713 format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
2714 flags, oacs, pc);
2716 if((which & FE_NEWSGROUPS) && e->newsgroups){
2717 int bogus = NIL;
2718 format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
2719 if (!e->ngpathexists && e->message_id &&
2720 strncmp (e->message_id,"<alpine.",8) &&
2721 strncmp (e->message_id,"<Pine.",6) &&
2722 strncmp (e->message_id,"<MS-C.",6) &&
2723 strncmp (e->message_id,"<MailManager.",13) &&
2724 strncmp (e->message_id,"<EasyMail.",11) &&
2725 strncmp (e->message_id,"<ML-",4)) bogus = T;
2727 if(bogus)
2728 q_status_message(SM_ORDER, 0, 3,
2729 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2732 if((which & FE_FOLLOWUPTO) && e->followup_to)
2733 format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
2735 if((which & FE_SUBJECT) && e->subject && e->subject[0]){
2736 char *freeme = NULL;
2738 q = "Subject: ";
2739 gf_puts(q, pc);
2741 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
2743 if(flags & FM_DISPLAY
2744 && (ps_global->display_keywords_in_subject
2745 || ps_global->display_keywordinits_in_subject)){
2747 /* don't bother if no keywords are defined */
2748 if(some_user_flags_defined(s))
2749 p2 = freeme = prepend_keyword_subject(s, n, p2,
2750 ps_global->display_keywords_in_subject ? KW : KWInit,
2751 NULL, ps_global->VAR_KW_BRACES);
2754 format_env_puts(p2, pc);
2756 if(freeme)
2757 fs_give((void **) &freeme);
2759 gf_puts(NEWLINE, pc);
2762 if((which & FE_SENDER) && e->sender
2763 && (!e->from || !address_is_same(e->sender, e->from)))
2764 format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
2766 if((which & FE_MESSAGEID) && e->message_id){
2767 q = "Message-ID: ";
2768 gf_puts(q, pc);
2769 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
2770 format_env_puts(p2, pc);
2771 gf_puts(NEWLINE, pc);
2774 if((which & FE_INREPLYTO) && e->in_reply_to){
2775 q = "In-Reply-To: ";
2776 gf_puts(q, pc);
2777 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->in_reply_to), SIZEOF_20KBUF-10000);
2778 format_env_puts(p2, pc);
2779 gf_puts(NEWLINE, pc);
2782 if((which & FE_REFERENCES) && e->references) {
2783 q = "References: ";
2784 gf_puts(q, pc);
2785 p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
2786 format_env_puts(p2, pc);
2787 gf_puts(NEWLINE, pc);
2795 * The argument fieldname is something like "Subject:..." or "Subject".
2796 * Look through the specs in speccolor for a match of the fieldname,
2797 * and return the color that goes with any match, or NULL.
2798 * Caller should free the color.
2800 COLOR_PAIR *
2801 hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
2803 SPEC_COLOR_S *hc = NULL;
2804 COLOR_PAIR *color_pair = NULL;
2805 char *colon, *fname;
2806 char fbuf[FBUF_LEN+1];
2807 int gotit;
2808 PATTERN_S *pat;
2810 colon = strindex(fieldname, ':');
2811 if(colon){
2812 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2813 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2814 fname = fbuf;
2816 else
2817 fname = fieldname;
2819 if(fname && *fname)
2820 for(hc = speccolor; hc; hc = hc->next)
2821 if(hc->spec && !strucmp(fname, hc->spec)){
2822 if(!hc->val)
2823 break;
2825 gotit = 0;
2826 for(pat = hc->val; !gotit && pat; pat = pat->next)
2827 if(srchstr(value, pat->substring))
2828 gotit++;
2830 if(gotit)
2831 break;
2834 if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
2835 color_pair = new_color_pair(hc->fg, hc->bg);
2837 return(color_pair);
2842 * The argument fieldname is something like "Subject:..." or "Subject".
2843 * Look through the specs in hdr_colors for a match of the fieldname,
2844 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2847 any_hdr_color(char *fieldname)
2849 SPEC_COLOR_S *hc = NULL;
2850 char *colon, *fname;
2851 char fbuf[FBUF_LEN+1];
2853 colon = strindex(fieldname, ':');
2854 if(colon){
2855 strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
2856 fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
2857 fname = fbuf;
2859 else
2860 fname = fieldname;
2862 if(fname && *fname)
2863 for(hc = ps_global->hdr_colors; hc; hc = hc->next)
2864 if(hc->spec && !strucmp(fname, hc->spec))
2865 break;
2867 return(hc ? 1 : 0);
2871 /*----------------------------------------------------------------------
2872 Format an address field, wrapping lines nicely at commas
2874 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2875 addr -- ADDRESS structure to format
2877 Result: A formatted, malloced string is returned.
2878 ----------------------------------------------------------------------*/
2879 void
2880 format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
2881 struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
2883 char *ptmp, *mtmp;
2884 int trailing = 0, group = 0;
2885 ADDRESS *atmp;
2887 if(!addr)
2888 return;
2891 * quickly run down address list to make sure none are patently bogus.
2892 * If so, just blat raw field out.
2894 for(atmp = addr; stream && atmp; atmp = atmp->next)
2895 if(atmp->host && atmp->host[0] == '.'){
2896 char *field, *fields[2];
2898 fields[1] = NULL;
2899 fields[0] = cpystr(field_name);
2900 if((ptmp = strchr(fields[0], ':')) != NULL)
2901 *ptmp = '\0';
2903 if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
2904 char *h, *t;
2906 for(t = h = field; *h ; t++)
2907 if(*t == '\015' && *(t+1) == '\012'){
2908 *t = '\0'; /* tie off line */
2909 format_env_puts(h, pc);
2910 if(*(h = (++t) + 1)) /* set new h and skip CRLF */
2911 gf_puts(NEWLINE, pc); /* more to write */
2912 else
2913 break;
2915 else if(!*t){ /* shouldn't happen much */
2916 if(h != t)
2917 format_env_puts(h, pc);
2919 break;
2922 fs_give((void **)&field);
2925 fs_give((void **)&fields[0]);
2926 gf_puts(NEWLINE, pc);
2927 dprint((2, "Error in \"%s\" field address\n",
2928 field_name ? field_name : "?"));
2929 return;
2932 gf_puts(field_name, pc);
2934 while(addr){
2935 atmp = addr->next; /* remember what's next */
2936 addr->next = NULL;
2937 if(!addr->host && addr->mailbox){
2938 mtmp = addr->mailbox;
2939 addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
2940 (unsigned char *)tmp_20k_buf,
2941 SIZEOF_20KBUF, addr->mailbox));
2944 ptmp = addr->personal; /* RFC 1522 personal name? */
2945 addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
2946 tmp_20k_buf[10000-1] = '\0';
2948 if(!trailing) /* 1st pass, just address */
2949 trailing++;
2950 else{ /* else comma, unless */
2951 if(!((group == 1 && addr->host) /* 1st addr in group, */
2952 || (!addr->host && !addr->mailbox))){ /* or end of group */
2953 gf_puts(",", pc);
2954 #if 0
2955 gf_puts(NEWLINE, pc); /* ONE address/line please */
2956 gf_puts(" ", pc);
2957 #endif
2960 gf_puts(" ", pc);
2963 pine_rfc822_write_address_noquote(addr, pc, &group);
2965 addr->personal = ptmp; /* restore old personal ptr */
2966 if(!addr->host && addr->mailbox){
2967 fs_give((void **)&addr->mailbox);
2968 addr->mailbox = mtmp;
2971 addr->next = atmp;
2972 addr = atmp;
2975 gf_puts(NEWLINE, pc);
2981 const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
2982 /* RFC822 continuation, must start with CRLF */
2983 #define RFC822CONT "\015\012 "
2985 /* Write RFC822 address with some quoting turned off.
2986 * Accepts:
2987 * address to interpret
2989 * (This is a copy of c-client's rfc822_write_address except
2990 * we don't quote double quote and dot in personal names. It writes
2991 * to a gf_io_t instead of to a buffer so that we don't have to worry
2992 * about fixed sized buffer overflowing. It's also special cased to deal
2993 * with only a single address.)
2995 * The idea is that there are some places where we'd just like to display
2996 * the personal name as is before applying confusing quoting. However,
2997 * we do want to be careful not to break things that should be quoted so
2998 * we'll only use this where we are sure. Quoting may look ugly but it
2999 * doesn't usually break anything.
3001 void
3002 pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
3004 extern const char *rspecials;
3006 if (adr->host) { /* ordinary address? */
3007 if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
3008 else { /* no, must use phrase <route-addr> form */
3009 if (adr->personal)
3010 pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
3012 gf_puts(" <", pc); /* write address delimiter */
3013 pine_rfc822_address(adr, pc);
3014 gf_puts (">", pc); /* closing delimiter */
3017 if(*group)
3018 (*group)++;
3020 else if (adr->mailbox) { /* start of group? */
3021 /* yes, write group name */
3022 pine_rfc822_cat (adr->mailbox, rspecials, pc);
3024 gf_puts (": ", pc); /* write group identifier */
3025 *group = 1; /* in a group */
3027 else if (*group) { /* must be end of group (but be paranoid) */
3028 gf_puts (";", pc);
3029 *group = 0; /* no longer in that group */
3034 /* Write RFC822 route-address to string
3035 * Accepts:
3036 * address to interpret
3039 void
3040 pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
3042 extern char *wspecials;
3044 if (adr && adr->host) { /* no-op if no address */
3045 if (adr->adl) { /* have an A-D-L? */
3046 gf_puts (adr->adl, pc);
3047 gf_puts (":", pc);
3049 /* write mailbox name */
3050 pine_rfc822_cat (adr->mailbox, wspecials, pc);
3051 if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
3052 gf_puts ("@", pc); /* host delimiter */
3053 gf_puts (adr->host, pc); /* write host name */
3059 /* Concatenate RFC822 string
3060 * Accepts:
3061 * pointer to string to concatenate
3062 * list of special characters
3065 void
3066 pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
3068 char *s;
3070 if (strpbrk (src,specials)) { /* any specials present? */
3071 gf_puts ("\"", pc); /* opening quote */
3072 /* truly bizarre characters in there? */
3073 while ((s = strpbrk (src,"\\\"")) != NULL) {
3074 char save[2];
3076 /* turn it into a null-terminated piece */
3077 save[0] = *s;
3078 save[1] = '\0';
3079 *s = '\0';
3080 gf_puts (src, pc); /* yes, output leader */
3081 *s = save[0];
3082 gf_puts ("\\", pc); /* quoting */
3083 gf_puts (save, pc); /* output the bizarre character */
3084 src = ++s; /* continue after the bizarre character */
3086 if (*src) gf_puts (src, pc);/* output non-bizarre string */
3087 gf_puts ("\"", pc); /* closing quote */
3089 else gf_puts (src, pc); /* otherwise it's the easy case */
3093 /*----------------------------------------------------------------------
3094 Format an address field, wrapping lines nicely at commas
3096 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3097 newsgrps -- ADDRESS structure to format
3099 Result: A formatted, malloced string is returned.
3101 The resuling lines formatted are 80 columns wide.
3102 ----------------------------------------------------------------------*/
3103 void
3104 format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
3106 char buf[MAILTMPLEN];
3107 int trailing = 0, llen, alen;
3108 char *next_ng;
3110 if(!newsgrps || !*newsgrps)
3111 return;
3113 gf_puts(field_name, pc);
3115 llen = strlen(field_name);
3116 while(*newsgrps){
3117 for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
3118 strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
3119 buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
3120 newsgrps = next_ng;
3121 if(*newsgrps)
3122 newsgrps++;
3123 alen = strlen(buf);
3124 if(!trailing){ /* first time thru, just address */
3125 llen += alen;
3126 trailing++;
3128 else{ /* else preceding comma */
3129 gf_puts(",", pc);
3130 llen++;
3132 if(alen + llen + 1 > 76){
3133 gf_puts(NEWLINE, pc);
3134 gf_puts(" ", pc);
3135 llen = alen + 5;
3137 else{
3138 gf_puts(" ", pc);
3139 llen += alen + 1;
3143 if(alen && llen > 76){ /* handle long addresses */
3144 register char *q, *p = &buf[alen-1];
3146 while(p > buf){
3147 if(isspace((unsigned char)*p)
3148 && (llen - (alen - (int)(p - buf))) < 76){
3149 for(q = buf; q < p; q++)
3150 (*pc)(*q); /* write character */
3152 gf_puts(NEWLINE, pc);
3153 gf_puts(" ", pc);
3154 gf_puts(p, pc);
3155 break;
3157 else
3158 p--;
3161 if(p == buf) /* no reasonable break point */
3162 gf_puts(buf, pc);
3164 else
3165 gf_puts(buf, pc);
3168 gf_puts(NEWLINE, pc);
3173 /*----------------------------------------------------------------------
3174 Format a text field that's part of some raw (non-envelope) message header
3176 Args: start --
3177 finish --
3178 pc --
3180 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3182 ----------------------------------------------------------------------*/
3184 format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
3186 register char *current;
3187 unsigned char *p, *tmp = NULL, c;
3188 size_t n, len;
3189 char ch;
3190 int rv = FHT_OK;
3192 ch = *finish;
3193 *finish = '\0';
3195 if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
3196 len = n+1;
3197 p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
3199 else{
3200 len = SIZEOF_20KBUF;
3201 p = (unsigned char *) tmp_20k_buf;
3204 if(islower((unsigned char)(*start)))
3205 *start = toupper((unsigned char)(*start));
3207 current = (char *) rfc1522_decode_to_utf8(p, len, start);
3209 /* output from start to finish */
3210 while(*current && rv == FHT_OK)
3211 if(ISRFCEOL(current)){
3212 if(!gf_puts(NEWLINE, pc))
3213 rv = FHT_WRTERR;
3215 current += 2;
3217 else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
3218 !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
3219 c = (unsigned char) *current++;
3220 if(!((*pc)(c >= 0x80 ? '~' : '^')
3221 && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
3222 rv = FHT_WRTERR;
3224 else if(!(*pc)(*current++))
3225 rv = FHT_WRTERR;
3227 if(tmp)
3228 fs_give((void **) &tmp);
3230 *finish = ch;
3232 return(rv);
3238 /*----------------------------------------------------------------------
3239 Format a text field that's part of some raw (non-envelope) message header
3241 Args: s --
3242 pc --
3244 Result: Output
3246 ----------------------------------------------------------------------*/
3248 format_env_puts(char *s, gf_io_t pc)
3250 if(ps_global->pass_ctrl_chars)
3251 return(gf_puts(s, pc));
3253 for(; *s; s++)
3254 if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
3255 if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
3256 && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
3257 return(0);
3259 else if(!(*pc)(*s))
3260 return(0);
3262 return(1);
3266 char *
3267 display_parameters(PARAMETER *params)
3269 int n, longest = 0;
3270 char *d, *printme;
3271 PARAMETER *p;
3272 PARMLIST_S *parmlist;
3274 for(p = params; p; p = p->next) /* ok if we include *'s */
3275 if(p->attribute && (n = strlen(p->attribute)) > longest)
3276 longest = MIN(32, n); /* shouldn't be any bigger than 32 */
3278 d = tmp_20k_buf;
3279 tmp_20k_buf[0] = '\0';
3280 if((parmlist = rfc2231_newparmlist(params)) != NULL){
3281 n = 0; /* n overloaded */
3282 while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
3283 if(n++){
3284 snprintf(d, 10000-(d-tmp_20k_buf), "\n");
3285 tmp_20k_buf[10000-1] = '\0';
3286 d += strlen(d);
3289 if(parmlist->value){
3290 if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
3291 snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
3292 sqzspaces(printme);
3294 else
3295 printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
3297 else
3298 printme = "";
3300 snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
3301 parmlist->attrib ? parmlist->attrib : "", printme);
3303 tmp_20k_buf[10000-1] = '\0';
3304 d += strlen(d);
3307 rfc2231_free_parmlist(&parmlist);
3310 return(tmp_20k_buf);
3314 /*----------------------------------------------------------------------
3315 Fetch the requested header fields from the msgno specified
3317 Args: stream -- mail stream of open folder
3318 msgno -- number of message to get header lines from
3319 fields -- array of pointers to desired fields
3321 Returns: allocated string containing matched header lines,
3322 NULL on error.
3323 ----*/
3324 char *
3325 pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
3327 STRINGLIST *sl;
3328 char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
3329 char **pflds = NULL, **pp = NULL, **qq;
3332 * If the user misconfigures it is possible to have one of the fields
3333 * set to the empty string instead of a header name. We want to catch
3334 * that here instead of asking the server the nonsensical question.
3336 for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
3337 if(!**pp)
3338 break;
3340 if(pp && *pp){ /* found an empty header field, fix it */
3341 pflds = copy_list_array(fields);
3342 for(pp = pflds; pp && *pp; pp++){
3343 if(!**pp){ /* scoot rest of the lines up */
3344 free_this = *pp;
3345 for(qq = pp; *qq; qq++)
3346 *qq = *(qq+1);
3348 if(free_this)
3349 fs_give((void **) &free_this);
3353 /* no headers to look for, return NULL */
3354 if(pflds && !*pflds && !(flags & FT_NOT)){
3355 free_list_array(&pflds);
3356 return(NULL);
3359 else
3360 pflds = fields;
3362 sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
3363 h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
3364 if (sl)
3365 free_strlst(&sl);
3367 if(!h){
3368 if(pflds && pflds != fields)
3369 free_list_array(&pflds);
3371 return(NULL);
3374 while(find_field(&h, tmp, sizeof(tmp))){
3375 for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
3378 /* interesting field? */
3379 if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
3381 * Hold off allocating space for matching fields until
3382 * we at least find one to copy...
3384 if(!match)
3385 match = m = fs_get(strlen(h) + strlen(p) + 1);
3387 while(*p) /* copy field name */
3388 *m++ = *p++;
3390 while(*h && (*m++ = *h++)) /* header includes colon */
3391 if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
3392 break;
3394 *m = '\0'; /* tie off match string */
3396 else{ /* no match, pass this field */
3397 while(*h && !(*h++ == '\n'
3398 && (*h == '\r' || !isspace((unsigned char)*h))))
3403 if(pflds && pflds != fields)
3404 free_list_array(&pflds);
3406 return(match ? match : cpystr(""));
3411 find_field(char **h, char *tmp, size_t ntmp)
3413 char *otmp = tmp;
3415 if(!h || !*h || !**h || isspace((unsigned char)**h))
3416 return(0);
3418 while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
3419 *tmp++ = *(*h)++;
3421 *tmp = '\0';
3422 return(1);
3426 static char *_last_embedded_fg_color, *_last_embedded_bg_color;
3430 embed_color(COLOR_PAIR *cp, gf_io_t pc)
3432 if(cp && cp->fg){
3433 if(_last_embedded_fg_color)
3434 fs_give((void **)&_last_embedded_fg_color);
3436 _last_embedded_fg_color = cpystr(cp->fg);
3438 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
3439 gf_puts(color_to_asciirgb(cp->fg), pc)))
3440 return 0;
3443 if(cp && cp->bg){
3444 if(_last_embedded_bg_color)
3445 fs_give((void **)&_last_embedded_bg_color);
3447 _last_embedded_bg_color = cpystr(cp->bg);
3449 if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
3450 gf_puts(color_to_asciirgb(cp->bg), pc)))
3451 return 0;
3454 return 1;
3458 COLOR_PAIR *
3459 get_cur_embedded_color(void)
3461 COLOR_PAIR *ret;
3463 if(_last_embedded_fg_color && _last_embedded_bg_color)
3464 ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
3465 else
3466 ret = pico_get_cur_color();
3468 return(ret);
3472 void
3473 clear_cur_embedded_color(void)
3475 if(_last_embedded_fg_color)
3476 fs_give((void **)&_last_embedded_fg_color);
3478 if(_last_embedded_bg_color)
3479 fs_give((void **)&_last_embedded_bg_color);
3484 scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
3486 *len = 0;
3488 if(pico_usingcolor()){
3489 char *fg = NULL, *bg = NULL, *s;
3491 if(ps_global->VAR_SLCTBL_FORE_COLOR
3492 && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
3493 ps_global->VAR_NORM_FORE_COLOR))
3494 fg = ps_global->VAR_SLCTBL_FORE_COLOR;
3496 if(ps_global->VAR_SLCTBL_BACK_COLOR
3497 && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
3498 ps_global->VAR_NORM_BACK_COLOR))
3499 bg = ps_global->VAR_SLCTBL_BACK_COLOR;
3501 if(bg || fg){
3502 COLOR_PAIR *tmp;
3505 * The blacks are just known good colors for
3506 * testing whether the other color is good.
3508 if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
3509 bg ? bg : colorx(COL_BLACK))) != NULL){
3510 if(pico_is_good_colorpair(tmp))
3511 for(s = color_embed(fg, bg);
3512 (*len) < buflen && (colorstring[*len] = *s);
3513 s++, (*len)++)
3516 free_color_pair(&tmp);
3520 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3521 strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
3522 *len += 2;
3526 colorstring[buflen-1] = '\0';
3528 return(*len != 0);
3533 scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
3535 *len = 0;
3536 if(pico_usingcolor()){
3537 char *fg = NULL, *bg = NULL, *s;
3538 char *basefg = NULL, *basebg = NULL;
3540 basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
3541 : ps_global->VAR_NORM_FORE_COLOR;
3542 basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
3543 : ps_global->VAR_NORM_BACK_COLOR;
3546 * We need to change the fg and bg colors back even if they
3547 * are the same as the Normal Colors so that color_a_quote
3548 * will have a chance to pick up those colors as the ones to
3549 * switch to. We don't do this before the handle above so that
3550 * the quote color will flow into the selectable item when
3551 * the selectable item color is partly the same as the
3552 * normal color. That is, suppose the normal color was black on
3553 * cyan and the selectable color was blue on cyan, only a fg color
3554 * change. We preserve the only-a-fg-color-change in a quote by
3555 * letting the quote background color flow into the selectable text.
3557 if(ps_global->VAR_SLCTBL_FORE_COLOR)
3558 fg = basefg;
3560 if(ps_global->VAR_SLCTBL_BACK_COLOR)
3561 bg = basebg;
3563 if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
3564 strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
3565 *len = 2;
3568 if(fg || bg)
3569 for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
3573 colorstring[buflen-1] = '\0';
3575 return(*len != 0);
3580 * Helper routine that is of limited use.
3581 * We need to tally up the screen width of
3582 * a UTF-8 string as we go through the string.
3583 * We just want the width of the character starting
3584 * at str (and no longer than remaining_octets).
3585 * If we're plopped into the middle of a UTF-8
3586 * character we just want to return width zero.
3589 width_at_this_position(unsigned char *str, unsigned long n)
3591 unsigned char *inputp = str;
3592 unsigned long remaining_octets = n;
3593 UCS ucs;
3594 int width = 0;
3596 ucs = (UCS) utf8_get(&inputp, &remaining_octets);
3597 if(!(ucs & U8G_ERROR || ucs == UBOGON)){
3598 width = wcellwidth(ucs);
3599 /* Writechar will print a '?' */
3600 if(width < 0)
3601 width = 1;
3604 return(width);