1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2021 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 /*======================================================================
22 Implements message data gathering and formatting
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"
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
71 static struct envelope_s
{
76 {"sender", FE_SENDER
},
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
},
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
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.
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
;
161 clear_cur_embedded_color();
163 if(!(flgs
& FM_DISPLAY
))
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
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 */
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc
)
190 && !gf_puts(NEWLINE
, pc
)))
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 if((decode_err
= format_body(msgno
, body
, handlesp
, &h
, flgs
, width
, pc
)) == NULL
)
210 clear_cur_embedded_color();
216 if(!(flgs
& FM_DISPLAY
))
217 q_status_message1(SM_ORDER
, 3, 4, _("Error writing message: %s"),
218 decode_err
? decode_err
: error_description(errno
));
219 clear_cur_embedded_color();
224 format_calendar_vevent(VCALENDAR_S
*vcal
, ATTACH_S
*a
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
, int cflags
)
226 int avail
, m1
, m2
, hwid
, i
, partwid
, padwid
;
227 int s1
, s2
, dwid
, minkey
;
230 VEVENT_SUMMARY_S
*vesy
, *vesummary
; /* vevent summary */
232 vesummary
= vesy
= ical_vevent_summary(vcal
);
234 if(vesy
== NULL
) return;
236 if((cflags
& FC_SUMMARY
) && (cflags
& FC_FULL
))
239 minkey
= -1; /* initialize to something negative */
241 for(; vesy
!= NULL
; vesy
= vesy
->next
){
243 margin
= (cflags
& FC_FULL
) ? NULL
244 : (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
246 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
249 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
252 hwid
= MAX(avail
, 0);
255 if(ps_global
->atmts
[1].description
== NULL
){
256 avail
= width
- m1
-2;
258 dwid
= MAX(MIN(40, avail
), 0);
260 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
261 repeat_char(dwid
, '-'));
263 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
268 if(cflags
& FC_SUMMARY
){
269 i
= utf8_width(_("Calendar Entry:"));
270 partwid
= MIN(i
, hwid
);
272 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
273 if(!gf_puts(tmp_20k_buf
, pc
))
277 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%-*.*w%*.*s",
278 partwid
, partwid
, _("Calendar Entry:"),
281 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
288 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
289 if(!gf_puts(tmp_20k_buf
, pc
))
293 avail
= width
- m1
- m2
;
295 s1
= MAX(MIN(1, avail
), 0);
298 dwid
= MAX(MIN(1, avail
), 0);
301 s2
= MAX(MIN(1, avail
), 0);
304 if(cflags
& FC_SUMMARY
)
305 utf8_snprintf(padding
, sizeof(padding
), "%*.*s%*.*w%*.*s",
306 s1
, s1
, "", dwid
, dwid
, "", s2
, s2
, "");
311 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
312 padding
, _("This event was cancelled!"));
313 if((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
)){
314 gf_puts(tmp_20k_buf
, pc
);
315 gf_puts(NEWLINE
, pc
);
322 if(vesy
->sender
!= NULL
){
323 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
324 padding
, _("Sent-by: "), vesy
->sender
);
325 gf_puts(tmp_20k_buf
, pc
);
326 gf_puts(NEWLINE
, pc
);
329 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
330 padding
, _("Organizer: "), vesy
->organizer
);
331 gf_puts(tmp_20k_buf
, pc
);
332 gf_puts(NEWLINE
, pc
);
333 } /* end of if(organizer) */
336 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
337 padding
, _("Location: "), vesy
->location
);
338 gf_puts(tmp_20k_buf
, pc
);
339 gf_puts(NEWLINE
, pc
);
340 } /* end of if location */
343 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
344 padding
, _("Start Date: "), vesy
->evstart
);
345 gf_puts(tmp_20k_buf
, pc
);
346 gf_puts(NEWLINE
, pc
);
347 } /* end of if dtstart */
352 for(i
= 0; vesy
->duration
[i
] != NULL
; i
++){
353 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
354 padding
, _("Duration: "), vesy
->duration
[i
]);
355 gf_puts(tmp_20k_buf
, pc
);
356 gf_puts(NEWLINE
, pc
);
358 } /* end of DURATION */
359 else if(vesy
->evend
){
360 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
361 padding
, _("End Date: "), vesy
->evend
);
362 gf_puts(tmp_20k_buf
, pc
);
363 gf_puts(NEWLINE
, pc
);
364 } else { /* end of if dtend */
365 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
366 padding
, _("No duration nor end time found for this event"));
367 gf_puts(tmp_20k_buf
, pc
);
368 gf_puts(NEWLINE
, pc
);
369 } /* end of else for if (duration) and if (dtend) */
372 #define MAX_DISPLAYED 3
375 for(i
= 0; vesy
->attendee
[i
] != NULL
; i
++){
376 if((cflags
& FC_SUMMARY
) && i
>= MAX_DISPLAYED
)
379 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
381 _("Attendee: "), vesy
->attendee
[i
]);
382 gf_puts(tmp_20k_buf
, pc
);
383 gf_puts(NEWLINE
, pc
);
385 } /* end of ATTENDEES */
388 if(cflags
& FC_SUMMARY
){
389 COLOR_PAIR
*lastc
= NULL
;
390 COLOR_PAIR
*hdrcolor
= NULL
;
392 if((flgs
& FM_DISPLAY
)
393 && !(flgs
& FM_NOCOLOR
)
395 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
396 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
397 && ps_global
->VAR_NORM_FORE_COLOR
398 && ps_global
->VAR_NORM_BACK_COLOR
399 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
400 ps_global
->VAR_NORM_FORE_COLOR
)
401 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
402 ps_global
->VAR_NORM_BACK_COLOR
))){
404 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
405 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
406 if(!pico_is_good_colorpair(hdrcolor
))
407 free_color_pair(&hdrcolor
);
411 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
414 gf_puts(padding
, pc
);
415 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
418 char buf
[16], color
[64];
422 h
= new_handle(handlesp
);
423 if(minkey
< 0) minkey
= h
->key
;
425 h
->h
.ical
.attach
= a
;
426 h
->h
.ical
.depth
= h
->key
- minkey
;
428 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
429 buf
[sizeof(buf
)-1] = '\0';
431 if(!(flgs
& FM_NOCOLOR
)
432 && handle_start_color(color
, sizeof(color
), &l
, 1)){
433 lastc
= get_cur_embedded_color();
434 if(!gf_nputs(color
, (long) l
, pc
))
437 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
438 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
441 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
442 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
445 tmp_20k_buf
[0] = '\0';
447 if(!format_env_puts(tmp_20k_buf
, pc
))
452 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
453 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
457 if(!embed_color(lastc
, pc
))
460 free_color_pair(&lastc
);
462 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
465 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
470 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
471 if(!gf_puts(tmp_20k_buf
, pc
))
475 if(!gf_puts(NEWLINE
, pc
))
478 avail
= width
- m1
-2;
480 dwid
= MAX(MIN(40, avail
), 0);
483 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
484 repeat_char(dwid
, '-'));
486 gf_puts(tmp_20k_buf
, pc
);
488 gf_puts(NEWLINE
, pc
);
490 free_vevent_summary(&vesummary
);
494 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
496 char *rawtext
, *caltext
;
497 unsigned long callen
;
498 VCALENDAR_S
*vcal
= NULL
;
502 if(flgs
& FM_NEW_MESS
) {
503 zero_atmts(ps_global
->atmts
);
504 describe_mime(body
, "", 1, 1, 0, flgs
);
507 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
508 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
509 b
= mail_body (ps_global
->mail_stream
, msgno
, (unsigned char *) a
->number
);
511 gf_puts(_("Error fetching calendar body part"), pc
);
512 gf_puts(NEWLINE
, pc
);
515 if(b
->sparep
== NULL
){
516 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
517 if(rawtext
== NULL
|| *rawtext
== '\0'){
518 gf_puts(_("Error fetching calendar text"), pc
);
519 gf_puts(NEWLINE
, pc
);
522 rawtext
[callen
] = '\0'; /* chop off cookie */
525 caltext
= rfc822_base64((unsigned char *) rawtext
, strlen(rawtext
), &callen
);
527 gf_puts(_("Error in calendar base64 encoding"), pc
);
528 gf_puts(NEWLINE
, pc
);
531 caltext
[callen
] = '\0';
534 case ENCQUOTEDPRINTABLE
:
535 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
537 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
538 gf_puts(NEWLINE
, pc
);
541 caltext
[callen
] = '\0';
544 default: caltext
= cpystr(rawtext
);
547 vcal
= ical_parse_text(caltext
);
548 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
549 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
550 fs_give((void **) &caltext
);
553 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
554 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
555 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
556 if(vcal
->comp
[VEvent
] != NULL
){
557 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
558 } /* else another type of entry in the calendar */
560 gf_puts(NEWLINE
, pc
);
568 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
570 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
571 int is_in_sig
= OUT_SIG_BLOCK
;
572 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
578 || (ps_global
->full_header
== 2
579 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
581 /*--- Server is not an IMAP2bis, It can't parse MIME
582 so we just show the text here. Hopefully the
583 message isn't a MIME message
587 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
588 msgno
, NULL
, NULL
, NIL
)) != NULL
){
590 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
591 return("Write Error");
593 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
597 * We need to translate the message
598 * into UTF-8, but that's trouble in the full header case
599 * because we don't know what to translate from. We'll just
600 * take a guess by looking for the first text part and
603 if(body
&& body
->type
== TYPETEXT
)
604 charset
= parameter_val(body
->parameter
, "charset");
605 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
606 && body
->nested
.part
->body
.type
== TYPETEXT
)
607 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
609 charset
= cpystr(ps_global
->display_charmap
);
611 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
612 /* transliterate message text to UTF-8 */
613 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
615 if (charset
) fs_give((void **) &charset
);
617 /* link in filters, similar to what is done in decode_text() */
618 if(!ps_global
->pass_ctrl_chars
){
619 gf_link_filter(gf_escape_filter
, NULL
);
621 gf_link_filter(gf_control_filter
,
622 gf_control_filter_opt(&filt_only_c0
));
625 gf_link_filter(gf_tag_filter
, NULL
);
627 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
628 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
629 || F_ON(F_SCAN_ADDR
, ps_global
))
631 gf_link_filter(gf_line_test
,
632 gf_line_test_opt(url_hilite
,
633 gf_url_hilite_opt(&uh
,handlesp
,0)));
636 if((flgs
& FM_DISPLAY
)
637 && !(flgs
& FM_NOCOLOR
)
639 && ps_global
->VAR_SIGNATURE_FORE_COLOR
640 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
641 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
644 if((flgs
& FM_DISPLAY
)
645 && !(flgs
& FM_NOCOLOR
)
647 && ps_global
->VAR_QUOTE1_FORE_COLOR
648 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
649 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
652 if(!(flgs
& FM_NOWRAP
)){
653 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
655 && !(flgs
& FM_NOCOLOR
)
656 && pico_usingcolor())
657 wrapflags
|= GFW_USECOLOR
;
658 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
660 ? NULL
: format_view_margin(),
665 gf_link_filter(gf_nvtnl_local
, NULL
);
666 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
667 /* TRANSLATORS: There was an error putting together a message for
668 viewing. The arg is the description of the error. */
669 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
670 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
671 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
672 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
673 && gf_puts(NEWLINE
, pc
))
681 if(!gf_puts(NEWLINE
, pc
)
682 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
683 || !gf_puts(NEWLINE
, pc
)
684 || !gf_puts(NEWLINE
, pc
))
685 return("Write Error");
691 /*======== Now loop through formatting all the parts =======*/
692 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
693 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
696 if(a
->body
->type
== TYPEMULTIPART
){
698 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
700 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
701 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
702 return("Write Error");
710 if(a
->suppress_editorial
)
713 if(!(flgs
& FM_NOEDITORIAL
)
714 && (!gf_puts(NEWLINE
, pc
)
715 || (decode_err
= part_desc(a
->number
, a
->body
,
717 ? (a
->can_display
!= MCD_NONE
)
719 : 3, width
, flgs
, pc
))))
720 return("Write Error");
725 switch(a
->body
->type
){
729 * If a message is multipart *and* the first part of it
730 * is text *and that text is empty, there is a good chance that
731 * there was actually something there that c-client was
732 * unable to parse. Here we report the empty message body
733 * and insert the raw RFC822.TEXT (if full-headers are
736 if(body
->type
== TYPEMULTIPART
737 && a
== ps_global
->atmts
738 && a
->body
->size
.bytes
== 0
739 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
742 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
743 "Empty or malformed message%s.",
744 ps_global
->full_header
== 2
745 ? ". Displaying raw text"
746 : ". Use \"H\" to see raw text");
747 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
749 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
750 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
751 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
752 return("Write Error");
754 if(ps_global
->full_header
== 2
755 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
756 a
->number
, pc
, flgs
))){
757 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
758 "%s%s [ Formatting error: %s ]%s%s",
759 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
760 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
761 if(!gf_puts(tmp_20k_buf
, pc
))
762 return("Write Error");
769 * Don't write our delimiter if this text part is
770 * the first part of a message/rfc822 segment...
772 if(show_parts
&& a
!= ps_global
->atmts
773 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
775 || (a
[-1].body
->type
== TYPEMULTIPART
776 && a
[-1].body
->subtype
777 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
778 && &a
[-1] != ps_global
->atmts
779 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
782 && !(flgs
& FM_NOEDITORIAL
)){
783 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
784 tmp1
= "Calendar entry";
786 tmp1
= a
->body
->description
? a
->body
->description
788 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
789 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
791 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
793 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
794 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
795 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
796 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
797 return("Write Error");
800 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
801 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
802 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
807 tmp1
= a
->body
->description
? a
->body
->description
808 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
810 : "Included Message";
811 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
812 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
814 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
816 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
818 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
819 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
820 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
821 return("Write Error");
823 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
824 /* imapenvonly, we may not have all the headers we need */
825 if(a
->body
->nested
.msg
->env
->imapenvonly
)
826 mail_fetch_header(ps_global
->mail_stream
, msgno
,
827 a
->number
, NULL
, NULL
, FT_PEEK
);
828 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
829 a
->body
->nested
.msg
->env
, hp
,
830 NULL
, handlesp
, flgs
, NULL
, pc
)){
831 case -1 : /* write error */
832 return("Write Error");
834 case 1 : /* fetch error */
835 if(!(gf_puts("[ Error fetching header ]", pc
)
836 && !gf_puts(NEWLINE
, pc
)))
837 return("Write Error");
842 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
843 int *margin
, avail
, m1
, m2
;
846 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
848 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
851 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
854 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
855 || !gf_puts(NEWLINE
, pc
)
856 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
857 return("Write Error");
860 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
861 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
864 if(!gf_puts(NEWLINE
, pc
))
865 return("Write Error");
870 if((decode_err
= part_desc(a
->number
, a
->body
,
871 (flgs
& FM_DISPLAY
) ? 1 : 3,
872 width
, flgs
, pc
)) != NULL
)
873 return("Write Error");
880 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
881 && format_blip_seen(msgno
)))
882 return("Cannot format body.");
890 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
894 if(flgs
& FM_NEW_MESS
) {
895 zero_atmts(ps_global
->atmts
);
896 describe_mime(body
, "", 1, 1, 0, flgs
);
899 /*----- First do the list of parts/attachments if needed ----*/
900 if((flgs
& FM_DISPLAY
)
901 && (ps_global
->atmts
[1].description
902 || (ps_global
->atmts
[0].body
903 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
904 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
905 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
906 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
907 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
908 COLOR_PAIR
*hdrcolor
= NULL
;
910 if((flgs
& FM_DISPLAY
)
911 && !(flgs
& FM_NOCOLOR
)
913 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
914 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
915 && ps_global
->VAR_NORM_FORE_COLOR
916 && ps_global
->VAR_NORM_BACK_COLOR
917 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
918 ps_global
->VAR_NORM_FORE_COLOR
)
919 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
920 ps_global
->VAR_NORM_BACK_COLOR
))){
922 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
923 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
924 if(!pico_is_good_colorpair(hdrcolor
))
925 free_color_pair(&hdrcolor
);
929 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
932 * Attachment list header
937 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
940 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
943 hwid
= MAX(avail
, 0);
945 i
= utf8_width(_("Parts/Attachments:"));
946 partwid
= MIN(i
, hwid
);
947 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
950 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
951 if(!gf_puts(tmp
, pc
))
955 utf8_snprintf(tmp
, sizeof(tmp
),
957 /* TRANSLATORS: A label */
958 partwid
, partwid
, _("Parts/Attachments:"),
961 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
965 /*----- Figure max display widths -----*/
966 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
967 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
970 if((n
= utf8_width(a
->number
)) > maxnumwid
)
973 if((n
= utf8_width(a
->size
)) > maxsizewid
)
978 * ----- adjust max lengths for nice display -----
980 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
984 avail
= width
- m1
- m2
;
986 s1
= MAX(MIN(1, avail
), 0);
989 dwid
= MAX(MIN(1, avail
), 0);
992 s2
= MAX(MIN(1, avail
), 0);
995 maxnumwid
= MIN(maxnumwid
, width
/3);
996 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
999 s3
= MAX(MIN(1, avail
), 0);
1002 shownwid
= MAX(MIN(5, avail
), 0);
1005 s4
= MAX(MIN(3, avail
), 0);
1008 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
1011 s5
= MAX(MIN(2, avail
), 0);
1014 descwid
= MAX(0, avail
);
1016 /*----- Format the list of attachments -----*/
1017 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
1018 COLOR_PAIR
*lastc
= NULL
;
1020 int thisdescwid
, padwid
;
1022 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
1025 if(a
->body
->type
== TYPEMULTIPART
1026 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1030 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1031 thisdescwid
= MIN(i
, descwid
);
1032 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1035 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1036 if(!gf_puts(tmp
, pc
))
1040 utf8_snprintf(tmp
, sizeof(tmp
),
1041 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1044 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1046 maxnumwid
, maxnumwid
,
1048 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1052 a
->shown
? "Shown" :
1053 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1057 a
->size
? a
->size
: "",
1059 thisdescwid
, thisdescwid
,
1060 (descwid
> 2 && a
->description
) ? a
->description
: "");
1062 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1065 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1066 char buf
[16], color
[64];
1070 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1074 h
= new_handle(handlesp
);
1078 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1079 buf
[sizeof(buf
)-1] = '\0';
1081 if(!(flgs
& FM_NOCOLOR
)
1082 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1083 lastc
= get_cur_embedded_color();
1084 if(!gf_nputs(color
, (long) l
, pc
))
1087 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1088 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1091 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1092 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1098 if(!format_env_puts(tmpp
, pc
))
1101 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1103 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1104 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1108 if(!embed_color(lastc
, pc
))
1111 free_color_pair(&lastc
);
1113 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1116 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1121 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1122 if(!gf_puts(tmp
, pc
))
1126 if(!gf_puts(NEWLINE
, pc
))
1131 * Dashed line after list
1135 avail
= width
- m1
- m2
;
1136 hwid
= MAX(avail
, 0);
1138 dashwid
= MAX(MIN(40, hwid
-2), 0);
1139 padwid
= hwid
- dashwid
;
1141 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1142 if(!gf_puts(tmp
, pc
))
1146 snprintf(tmp
, sizeof(tmp
),
1148 repeat_char(dashwid
, '-'),
1149 padwid
, padwid
, "");
1152 avail
= width
- m1
-2;
1154 dashwid
= MAX(MIN(40, avail
), 0);
1157 snprintf(tmp
, sizeof(tmp
),
1160 repeat_char(dashwid
, '-'));
1163 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1167 free_color_pair(&hdrcolor
);
1176 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1177 * of body part fetches as we're formatting) for the
1178 * given message isn't set (likely because there
1179 * weren't any parts suitable for display), then make
1180 * sure to set it here.
1183 format_blip_seen(long int msgno
)
1187 if(msgno
> 0L && ps_global
->mail_stream
1188 && msgno
<= ps_global
->mail_stream
->nmsgs
1189 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1191 && !ps_global
->mail_stream
->rdonly
)
1192 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1199 * is_an_env_hdr - is this name a header in the envelope structure?
1201 * name - the header name to check
1204 is_an_env_hdr(char *name
)
1208 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1209 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1219 * is_an_addr_hdr - is this an address header?
1221 * name - the header name to check
1224 is_an_addr_hdr(char *fieldname
)
1226 char fbuf
[FBUF_LEN
+1];
1227 char *colon
, *fname
;
1228 static char *addr_headers
[] = {
1244 /* so it is pointing to NULL */
1245 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1247 if((colon
= strindex(fieldname
, ':')) != NULL
){
1248 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1249 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1255 if(fname
&& *fname
){
1256 for(p
= addr_headers
; *p
; p
++)
1257 if(!strucmp(fname
, *p
))
1261 return((*p
) ? 1 : 0);
1266 * Format a single field from the envelope
1269 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1270 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1275 fmt_env
= format_envelope
;
1277 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1278 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1279 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1286 * Look through header string beginning with "begin", for the next
1287 * occurrence of header "field". Set "start" to that. Set "end" to point one
1288 * position past all of the continuation lines that go with "field".
1289 * That is, if "end" is converted to a null
1290 * character then the string "start" will be the next occurrence of header
1291 * "field" including all of its continuation lines. Assume we
1292 * have CRLF's as end of lines.
1294 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1295 * make "end" the end of that header.
1297 * Returns 1 if found, 0 if not.
1300 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1302 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1307 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1313 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1314 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1315 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1316 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1319 * We require that start is at the beginning of a line, so
1320 * either it equals begin (which we assume is the beginning of a
1321 * line) or it is preceded by a CRLF.
1324 *start
= srchstr(begin_srch
, tmpfield
);
1325 while(*start
&& *start
!= begin
1326 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1327 begin_srch
= *start
+ 1;
1328 *start
= srchstr(begin_srch
, tmpfield
);
1335 for(p
= *start
; *p
; p
++){
1337 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1339 * The final 015 in the test above is to test for the end
1356 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1360 if(pico_usingcolor()){
1361 char *fg
= NULL
, *bg
= NULL
, *s
;
1362 char *basefg
= NULL
, *basebg
= NULL
;
1364 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1365 : ps_global
->VAR_NORM_FORE_COLOR
;
1366 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1367 : ps_global
->VAR_NORM_BACK_COLOR
;
1369 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1370 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1371 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1373 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1374 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1375 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1381 * The blacks are just known good colors for
1382 * testing whether the other color is good.
1384 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1385 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1386 if(pico_is_good_colorpair(tmp
))
1387 for(s
= color_embed(fg
, bg
);
1388 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1392 free_color_pair(&tmp
);
1396 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1397 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1402 colorstring
[buflen
-1] = '\0';
1409 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1412 if(pico_usingcolor()){
1413 char *fg
= NULL
, *bg
= NULL
, *s
;
1416 * We need to change the fg and bg colors back even if they
1417 * are the same as the Normal Colors so that color_a_quote
1418 * will have a chance to pick up those colors as the ones to
1419 * switch to. We don't do this before the handle above so that
1420 * the quote color will flow into the selectable item when
1421 * the selectable item color is partly the same as the
1422 * normal color. That is, suppose the normal color was black on
1423 * cyan and the selectable color was blue on cyan, only a fg color
1424 * change. We preserve the only-a-fg-color-change in a quote by
1425 * letting the quote background color flow into the selectable text.
1427 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1428 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1430 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1431 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1433 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1434 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1439 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1443 colorstring
[buflen
-1] = '\0';
1450 url_embed(int embed
)
1452 static char buf
[3] = {TAG_EMBED
};
1460 * Paint the signature.
1463 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1465 struct variable
*vars
= ps_global
->vars
;
1467 COLOR_PAIR
*col
= NULL
;
1469 if(is_in_sig
== NULL
)
1472 in_sig_block
= (int *) is_in_sig
;
1474 if(!strcmp(line
, SIGDASHES
))
1475 *in_sig_block
= START_SIG_BLOCK
;
1476 else if(*line
== '\0')
1478 * Suggested by Eduardo: allow for a blank line right after
1481 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1482 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1484 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1485 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1487 if(*in_sig_block
!= OUT_SIG_BLOCK
1488 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1489 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1490 VAR_SIGNATURE_BACK_COLOR
))){
1491 if(!pico_is_good_colorpair(col
))
1492 free_color_pair(&col
);
1496 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1498 ins
= gf_line_test_new_ins(ins
, line
,
1499 color_embed(col
->fg
, col
->bg
),
1502 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1503 fg
[sizeof(fg
)-1] = '\0';
1504 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1505 bg
[sizeof(bg
)-1] = '\0';
1508 * Loop watching colors, and override with
1509 * signature color whenever the normal foreground and background
1510 * colors are in force.
1514 if(*p
++ == TAG_EMBED
){
1518 p
+= *p
+ 1; /* skip handle key */
1522 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1523 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1524 p
+= RGBLEN
; /* advance past color value */
1526 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1527 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1528 ins
= gf_line_test_new_ins(ins
, p
,
1529 color_embed(col
->fg
,NULL
),
1534 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1535 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1536 p
+= RGBLEN
; /* advance past color value */
1538 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1539 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1540 ins
= gf_line_test_new_ins(ins
, p
,
1541 color_embed(NULL
,col
->bg
),
1551 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1552 color_embed(VAR_NORM_FORE_COLOR
,
1553 VAR_NORM_BACK_COLOR
),
1555 free_color_pair(&col
);
1563 * Line filter to add color to displayed headers.
1566 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1568 static char field
[FBUF_LEN
+ 1];
1569 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1570 char *p
, *q
, *value
, *beg
;
1572 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1573 struct variable
*vars
= ps_global
->vars
;
1575 field
[FBUF_LEN
] = '\0';
1577 if(isspace((unsigned char)*line
)) /* continuation line */
1580 if(!(value
= strindex(line
, ':')))
1583 memset(field
, 0, sizeof(field
));
1584 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1587 for(value
++; isspace((unsigned char)*value
); value
++)
1590 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1591 fg
[sizeof(fg
)-1] = '\0';
1592 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1593 bg
[sizeof(bg
)-1] = '\0';
1596 * Split into two cases depending on whether this is a header which
1597 * contains addresses or not. We may color addresses separately.
1599 if(is_an_addr_hdr(field
)){
1602 * If none of the patterns are for this header, don't bother parsing
1603 * and checking each address.
1605 if(!any_hdr_color(field
))
1609 * First check for patternless patterns which color whole line.
1611 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1612 if(pico_is_good_colorpair(color
)){
1613 ins
= gf_line_test_new_ins(ins
, value
,
1614 color_embed(color
->fg
, color
->bg
),
1616 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1617 fg
[sizeof(fg
)-1] = '\0';
1618 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1619 bg
[sizeof(bg
)-1] = '\0';
1623 free_color_pair(&color
);
1627 * Then go through checking address by address.
1628 * Keep track of quotes and watch for color changes, and override
1629 * with most recent header color whenever the normal foreground
1630 * and background colors are in force.
1636 /* skip next character */
1637 if(*(p
+1) && (in_comment
|| in_quote
))
1646 in_quote
= 1 - in_quote
;
1664 if(!(in_quote
|| in_comment
)){
1665 /* we reached the end of this address */
1668 free_color_pair(&color
);
1670 if((color
= hdr_color(field
, beg
,
1671 ps_global
->hdr_colors
)) != NULL
){
1672 if(pico_is_good_colorpair(color
)){
1674 ins
= gf_line_test_new_ins(ins
, beg
,
1675 color_embed(color
->fg
,
1679 for(q
= p
; q
> beg
&&
1680 isspace((unsigned char)*(q
-1)); q
--)
1683 ins
= gf_line_test_new_ins(ins
, q
,
1684 color_embed(fg
, bg
),
1688 free_color_pair(&color
);
1693 for(p
++; isspace((unsigned char)*p
); p
++)
1707 p
+= *p
+ 1; /* skip handle key */
1712 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1713 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1714 p
+= RGBLEN
; /* advance past color value */
1716 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1717 ins
= gf_line_test_new_ins(ins
, p
,
1718 color_embed(color
->fg
,NULL
),
1724 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1725 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1726 p
+= RGBLEN
; /* advance past color value */
1728 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1729 ins
= gf_line_test_new_ins(ins
, p
,
1730 color_embed(NULL
,color
->bg
),
1747 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1750 if(*q
&& !(in_quote
|| in_comment
)){
1751 /* we reached the end of this address */
1753 free_color_pair(&color
);
1755 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1756 if(pico_is_good_colorpair(color
)){
1758 ins
= gf_line_test_new_ins(ins
, beg
,
1759 color_embed(color
->fg
,
1762 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1765 ins
= gf_line_test_new_ins(ins
, q
,
1766 color_embed(fg
, bg
),
1770 free_color_pair(&color
);
1775 free_color_pair(&color
);
1778 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1779 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1780 VAR_HEADER_GENERAL_BACK_COLOR
),
1785 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1788 if(pico_is_good_colorpair(color
)){
1789 ins
= gf_line_test_new_ins(ins
, value
,
1790 color_embed(color
->fg
, color
->bg
),
1794 * Loop watching colors, and override with header
1795 * color whenever the normal foreground and background
1796 * colors are in force.
1800 if(*p
++ == TAG_EMBED
){
1804 p
+= *p
+ 1; /* skip handle key */
1808 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1809 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1810 p
+= RGBLEN
; /* advance past color value */
1812 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1813 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1814 ins
= gf_line_test_new_ins(ins
, p
,
1815 color_embed(color
->fg
,NULL
),
1820 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1821 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1822 p
+= RGBLEN
; /* advance past color value */
1824 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1825 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1826 ins
= gf_line_test_new_ins(ins
, p
,
1827 color_embed(NULL
,color
->bg
),
1837 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1838 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1839 VAR_HEADER_GENERAL_BACK_COLOR
),
1843 free_color_pair(&color
);
1852 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1854 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1855 *weburlp
= NULL
, *mailurlp
= NULL
;
1856 int n
, n1
, n2
, n3
, l
;
1857 char buf
[256], color
[256];
1861 for(lp
= line
; ; lp
= up
+ n
){
1862 /* scan for all of them so we can choose the first */
1863 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1864 urlp
= rfc1738_scan(lp
, &n1
);
1865 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1866 weburlp
= web_host_scan(lp
, &n2
);
1867 if(F_ON(F_SCAN_ADDR
,ps_global
))
1868 mailurlp
= mail_addr_scan(lp
, &n3
);
1870 if(urlp
|| weburlp
|| mailurlp
){
1872 weburlp
? weburlp
: mailurlp
;
1873 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1875 if(mailurlp
&& mailurlp
< up
)
1880 weburlp
= mailurlp
= NULL
;
1882 else if(up
== weburlp
){
1894 uh
= (URL_HILITE_S
*) local
;
1896 h
= new_handle(uh
->handlesp
);
1898 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1899 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1900 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1901 h
->h
.url
.path
[n
+10-1] = '\0';
1903 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1904 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1905 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1906 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1909 buf
[1] = TAG_HANDLE
;
1910 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1911 buf
[sizeof(buf
)-1] = '\0';
1912 buf
[2] = strlen(&buf
[3]);
1913 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1915 /* in case it was the current selection */
1916 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1918 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1919 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1921 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1923 urlp
= weburlp
= mailurlp
= NULL
;
1931 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1933 static int check_for_urls
= 0;
1936 if(isspace((unsigned char)*line
)) /* continuation, check or not
1937 depending on last line */
1941 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1946 if(((ft
= pine_header_standard(line
)) == FreeText
1948 || ft
== TypeUnknown
)
1949 && strucmp(line
, "message-id")
1950 && strucmp(line
, "newsgroups")
1951 && strucmp(line
, "references")
1952 && strucmp(line
, "in-reply-to")
1953 && strucmp(line
, "received")
1954 && strucmp(line
, "date")){
1963 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1970 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1975 struct variable
*vars
= ps_global
->vars
;
1980 total_wid
= *((int *) local
);
1982 /* calculate width of line */
1992 p
+= *p
+ 1; /* skip handle key */
1997 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
2009 default: /* literal embed char */
2017 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
2023 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2029 if(total_wid
> wid
){
2030 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2031 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2032 VAR_HEADER_GENERAL_BACK_COLOR
),
2034 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2035 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2036 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2037 color_embed(VAR_NORM_FORE_COLOR
,
2038 VAR_NORM_BACK_COLOR
),
2050 url_external_specific_handler(char *url
, int len
)
2052 static char list
[UES_LEN
* UES_MAX
];
2058 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2059 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2062 else{ /* initialize! */
2063 char **l
, *test
, *cmd
, *p
, *p2
;
2066 memset(list
, 0, sizeof(list
));
2067 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2068 get_pair(*l
, &test
, &cmd
, 1, 1);
2070 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2073 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2074 if((p2
= strchr(p
, ',')) != NULL
){
2075 if((n
= p2
- p
) < UES_LEN
){
2076 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2081 "* * * HANDLER TOO LONG: %.*s\n", n
,
2087 if(strlen(p
) <= UES_LEN
){
2088 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2097 fs_give((void **) &test
);
2100 fs_give((void **) &cmd
);
2109 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2110 imapuid_t
*uid
, char **search
, int silent
)
2112 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2113 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2116 int rv
= URL_IMAP_ERROR
;
2119 * Since we're planting nulls, operate on a temporary copy...
2121 scheme
= silent
? NULL
: "IMAP";
2122 url
= cpystr(true_url
+ 7);
2124 /* Try to pick apart the "iserver" portion */
2125 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2129 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2131 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2134 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2136 server
= rfc1738_str(p
);
2138 /* only ";auth=*" supported (and also ";auth=anonymous") */
2139 if((p
= srchstr(url
, ";auth=")) != NULL
){
2141 auth
= rfc1738_str(p
+ 6);
2145 user
= rfc1738_str(url
);
2148 server
= rfc1738_str(url
);
2151 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2154 * "iserver" in hand, pick apart the "icommand"...
2157 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2161 * No "icommand" (all top-level folders) or "imailboxlist"...
2164 *p
= '\0'; /* tie off criteria */
2165 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2166 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2167 rv
|= URL_IMAP_IMBXLSTLSUB
;
2168 else if(strucmp(p
, "list"))
2169 return(url_bogus_imap(&url
, scheme
,
2170 "Invalid list type specified"));
2173 rv
|= URL_IMAP_ISERVERONLY
;
2177 /* build folder list from specified server/criteria/list-method */
2178 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2179 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2180 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2181 user
? "user=\"" : "Anonymous",
2184 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2185 (*folder
)[l
] = '\0';
2186 rv
|= URL_IMAP_IMAILBOXLIST
;
2189 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2190 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2191 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2192 *section
= '\0'; /* tie off UID */
2193 section
= rfc1738_str(section
+ 10);
2194 /* BUG: verify valid section spec ala rfc 2060 */
2196 "-- URL IMAP FOLDER: section not used: %s\n",
2197 section
? section
: "?"));
2200 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2201 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2203 /* optional "uidvalidity"? */
2204 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2207 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2208 return(url_bogus_imap(&url
, scheme
,
2209 "Invalid UIDVALIDITY"));
2212 mailbox
= rfc1738_str(cmd
);
2213 rv
= URL_IMAP_IMESSAGEPART
;
2215 else{ /* "imessagelist" */
2216 /* optional "uidvalidity"? */
2217 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2220 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2221 return(url_bogus_imap(&url
, scheme
,
2222 "Invalid UIDVALIDITY"));
2225 /* optional "enc_search"? */
2226 if((p
= strchr(cmd
, '?')) != NULL
){
2229 *search
= cpystr(rfc1738_str(p
+ 1));
2230 /* BUG: verify valid search spec ala rfc 2060 */
2233 mailbox
= rfc1738_str(cmd
);
2234 rv
= URL_IMAP_IMESSAGELIST
;
2237 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2238 q_status_message(SM_ORDER
, 3, 3,
2239 "Unsupported authentication method. Using standard login.");
2242 * At this point our structure should contain the
2243 * digested url. Now put it together for c-client...
2245 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2246 + (user
? (strlen(user
)+2) : 9);
2247 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2248 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2249 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2250 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2254 (*folder
)[l
] = '\0';
2257 fs_give((void **) &url
);
2263 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2265 fs_give((void **) freeme
);
2266 (void) url_bogus(url
, problem
);
2267 return(URL_IMAP_ERROR
);
2272 * url_bogus - report url syntax errors and such
2275 url_bogus(char *url
, char *reason
)
2277 dprint((2, "-- bogus url \"%s\": %s\n",
2278 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2280 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2281 "Malformed \"%.*s\" URL: %.200s",
2282 (void *) (strchr(url
, ':') - url
), url
, reason
);
2289 /*----------------------------------------------------------------------
2290 Format header text suitable for display
2292 Args: stream -- mail stream for various header text fetches
2293 msgno -- sequence number in stream of message we're interested in
2294 section -- which section of message
2295 env -- pointer to msg's envelope
2296 hdrs -- struct containing what's to get formatted
2297 prefix -- prefix to append to each output line
2298 handlesp -- address of pointer to the message's handles
2299 flags -- FM_ flags, see pith/mailview.h
2300 final_pc -- function to write header text with
2302 Result: 0 if all's well, -1 if write error, 1 if fetch error
2304 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2305 in the local convention.
2309 #define FHT_WRTERR -1
2310 #define FHT_FTCHERR 1
2312 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2313 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2314 fmt_env_t fmt_env
, gf_io_t final_pc
)
2318 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2322 gf_io_t tmp_pc
, tmp_gc
;
2323 struct variable
*vars
= ps_global
->vars
;
2325 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2326 gf_set_so_writec(&tmp_pc
, tmp_store
);
2331 fmt_env
= format_envelope
;
2333 if(ps_global
->full_header
== 2){
2334 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2338 * First, calculate how big a fields array we need.
2341 /* Custom header viewing list specified */
2342 if(hdrs
->type
== HD_LIST
){
2343 /* view all these headers */
2345 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2346 if(!is_an_env_hdr(q
))
2349 /* view all except these headers */
2351 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2355 nfields
--; /* subtract one for ALL_EXCEPT field */
2359 nfields
= 6; /* default view */
2361 /* allocate pointer space */
2363 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2364 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2367 if(hdrs
->type
== HD_LIST
){
2368 /* view all these headers */
2370 /* put the non-envelope headers in fields */
2372 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2373 if(!is_an_env_hdr(q
))
2376 /* view all except these headers */
2378 /* put the list of headers not to view in fields */
2380 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2381 if(strucmp(ALL_EXCEPT
, q
))
2389 fields
[i
= 0] = "Resent-Date";
2390 fields
[++i
] = "Resent-From";
2391 fields
[++i
] = "Resent-To";
2392 fields
[++i
] = "Resent-cc";
2393 fields
[++i
] = "Resent-Subject";
2399 /* custom view with exception list */
2400 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2402 * Go through each header in h and print it.
2403 * First we check to see if it is an envelope header so we
2404 * can print our envelope version of it instead of the raw version.
2407 /* fetch all the other headers */
2409 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2412 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2414 char tmp
[MAILTMPLEN
+1];
2417 colon_loc
= strindex(start
, ':');
2418 if(colon_loc
&& colon_loc
< finish
){
2419 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2420 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2425 if(colon_loc
&& is_an_env_hdr(tmp
)){
2426 char *dummystart
, *dummyfinish
;
2429 * Pretty format for env hdrs.
2430 * If the same header appears more than once, only
2431 * print the last to avoid duplicates.
2432 * They should have been combined in the env when parsed.
2434 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2436 format_env_hdr(stream
, msgno
, section
, env
,
2437 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2440 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2441 hdrs
->charset
, flags
)) != 0)
2448 /* custom view or default */
2450 /* fetch the non-envelope headers */
2452 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2454 /* default envelope for default view */
2455 if(hdrs
->type
== HD_BFIELD
)
2456 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2458 /* go through each header in list, v initialized above */
2459 for(; (q
= *v
) != NULL
; v
++){
2460 if(is_an_env_hdr(q
)){
2461 /* pretty format for env hdrs */
2462 format_env_hdr(stream
, msgno
, section
, env
,
2463 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2467 * Go through h finding all occurrences of this header
2468 * and all continuation lines, and output.
2471 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2473 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2474 hdrs
->charset
, flags
)) != 0)
2487 gf_clear_so_writec(tmp_store
);
2489 if(!rv
){ /* valid data? Do wrapping and filtering... */
2491 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2492 STORE_S
*df_store
= NULL
;
2494 so_seek(tmp_store
, 0L, 0);
2496 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2499 * Test for and act on any display filter
2500 * This barely makes sense. The display filter is going
2501 * to be getting UTF-8'ized headers here. In pre-alpine
2502 * pine the display filter was being fed already decoded
2503 * headers in whatever character set they were in.
2504 * The good news is that that didn't make much
2505 * sense either, so this shouldn't break anything.
2506 * It seems unlikely that anybody is doing anything useful
2507 * with the header part of display filters.
2509 if(ps_global
->tools
.display_filter
2510 && ps_global
->tools
.display_filter_trigger
2511 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2512 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2514 gf_set_so_writec(&tmp_pc
, df_store
);
2515 gf_set_so_readc(&tmp_gc
, df_store
);
2516 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2517 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2518 _("Formatting error: %s"), errstr
);
2522 so_seek(df_store
, 0L, 0);
2524 gf_clear_so_writec(df_store
);
2527 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2528 "No space for filtered text.");
2533 so_seek(tmp_store
, 0L, 0);
2534 gf_set_so_readc(&tmp_gc
, tmp_store
);
2538 int *margin
, wrapflags
= GFW_ONCOMMA
;
2541 gf_link_filter(gf_local_nvtnl
, NULL
);
2543 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2544 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2545 || F_ON(F_SCAN_ADDR
, ps_global
))
2547 gf_link_filter(gf_line_test
,
2548 gf_line_test_opt(url_hilite_hdr
,
2549 gf_url_hilite_opt(&uh
,handlesp
,1)));
2550 wrapflags
|= GFW_HANDLES
;
2553 if((flags
& FM_DISPLAY
)
2554 && !(flags
& FM_NOCOLOR
)
2555 && pico_usingcolor()
2556 && ((VAR_NORM_FORE_COLOR
2557 && VAR_HEADER_GENERAL_FORE_COLOR
2558 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2560 (VAR_NORM_BACK_COLOR
2561 && VAR_HEADER_GENERAL_BACK_COLOR
2562 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2563 wrapflags
|= GFW_HDRCOLOR
;
2565 if((flags
& FM_DISPLAY
)
2566 && !(flags
& FM_NOCOLOR
)
2567 && pico_usingcolor()
2568 && ps_global
->hdr_colors
){
2569 gf_link_filter(gf_line_test
,
2570 gf_line_test_opt(color_headers
, NULL
));
2571 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2574 if(prefix
&& *prefix
)
2575 column
= MAX(column
-strlen(prefix
), 50);
2577 margin
= format_view_margin();
2579 if(!(flags
& FM_NOWRAP
))
2580 gf_link_filter(gf_wrap
,
2581 gf_wrap_filter_opt(column
, column
,
2582 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2585 if(prefix
&& *prefix
)
2586 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2588 if((flags
& FM_DISPLAY
)
2589 && !(flags
& FM_NOCOLOR
)
2590 && pico_usingcolor()
2591 && ((VAR_NORM_FORE_COLOR
2592 && VAR_HEADER_GENERAL_FORE_COLOR
2593 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2595 (VAR_NORM_BACK_COLOR
2596 && VAR_HEADER_GENERAL_BACK_COLOR
2597 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2601 right_margin
= margin
? margin
[1] : 0;
2602 total_wid
= column
- right_margin
;
2604 gf_link_filter(gf_line_test
,
2605 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2606 wrapflags
|= GFW_HANDLES
;
2609 gf_link_filter(gf_nvtnl_local
, NULL
);
2611 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2613 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2614 "Can't build header : %.200s", errstr
);
2619 gf_clear_so_readc(df_store
);
2623 gf_clear_so_readc(tmp_store
);
2626 so_give(&tmp_store
);
2629 fs_give((void **)&h
);
2632 fs_give((void **)&fields
);
2638 /*----------------------------------------------------------------------
2639 Format RAW header text for display
2641 Args: stream -- mail stream for various header text fetches
2642 rawno -- sequence number in stream of message we're interested in
2643 section -- which section of message
2644 pc -- function to write header text with
2646 Result: 0 if all's well, -1 if write error, 1 if fetch error
2648 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2649 in the local convention.
2653 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2655 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2662 if(!gf_puts(NEWLINE
, pc
))
2665 if(ISRFCEOL(h
)) /* all done! */
2668 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2669 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2670 c
= (unsigned char) *h
++;
2671 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2672 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2675 else if(!(*pc
)(*h
++))
2680 return(FHT_FTCHERR
);
2687 /*----------------------------------------------------------------------
2688 Format c-client envelope data suitable for display
2690 Args: s -- mail stream for various header text fetches
2691 n -- raw sequence number in stream of message we're interested in
2692 sect -- which section of message
2693 e -- pointer to msg's envelope
2694 pc -- function to write header text with
2695 which -- which header lines to write
2697 flags -- FM_ flags, see pith/mailview.h
2699 Result: 0 if all's well, -1 if write error, 1 if fetch error
2701 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2702 in the local convention.
2706 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2707 long int which
, char *oacs
, int flags
)
2709 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2714 if((which
& FE_DATE
) && e
->date
) {
2716 snprintf(buftmp
, sizeof(buftmp
), "%s",
2717 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2718 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2719 buftmp
[sizeof(buftmp
)-1] = '\0';
2720 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2721 SIZEOF_20KBUF
, buftmp
);
2723 format_env_puts(p2
, pc
);
2724 gf_puts(NEWLINE
, pc
);
2727 if((which
& FE_FROM
) && e
->from
)
2728 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2730 if((which
& FE_REPLYTO
) && e
->reply_to
2731 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2732 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2734 if((which
& FE_TO
) && e
->to
)
2735 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2737 if((which
& FE_CC
) && e
->cc
)
2738 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2740 if((which
& FE_BCC
) && e
->bcc
)
2741 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2743 if((which
& FE_RETURNPATH
) && e
->return_path
)
2744 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2747 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2749 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2750 if (!e
->ngpathexists
&& e
->message_id
&&
2751 strncmp (e
->message_id
,"<alpine.",8) &&
2752 strncmp (e
->message_id
,"<Pine.",6) &&
2753 strncmp (e
->message_id
,"<MS-C.",6) &&
2754 strncmp (e
->message_id
,"<MailManager.",13) &&
2755 strncmp (e
->message_id
,"<EasyMail.",11) &&
2756 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2759 q_status_message(SM_ORDER
, 0, 3,
2760 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2763 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2764 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2766 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2767 char *freeme
= NULL
;
2772 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2774 if(flags
& FM_DISPLAY
2775 && (ps_global
->display_keywords_in_subject
2776 || ps_global
->display_keywordinits_in_subject
)){
2778 /* don't bother if no keywords are defined */
2779 if(some_user_flags_defined(s
))
2780 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2781 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2782 NULL
, ps_global
->VAR_KW_BRACES
);
2785 format_env_puts(p2
, pc
);
2788 fs_give((void **) &freeme
);
2790 gf_puts(NEWLINE
, pc
);
2793 if((which
& FE_SENDER
) && e
->sender
2794 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2795 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2797 if((which
& FE_MESSAGEID
) && e
->message_id
){
2800 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2801 format_env_puts(p2
, pc
);
2802 gf_puts(NEWLINE
, pc
);
2805 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2806 q
= "In-Reply-To: ";
2808 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);
2809 format_env_puts(p2
, pc
);
2810 gf_puts(NEWLINE
, pc
);
2813 if((which
& FE_REFERENCES
) && e
->references
) {
2816 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2817 format_env_puts(p2
, pc
);
2818 gf_puts(NEWLINE
, pc
);
2826 * The argument fieldname is something like "Subject:..." or "Subject".
2827 * Look through the specs in speccolor for a match of the fieldname,
2828 * and return the color that goes with any match, or NULL.
2829 * Caller should free the color.
2832 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2834 SPEC_COLOR_S
*hc
= NULL
;
2835 COLOR_PAIR
*color_pair
= NULL
;
2836 char *colon
, *fname
;
2837 char fbuf
[FBUF_LEN
+1];
2841 colon
= strindex(fieldname
, ':');
2843 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2844 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2851 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2852 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2857 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2858 if(srchstr(value
, pat
->substring
))
2865 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2866 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2873 * The argument fieldname is something like "Subject:..." or "Subject".
2874 * Look through the specs in hdr_colors for a match of the fieldname,
2875 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2878 any_hdr_color(char *fieldname
)
2880 SPEC_COLOR_S
*hc
= NULL
;
2881 char *colon
, *fname
;
2882 char fbuf
[FBUF_LEN
+1];
2884 colon
= strindex(fieldname
, ':');
2886 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2887 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2894 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2895 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2902 /*----------------------------------------------------------------------
2903 Format an address field, wrapping lines nicely at commas
2905 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2906 addr -- ADDRESS structure to format
2908 Result: A formatted, malloced string is returned.
2909 ----------------------------------------------------------------------*/
2911 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2912 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2914 char *ptmp
, *mtmp
= NULL
;
2915 int trailing
= 0, group
= 0;
2922 * quickly run down address list to make sure none are patently bogus.
2923 * If so, just blat raw field out.
2925 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2926 if(atmp
->host
&& atmp
->host
[0] == '.'){
2927 char *field
, *fields
[2];
2930 fields
[0] = cpystr(field_name
);
2931 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2934 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2937 for(t
= h
= field
; *h
; t
++)
2938 if(*t
== '\015' && *(t
+1) == '\012'){
2939 *t
= '\0'; /* tie off line */
2940 format_env_puts(h
, pc
);
2941 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2942 gf_puts(NEWLINE
, pc
); /* more to write */
2946 else if(!*t
){ /* shouldn't happen much */
2948 format_env_puts(h
, pc
);
2953 fs_give((void **)&field
);
2956 fs_give((void **)&fields
[0]);
2957 gf_puts(NEWLINE
, pc
);
2958 dprint((2, "Error in \"%s\" field address\n",
2959 field_name
? field_name
: "?"));
2963 gf_puts(field_name
, pc
);
2966 atmp
= addr
->next
; /* remember what's next */
2968 if(!addr
->host
&& addr
->mailbox
){
2969 mtmp
= addr
->mailbox
;
2970 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2971 (unsigned char *)tmp_20k_buf
,
2972 SIZEOF_20KBUF
, addr
->mailbox
));
2975 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2976 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2977 tmp_20k_buf
[10000-1] = '\0';
2979 if(!trailing
) /* 1st pass, just address */
2981 else{ /* else comma, unless */
2982 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2983 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2986 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2994 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2996 addr
->personal
= ptmp
; /* restore old personal ptr */
2997 if(!addr
->host
&& addr
->mailbox
){
2998 fs_give((void **)&addr
->mailbox
);
2999 addr
->mailbox
= mtmp
;
3006 gf_puts(NEWLINE
, pc
);
3012 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3013 /* RFC822 continuation, must start with CRLF */
3014 #define RFC822CONT "\015\012 "
3016 /* Write RFC822 address with some quoting turned off.
3018 * address to interpret
3020 * (This is a copy of c-client's rfc822_write_address except
3021 * we don't quote double quote and dot in personal names. It writes
3022 * to a gf_io_t instead of to a buffer so that we don't have to worry
3023 * about fixed sized buffer overflowing. It's also special cased to deal
3024 * with only a single address.)
3026 * The idea is that there are some places where we'd just like to display
3027 * the personal name as is before applying confusing quoting. However,
3028 * we do want to be careful not to break things that should be quoted so
3029 * we'll only use this where we are sure. Quoting may look ugly but it
3030 * doesn't usually break anything.
3033 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3035 extern const char *rspecials
;
3037 if (adr
->host
) { /* ordinary address? */
3038 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3039 else { /* no, must use phrase <route-addr> form */
3041 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3043 gf_puts(" <", pc
); /* write address delimiter */
3044 pine_rfc822_address(adr
, pc
);
3045 gf_puts (">", pc
); /* closing delimiter */
3051 else if (adr
->mailbox
) { /* start of group? */
3052 /* yes, write group name */
3053 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3055 gf_puts (": ", pc
); /* write group identifier */
3056 *group
= 1; /* in a group */
3058 else if (*group
) { /* must be end of group (but be paranoid) */
3060 *group
= 0; /* no longer in that group */
3065 /* Write RFC822 route-address to string
3067 * address to interpret
3071 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3073 extern char *wspecials
;
3075 if (adr
&& adr
->host
) { /* no-op if no address */
3076 if (adr
->adl
) { /* have an A-D-L? */
3077 gf_puts (adr
->adl
, pc
);
3080 /* write mailbox name */
3081 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3082 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3083 gf_puts ("@", pc
); /* host delimiter */
3084 gf_puts (adr
->host
, pc
); /* write host name */
3090 /* Concatenate RFC822 string
3092 * pointer to string to concatenate
3093 * list of special characters
3097 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3101 if (strpbrk (src
,specials
)) { /* any specials present? */
3102 gf_puts ("\"", pc
); /* opening quote */
3103 /* truly bizarre characters in there? */
3104 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3107 /* turn it into a null-terminated piece */
3111 gf_puts (src
, pc
); /* yes, output leader */
3113 gf_puts ("\\", pc
); /* quoting */
3114 gf_puts (save
, pc
); /* output the bizarre character */
3115 src
= ++s
; /* continue after the bizarre character */
3117 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3118 gf_puts ("\"", pc
); /* closing quote */
3120 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3124 /*----------------------------------------------------------------------
3125 Format an address field, wrapping lines nicely at commas
3127 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3128 newsgrps -- ADDRESS structure to format
3130 Result: A formatted, malloced string is returned.
3132 The resulting lines formatted are 80 columns wide.
3133 ----------------------------------------------------------------------*/
3135 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3137 char buf
[MAILTMPLEN
];
3138 int trailing
= 0, llen
, alen
;
3141 if(!newsgrps
|| !*newsgrps
)
3144 gf_puts(field_name
, pc
);
3146 llen
= strlen(field_name
);
3148 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3149 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3150 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3155 if(!trailing
){ /* first time thru, just address */
3159 else{ /* else preceding comma */
3163 if(alen
+ llen
+ 1 > 76){
3164 gf_puts(NEWLINE
, pc
);
3174 if(alen
&& llen
> 76){ /* handle long addresses */
3175 register char *q
, *p
= &buf
[alen
-1];
3178 if(isspace((unsigned char)*p
)
3179 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3180 for(q
= buf
; q
< p
; q
++)
3181 (*pc
)(*q
); /* write character */
3183 gf_puts(NEWLINE
, pc
);
3192 if(p
== buf
) /* no reasonable break point */
3199 gf_puts(NEWLINE
, pc
);
3204 /*----------------------------------------------------------------------
3205 Format a text field that's part of some raw (non-envelope) message header
3211 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3213 ----------------------------------------------------------------------*/
3215 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3217 register char *current
;
3218 unsigned char *p
, *tmp
= NULL
, c
;
3226 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3228 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3231 len
= SIZEOF_20KBUF
;
3232 p
= (unsigned char *) tmp_20k_buf
;
3235 if(islower((unsigned char)(*start
)))
3236 *start
= toupper((unsigned char)(*start
));
3238 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3240 /* output from start to finish */
3241 while(*current
&& rv
== FHT_OK
)
3242 if(ISRFCEOL(current
)){
3243 if(!gf_puts(NEWLINE
, pc
))
3248 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3249 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3250 c
= (unsigned char) *current
++;
3251 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3252 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3255 else if(!(*pc
)(*current
++))
3259 fs_give((void **) &tmp
);
3269 /*----------------------------------------------------------------------
3270 Format a text field that's part of some raw (non-envelope) message header
3277 ----------------------------------------------------------------------*/
3279 format_env_puts(char *s
, gf_io_t pc
)
3281 if(ps_global
->pass_ctrl_chars
)
3282 return(gf_puts(s
, pc
));
3285 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3286 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3287 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3298 display_parameters(PARAMETER
*params
)
3303 PARMLIST_S
*parmlist
;
3305 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3306 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3307 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3310 tmp_20k_buf
[0] = '\0';
3311 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3312 n
= 0; /* n overloaded */
3313 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3315 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3316 tmp_20k_buf
[10000-1] = '\0';
3320 if(parmlist
->value
){
3321 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3322 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3326 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3331 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3332 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3334 tmp_20k_buf
[10000-1] = '\0';
3338 rfc2231_free_parmlist(&parmlist
);
3341 return(tmp_20k_buf
);
3345 /*----------------------------------------------------------------------
3346 Fetch the requested header fields from the msgno specified
3348 Args: stream -- mail stream of open folder
3349 msgno -- number of message to get header lines from
3350 fields -- array of pointers to desired fields
3352 Returns: allocated string containing matched header lines,
3356 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3359 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3360 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3363 * If the user misconfigures it is possible to have one of the fields
3364 * set to the empty string instead of a header name. We want to catch
3365 * that here instead of asking the server the nonsensical question.
3367 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3371 if(pp
&& *pp
){ /* found an empty header field, fix it */
3372 pflds
= copy_list_array(fields
);
3373 for(pp
= pflds
; pp
&& *pp
; pp
++){
3374 if(!**pp
){ /* scoot rest of the lines up */
3376 for(qq
= pp
; *qq
; qq
++)
3380 fs_give((void **) &free_this
);
3384 /* no headers to look for, return NULL */
3385 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3386 free_list_array(&pflds
);
3393 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3394 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3399 if(pflds
&& pflds
!= fields
)
3400 free_list_array(&pflds
);
3405 while(find_field(&h
, tmp
, sizeof(tmp
))){
3406 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3409 /* interesting field? */
3410 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3412 * Hold off allocating space for matching fields until
3413 * we at least find one to copy...
3416 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3418 while(*p
) /* copy field name */
3421 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3422 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3425 *m
= '\0'; /* tie off match string */
3427 else{ /* no match, pass this field */
3428 while(*h
&& !(*h
++ == '\n'
3429 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3434 if(pflds
&& pflds
!= fields
)
3435 free_list_array(&pflds
);
3437 return(match
? match
: cpystr(""));
3442 find_field(char **h
, char *tmp
, size_t ntmp
)
3446 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3449 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3457 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3461 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3464 if(_last_embedded_fg_color
)
3465 fs_give((void **)&_last_embedded_fg_color
);
3467 _last_embedded_fg_color
= cpystr(cp
->fg
);
3469 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3470 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3475 if(_last_embedded_bg_color
)
3476 fs_give((void **)&_last_embedded_bg_color
);
3478 _last_embedded_bg_color
= cpystr(cp
->bg
);
3480 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3481 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3490 get_cur_embedded_color(void)
3494 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3495 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3497 ret
= pico_get_cur_color();
3504 clear_cur_embedded_color(void)
3506 if(_last_embedded_fg_color
)
3507 fs_give((void **)&_last_embedded_fg_color
);
3509 if(_last_embedded_bg_color
)
3510 fs_give((void **)&_last_embedded_bg_color
);
3515 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3519 if(pico_usingcolor()){
3520 char *fg
= NULL
, *bg
= NULL
, *s
;
3522 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3523 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3524 ps_global
->VAR_NORM_FORE_COLOR
))
3525 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3527 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3528 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3529 ps_global
->VAR_NORM_BACK_COLOR
))
3530 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3536 * The blacks are just known good colors for
3537 * testing whether the other color is good.
3539 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3540 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3541 if(pico_is_good_colorpair(tmp
))
3542 for(s
= color_embed(fg
, bg
);
3543 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3547 free_color_pair(&tmp
);
3551 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3552 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3557 colorstring
[buflen
-1] = '\0';
3564 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3567 if(pico_usingcolor()){
3568 char *fg
= NULL
, *bg
= NULL
, *s
;
3569 char *basefg
= NULL
, *basebg
= NULL
;
3571 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3572 : ps_global
->VAR_NORM_FORE_COLOR
;
3573 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3574 : ps_global
->VAR_NORM_BACK_COLOR
;
3577 * We need to change the fg and bg colors back even if they
3578 * are the same as the Normal Colors so that color_a_quote
3579 * will have a chance to pick up those colors as the ones to
3580 * switch to. We don't do this before the handle above so that
3581 * the quote color will flow into the selectable item when
3582 * the selectable item color is partly the same as the
3583 * normal color. That is, suppose the normal color was black on
3584 * cyan and the selectable color was blue on cyan, only a fg color
3585 * change. We preserve the only-a-fg-color-change in a quote by
3586 * letting the quote background color flow into the selectable text.
3588 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3591 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3594 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3595 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3600 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3604 colorstring
[buflen
-1] = '\0';
3611 * Helper routine that is of limited use.
3612 * We need to tally up the screen width of
3613 * a UTF-8 string as we go through the string.
3614 * We just want the width of the character starting
3615 * at str (and no longer than remaining_octets).
3616 * If we're plopped into the middle of a UTF-8
3617 * character we just want to return width zero.
3620 width_at_this_position(unsigned char *str
, unsigned long n
)
3622 unsigned char *inputp
= str
;
3623 unsigned long remaining_octets
= n
;
3627 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3628 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3629 width
= wcellwidth(ucs
);
3630 /* Writechar will print a '?' */