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-2018 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 ical_remove_escapes(&vesy
->location
);
337 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
338 padding
, _("Location: "), vesy
->location
);
339 gf_puts(tmp_20k_buf
, pc
);
340 gf_puts(NEWLINE
, pc
);
341 } /* end of if location */
344 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
345 padding
, _("Start Date: "), vesy
->evstart
);
346 gf_puts(tmp_20k_buf
, pc
);
347 gf_puts(NEWLINE
, pc
);
348 } /* end of if dtstart */
353 for(i
= 0; vesy
->duration
[i
] != NULL
; i
++){
354 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
355 padding
, _("Duration: "), vesy
->duration
[i
]);
356 gf_puts(tmp_20k_buf
, pc
);
357 gf_puts(NEWLINE
, pc
);
359 } /* end of DURATION */
360 else if(vesy
->evend
){
361 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
362 padding
, _("End Date: "), vesy
->evend
);
363 gf_puts(tmp_20k_buf
, pc
);
364 gf_puts(NEWLINE
, pc
);
365 } else { /* end of if dtend */
366 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
367 padding
, _("No duration nor end time found for this event"));
368 gf_puts(tmp_20k_buf
, pc
);
369 gf_puts(NEWLINE
, pc
);
370 } /* end of else for if (duration) and if (dtend) */
373 #define MAX_DISPLAYED 3
376 for(i
= 0; vesy
->attendee
[i
] != NULL
; i
++){
377 if((cflags
& FC_SUMMARY
) && i
>= MAX_DISPLAYED
)
380 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
382 _("Attendee: "), vesy
->attendee
[i
]);
383 gf_puts(tmp_20k_buf
, pc
);
384 gf_puts(NEWLINE
, pc
);
386 } /* end of ATTENDEES */
389 if(cflags
& FC_SUMMARY
){
390 COLOR_PAIR
*lastc
= NULL
;
393 COLOR_PAIR
*hdrcolor
= NULL
;
395 if((flgs
& FM_DISPLAY
)
396 && !(flgs
& FM_NOCOLOR
)
398 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
399 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
400 && ps_global
->VAR_NORM_FORE_COLOR
401 && ps_global
->VAR_NORM_BACK_COLOR
402 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
403 ps_global
->VAR_NORM_FORE_COLOR
)
404 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
405 ps_global
->VAR_NORM_BACK_COLOR
))){
407 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
408 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
409 if(!pico_is_good_colorpair(hdrcolor
))
410 free_color_pair(&hdrcolor
);
414 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
417 gf_puts(padding
, pc
);
418 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
421 char buf
[16], color
[64];
425 h
= new_handle(handlesp
);
426 if(minkey
< 0) minkey
= h
->key
;
428 h
->h
.ical
.attach
= a
;
429 h
->h
.ical
.depth
= h
->key
- minkey
;
431 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
432 buf
[sizeof(buf
)-1] = '\0';
434 if(!(flgs
& FM_NOCOLOR
)
435 && handle_start_color(color
, sizeof(color
), &l
, 1)){
436 lastc
= get_cur_embedded_color();
437 if(!gf_nputs(color
, (long) l
, pc
))
440 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
441 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
444 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
445 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
448 tmp_20k_buf
[0] = '\0';
450 if(!format_env_puts(tmp_20k_buf
, pc
))
455 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
456 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
460 if(!embed_color(lastc
, pc
))
463 free_color_pair(&lastc
);
465 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
468 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
473 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
474 if(!gf_puts(tmp_20k_buf
, pc
))
478 if(!gf_puts(NEWLINE
, pc
))
481 avail
= width
- m1
-2;
483 dwid
= MAX(MIN(40, avail
), 0);
486 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
487 repeat_char(dwid
, '-'));
489 gf_puts(tmp_20k_buf
, pc
);
491 gf_puts(NEWLINE
, pc
);
493 free_vevent_summary(&vesummary
);
497 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
499 char *rawtext
, *caltext
;
500 unsigned long callen
;
501 VCALENDAR_S
*vcal
= NULL
;
505 if(flgs
& FM_NEW_MESS
) {
506 zero_atmts(ps_global
->atmts
);
507 describe_mime(body
, "", 1, 1, 0, flgs
);
510 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
511 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
512 b
= mail_body (ps_global
->mail_stream
, msgno
, a
->number
);
514 gf_puts(_("Error fetching calendar body part"), pc
);
515 gf_puts(NEWLINE
, pc
);
518 if(b
->sparep
== NULL
){
519 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
520 if(rawtext
== NULL
|| *rawtext
== '\0'){
521 gf_puts(_("Error fetching calendar text"), pc
);
522 gf_puts(NEWLINE
, pc
);
525 rawtext
[callen
] = '\0'; /* chop off cookie */
528 caltext
= rfc822_base64(rawtext
, strlen(rawtext
), &callen
);
530 gf_puts(_("Error in calendar base64 encoding"), pc
);
531 gf_puts(NEWLINE
, pc
);
534 caltext
[callen
] = '\0';
537 case ENCQUOTEDPRINTABLE
:
538 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
540 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
541 gf_puts(NEWLINE
, pc
);
544 caltext
[callen
] = '\0';
547 default: caltext
= cpystr(rawtext
);
550 vcal
= ical_parse_text(caltext
);
551 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
552 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
553 fs_give((void **) &caltext
);
556 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
557 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
558 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
559 if(vcal
->comp
[VEvent
] != NULL
){
560 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
561 } /* else another type of entry in the calendar */
563 gf_puts(NEWLINE
, pc
);
571 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
573 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
574 int is_in_sig
= OUT_SIG_BLOCK
;
575 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
581 || (ps_global
->full_header
== 2
582 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
584 /*--- Server is not an IMAP2bis, It can't parse MIME
585 so we just show the text here. Hopefully the
586 message isn't a MIME message
590 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
591 msgno
, NULL
, NULL
, NIL
)) != NULL
){
593 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
594 return("Write Error");
596 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
600 * We need to translate the message
601 * into UTF-8, but that's trouble in the full header case
602 * because we don't know what to translate from. We'll just
603 * take a guess by looking for the first text part and
606 if(body
&& body
->type
== TYPETEXT
)
607 charset
= parameter_val(body
->parameter
, "charset");
608 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
609 && body
->nested
.part
->body
.type
== TYPETEXT
)
610 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
612 charset
= cpystr(ps_global
->display_charmap
);
614 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
615 /* transliterate message text to UTF-8 */
616 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
618 if (charset
) fs_give((void **) &charset
);
620 /* link in filters, similar to what is done in decode_text() */
621 if(!ps_global
->pass_ctrl_chars
){
622 gf_link_filter(gf_escape_filter
, NULL
);
624 gf_link_filter(gf_control_filter
,
625 gf_control_filter_opt(&filt_only_c0
));
628 gf_link_filter(gf_tag_filter
, NULL
);
630 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
631 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
632 || F_ON(F_SCAN_ADDR
, ps_global
))
634 gf_link_filter(gf_line_test
,
635 gf_line_test_opt(url_hilite
,
636 gf_url_hilite_opt(&uh
,handlesp
,0)));
639 if((flgs
& FM_DISPLAY
)
640 && !(flgs
& FM_NOCOLOR
)
642 && ps_global
->VAR_SIGNATURE_FORE_COLOR
643 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
644 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
647 if((flgs
& FM_DISPLAY
)
648 && !(flgs
& FM_NOCOLOR
)
650 && ps_global
->VAR_QUOTE1_FORE_COLOR
651 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
652 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
655 if(!(flgs
& FM_NOWRAP
)){
656 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
658 && !(flgs
& FM_NOCOLOR
)
659 && pico_usingcolor())
660 wrapflags
|= GFW_USECOLOR
;
661 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
663 ? NULL
: format_view_margin(),
668 gf_link_filter(gf_nvtnl_local
, NULL
);
669 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
670 /* TRANSLATORS: There was an error putting together a message for
671 viewing. The arg is the description of the error. */
672 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
673 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
674 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
675 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
676 && gf_puts(NEWLINE
, pc
))
684 if(!gf_puts(NEWLINE
, pc
)
685 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
686 || !gf_puts(NEWLINE
, pc
)
687 || !gf_puts(NEWLINE
, pc
))
688 return("Write Error");
694 /*======== Now loop through formatting all the parts =======*/
695 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
696 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
699 if(a
->body
->type
== TYPEMULTIPART
){
701 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
703 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
704 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
705 return("Write Error");
713 if(a
->suppress_editorial
)
716 if(!(flgs
& FM_NOEDITORIAL
)
717 && (!gf_puts(NEWLINE
, pc
)
718 || (decode_err
= part_desc(a
->number
, a
->body
,
720 ? (a
->can_display
!= MCD_NONE
)
722 : 3, width
, flgs
, pc
))))
723 return("Write Error");
728 switch(a
->body
->type
){
732 * If a message is multipart *and* the first part of it
733 * is text *and that text is empty, there is a good chance that
734 * there was actually something there that c-client was
735 * unable to parse. Here we report the empty message body
736 * and insert the raw RFC822.TEXT (if full-headers are
739 if(body
->type
== TYPEMULTIPART
740 && a
== ps_global
->atmts
741 && a
->body
->size
.bytes
== 0
742 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
745 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
746 "Empty or malformed message%s.",
747 ps_global
->full_header
== 2
748 ? ". Displaying raw text"
749 : ". Use \"H\" to see raw text");
750 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
752 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
753 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
754 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
755 return("Write Error");
757 if(ps_global
->full_header
== 2
758 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
759 a
->number
, pc
, flgs
))){
760 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
761 "%s%s [ Formatting error: %s ]%s%s",
762 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
763 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
764 if(!gf_puts(tmp_20k_buf
, pc
))
765 return("Write Error");
772 * Don't write our delimiter if this text part is
773 * the first part of a message/rfc822 segment...
775 if(show_parts
&& a
!= ps_global
->atmts
776 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
778 || (a
[-1].body
->type
== TYPEMULTIPART
779 && a
[-1].body
->subtype
780 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
781 && &a
[-1] != ps_global
->atmts
782 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
785 && !(flgs
& FM_NOEDITORIAL
)){
786 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
787 tmp1
= "Calendar entry";
789 tmp1
= a
->body
->description
? a
->body
->description
791 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
792 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
794 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
796 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
797 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
798 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
799 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
800 return("Write Error");
803 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
804 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
805 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
810 tmp1
= a
->body
->description
? a
->body
->description
811 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
813 : "Included Message";
814 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
815 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
817 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
819 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
821 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
822 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
823 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
824 return("Write Error");
826 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
827 /* imapenvonly, we may not have all the headers we need */
828 if(a
->body
->nested
.msg
->env
->imapenvonly
)
829 mail_fetch_header(ps_global
->mail_stream
, msgno
,
830 a
->number
, NULL
, NULL
, FT_PEEK
);
831 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
832 a
->body
->nested
.msg
->env
, hp
,
833 NULL
, handlesp
, flgs
, NULL
, pc
)){
834 case -1 : /* write error */
835 return("Write Error");
837 case 1 : /* fetch error */
838 if(!(gf_puts("[ Error fetching header ]", pc
)
839 && !gf_puts(NEWLINE
, pc
)))
840 return("Write Error");
845 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
846 int *margin
, avail
, m1
, m2
;
849 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
851 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
854 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
857 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
858 || !gf_puts(NEWLINE
, pc
)
859 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
860 return("Write Error");
863 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
864 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
867 if(!gf_puts(NEWLINE
, pc
))
868 return("Write Error");
873 if((decode_err
= part_desc(a
->number
, a
->body
,
874 (flgs
& FM_DISPLAY
) ? 1 : 3,
875 width
, flgs
, pc
)) != NULL
)
876 return("Write Error");
883 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
884 && format_blip_seen(msgno
)))
885 return("Cannot format body.");
893 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
897 if(flgs
& FM_NEW_MESS
) {
898 zero_atmts(ps_global
->atmts
);
899 describe_mime(body
, "", 1, 1, 0, flgs
);
902 /*----- First do the list of parts/attachments if needed ----*/
903 if((flgs
& FM_DISPLAY
)
904 && (ps_global
->atmts
[1].description
905 || (ps_global
->atmts
[0].body
906 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
907 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
908 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
909 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
910 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
911 COLOR_PAIR
*hdrcolor
= NULL
;
913 if((flgs
& FM_DISPLAY
)
914 && !(flgs
& FM_NOCOLOR
)
916 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
917 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
918 && ps_global
->VAR_NORM_FORE_COLOR
919 && ps_global
->VAR_NORM_BACK_COLOR
920 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
921 ps_global
->VAR_NORM_FORE_COLOR
)
922 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
923 ps_global
->VAR_NORM_BACK_COLOR
))){
925 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
926 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
927 if(!pico_is_good_colorpair(hdrcolor
))
928 free_color_pair(&hdrcolor
);
932 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
935 * Attachment list header
940 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
943 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
946 hwid
= MAX(avail
, 0);
948 i
= utf8_width(_("Parts/Attachments:"));
949 partwid
= MIN(i
, hwid
);
950 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
953 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
954 if(!gf_puts(tmp
, pc
))
958 utf8_snprintf(tmp
, sizeof(tmp
),
960 /* TRANSLATORS: A label */
961 partwid
, partwid
, _("Parts/Attachments:"),
964 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
968 /*----- Figure max display widths -----*/
969 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
970 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
973 if((n
= utf8_width(a
->number
)) > maxnumwid
)
976 if((n
= utf8_width(a
->size
)) > maxsizewid
)
981 * ----- adjust max lengths for nice display -----
983 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
987 avail
= width
- m1
- m2
;
989 s1
= MAX(MIN(1, avail
), 0);
992 dwid
= MAX(MIN(1, avail
), 0);
995 s2
= MAX(MIN(1, avail
), 0);
998 maxnumwid
= MIN(maxnumwid
, width
/3);
999 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
1002 s3
= MAX(MIN(1, avail
), 0);
1005 shownwid
= MAX(MIN(5, avail
), 0);
1008 s4
= MAX(MIN(3, avail
), 0);
1011 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
1014 s5
= MAX(MIN(2, avail
), 0);
1017 descwid
= MAX(0, avail
);
1019 /*----- Format the list of attachments -----*/
1020 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
1021 COLOR_PAIR
*lastc
= NULL
;
1023 int thisdescwid
, padwid
;
1025 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
1028 if(a
->body
->type
== TYPEMULTIPART
1029 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1033 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1034 thisdescwid
= MIN(i
, descwid
);
1035 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1038 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1039 if(!gf_puts(tmp
, pc
))
1043 utf8_snprintf(tmp
, sizeof(tmp
),
1044 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1047 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1049 maxnumwid
, maxnumwid
,
1051 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1055 a
->shown
? "Shown" :
1056 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1060 a
->size
? a
->size
: "",
1062 thisdescwid
, thisdescwid
,
1063 (descwid
> 2 && a
->description
) ? a
->description
: "");
1065 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1068 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1069 char buf
[16], color
[64];
1073 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1077 h
= new_handle(handlesp
);
1081 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1082 buf
[sizeof(buf
)-1] = '\0';
1084 if(!(flgs
& FM_NOCOLOR
)
1085 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1086 lastc
= get_cur_embedded_color();
1087 if(!gf_nputs(color
, (long) l
, pc
))
1090 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1091 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1094 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1095 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1101 if(!format_env_puts(tmpp
, pc
))
1104 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1106 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1107 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1111 if(!embed_color(lastc
, pc
))
1114 free_color_pair(&lastc
);
1116 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1119 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1124 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1125 if(!gf_puts(tmp
, pc
))
1129 if(!gf_puts(NEWLINE
, pc
))
1134 * Dashed line after list
1138 avail
= width
- m1
- m2
;
1139 hwid
= MAX(avail
, 0);
1141 dashwid
= MAX(MIN(40, hwid
-2), 0);
1142 padwid
= hwid
- dashwid
;
1144 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1145 if(!gf_puts(tmp
, pc
))
1149 snprintf(tmp
, sizeof(tmp
),
1151 repeat_char(dashwid
, '-'),
1152 padwid
, padwid
, "");
1155 avail
= width
- m1
-2;
1157 dashwid
= MAX(MIN(40, avail
), 0);
1160 snprintf(tmp
, sizeof(tmp
),
1163 repeat_char(dashwid
, '-'));
1166 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1170 free_color_pair(&hdrcolor
);
1179 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1180 * of body part fetches as we're formatting) for the
1181 * given message isn't set (likely because there
1182 * weren't any parts suitable for display), then make
1183 * sure to set it here.
1186 format_blip_seen(long int msgno
)
1190 if(msgno
> 0L && ps_global
->mail_stream
1191 && msgno
<= ps_global
->mail_stream
->nmsgs
1192 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1194 && !ps_global
->mail_stream
->rdonly
)
1195 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1202 * is_an_env_hdr - is this name a header in the envelope structure?
1204 * name - the header name to check
1207 is_an_env_hdr(char *name
)
1211 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1212 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1222 * is_an_addr_hdr - is this an address header?
1224 * name - the header name to check
1227 is_an_addr_hdr(char *fieldname
)
1229 char fbuf
[FBUF_LEN
+1];
1230 char *colon
, *fname
;
1231 static char *addr_headers
[] = {
1247 /* so it is pointing to NULL */
1248 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1250 if((colon
= strindex(fieldname
, ':')) != NULL
){
1251 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1252 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1258 if(fname
&& *fname
){
1259 for(p
= addr_headers
; *p
; p
++)
1260 if(!strucmp(fname
, *p
))
1264 return((*p
) ? 1 : 0);
1269 * Format a single field from the envelope
1272 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1273 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1278 fmt_env
= format_envelope
;
1280 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1281 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1282 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1289 * Look through header string beginning with "begin", for the next
1290 * occurrence of header "field". Set "start" to that. Set "end" to point one
1291 * position past all of the continuation lines that go with "field".
1292 * That is, if "end" is converted to a null
1293 * character then the string "start" will be the next occurence of header
1294 * "field" including all of its continuation lines. Assume we
1295 * have CRLF's as end of lines.
1297 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1298 * make "end" the end of that header.
1300 * Returns 1 if found, 0 if not.
1303 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1305 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1310 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1316 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1317 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1318 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1319 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1322 * We require that start is at the beginning of a line, so
1323 * either it equals begin (which we assume is the beginning of a
1324 * line) or it is preceded by a CRLF.
1327 *start
= srchstr(begin_srch
, tmpfield
);
1328 while(*start
&& *start
!= begin
1329 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1330 begin_srch
= *start
+ 1;
1331 *start
= srchstr(begin_srch
, tmpfield
);
1338 for(p
= *start
; *p
; p
++){
1340 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1342 * The final 015 in the test above is to test for the end
1359 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1363 if(pico_usingcolor()){
1364 char *fg
= NULL
, *bg
= NULL
, *s
;
1365 char *basefg
= NULL
, *basebg
= NULL
;
1367 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1368 : ps_global
->VAR_NORM_FORE_COLOR
;
1369 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1370 : ps_global
->VAR_NORM_BACK_COLOR
;
1372 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1373 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1374 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1376 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1377 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1378 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1384 * The blacks are just known good colors for
1385 * testing whether the other color is good.
1387 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1388 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1389 if(pico_is_good_colorpair(tmp
))
1390 for(s
= color_embed(fg
, bg
);
1391 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1395 free_color_pair(&tmp
);
1399 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1400 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1405 colorstring
[buflen
-1] = '\0';
1412 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1415 if(pico_usingcolor()){
1416 char *fg
= NULL
, *bg
= NULL
, *s
;
1419 * We need to change the fg and bg colors back even if they
1420 * are the same as the Normal Colors so that color_a_quote
1421 * will have a chance to pick up those colors as the ones to
1422 * switch to. We don't do this before the handle above so that
1423 * the quote color will flow into the selectable item when
1424 * the selectable item color is partly the same as the
1425 * normal color. That is, suppose the normal color was black on
1426 * cyan and the selectable color was blue on cyan, only a fg color
1427 * change. We preserve the only-a-fg-color-change in a quote by
1428 * letting the quote background color flow into the selectable text.
1430 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1431 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1433 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1434 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1436 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1437 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1442 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1446 colorstring
[buflen
-1] = '\0';
1453 url_embed(int embed
)
1455 static char buf
[3] = {TAG_EMBED
};
1463 * Paint the signature.
1466 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1468 struct variable
*vars
= ps_global
->vars
;
1470 COLOR_PAIR
*col
= NULL
;
1472 if(is_in_sig
== NULL
)
1475 in_sig_block
= (int *) is_in_sig
;
1477 if(!strcmp(line
, SIGDASHES
))
1478 *in_sig_block
= START_SIG_BLOCK
;
1479 else if(*line
== '\0')
1481 * Suggested by Eduardo: allow for a blank line right after
1484 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1485 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1487 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1488 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1490 if(*in_sig_block
!= OUT_SIG_BLOCK
1491 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1492 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1493 VAR_SIGNATURE_BACK_COLOR
))){
1494 if(!pico_is_good_colorpair(col
))
1495 free_color_pair(&col
);
1499 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1501 ins
= gf_line_test_new_ins(ins
, line
,
1502 color_embed(col
->fg
, col
->bg
),
1505 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1506 fg
[sizeof(fg
)-1] = '\0';
1507 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1508 bg
[sizeof(bg
)-1] = '\0';
1511 * Loop watching colors, and override with
1512 * signature color whenever the normal foreground and background
1513 * colors are in force.
1517 if(*p
++ == TAG_EMBED
){
1521 p
+= *p
+ 1; /* skip handle key */
1525 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1526 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1527 p
+= RGBLEN
; /* advance past color value */
1529 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1530 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1531 ins
= gf_line_test_new_ins(ins
, p
,
1532 color_embed(col
->fg
,NULL
),
1537 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1538 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1539 p
+= RGBLEN
; /* advance past color value */
1541 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1542 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1543 ins
= gf_line_test_new_ins(ins
, p
,
1544 color_embed(NULL
,col
->bg
),
1554 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1555 color_embed(VAR_NORM_FORE_COLOR
,
1556 VAR_NORM_BACK_COLOR
),
1558 free_color_pair(&col
);
1566 * Line filter to add color to displayed headers.
1569 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1571 static char field
[FBUF_LEN
+ 1];
1572 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1573 char *p
, *q
, *value
, *beg
;
1575 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1576 struct variable
*vars
= ps_global
->vars
;
1578 field
[FBUF_LEN
] = '\0';
1580 if(isspace((unsigned char)*line
)) /* continuation line */
1583 if(!(value
= strindex(line
, ':')))
1586 memset(field
, 0, sizeof(field
));
1587 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1590 for(value
++; isspace((unsigned char)*value
); value
++)
1593 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1594 fg
[sizeof(fg
)-1] = '\0';
1595 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1596 bg
[sizeof(bg
)-1] = '\0';
1599 * Split into two cases depending on whether this is a header which
1600 * contains addresses or not. We may color addresses separately.
1602 if(is_an_addr_hdr(field
)){
1605 * If none of the patterns are for this header, don't bother parsing
1606 * and checking each address.
1608 if(!any_hdr_color(field
))
1612 * First check for patternless patterns which color whole line.
1614 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1615 if(pico_is_good_colorpair(color
)){
1616 ins
= gf_line_test_new_ins(ins
, value
,
1617 color_embed(color
->fg
, color
->bg
),
1619 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1620 fg
[sizeof(fg
)-1] = '\0';
1621 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1622 bg
[sizeof(bg
)-1] = '\0';
1626 free_color_pair(&color
);
1630 * Then go through checking address by address.
1631 * Keep track of quotes and watch for color changes, and override
1632 * with most recent header color whenever the normal foreground
1633 * and background colors are in force.
1639 /* skip next character */
1640 if(*(p
+1) && (in_comment
|| in_quote
))
1649 in_quote
= 1 - in_quote
;
1667 if(!(in_quote
|| in_comment
)){
1668 /* we reached the end of this address */
1671 free_color_pair(&color
);
1673 if((color
= hdr_color(field
, beg
,
1674 ps_global
->hdr_colors
)) != NULL
){
1675 if(pico_is_good_colorpair(color
)){
1677 ins
= gf_line_test_new_ins(ins
, beg
,
1678 color_embed(color
->fg
,
1682 for(q
= p
; q
> beg
&&
1683 isspace((unsigned char)*(q
-1)); q
--)
1686 ins
= gf_line_test_new_ins(ins
, q
,
1687 color_embed(fg
, bg
),
1691 free_color_pair(&color
);
1696 for(p
++; isspace((unsigned char)*p
); p
++)
1710 p
+= *p
+ 1; /* skip handle key */
1715 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1716 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1717 p
+= RGBLEN
; /* advance past color value */
1719 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1720 ins
= gf_line_test_new_ins(ins
, p
,
1721 color_embed(color
->fg
,NULL
),
1727 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1728 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1729 p
+= RGBLEN
; /* advance past color value */
1731 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1732 ins
= gf_line_test_new_ins(ins
, p
,
1733 color_embed(NULL
,color
->bg
),
1750 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1753 if(*q
&& !(in_quote
|| in_comment
)){
1754 /* we reached the end of this address */
1756 free_color_pair(&color
);
1758 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1759 if(pico_is_good_colorpair(color
)){
1761 ins
= gf_line_test_new_ins(ins
, beg
,
1762 color_embed(color
->fg
,
1765 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1768 ins
= gf_line_test_new_ins(ins
, q
,
1769 color_embed(fg
, bg
),
1773 free_color_pair(&color
);
1778 free_color_pair(&color
);
1781 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1782 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1783 VAR_HEADER_GENERAL_BACK_COLOR
),
1788 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1791 if(pico_is_good_colorpair(color
)){
1792 ins
= gf_line_test_new_ins(ins
, value
,
1793 color_embed(color
->fg
, color
->bg
),
1797 * Loop watching colors, and override with header
1798 * color whenever the normal foreground and background
1799 * colors are in force.
1803 if(*p
++ == TAG_EMBED
){
1807 p
+= *p
+ 1; /* skip handle key */
1811 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1812 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1813 p
+= RGBLEN
; /* advance past color value */
1815 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1816 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1817 ins
= gf_line_test_new_ins(ins
, p
,
1818 color_embed(color
->fg
,NULL
),
1823 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1824 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1825 p
+= RGBLEN
; /* advance past color value */
1827 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1828 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1829 ins
= gf_line_test_new_ins(ins
, p
,
1830 color_embed(NULL
,color
->bg
),
1840 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1841 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1842 VAR_HEADER_GENERAL_BACK_COLOR
),
1846 free_color_pair(&color
);
1855 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1857 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1858 *weburlp
= NULL
, *mailurlp
= NULL
;
1859 int n
, n1
, n2
, n3
, l
;
1860 char buf
[256], color
[256];
1864 for(lp
= line
; ; lp
= up
+ n
){
1865 /* scan for all of them so we can choose the first */
1866 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1867 urlp
= rfc1738_scan(lp
, &n1
);
1868 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1869 weburlp
= web_host_scan(lp
, &n2
);
1870 if(F_ON(F_SCAN_ADDR
,ps_global
))
1871 mailurlp
= mail_addr_scan(lp
, &n3
);
1873 if(urlp
|| weburlp
|| mailurlp
){
1875 weburlp
? weburlp
: mailurlp
;
1876 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1878 if(mailurlp
&& mailurlp
< up
)
1883 weburlp
= mailurlp
= NULL
;
1885 else if(up
== weburlp
){
1897 uh
= (URL_HILITE_S
*) local
;
1899 h
= new_handle(uh
->handlesp
);
1901 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1902 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1903 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1904 h
->h
.url
.path
[n
+10-1] = '\0';
1906 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1907 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1908 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1909 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1912 buf
[1] = TAG_HANDLE
;
1913 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1914 buf
[sizeof(buf
)-1] = '\0';
1915 buf
[2] = strlen(&buf
[3]);
1916 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1918 /* in case it was the current selection */
1919 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1921 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1922 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1924 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1926 urlp
= weburlp
= mailurlp
= NULL
;
1934 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1936 static int check_for_urls
= 0;
1939 if(isspace((unsigned char)*line
)) /* continuation, check or not
1940 depending on last line */
1944 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1949 if(((ft
= pine_header_standard(line
)) == FreeText
1951 || ft
== TypeUnknown
)
1952 && strucmp(line
, "message-id")
1953 && strucmp(line
, "newsgroups")
1954 && strucmp(line
, "references")
1955 && strucmp(line
, "in-reply-to")
1956 && strucmp(line
, "received")
1957 && strucmp(line
, "date")){
1966 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1973 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1978 struct variable
*vars
= ps_global
->vars
;
1983 total_wid
= *((int *) local
);
1985 /* calculate width of line */
1995 p
+= *p
+ 1; /* skip handle key */
2000 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
2012 default: /* literal embed char */
2020 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
2026 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2032 if(total_wid
> wid
){
2033 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2034 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2035 VAR_HEADER_GENERAL_BACK_COLOR
),
2037 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2038 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2039 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2040 color_embed(VAR_NORM_FORE_COLOR
,
2041 VAR_NORM_BACK_COLOR
),
2053 url_external_specific_handler(char *url
, int len
)
2055 static char list
[UES_LEN
* UES_MAX
];
2061 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2062 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2065 else{ /* initialize! */
2066 char **l
, *test
, *cmd
, *p
, *p2
;
2069 memset(list
, 0, sizeof(list
));
2070 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2071 get_pair(*l
, &test
, &cmd
, 1, 1);
2073 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2076 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2077 if((p2
= strchr(p
, ',')) != NULL
){
2078 if((n
= p2
- p
) < UES_LEN
){
2079 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2084 "* * * HANLDER TOO LONG: %.*s\n", n
,
2090 if(strlen(p
) <= UES_LEN
){
2091 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2100 fs_give((void **) &test
);
2103 fs_give((void **) &cmd
);
2112 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2113 imapuid_t
*uid
, char **search
, int silent
)
2115 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2116 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2119 int rv
= URL_IMAP_ERROR
;
2122 * Since we're planting nulls, operate on a temporary copy...
2124 scheme
= silent
? NULL
: "IMAP";
2125 url
= cpystr(true_url
+ 7);
2127 /* Try to pick apart the "iserver" portion */
2128 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2132 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2134 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2137 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2139 server
= rfc1738_str(p
);
2141 /* only ";auth=*" supported (and also ";auth=anonymous") */
2142 if((p
= srchstr(url
, ";auth=")) != NULL
){
2144 auth
= rfc1738_str(p
+ 6);
2148 user
= rfc1738_str(url
);
2151 server
= rfc1738_str(url
);
2154 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2157 * "iserver" in hand, pick apart the "icommand"...
2160 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2164 * No "icommand" (all top-level folders) or "imailboxlist"...
2167 *p
= '\0'; /* tie off criteria */
2168 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2169 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2170 rv
|= URL_IMAP_IMBXLSTLSUB
;
2171 else if(strucmp(p
, "list"))
2172 return(url_bogus_imap(&url
, scheme
,
2173 "Invalid list type specified"));
2176 rv
|= URL_IMAP_ISERVERONLY
;
2180 /* build folder list from specified server/criteria/list-method */
2181 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2182 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2183 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2184 user
? "user=\"" : "Anonymous",
2187 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2188 (*folder
)[l
] = '\0';
2189 rv
|= URL_IMAP_IMAILBOXLIST
;
2192 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2193 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2194 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2195 *section
= '\0'; /* tie off UID */
2196 section
= rfc1738_str(section
+ 10);
2197 /* BUG: verify valid section spec ala rfc 2060 */
2199 "-- URL IMAP FOLDER: section not used: %s\n",
2200 section
? section
: "?"));
2203 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2204 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2206 /* optional "uidvalidity"? */
2207 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2210 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2211 return(url_bogus_imap(&url
, scheme
,
2212 "Invalid UIDVALIDITY"));
2215 mailbox
= rfc1738_str(cmd
);
2216 rv
= URL_IMAP_IMESSAGEPART
;
2218 else{ /* "imessagelist" */
2219 /* optional "uidvalidity"? */
2220 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2223 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2224 return(url_bogus_imap(&url
, scheme
,
2225 "Invalid UIDVALIDITY"));
2228 /* optional "enc_search"? */
2229 if((p
= strchr(cmd
, '?')) != NULL
){
2232 *search
= cpystr(rfc1738_str(p
+ 1));
2233 /* BUG: verify valid search spec ala rfc 2060 */
2236 mailbox
= rfc1738_str(cmd
);
2237 rv
= URL_IMAP_IMESSAGELIST
;
2240 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2241 q_status_message(SM_ORDER
, 3, 3,
2242 "Unsupported authentication method. Using standard login.");
2245 * At this point our structure should contain the
2246 * digested url. Now put it together for c-client...
2248 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2249 + (user
? (strlen(user
)+2) : 9);
2250 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2251 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2252 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2253 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2257 (*folder
)[l
] = '\0';
2260 fs_give((void **) &url
);
2266 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2268 fs_give((void **) freeme
);
2269 (void) url_bogus(url
, problem
);
2270 return(URL_IMAP_ERROR
);
2275 * url_bogus - report url syntax errors and such
2278 url_bogus(char *url
, char *reason
)
2280 dprint((2, "-- bogus url \"%s\": %s\n",
2281 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2283 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2284 "Malformed \"%.*s\" URL: %.200s",
2285 (void *) (strchr(url
, ':') - url
), url
, reason
);
2292 /*----------------------------------------------------------------------
2293 Format header text suitable for display
2295 Args: stream -- mail stream for various header text fetches
2296 msgno -- sequence number in stream of message we're interested in
2297 section -- which section of message
2298 env -- pointer to msg's envelope
2299 hdrs -- struct containing what's to get formatted
2300 prefix -- prefix to append to each output line
2301 handlesp -- address of pointer to the message's handles
2302 flags -- FM_ flags, see pith/mailview.h
2303 final_pc -- function to write header text with
2305 Result: 0 if all's well, -1 if write error, 1 if fetch error
2307 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2308 in the local convention.
2312 #define FHT_WRTERR -1
2313 #define FHT_FTCHERR 1
2315 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2316 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2317 fmt_env_t fmt_env
, gf_io_t final_pc
)
2321 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2325 gf_io_t tmp_pc
, tmp_gc
;
2326 struct variable
*vars
= ps_global
->vars
;
2328 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2329 gf_set_so_writec(&tmp_pc
, tmp_store
);
2334 fmt_env
= format_envelope
;
2336 if(ps_global
->full_header
== 2){
2337 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2341 * First, calculate how big a fields array we need.
2344 /* Custom header viewing list specified */
2345 if(hdrs
->type
== HD_LIST
){
2346 /* view all these headers */
2348 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2349 if(!is_an_env_hdr(q
))
2352 /* view all except these headers */
2354 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2358 nfields
--; /* subtract one for ALL_EXCEPT field */
2362 nfields
= 6; /* default view */
2364 /* allocate pointer space */
2366 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2367 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2370 if(hdrs
->type
== HD_LIST
){
2371 /* view all these headers */
2373 /* put the non-envelope headers in fields */
2375 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2376 if(!is_an_env_hdr(q
))
2379 /* view all except these headers */
2381 /* put the list of headers not to view in fields */
2383 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2384 if(strucmp(ALL_EXCEPT
, q
))
2392 fields
[i
= 0] = "Resent-Date";
2393 fields
[++i
] = "Resent-From";
2394 fields
[++i
] = "Resent-To";
2395 fields
[++i
] = "Resent-cc";
2396 fields
[++i
] = "Resent-Subject";
2402 /* custom view with exception list */
2403 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2405 * Go through each header in h and print it.
2406 * First we check to see if it is an envelope header so we
2407 * can print our envelope version of it instead of the raw version.
2410 /* fetch all the other headers */
2412 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2415 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2417 char tmp
[MAILTMPLEN
+1];
2420 colon_loc
= strindex(start
, ':');
2421 if(colon_loc
&& colon_loc
< finish
){
2422 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2423 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2428 if(colon_loc
&& is_an_env_hdr(tmp
)){
2429 char *dummystart
, *dummyfinish
;
2432 * Pretty format for env hdrs.
2433 * If the same header appears more than once, only
2434 * print the last to avoid duplicates.
2435 * They should have been combined in the env when parsed.
2437 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2439 format_env_hdr(stream
, msgno
, section
, env
,
2440 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2443 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2444 hdrs
->charset
, flags
)) != 0)
2451 /* custom view or default */
2453 /* fetch the non-envelope headers */
2455 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2457 /* default envelope for default view */
2458 if(hdrs
->type
== HD_BFIELD
)
2459 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2461 /* go through each header in list, v initialized above */
2462 for(; (q
= *v
) != NULL
; v
++){
2463 if(is_an_env_hdr(q
)){
2464 /* pretty format for env hdrs */
2465 format_env_hdr(stream
, msgno
, section
, env
,
2466 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2470 * Go through h finding all occurences of this header
2471 * and all continuation lines, and output.
2474 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2476 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2477 hdrs
->charset
, flags
)) != 0)
2490 gf_clear_so_writec(tmp_store
);
2492 if(!rv
){ /* valid data? Do wrapping and filtering... */
2494 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2495 STORE_S
*df_store
= NULL
;
2497 so_seek(tmp_store
, 0L, 0);
2499 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2502 * Test for and act on any display filter
2503 * This barely makes sense. The display filter is going
2504 * to be getting UTF-8'ized headers here. In pre-alpine
2505 * pine the display filter was being fed already decoded
2506 * headers in whatever character set they were in.
2507 * The good news is that that didn't make much
2508 * sense either, so this shouldn't break anything.
2509 * It seems unlikely that anybody is doing anything useful
2510 * with the header part of display filters.
2512 if(ps_global
->tools
.display_filter
2513 && ps_global
->tools
.display_filter_trigger
2514 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2515 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2517 gf_set_so_writec(&tmp_pc
, df_store
);
2518 gf_set_so_readc(&tmp_gc
, df_store
);
2519 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2520 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2521 _("Formatting error: %s"), errstr
);
2525 so_seek(df_store
, 0L, 0);
2527 gf_clear_so_writec(df_store
);
2530 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2531 "No space for filtered text.");
2536 so_seek(tmp_store
, 0L, 0);
2537 gf_set_so_readc(&tmp_gc
, tmp_store
);
2541 int *margin
, wrapflags
= GFW_ONCOMMA
;
2544 gf_link_filter(gf_local_nvtnl
, NULL
);
2546 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2547 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2548 || F_ON(F_SCAN_ADDR
, ps_global
))
2550 gf_link_filter(gf_line_test
,
2551 gf_line_test_opt(url_hilite_hdr
,
2552 gf_url_hilite_opt(&uh
,handlesp
,1)));
2553 wrapflags
|= GFW_HANDLES
;
2556 if((flags
& FM_DISPLAY
)
2557 && !(flags
& FM_NOCOLOR
)
2558 && pico_usingcolor()
2559 && ((VAR_NORM_FORE_COLOR
2560 && VAR_HEADER_GENERAL_FORE_COLOR
2561 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2563 (VAR_NORM_BACK_COLOR
2564 && VAR_HEADER_GENERAL_BACK_COLOR
2565 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2566 wrapflags
|= GFW_HDRCOLOR
;
2568 if((flags
& FM_DISPLAY
)
2569 && !(flags
& FM_NOCOLOR
)
2570 && pico_usingcolor()
2571 && ps_global
->hdr_colors
){
2572 gf_link_filter(gf_line_test
,
2573 gf_line_test_opt(color_headers
, NULL
));
2574 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2577 if(prefix
&& *prefix
)
2578 column
= MAX(column
-strlen(prefix
), 50);
2580 margin
= format_view_margin();
2582 if(!(flags
& FM_NOWRAP
))
2583 gf_link_filter(gf_wrap
,
2584 gf_wrap_filter_opt(column
, column
,
2585 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2588 if(prefix
&& *prefix
)
2589 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2591 if((flags
& FM_DISPLAY
)
2592 && !(flags
& FM_NOCOLOR
)
2593 && pico_usingcolor()
2594 && ((VAR_NORM_FORE_COLOR
2595 && VAR_HEADER_GENERAL_FORE_COLOR
2596 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2598 (VAR_NORM_BACK_COLOR
2599 && VAR_HEADER_GENERAL_BACK_COLOR
2600 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2604 right_margin
= margin
? margin
[1] : 0;
2605 total_wid
= column
- right_margin
;
2607 gf_link_filter(gf_line_test
,
2608 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2609 wrapflags
|= GFW_HANDLES
;
2612 gf_link_filter(gf_nvtnl_local
, NULL
);
2614 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2616 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2617 "Can't build header : %.200s", errstr
);
2622 gf_clear_so_readc(df_store
);
2626 gf_clear_so_readc(tmp_store
);
2629 so_give(&tmp_store
);
2632 fs_give((void **)&h
);
2635 fs_give((void **)&fields
);
2641 /*----------------------------------------------------------------------
2642 Format RAW header text for display
2644 Args: stream -- mail stream for various header text fetches
2645 rawno -- sequence number in stream of message we're interested in
2646 section -- which section of message
2647 pc -- function to write header text with
2649 Result: 0 if all's well, -1 if write error, 1 if fetch error
2651 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2652 in the local convention.
2656 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2658 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2665 if(!gf_puts(NEWLINE
, pc
))
2668 if(ISRFCEOL(h
)) /* all done! */
2671 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2672 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2673 c
= (unsigned char) *h
++;
2674 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2675 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2678 else if(!(*pc
)(*h
++))
2683 return(FHT_FTCHERR
);
2690 /*----------------------------------------------------------------------
2691 Format c-client envelope data suitable for display
2693 Args: s -- mail stream for various header text fetches
2694 n -- raw sequence number in stream of message we're interested in
2695 sect -- which section of message
2696 e -- pointer to msg's envelope
2697 pc -- function to write header text with
2698 which -- which header lines to write
2700 flags -- FM_ flags, see pith/mailview.h
2702 Result: 0 if all's well, -1 if write error, 1 if fetch error
2704 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2705 in the local convention.
2709 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2710 long int which
, char *oacs
, int flags
)
2712 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2717 if((which
& FE_DATE
) && e
->date
) {
2719 snprintf(buftmp
, sizeof(buftmp
), "%s",
2720 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2721 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2722 buftmp
[sizeof(buftmp
)-1] = '\0';
2723 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2724 SIZEOF_20KBUF
, buftmp
);
2726 format_env_puts(p2
, pc
);
2727 gf_puts(NEWLINE
, pc
);
2730 if((which
& FE_FROM
) && e
->from
)
2731 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2733 if((which
& FE_REPLYTO
) && e
->reply_to
2734 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2735 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2737 if((which
& FE_TO
) && e
->to
)
2738 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2740 if((which
& FE_CC
) && e
->cc
)
2741 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2743 if((which
& FE_BCC
) && e
->bcc
)
2744 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2746 if((which
& FE_RETURNPATH
) && e
->return_path
)
2747 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2750 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2752 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2753 if (!e
->ngpathexists
&& e
->message_id
&&
2754 strncmp (e
->message_id
,"<alpine.",8) &&
2755 strncmp (e
->message_id
,"<Pine.",6) &&
2756 strncmp (e
->message_id
,"<MS-C.",6) &&
2757 strncmp (e
->message_id
,"<MailManager.",13) &&
2758 strncmp (e
->message_id
,"<EasyMail.",11) &&
2759 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2762 q_status_message(SM_ORDER
, 0, 3,
2763 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2766 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2767 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2769 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2770 char *freeme
= NULL
;
2775 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2777 if(flags
& FM_DISPLAY
2778 && (ps_global
->display_keywords_in_subject
2779 || ps_global
->display_keywordinits_in_subject
)){
2781 /* don't bother if no keywords are defined */
2782 if(some_user_flags_defined(s
))
2783 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2784 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2785 NULL
, ps_global
->VAR_KW_BRACES
);
2788 format_env_puts(p2
, pc
);
2791 fs_give((void **) &freeme
);
2793 gf_puts(NEWLINE
, pc
);
2796 if((which
& FE_SENDER
) && e
->sender
2797 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2798 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2800 if((which
& FE_MESSAGEID
) && e
->message_id
){
2803 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2804 format_env_puts(p2
, pc
);
2805 gf_puts(NEWLINE
, pc
);
2808 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2809 q
= "In-Reply-To: ";
2811 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);
2812 format_env_puts(p2
, pc
);
2813 gf_puts(NEWLINE
, pc
);
2816 if((which
& FE_REFERENCES
) && e
->references
) {
2819 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2820 format_env_puts(p2
, pc
);
2821 gf_puts(NEWLINE
, pc
);
2829 * The argument fieldname is something like "Subject:..." or "Subject".
2830 * Look through the specs in speccolor for a match of the fieldname,
2831 * and return the color that goes with any match, or NULL.
2832 * Caller should free the color.
2835 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2837 SPEC_COLOR_S
*hc
= NULL
;
2838 COLOR_PAIR
*color_pair
= NULL
;
2839 char *colon
, *fname
;
2840 char fbuf
[FBUF_LEN
+1];
2844 colon
= strindex(fieldname
, ':');
2846 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2847 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2854 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2855 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2860 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2861 if(srchstr(value
, pat
->substring
))
2868 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2869 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2876 * The argument fieldname is something like "Subject:..." or "Subject".
2877 * Look through the specs in hdr_colors for a match of the fieldname,
2878 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2881 any_hdr_color(char *fieldname
)
2883 SPEC_COLOR_S
*hc
= NULL
;
2884 char *colon
, *fname
;
2885 char fbuf
[FBUF_LEN
+1];
2887 colon
= strindex(fieldname
, ':');
2889 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2890 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2897 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2898 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2905 /*----------------------------------------------------------------------
2906 Format an address field, wrapping lines nicely at commas
2908 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2909 addr -- ADDRESS structure to format
2911 Result: A formatted, malloced string is returned.
2912 ----------------------------------------------------------------------*/
2914 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2915 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2918 int trailing
= 0, group
= 0;
2925 * quickly run down address list to make sure none are patently bogus.
2926 * If so, just blat raw field out.
2928 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2929 if(atmp
->host
&& atmp
->host
[0] == '.'){
2930 char *field
, *fields
[2];
2933 fields
[0] = cpystr(field_name
);
2934 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2937 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2940 for(t
= h
= field
; *h
; t
++)
2941 if(*t
== '\015' && *(t
+1) == '\012'){
2942 *t
= '\0'; /* tie off line */
2943 format_env_puts(h
, pc
);
2944 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2945 gf_puts(NEWLINE
, pc
); /* more to write */
2949 else if(!*t
){ /* shouldn't happen much */
2951 format_env_puts(h
, pc
);
2956 fs_give((void **)&field
);
2959 fs_give((void **)&fields
[0]);
2960 gf_puts(NEWLINE
, pc
);
2961 dprint((2, "Error in \"%s\" field address\n",
2962 field_name
? field_name
: "?"));
2966 gf_puts(field_name
, pc
);
2969 atmp
= addr
->next
; /* remember what's next */
2971 if(!addr
->host
&& addr
->mailbox
){
2972 mtmp
= addr
->mailbox
;
2973 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2974 (unsigned char *)tmp_20k_buf
,
2975 SIZEOF_20KBUF
, addr
->mailbox
));
2978 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2979 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2980 tmp_20k_buf
[10000-1] = '\0';
2982 if(!trailing
) /* 1st pass, just address */
2984 else{ /* else comma, unless */
2985 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2986 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2989 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2997 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2999 addr
->personal
= ptmp
; /* restore old personal ptr */
3000 if(!addr
->host
&& addr
->mailbox
){
3001 fs_give((void **)&addr
->mailbox
);
3002 addr
->mailbox
= mtmp
;
3009 gf_puts(NEWLINE
, pc
);
3015 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3016 /* RFC822 continuation, must start with CRLF */
3017 #define RFC822CONT "\015\012 "
3019 /* Write RFC822 address with some quoting turned off.
3021 * address to interpret
3023 * (This is a copy of c-client's rfc822_write_address except
3024 * we don't quote double quote and dot in personal names. It writes
3025 * to a gf_io_t instead of to a buffer so that we don't have to worry
3026 * about fixed sized buffer overflowing. It's also special cased to deal
3027 * with only a single address.)
3029 * The idea is that there are some places where we'd just like to display
3030 * the personal name as is before applying confusing quoting. However,
3031 * we do want to be careful not to break things that should be quoted so
3032 * we'll only use this where we are sure. Quoting may look ugly but it
3033 * doesn't usually break anything.
3036 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3038 extern const char *rspecials
;
3040 if (adr
->host
) { /* ordinary address? */
3041 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3042 else { /* no, must use phrase <route-addr> form */
3044 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3046 gf_puts(" <", pc
); /* write address delimiter */
3047 pine_rfc822_address(adr
, pc
);
3048 gf_puts (">", pc
); /* closing delimiter */
3054 else if (adr
->mailbox
) { /* start of group? */
3055 /* yes, write group name */
3056 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3058 gf_puts (": ", pc
); /* write group identifier */
3059 *group
= 1; /* in a group */
3061 else if (*group
) { /* must be end of group (but be paranoid) */
3063 *group
= 0; /* no longer in that group */
3068 /* Write RFC822 route-address to string
3070 * address to interpret
3074 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3076 extern char *wspecials
;
3078 if (adr
&& adr
->host
) { /* no-op if no address */
3079 if (adr
->adl
) { /* have an A-D-L? */
3080 gf_puts (adr
->adl
, pc
);
3083 /* write mailbox name */
3084 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3085 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3086 gf_puts ("@", pc
); /* host delimiter */
3087 gf_puts (adr
->host
, pc
); /* write host name */
3093 /* Concatenate RFC822 string
3095 * pointer to string to concatenate
3096 * list of special characters
3100 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3104 if (strpbrk (src
,specials
)) { /* any specials present? */
3105 gf_puts ("\"", pc
); /* opening quote */
3106 /* truly bizarre characters in there? */
3107 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3110 /* turn it into a null-terminated piece */
3114 gf_puts (src
, pc
); /* yes, output leader */
3116 gf_puts ("\\", pc
); /* quoting */
3117 gf_puts (save
, pc
); /* output the bizarre character */
3118 src
= ++s
; /* continue after the bizarre character */
3120 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3121 gf_puts ("\"", pc
); /* closing quote */
3123 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3127 /*----------------------------------------------------------------------
3128 Format an address field, wrapping lines nicely at commas
3130 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3131 newsgrps -- ADDRESS structure to format
3133 Result: A formatted, malloced string is returned.
3135 The resuling lines formatted are 80 columns wide.
3136 ----------------------------------------------------------------------*/
3138 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3140 char buf
[MAILTMPLEN
];
3141 int trailing
= 0, llen
, alen
;
3144 if(!newsgrps
|| !*newsgrps
)
3147 gf_puts(field_name
, pc
);
3149 llen
= strlen(field_name
);
3151 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3152 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3153 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3158 if(!trailing
){ /* first time thru, just address */
3162 else{ /* else preceding comma */
3166 if(alen
+ llen
+ 1 > 76){
3167 gf_puts(NEWLINE
, pc
);
3177 if(alen
&& llen
> 76){ /* handle long addresses */
3178 register char *q
, *p
= &buf
[alen
-1];
3181 if(isspace((unsigned char)*p
)
3182 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3183 for(q
= buf
; q
< p
; q
++)
3184 (*pc
)(*q
); /* write character */
3186 gf_puts(NEWLINE
, pc
);
3195 if(p
== buf
) /* no reasonable break point */
3202 gf_puts(NEWLINE
, pc
);
3207 /*----------------------------------------------------------------------
3208 Format a text field that's part of some raw (non-envelope) message header
3214 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3216 ----------------------------------------------------------------------*/
3218 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3220 register char *current
;
3221 unsigned char *p
, *tmp
= NULL
, c
;
3229 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3231 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3234 len
= SIZEOF_20KBUF
;
3235 p
= (unsigned char *) tmp_20k_buf
;
3238 if(islower((unsigned char)(*start
)))
3239 *start
= toupper((unsigned char)(*start
));
3241 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3243 /* output from start to finish */
3244 while(*current
&& rv
== FHT_OK
)
3245 if(ISRFCEOL(current
)){
3246 if(!gf_puts(NEWLINE
, pc
))
3251 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3252 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3253 c
= (unsigned char) *current
++;
3254 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3255 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3258 else if(!(*pc
)(*current
++))
3262 fs_give((void **) &tmp
);
3272 /*----------------------------------------------------------------------
3273 Format a text field that's part of some raw (non-envelope) message header
3280 ----------------------------------------------------------------------*/
3282 format_env_puts(char *s
, gf_io_t pc
)
3284 if(ps_global
->pass_ctrl_chars
)
3285 return(gf_puts(s
, pc
));
3288 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3289 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3290 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3301 display_parameters(PARAMETER
*params
)
3306 PARMLIST_S
*parmlist
;
3308 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3309 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3310 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3313 tmp_20k_buf
[0] = '\0';
3314 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3315 n
= 0; /* n overloaded */
3316 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3318 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3319 tmp_20k_buf
[10000-1] = '\0';
3323 if(parmlist
->value
){
3324 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3325 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3329 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3334 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3335 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3337 tmp_20k_buf
[10000-1] = '\0';
3341 rfc2231_free_parmlist(&parmlist
);
3344 return(tmp_20k_buf
);
3348 /*----------------------------------------------------------------------
3349 Fetch the requested header fields from the msgno specified
3351 Args: stream -- mail stream of open folder
3352 msgno -- number of message to get header lines from
3353 fields -- array of pointers to desired fields
3355 Returns: allocated string containing matched header lines,
3359 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3362 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3363 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3366 * If the user misconfigures it is possible to have one of the fields
3367 * set to the empty string instead of a header name. We want to catch
3368 * that here instead of asking the server the nonsensical question.
3370 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3374 if(pp
&& *pp
){ /* found an empty header field, fix it */
3375 pflds
= copy_list_array(fields
);
3376 for(pp
= pflds
; pp
&& *pp
; pp
++){
3377 if(!**pp
){ /* scoot rest of the lines up */
3379 for(qq
= pp
; *qq
; qq
++)
3383 fs_give((void **) &free_this
);
3387 /* no headers to look for, return NULL */
3388 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3389 free_list_array(&pflds
);
3396 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3397 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3402 if(pflds
&& pflds
!= fields
)
3403 free_list_array(&pflds
);
3408 while(find_field(&h
, tmp
, sizeof(tmp
))){
3409 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3412 /* interesting field? */
3413 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3415 * Hold off allocating space for matching fields until
3416 * we at least find one to copy...
3419 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3421 while(*p
) /* copy field name */
3424 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3425 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3428 *m
= '\0'; /* tie off match string */
3430 else{ /* no match, pass this field */
3431 while(*h
&& !(*h
++ == '\n'
3432 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3437 if(pflds
&& pflds
!= fields
)
3438 free_list_array(&pflds
);
3440 return(match
? match
: cpystr(""));
3445 find_field(char **h
, char *tmp
, size_t ntmp
)
3449 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3452 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3460 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3464 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3467 if(_last_embedded_fg_color
)
3468 fs_give((void **)&_last_embedded_fg_color
);
3470 _last_embedded_fg_color
= cpystr(cp
->fg
);
3472 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3473 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3478 if(_last_embedded_bg_color
)
3479 fs_give((void **)&_last_embedded_bg_color
);
3481 _last_embedded_bg_color
= cpystr(cp
->bg
);
3483 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3484 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3493 get_cur_embedded_color(void)
3497 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3498 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3500 ret
= pico_get_cur_color();
3507 clear_cur_embedded_color(void)
3509 if(_last_embedded_fg_color
)
3510 fs_give((void **)&_last_embedded_fg_color
);
3512 if(_last_embedded_bg_color
)
3513 fs_give((void **)&_last_embedded_bg_color
);
3518 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3522 if(pico_usingcolor()){
3523 char *fg
= NULL
, *bg
= NULL
, *s
;
3525 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3526 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3527 ps_global
->VAR_NORM_FORE_COLOR
))
3528 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3530 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3531 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3532 ps_global
->VAR_NORM_BACK_COLOR
))
3533 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3539 * The blacks are just known good colors for
3540 * testing whether the other color is good.
3542 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3543 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3544 if(pico_is_good_colorpair(tmp
))
3545 for(s
= color_embed(fg
, bg
);
3546 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3550 free_color_pair(&tmp
);
3554 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3555 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3560 colorstring
[buflen
-1] = '\0';
3567 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3570 if(pico_usingcolor()){
3571 char *fg
= NULL
, *bg
= NULL
, *s
;
3572 char *basefg
= NULL
, *basebg
= NULL
;
3574 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3575 : ps_global
->VAR_NORM_FORE_COLOR
;
3576 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3577 : ps_global
->VAR_NORM_BACK_COLOR
;
3580 * We need to change the fg and bg colors back even if they
3581 * are the same as the Normal Colors so that color_a_quote
3582 * will have a chance to pick up those colors as the ones to
3583 * switch to. We don't do this before the handle above so that
3584 * the quote color will flow into the selectable item when
3585 * the selectable item color is partly the same as the
3586 * normal color. That is, suppose the normal color was black on
3587 * cyan and the selectable color was blue on cyan, only a fg color
3588 * change. We preserve the only-a-fg-color-change in a quote by
3589 * letting the quote background color flow into the selectable text.
3591 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3594 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3597 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3598 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3603 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3607 colorstring
[buflen
-1] = '\0';
3614 * Helper routine that is of limited use.
3615 * We need to tally up the screen width of
3616 * a UTF-8 string as we go through the string.
3617 * We just want the width of the character starting
3618 * at str (and no longer than remaining_octets).
3619 * If we're plopped into the middle of a UTF-8
3620 * character we just want to return width zero.
3623 width_at_this_position(unsigned char *str
, unsigned long n
)
3625 unsigned char *inputp
= str
;
3626 unsigned long remaining_octets
= n
;
3630 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3631 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3632 width
= wcellwidth(ucs
);
3633 /* Writechar will print a '?' */