2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2009 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 /*======================================================================
18 Implements message data gathering and formatting
24 #include "../pith/ical.h"
25 #include "../pith/body.h"
26 #include "../pith/mailpart.h"
27 #include "../pith/mailview.h"
28 #include "../pith/conf.h"
29 #include "../pith/msgno.h"
30 #include "../pith/editorial.h"
31 #include "../pith/mimedesc.h"
32 #include "../pith/margin.h"
33 #include "../pith/color.h"
34 #include "../pith/strlst.h"
35 #include "../pith/charset.h"
36 #include "../pith/status.h"
37 #include "../pith/maillist.h"
38 #include "../pith/mailcmd.h"
39 #include "../pith/mailindx.h"
40 #include "../pith/imap.h"
41 #include "../pith/detach.h"
42 #include "../pith/text.h"
43 #include "../pith/url.h"
44 #include "../pith/rfc2231.h"
45 #include "../pith/list.h"
46 #include "../pith/stream.h"
47 #include "../pith/send.h"
48 #include "../pith/filter.h"
49 #include "../pith/string.h"
50 #include "../pith/ablookup.h"
51 #include "../pith/escapes.h"
52 #include "../pith/keyword.h"
53 #include "../pith/smime.h"
58 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
62 * This is a list of header fields that are represented canonically
63 * by the c-client's ENVELOPE structure. The list is used by the
64 * two functions below to decide if a given field is included in this
67 static struct envelope_s
{
72 {"sender", FE_SENDER
},
77 {"newsgroups", FE_NEWSGROUPS
},
78 {"subject", FE_SUBJECT
},
79 {"message-id", FE_MESSAGEID
},
80 {"reply-to", FE_REPLYTO
},
81 {"followup-to", FE_FOLLOWUPTO
},
82 {"in-reply-to", FE_INREPLYTO
},
83 /* {"return-path", FE_RETURNPATH}, not usually filled in */
84 {"references", FE_REFERENCES
},
90 * Hook for optional display of rfc2369 content
92 int (*pith_opt_rfc2369_editorial
)(long, HANDLE_S
**, int, int, gf_o_t
);
100 int format_blip_seen(long);
101 int is_an_env_hdr(char *);
102 int is_an_addr_hdr(char *);
103 void format_env_hdr(MAILSTREAM
*, long, char *, ENVELOPE
*,
104 fmt_env_t
, gf_o_t
, char *, char *, int);
105 int delineate_this_header(char *, char *, char **, char **);
106 char *url_embed(int);
107 int color_headers(long, char *, LT_INS_S
**, void *);
108 int url_hilite_hdr(long, char *, LT_INS_S
**, void *);
109 int pad_to_right_edge(long, char *, LT_INS_S
**, void *);
110 int url_bogus_imap(char **, char *, char *);
111 int format_raw_header(MAILSTREAM
*, long, char *, gf_o_t
);
112 void format_envelope(MAILSTREAM
*, long, char *, ENVELOPE
*,
113 gf_o_t
, long, char *, int);
114 int any_hdr_color(char *);
115 void format_addr_string(MAILSTREAM
*, long, char *, char *,
116 ADDRESS
*, int, char *, gf_o_t
);
117 void pine_rfc822_write_address_noquote(ADDRESS
*, gf_o_t
, int *);
118 void format_newsgroup_string(char *, char *, int, gf_o_t
);
119 int format_raw_hdr_string(char *, char *, gf_o_t
, char *, int);
120 int format_env_puts(char *, gf_o_t
);
121 int find_field(char **, char *, size_t);
122 int embed_color(COLOR_PAIR
*, gf_o_t
);
123 COLOR_PAIR
*get_cur_embedded_color(void);
124 void clear_cur_embedded_color(void);
125 void format_calendar_vevent(VCALENDAR_S
*, ATTACH_S
*, HANDLE_S
**, int, int, gf_o_t
, int);
129 /*----------------------------------------------------------------------
130 Format a message message for viewing
132 Args: msgno -- The number of the message to view
133 env -- pointer to the message's envelope
134 body -- pointer to the message's body
135 handlesp -- address of pointer to the message's handles
136 flgs -- possible flags listed in pith/mailview.h with
138 pc -- write to this function
140 Result: Returns true if no problems encountered, else false.
142 First the envelope is formatted; next a list of all attachments is
143 formatted if there is more than one. Then all the body parts are
144 formatted, fetching them as needed. This includes headers of included
145 message. Richtext is also formatted. An entry is made in the text for
146 parts that are not displayed or can't be displayed.
150 format_message(long int msgno
, ENVELOPE
*env
, struct mail_bodystruct
*body
,
151 HANDLE_S
**handlesp
, int flgs
, gf_o_t pc
)
153 char *decode_err
= NULL
;
157 clear_cur_embedded_color();
159 if(!(flgs
& FM_DISPLAY
))
162 width
= (flgs
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
164 /*---- format and copy envelope ----*/
165 if(!(flgs
& FM_NOEDITORIAL
)){
166 if(ps_global
->full_header
== 1)
167 /* TRANSLATORS: User is viewing a message and all the quoted text is
169 q_status_message(SM_INFO
, 0, 3, _("All quoted text being included"));
170 else if(ps_global
->full_header
== 2)
171 q_status_message(SM_INFO
, 0, 3,
172 /* TRANSLATORS: User is viewing a message and all of
173 the header text is being shown. */
174 _("Full header mode ON. All header text being included"));
177 HD_INIT(&h
, ps_global
->VAR_VIEW_HEADERS
, ps_global
->view_all_except
, FE_DEFAULT
);
178 switch(format_header(ps_global
->mail_stream
, msgno
, NULL
,
179 env
, &h
, NULL
, handlesp
, flgs
, NULL
, pc
)){
181 case -1 : /* write error */
184 case 1 : /* fetch error */
185 if(!(gf_puts("[ Error fetching header ]", pc
)
186 && !gf_puts(NEWLINE
, pc
)))
193 || (ps_global
->full_header
== 2
194 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
)))){
195 format_attachment_list(msgno
, body
, handlesp
, flgs
, width
, pc
);
196 format_calendar(msgno
, body
, handlesp
, flgs
, width
, pc
);
199 /* write delimiter and body */
200 if(gf_puts(NEWLINE
, pc
)){
201 if((decode_err
= format_body(msgno
, body
, handlesp
, &h
, flgs
, width
, pc
)) == NULL
)
206 clear_cur_embedded_color();
212 if(!(flgs
& FM_DISPLAY
))
213 q_status_message1(SM_ORDER
, 3, 4, _("Error writing message: %s"),
214 decode_err
? decode_err
: error_description(errno
));
215 clear_cur_embedded_color();
220 format_calendar_vevent(VCALENDAR_S
*vcal
, ATTACH_S
*a
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_o_t pc
, int cflags
)
222 int avail
, m1
, m2
, hwid
, i
, partwid
, padwid
;
223 int s1
, s2
, dwid
, minkey
;
226 VEVENT_SUMMARY_S
*vesy
, *vesummary
; /* vevent summary */
228 vesummary
= vesy
= ical_vevent_summary(vcal
);
230 if(vesy
== NULL
) return;
232 if((cflags
& FC_SUMMARY
) && (cflags
& FC_FULL
))
235 minkey
= -1; /* initialize to something negative */
237 for(; vesy
!= NULL
; vesy
= vesy
->next
){
239 margin
= (cflags
& FC_FULL
) ? NULL
240 : (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
242 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
245 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
248 hwid
= MAX(avail
, 0);
251 if(ps_global
->atmts
[1].description
== NULL
){
252 avail
= width
- m1
-2;
254 dwid
= MAX(MIN(40, avail
), 0);
256 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
257 repeat_char(dwid
, '-'));
259 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
264 if(cflags
& FC_SUMMARY
){
265 i
= utf8_width(_("Calendar Entry:"));
266 partwid
= MIN(i
, hwid
);
268 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
269 if(!gf_puts(tmp_20k_buf
, pc
))
273 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%-*.*w%*.*s",
274 partwid
, partwid
, _("Calendar Entry:"),
277 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
284 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
285 if(!gf_puts(tmp_20k_buf
, pc
))
289 avail
= width
- m1
- m2
;
291 s1
= MAX(MIN(1, avail
), 0);
294 dwid
= MAX(MIN(1, avail
), 0);
297 s2
= MAX(MIN(1, avail
), 0);
300 if(cflags
& FC_SUMMARY
)
301 utf8_snprintf(padding
, sizeof(padding
), "%*.*s%*.*w%*.*s",
302 s1
, s1
, "", dwid
, dwid
, "", s2
, s2
, "");
307 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
308 padding
, _("This event was cancelled!"));
309 if((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
)){
310 gf_puts(tmp_20k_buf
, pc
);
311 gf_puts(NEWLINE
, pc
);
318 if(vesy
->sender
!= NULL
){
319 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
320 padding
, _("Sent-by: "), vesy
->sender
);
321 gf_puts(tmp_20k_buf
, pc
);
322 gf_puts(NEWLINE
, pc
);
325 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
326 padding
, _("Organizer: "), vesy
->organizer
);
327 gf_puts(tmp_20k_buf
, pc
);
328 gf_puts(NEWLINE
, pc
);
329 } /* end of if(organizer) */
332 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
333 padding
, _("Location: "), vesy
->location
);
334 gf_puts(tmp_20k_buf
, pc
);
335 gf_puts(NEWLINE
, pc
);
336 } /* end of if location */
339 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
340 padding
, _("Start Date: "), vesy
->evstart
);
341 gf_puts(tmp_20k_buf
, pc
);
342 gf_puts(NEWLINE
, pc
);
343 } /* end of if dtstart */
348 for(i
= 0; vesy
->duration
[i
] != NULL
; i
++){
349 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
350 padding
, _("Duration: "), vesy
->duration
[i
]);
351 gf_puts(tmp_20k_buf
, pc
);
352 gf_puts(NEWLINE
, pc
);
354 } /* end of DURATION */
355 else if(vesy
->evend
){
356 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
357 padding
, _("End Date: "), vesy
->evend
);
358 gf_puts(tmp_20k_buf
, pc
);
359 gf_puts(NEWLINE
, pc
);
360 } else { /* end of if dtend */
361 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
362 padding
, _("No duration nor end time found for this event"));
363 gf_puts(tmp_20k_buf
, pc
);
364 gf_puts(NEWLINE
, pc
);
365 } /* end of else for if (duration) and if (dtend) */
368 #define MAX_DISPLAYED 3
371 for(i
= 0; vesy
->attendee
[i
] != NULL
; i
++){
372 if((cflags
& FC_SUMMARY
) && i
>= MAX_DISPLAYED
)
375 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
377 _("Attendee: "), vesy
->attendee
[i
]);
378 gf_puts(tmp_20k_buf
, pc
);
379 gf_puts(NEWLINE
, pc
);
381 } /* end of ATTENDEES */
384 if(cflags
& FC_SUMMARY
){
385 COLOR_PAIR
*lastc
= NULL
;
386 COLOR_PAIR
*hdrcolor
= NULL
;
388 if((flgs
& FM_DISPLAY
)
389 && !(flgs
& FM_NOCOLOR
)
391 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
392 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
393 && ps_global
->VAR_NORM_FORE_COLOR
394 && ps_global
->VAR_NORM_BACK_COLOR
395 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
396 ps_global
->VAR_NORM_FORE_COLOR
)
397 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
398 ps_global
->VAR_NORM_BACK_COLOR
))){
400 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
401 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
402 if(!pico_is_good_colorpair(hdrcolor
))
403 free_color_pair(&hdrcolor
);
407 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
410 gf_puts(padding
, pc
);
411 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
414 char buf
[16], color
[64];
418 h
= new_handle(handlesp
);
419 if(minkey
< 0) minkey
= h
->key
;
421 h
->h
.ical
.attach
= a
;
422 h
->h
.ical
.depth
= h
->key
- minkey
;
424 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
425 buf
[sizeof(buf
)-1] = '\0';
427 if(!(flgs
& FM_NOCOLOR
)
428 && handle_start_color(color
, sizeof(color
), &l
, 1)){
429 lastc
= get_cur_embedded_color();
430 if(!gf_nputs(color
, (long) l
, pc
))
433 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
434 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
437 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
438 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
441 tmp_20k_buf
[0] = '\0';
443 if(!format_env_puts(tmp_20k_buf
, pc
))
448 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
449 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
453 if(!embed_color(lastc
, pc
))
456 free_color_pair(&lastc
);
458 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
461 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
466 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
467 if(!gf_puts(tmp_20k_buf
, pc
))
471 if(!gf_puts(NEWLINE
, pc
))
474 avail
= width
- m1
-2;
476 dwid
= MAX(MIN(40, avail
), 0);
479 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
480 repeat_char(dwid
, '-'));
482 gf_puts(tmp_20k_buf
, pc
);
484 gf_puts(NEWLINE
, pc
);
486 free_vevent_summary(&vesummary
);
490 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_o_t pc
)
492 char *rawtext
, *caltext
;
493 unsigned long callen
;
494 VCALENDAR_S
*vcal
= NULL
;
498 if(flgs
& FM_NEW_MESS
) {
499 zero_atmts(ps_global
->atmts
);
500 describe_mime(body
, "", 1, 1, 0, flgs
);
503 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
504 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
505 b
= mail_body (ps_global
->mail_stream
, msgno
, (unsigned char *) a
->number
);
507 gf_puts(_("Error fetching calendar body part"), pc
);
508 gf_puts(NEWLINE
, pc
);
511 if(b
->sparep
== NULL
){
512 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
513 if(rawtext
== NULL
|| *rawtext
== '\0'){
514 gf_puts(_("Error fetching calendar text"), pc
);
515 gf_puts(NEWLINE
, pc
);
518 rawtext
[callen
] = '\0'; /* chop off cookie */
521 caltext
= rfc822_base64((unsigned char *) rawtext
, strlen(rawtext
), &callen
);
523 gf_puts(_("Error in calendar base64 encoding"), pc
);
524 gf_puts(NEWLINE
, pc
);
527 caltext
[callen
] = '\0';
530 case ENCQUOTEDPRINTABLE
:
531 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
533 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
534 gf_puts(NEWLINE
, pc
);
537 caltext
[callen
] = '\0';
540 default: caltext
= cpystr(rawtext
);
543 vcal
= ical_parse_text(caltext
);
544 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
545 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
546 fs_give((void **) &caltext
);
549 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
550 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
551 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
552 if(vcal
->comp
[VEvent
] != NULL
){
553 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
554 } /* else another type of entry in the calendar */
556 gf_puts(NEWLINE
, pc
);
564 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_o_t pc
)
566 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
567 int is_in_sig
= OUT_SIG_BLOCK
;
568 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
574 || (ps_global
->full_header
== 2
575 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
577 /*--- Server is not an IMAP2bis, It can't parse MIME
578 so we just show the text here. Hopefully the
579 message isn't a MIME message
583 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
584 msgno
, NULL
, NULL
, NIL
)) != NULL
){
586 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
587 return("Write Error");
589 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
593 * We need to translate the message
594 * into UTF-8, but that's trouble in the full header case
595 * because we don't know what to translate from. We'll just
596 * take a guess by looking for the first text part and
599 if(body
&& body
->type
== TYPETEXT
)
600 charset
= parameter_val(body
->parameter
, "charset");
601 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
602 && body
->nested
.part
->body
.type
== TYPETEXT
)
603 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
605 charset
= cpystr(ps_global
->display_charmap
);
607 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
608 /* transliterate message text to UTF-8 */
609 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
611 if (charset
) fs_give((void **) &charset
);
613 /* link in filters, similar to what is done in decode_text() */
614 if(!ps_global
->pass_ctrl_chars
){
615 gf_link_filter(gf_escape_filter
, NULL
);
617 gf_link_filter(gf_control_filter
,
618 gf_control_filter_opt(&filt_only_c0
));
621 gf_link_filter(gf_tag_filter
, NULL
);
623 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
624 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
625 || F_ON(F_SCAN_ADDR
, ps_global
))
627 gf_link_filter(gf_line_test
,
628 gf_line_test_opt(url_hilite
,
629 gf_url_hilite_opt(&uh
,handlesp
,0)));
632 if((flgs
& FM_DISPLAY
)
633 && !(flgs
& FM_NOCOLOR
)
635 && ps_global
->VAR_SIGNATURE_FORE_COLOR
636 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
637 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
640 if((flgs
& FM_DISPLAY
)
641 && !(flgs
& FM_NOCOLOR
)
643 && ps_global
->VAR_QUOTE1_FORE_COLOR
644 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
645 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
648 if(!(flgs
& FM_NOWRAP
)){
649 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
651 && !(flgs
& FM_NOCOLOR
)
652 && pico_usingcolor())
653 wrapflags
|= GFW_USECOLOR
;
654 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
656 ? NULL
: format_view_margin(),
661 gf_link_filter(gf_nvtnl_local
, NULL
);
662 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
663 /* TRANSLATORS: There was an error putting together a message for
664 viewing. The arg is the description of the error. */
665 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
666 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
667 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
668 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
669 && gf_puts(NEWLINE
, pc
))
677 if(!gf_puts(NEWLINE
, pc
)
678 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
679 || !gf_puts(NEWLINE
, pc
)
680 || !gf_puts(NEWLINE
, pc
))
681 return("Write Error");
687 /*======== Now loop through formatting all the parts =======*/
688 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
689 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
692 if(a
->body
->type
== TYPEMULTIPART
){
694 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
696 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
697 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
698 return("Write Error");
706 if(a
->suppress_editorial
)
709 if(!(flgs
& FM_NOEDITORIAL
)
710 && (!gf_puts(NEWLINE
, pc
)
711 || (decode_err
= part_desc(a
->number
, a
->body
,
713 ? (a
->can_display
!= MCD_NONE
)
715 : 3, width
, flgs
, pc
))))
716 return("Write Error");
721 switch(a
->body
->type
){
725 * If a message is multipart *and* the first part of it
726 * is text *and that text is empty, there is a good chance that
727 * there was actually something there that c-client was
728 * unable to parse. Here we report the empty message body
729 * and insert the raw RFC822.TEXT (if full-headers are
732 if(body
->type
== TYPEMULTIPART
733 && a
== ps_global
->atmts
734 && a
->body
->size
.bytes
== 0
735 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
738 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
739 "Empty or malformed message%s.",
740 ps_global
->full_header
== 2
741 ? ". Displaying raw text"
742 : ". Use \"H\" to see raw text");
743 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
745 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
746 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
747 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
748 return("Write Error");
750 if(ps_global
->full_header
== 2
751 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
752 a
->number
, pc
, flgs
))){
753 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
754 "%s%s [ Formatting error: %s ]%s%s",
755 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
756 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
757 if(!gf_puts(tmp_20k_buf
, pc
))
758 return("Write Error");
765 * Don't write our delimiter if this text part is
766 * the first part of a message/rfc822 segment...
768 if(show_parts
&& a
!= ps_global
->atmts
769 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
771 || (a
[-1].body
->type
== TYPEMULTIPART
772 && a
[-1].body
->subtype
773 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
774 && &a
[-1] != ps_global
->atmts
775 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
778 && !(flgs
& FM_NOEDITORIAL
)){
779 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
780 tmp1
= "Calendar entry";
782 tmp1
= a
->body
->description
? a
->body
->description
784 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
785 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
787 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
789 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
790 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
791 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
792 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
793 return("Write Error");
796 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
797 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
798 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
803 tmp1
= a
->body
->description
? a
->body
->description
804 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
806 : "Included Message";
807 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
808 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
810 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
812 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
814 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
815 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
816 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
817 return("Write Error");
819 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
820 /* imapenvonly, we may not have all the headers we need */
821 if(a
->body
->nested
.msg
->env
->imapenvonly
)
822 mail_fetch_header(ps_global
->mail_stream
, msgno
,
823 a
->number
, NULL
, NULL
, FT_PEEK
);
824 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
825 a
->body
->nested
.msg
->env
, hp
,
826 NULL
, handlesp
, flgs
, NULL
, pc
)){
827 case -1 : /* write error */
828 return("Write Error");
830 case 1 : /* fetch error */
831 if(!(gf_puts("[ Error fetching header ]", pc
)
832 && !gf_puts(NEWLINE
, pc
)))
833 return("Write Error");
838 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
839 int *margin
, avail
, m1
, m2
;
842 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
844 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
847 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
850 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
851 || !gf_puts(NEWLINE
, pc
)
852 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
853 return("Write Error");
856 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
857 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
860 if(!gf_puts(NEWLINE
, pc
))
861 return("Write Error");
866 if((decode_err
= part_desc(a
->number
, a
->body
,
867 (flgs
& FM_DISPLAY
) ? 1 : 3,
868 width
, flgs
, pc
)) != NULL
)
869 return("Write Error");
876 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
877 && format_blip_seen(msgno
)))
878 return("Cannot format body.");
886 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_o_t pc
)
890 if(flgs
& FM_NEW_MESS
) {
891 zero_atmts(ps_global
->atmts
);
892 describe_mime(body
, "", 1, 1, 0, flgs
);
895 /*----- First do the list of parts/attachments if needed ----*/
896 if((flgs
& FM_DISPLAY
)
897 && (ps_global
->atmts
[1].description
898 || (ps_global
->atmts
[0].body
899 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
900 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
901 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
902 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
903 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
904 COLOR_PAIR
*hdrcolor
= NULL
;
906 if((flgs
& FM_DISPLAY
)
907 && !(flgs
& FM_NOCOLOR
)
909 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
910 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
911 && ps_global
->VAR_NORM_FORE_COLOR
912 && ps_global
->VAR_NORM_BACK_COLOR
913 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
914 ps_global
->VAR_NORM_FORE_COLOR
)
915 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
916 ps_global
->VAR_NORM_BACK_COLOR
))){
918 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
919 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
920 if(!pico_is_good_colorpair(hdrcolor
))
921 free_color_pair(&hdrcolor
);
925 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
928 * Attachment list header
933 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
936 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
939 hwid
= MAX(avail
, 0);
941 i
= utf8_width(_("Parts/Attachments:"));
942 partwid
= MIN(i
, hwid
);
943 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
946 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
947 if(!gf_puts(tmp
, pc
))
951 utf8_snprintf(tmp
, sizeof(tmp
),
953 /* TRANSLATORS: A label */
954 partwid
, partwid
, _("Parts/Attachments:"),
957 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
961 /*----- Figure max display widths -----*/
962 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
963 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
966 if((n
= utf8_width(a
->number
)) > maxnumwid
)
969 if((n
= utf8_width(a
->size
)) > maxsizewid
)
974 * ----- adjust max lengths for nice display -----
976 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
980 avail
= width
- m1
- m2
;
982 s1
= MAX(MIN(1, avail
), 0);
985 dwid
= MAX(MIN(1, avail
), 0);
988 s2
= MAX(MIN(1, avail
), 0);
991 maxnumwid
= MIN(maxnumwid
, width
/3);
992 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
995 s3
= MAX(MIN(1, avail
), 0);
998 shownwid
= MAX(MIN(5, avail
), 0);
1001 s4
= MAX(MIN(3, avail
), 0);
1004 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
1007 s5
= MAX(MIN(2, avail
), 0);
1010 descwid
= MAX(0, avail
);
1012 /*----- Format the list of attachments -----*/
1013 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
1014 COLOR_PAIR
*lastc
= NULL
;
1016 int thisdescwid
, padwid
;
1018 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
1021 if(a
->body
->type
== TYPEMULTIPART
1022 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1026 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1027 thisdescwid
= MIN(i
, descwid
);
1028 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1031 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1032 if(!gf_puts(tmp
, pc
))
1036 utf8_snprintf(tmp
, sizeof(tmp
),
1037 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1040 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1042 maxnumwid
, maxnumwid
,
1044 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1048 a
->shown
? "Shown" :
1049 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1053 a
->size
? a
->size
: "",
1055 thisdescwid
, thisdescwid
,
1056 (descwid
> 2 && a
->description
) ? a
->description
: "");
1058 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1061 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1062 char buf
[16], color
[64];
1066 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1070 h
= new_handle(handlesp
);
1074 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1075 buf
[sizeof(buf
)-1] = '\0';
1077 if(!(flgs
& FM_NOCOLOR
)
1078 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1079 lastc
= get_cur_embedded_color();
1080 if(!gf_nputs(color
, (long) l
, pc
))
1083 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1084 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1087 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1088 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1094 if(!format_env_puts(tmpp
, pc
))
1097 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1099 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1100 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1104 if(!embed_color(lastc
, pc
))
1107 free_color_pair(&lastc
);
1109 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1112 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1117 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1118 if(!gf_puts(tmp
, pc
))
1122 if(!gf_puts(NEWLINE
, pc
))
1127 * Dashed line after list
1131 avail
= width
- m1
- m2
;
1132 hwid
= MAX(avail
, 0);
1134 dashwid
= MAX(MIN(40, hwid
-2), 0);
1135 padwid
= hwid
- dashwid
;
1137 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1138 if(!gf_puts(tmp
, pc
))
1142 snprintf(tmp
, sizeof(tmp
),
1144 repeat_char(dashwid
, '-'),
1145 padwid
, padwid
, "");
1148 avail
= width
- m1
-2;
1150 dashwid
= MAX(MIN(40, avail
), 0);
1153 snprintf(tmp
, sizeof(tmp
),
1156 repeat_char(dashwid
, '-'));
1159 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1163 free_color_pair(&hdrcolor
);
1172 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1173 * of body part fetches as we're formatting) for the
1174 * given message isn't set (likely because there
1175 * weren't any parts suitable for display), then make
1176 * sure to set it here.
1179 format_blip_seen(long int msgno
)
1183 if(msgno
> 0L && ps_global
->mail_stream
1184 && msgno
<= ps_global
->mail_stream
->nmsgs
1185 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1187 && !ps_global
->mail_stream
->rdonly
)
1188 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1195 * is_an_env_hdr - is this name a header in the envelope structure?
1197 * name - the header name to check
1200 is_an_env_hdr(char *name
)
1204 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1205 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1215 * is_an_addr_hdr - is this an address header?
1217 * name - the header name to check
1220 is_an_addr_hdr(char *fieldname
)
1222 char fbuf
[FBUF_LEN
+1];
1223 char *colon
, *fname
;
1224 static char *addr_headers
[] = {
1240 /* so it is pointing to NULL */
1241 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1243 if((colon
= strindex(fieldname
, ':')) != NULL
){
1244 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1245 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1251 if(fname
&& *fname
){
1252 for(p
= addr_headers
; *p
; p
++)
1253 if(!strucmp(fname
, *p
))
1257 return((*p
) ? 1 : 0);
1262 * Format a single field from the envelope
1265 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1266 fmt_env_t fmt_env
, gf_o_t pc
, char *field
, char *oacs
, int flags
)
1271 fmt_env
= format_envelope
;
1273 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1274 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1275 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1282 * Look through header string beginning with "begin", for the next
1283 * occurrence of header "field". Set "start" to that. Set "end" to point one
1284 * position past all of the continuation lines that go with "field".
1285 * That is, if "end" is converted to a null
1286 * character then the string "start" will be the next occurrence of header
1287 * "field" including all of its continuation lines. Assume we
1288 * have CRLF's as end of lines.
1290 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1291 * make "end" the end of that header.
1293 * Returns 1 if found, 0 if not.
1296 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1298 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1303 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1309 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1310 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1311 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1312 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1315 * We require that start is at the beginning of a line, so
1316 * either it equals begin (which we assume is the beginning of a
1317 * line) or it is preceded by a CRLF.
1320 *start
= srchstr(begin_srch
, tmpfield
);
1321 while(*start
&& *start
!= begin
1322 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1323 begin_srch
= *start
+ 1;
1324 *start
= srchstr(begin_srch
, tmpfield
);
1331 for(p
= *start
; *p
; p
++){
1333 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1335 * The final 015 in the test above is to test for the end
1352 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1356 if(pico_usingcolor()){
1357 char *fg
= NULL
, *bg
= NULL
, *s
;
1358 char *basefg
= NULL
, *basebg
= NULL
;
1360 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1361 : ps_global
->VAR_NORM_FORE_COLOR
;
1362 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1363 : ps_global
->VAR_NORM_BACK_COLOR
;
1365 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1366 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1367 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1369 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1370 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1371 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1377 * The blacks are just known good colors for
1378 * testing whether the other color is good.
1380 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1381 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1382 if(pico_is_good_colorpair(tmp
))
1383 for(s
= color_embed(fg
, bg
);
1384 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1388 free_color_pair(&tmp
);
1392 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1393 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1398 colorstring
[buflen
-1] = '\0';
1405 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1408 if(pico_usingcolor()){
1409 char *fg
= NULL
, *bg
= NULL
, *s
;
1412 * We need to change the fg and bg colors back even if they
1413 * are the same as the Normal Colors so that color_a_quote
1414 * will have a chance to pick up those colors as the ones to
1415 * switch to. We don't do this before the handle above so that
1416 * the quote color will flow into the selectable item when
1417 * the selectable item color is partly the same as the
1418 * normal color. That is, suppose the normal color was black on
1419 * cyan and the selectable color was blue on cyan, only a fg color
1420 * change. We preserve the only-a-fg-color-change in a quote by
1421 * letting the quote background color flow into the selectable text.
1423 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1424 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1426 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1427 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1429 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1430 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1435 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1439 colorstring
[buflen
-1] = '\0';
1446 url_embed(int embed
)
1448 static char buf
[3] = {TAG_EMBED
};
1456 * Paint the signature.
1459 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1461 struct variable
*vars
= ps_global
->vars
;
1463 COLOR_PAIR
*col
= NULL
;
1465 if(is_in_sig
== NULL
)
1468 in_sig_block
= (int *) is_in_sig
;
1470 if(!strcmp(line
, SIGDASHES
))
1471 *in_sig_block
= START_SIG_BLOCK
;
1472 else if(*line
== '\0')
1474 * Suggested by Eduardo: allow for a blank line right after
1477 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1478 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1480 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1481 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1483 if(*in_sig_block
!= OUT_SIG_BLOCK
1484 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1485 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1486 VAR_SIGNATURE_BACK_COLOR
))){
1487 if(!pico_is_good_colorpair(col
))
1488 free_color_pair(&col
);
1492 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1494 ins
= gf_line_test_new_ins(ins
, line
,
1495 color_embed(col
->fg
, col
->bg
),
1498 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1499 fg
[sizeof(fg
)-1] = '\0';
1500 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1501 bg
[sizeof(bg
)-1] = '\0';
1504 * Loop watching colors, and override with
1505 * signature color whenever the normal foreground and background
1506 * colors are in force.
1510 if(*p
++ == TAG_EMBED
){
1514 p
+= *p
+ 1; /* skip handle key */
1518 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1519 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1520 p
+= RGBLEN
; /* advance past color value */
1522 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1523 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1524 ins
= gf_line_test_new_ins(ins
, p
,
1525 color_embed(col
->fg
,NULL
),
1530 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1531 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1532 p
+= RGBLEN
; /* advance past color value */
1534 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1535 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1536 ins
= gf_line_test_new_ins(ins
, p
,
1537 color_embed(NULL
,col
->bg
),
1547 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1548 color_embed(VAR_NORM_FORE_COLOR
,
1549 VAR_NORM_BACK_COLOR
),
1551 free_color_pair(&col
);
1559 * Line filter to add color to displayed headers.
1562 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1564 static char field
[FBUF_LEN
+ 1];
1565 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1566 char *p
, *q
, *value
, *beg
;
1568 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1569 struct variable
*vars
= ps_global
->vars
;
1571 field
[FBUF_LEN
] = '\0';
1573 if(isspace((unsigned char)*line
)) /* continuation line */
1576 if(!(value
= strindex(line
, ':')))
1579 memset(field
, 0, sizeof(field
));
1580 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1583 for(value
++; isspace((unsigned char)*value
); value
++)
1586 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1587 fg
[sizeof(fg
)-1] = '\0';
1588 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1589 bg
[sizeof(bg
)-1] = '\0';
1592 * Split into two cases depending on whether this is a header which
1593 * contains addresses or not. We may color addresses separately.
1595 if(is_an_addr_hdr(field
)){
1598 * If none of the patterns are for this header, don't bother parsing
1599 * and checking each address.
1601 if(!any_hdr_color(field
))
1605 * First check for patternless patterns which color whole line.
1607 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1608 if(pico_is_good_colorpair(color
)){
1609 ins
= gf_line_test_new_ins(ins
, value
,
1610 color_embed(color
->fg
, color
->bg
),
1612 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1613 fg
[sizeof(fg
)-1] = '\0';
1614 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1615 bg
[sizeof(bg
)-1] = '\0';
1619 free_color_pair(&color
);
1623 * Then go through checking address by address.
1624 * Keep track of quotes and watch for color changes, and override
1625 * with most recent header color whenever the normal foreground
1626 * and background colors are in force.
1632 /* skip next character */
1633 if(*(p
+1) && (in_comment
|| in_quote
))
1642 in_quote
= 1 - in_quote
;
1660 if(!(in_quote
|| in_comment
)){
1661 /* we reached the end of this address */
1664 free_color_pair(&color
);
1666 if((color
= hdr_color(field
, beg
,
1667 ps_global
->hdr_colors
)) != NULL
){
1668 if(pico_is_good_colorpair(color
)){
1670 ins
= gf_line_test_new_ins(ins
, beg
,
1671 color_embed(color
->fg
,
1675 for(q
= p
; q
> beg
&&
1676 isspace((unsigned char)*(q
-1)); q
--)
1679 ins
= gf_line_test_new_ins(ins
, q
,
1680 color_embed(fg
, bg
),
1684 free_color_pair(&color
);
1689 for(p
++; isspace((unsigned char)*p
); p
++)
1703 p
+= *p
+ 1; /* skip handle key */
1708 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1709 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1710 p
+= RGBLEN
; /* advance past color value */
1712 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1713 ins
= gf_line_test_new_ins(ins
, p
,
1714 color_embed(color
->fg
,NULL
),
1720 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1721 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1722 p
+= RGBLEN
; /* advance past color value */
1724 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1725 ins
= gf_line_test_new_ins(ins
, p
,
1726 color_embed(NULL
,color
->bg
),
1743 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1746 if(*q
&& !(in_quote
|| in_comment
)){
1747 /* we reached the end of this address */
1749 free_color_pair(&color
);
1751 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1752 if(pico_is_good_colorpair(color
)){
1754 ins
= gf_line_test_new_ins(ins
, beg
,
1755 color_embed(color
->fg
,
1758 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1761 ins
= gf_line_test_new_ins(ins
, q
,
1762 color_embed(fg
, bg
),
1766 free_color_pair(&color
);
1771 free_color_pair(&color
);
1774 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1775 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1776 VAR_HEADER_GENERAL_BACK_COLOR
),
1781 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1784 if(pico_is_good_colorpair(color
)){
1785 ins
= gf_line_test_new_ins(ins
, value
,
1786 color_embed(color
->fg
, color
->bg
),
1790 * Loop watching colors, and override with header
1791 * color whenever the normal foreground and background
1792 * colors are in force.
1796 if(*p
++ == TAG_EMBED
){
1800 p
+= *p
+ 1; /* skip handle key */
1804 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1805 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1806 p
+= RGBLEN
; /* advance past color value */
1808 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1809 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1810 ins
= gf_line_test_new_ins(ins
, p
,
1811 color_embed(color
->fg
,NULL
),
1816 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1817 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1818 p
+= RGBLEN
; /* advance past color value */
1820 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1821 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1822 ins
= gf_line_test_new_ins(ins
, p
,
1823 color_embed(NULL
,color
->bg
),
1833 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1834 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1835 VAR_HEADER_GENERAL_BACK_COLOR
),
1839 free_color_pair(&color
);
1848 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1850 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1851 *weburlp
= NULL
, *mailurlp
= NULL
;
1852 int n
, n1
, n2
, n3
, l
;
1853 char buf
[256], color
[256];
1857 for(lp
= line
; ; lp
= up
+ n
){
1858 /* scan for all of them so we can choose the first */
1859 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1860 urlp
= rfc1738_scan(lp
, &n1
);
1861 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1862 weburlp
= web_host_scan(lp
, &n2
);
1863 if(F_ON(F_SCAN_ADDR
,ps_global
))
1864 mailurlp
= mail_addr_scan(lp
, &n3
);
1866 if(urlp
|| weburlp
|| mailurlp
){
1868 weburlp
? weburlp
: mailurlp
;
1869 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1871 if(mailurlp
&& mailurlp
< up
)
1876 weburlp
= mailurlp
= NULL
;
1878 else if(up
== weburlp
){
1890 uh
= (URL_HILITE_S
*) local
;
1892 h
= new_handle(uh
->handlesp
);
1894 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1895 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1896 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1897 h
->h
.url
.path
[n
+10-1] = '\0';
1899 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1900 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1901 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1902 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1905 buf
[1] = TAG_HANDLE
;
1906 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1907 buf
[sizeof(buf
)-1] = '\0';
1908 buf
[2] = strlen(&buf
[3]);
1909 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1911 /* in case it was the current selection */
1912 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1914 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1915 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1917 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1919 urlp
= weburlp
= mailurlp
= NULL
;
1927 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1929 static int check_for_urls
= 0;
1932 if(isspace((unsigned char)*line
)) /* continuation, check or not
1933 depending on last line */
1937 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1942 if(((ft
= pine_header_standard(line
)) == FreeText
1944 || ft
== TypeUnknown
)
1945 && strucmp(line
, "message-id")
1946 && strucmp(line
, "newsgroups")
1947 && strucmp(line
, "references")
1948 && strucmp(line
, "in-reply-to")
1949 && strucmp(line
, "received")
1950 && strucmp(line
, "date")){
1959 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1966 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1971 struct variable
*vars
= ps_global
->vars
;
1976 total_wid
= *((int *) local
);
1978 /* calculate width of line */
1988 p
+= *p
+ 1; /* skip handle key */
1993 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
2005 default: /* literal embed char */
2013 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
2019 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2025 if(total_wid
> wid
){
2026 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2027 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2028 VAR_HEADER_GENERAL_BACK_COLOR
),
2030 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2031 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2032 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2033 color_embed(VAR_NORM_FORE_COLOR
,
2034 VAR_NORM_BACK_COLOR
),
2046 url_external_specific_handler(char *url
, int len
)
2048 static char list
[UES_LEN
* UES_MAX
];
2054 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2055 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2058 else{ /* initialize! */
2059 char **l
, *test
, *cmd
, *p
, *p2
;
2062 memset(list
, 0, sizeof(list
));
2063 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2064 get_pair(*l
, &test
, &cmd
, 1, 1);
2066 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2069 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2070 if((p2
= strchr(p
, ',')) != NULL
){
2071 if((n
= p2
- p
) < UES_LEN
){
2072 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2077 "* * * HANDLER TOO LONG: %.*s\n", n
,
2083 if(strlen(p
) <= UES_LEN
){
2084 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2093 fs_give((void **) &test
);
2096 fs_give((void **) &cmd
);
2105 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2106 imapuid_t
*uid
, char **search
, int silent
)
2108 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2109 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2112 int rv
= URL_IMAP_ERROR
;
2115 * Since we're planting nulls, operate on a temporary copy...
2117 scheme
= silent
? NULL
: "IMAP";
2118 url
= cpystr(true_url
+ 7);
2120 /* Try to pick apart the "iserver" portion */
2121 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2125 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2127 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2130 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2132 server
= rfc1738_str(p
);
2134 /* only ";auth=*" supported (and also ";auth=anonymous") */
2135 if((p
= srchstr(url
, ";auth=")) != NULL
){
2137 auth
= rfc1738_str(p
+ 6);
2141 user
= rfc1738_str(url
);
2144 server
= rfc1738_str(url
);
2147 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2150 * "iserver" in hand, pick apart the "icommand"...
2153 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2157 * No "icommand" (all top-level folders) or "imailboxlist"...
2160 *p
= '\0'; /* tie off criteria */
2161 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2162 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2163 rv
|= URL_IMAP_IMBXLSTLSUB
;
2164 else if(strucmp(p
, "list"))
2165 return(url_bogus_imap(&url
, scheme
,
2166 "Invalid list type specified"));
2169 rv
|= URL_IMAP_ISERVERONLY
;
2173 /* build folder list from specified server/criteria/list-method */
2174 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2175 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2176 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2177 user
? "user=\"" : "Anonymous",
2180 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2181 (*folder
)[l
] = '\0';
2182 rv
|= URL_IMAP_IMAILBOXLIST
;
2185 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2186 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2187 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2188 *section
= '\0'; /* tie off UID */
2189 section
= rfc1738_str(section
+ 10);
2190 /* BUG: verify valid section spec ala rfc 2060 */
2192 "-- URL IMAP FOLDER: section not used: %s\n",
2193 section
? section
: "?"));
2196 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2197 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2199 /* optional "uidvalidity"? */
2200 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2203 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2204 return(url_bogus_imap(&url
, scheme
,
2205 "Invalid UIDVALIDITY"));
2208 mailbox
= rfc1738_str(cmd
);
2209 rv
= URL_IMAP_IMESSAGEPART
;
2211 else{ /* "imessagelist" */
2212 /* optional "uidvalidity"? */
2213 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2216 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2217 return(url_bogus_imap(&url
, scheme
,
2218 "Invalid UIDVALIDITY"));
2221 /* optional "enc_search"? */
2222 if((p
= strchr(cmd
, '?')) != NULL
){
2225 *search
= cpystr(rfc1738_str(p
+ 1));
2226 /* BUG: verify valid search spec ala rfc 2060 */
2229 mailbox
= rfc1738_str(cmd
);
2230 rv
= URL_IMAP_IMESSAGELIST
;
2233 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2234 q_status_message(SM_ORDER
, 3, 3,
2235 "Unsupported authentication method. Using standard login.");
2238 * At this point our structure should contain the
2239 * digested url. Now put it together for c-client...
2241 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2242 + (user
? (strlen(user
)+2) : 9);
2243 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2244 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2245 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2246 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2250 (*folder
)[l
] = '\0';
2253 fs_give((void **) &url
);
2259 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2261 fs_give((void **) freeme
);
2262 (void) url_bogus(url
, problem
);
2263 return(URL_IMAP_ERROR
);
2268 * url_bogus - report url syntax errors and such
2271 url_bogus(char *url
, char *reason
)
2273 dprint((2, "-- bogus url \"%s\": %s\n",
2274 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2276 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2277 "Malformed \"%.*s\" URL: %.200s",
2278 (void *) (strchr(url
, ':') - url
), url
, reason
);
2285 /*----------------------------------------------------------------------
2286 Format header text suitable for display
2288 Args: stream -- mail stream for various header text fetches
2289 msgno -- sequence number in stream of message we're interested in
2290 section -- which section of message
2291 env -- pointer to msg's envelope
2292 hdrs -- struct containing what's to get formatted
2293 prefix -- prefix to append to each output line
2294 handlesp -- address of pointer to the message's handles
2295 flags -- FM_ flags, see pith/mailview.h
2296 final_pc -- function to write header text with
2298 Result: 0 if all's well, -1 if write error, 1 if fetch error
2300 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2301 in the local convention.
2305 #define FHT_WRTERR -1
2306 #define FHT_FTCHERR 1
2308 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2309 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2310 fmt_env_t fmt_env
, gf_o_t final_pc
)
2314 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2320 struct variable
*vars
= ps_global
->vars
;
2322 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2323 gf_set_so_writec(&tmp_pc
, tmp_store
);
2328 fmt_env
= format_envelope
;
2330 if(ps_global
->full_header
== 2){
2331 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2335 * First, calculate how big a fields array we need.
2338 /* Custom header viewing list specified */
2339 if(hdrs
->type
== HD_LIST
){
2340 /* view all these headers */
2342 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2343 if(!is_an_env_hdr(q
))
2346 /* view all except these headers */
2348 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2352 nfields
--; /* subtract one for ALL_EXCEPT field */
2356 nfields
= 6; /* default view */
2358 /* allocate pointer space */
2360 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2361 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2364 if(hdrs
->type
== HD_LIST
){
2365 /* view all these headers */
2367 /* put the non-envelope headers in fields */
2369 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2370 if(!is_an_env_hdr(q
))
2373 /* view all except these headers */
2375 /* put the list of headers not to view in fields */
2377 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2378 if(strucmp(ALL_EXCEPT
, q
))
2386 fields
[i
= 0] = "Resent-Date";
2387 fields
[++i
] = "Resent-From";
2388 fields
[++i
] = "Resent-To";
2389 fields
[++i
] = "Resent-cc";
2390 fields
[++i
] = "Resent-Subject";
2396 /* custom view with exception list */
2397 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2399 * Go through each header in h and print it.
2400 * First we check to see if it is an envelope header so we
2401 * can print our envelope version of it instead of the raw version.
2404 /* fetch all the other headers */
2406 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2409 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2411 char tmp
[MAILTMPLEN
+1];
2414 colon_loc
= strindex(start
, ':');
2415 if(colon_loc
&& colon_loc
< finish
){
2416 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2417 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2422 if(colon_loc
&& is_an_env_hdr(tmp
)){
2423 char *dummystart
, *dummyfinish
;
2426 * Pretty format for env hdrs.
2427 * If the same header appears more than once, only
2428 * print the last to avoid duplicates.
2429 * They should have been combined in the env when parsed.
2431 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2433 format_env_hdr(stream
, msgno
, section
, env
,
2434 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2437 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2438 hdrs
->charset
, flags
)) != 0)
2445 /* custom view or default */
2447 /* fetch the non-envelope headers */
2449 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2451 /* default envelope for default view */
2452 if(hdrs
->type
== HD_BFIELD
)
2453 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2455 /* go through each header in list, v initialized above */
2456 for(; (q
= *v
) != NULL
; v
++){
2457 if(is_an_env_hdr(q
)){
2458 /* pretty format for env hdrs */
2459 format_env_hdr(stream
, msgno
, section
, env
,
2460 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2464 * Go through h finding all occurrences of this header
2465 * and all continuation lines, and output.
2468 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2470 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2471 hdrs
->charset
, flags
)) != 0)
2484 gf_clear_so_writec(tmp_store
);
2486 if(!rv
){ /* valid data? Do wrapping and filtering... */
2488 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2489 STORE_S
*df_store
= NULL
;
2491 so_seek(tmp_store
, 0L, 0);
2493 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2496 * Test for and act on any display filter
2497 * This barely makes sense. The display filter is going
2498 * to be getting UTF-8'ized headers here. In pre-alpine
2499 * pine the display filter was being fed already decoded
2500 * headers in whatever character set they were in.
2501 * The good news is that that didn't make much
2502 * sense either, so this shouldn't break anything.
2503 * It seems unlikely that anybody is doing anything useful
2504 * with the header part of display filters.
2506 if(ps_global
->tools
.display_filter
2507 && ps_global
->tools
.display_filter_trigger
2508 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2509 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2511 gf_set_so_writec(&tmp_pc
, df_store
);
2512 gf_set_so_readc(&tmp_gc
, df_store
);
2513 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2514 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2515 _("Formatting error: %s"), errstr
);
2519 so_seek(df_store
, 0L, 0);
2521 gf_clear_so_writec(df_store
);
2524 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2525 "No space for filtered text.");
2530 so_seek(tmp_store
, 0L, 0);
2531 gf_set_so_readc(&tmp_gc
, tmp_store
);
2535 int *margin
, wrapflags
= GFW_ONCOMMA
;
2538 gf_link_filter(gf_local_nvtnl
, NULL
);
2540 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2541 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2542 || F_ON(F_SCAN_ADDR
, ps_global
))
2544 gf_link_filter(gf_line_test
,
2545 gf_line_test_opt(url_hilite_hdr
,
2546 gf_url_hilite_opt(&uh
,handlesp
,1)));
2547 wrapflags
|= GFW_HANDLES
;
2550 if((flags
& FM_DISPLAY
)
2551 && !(flags
& FM_NOCOLOR
)
2552 && pico_usingcolor()
2553 && ((VAR_NORM_FORE_COLOR
2554 && VAR_HEADER_GENERAL_FORE_COLOR
2555 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2557 (VAR_NORM_BACK_COLOR
2558 && VAR_HEADER_GENERAL_BACK_COLOR
2559 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2560 wrapflags
|= GFW_HDRCOLOR
;
2562 if((flags
& FM_DISPLAY
)
2563 && !(flags
& FM_NOCOLOR
)
2564 && pico_usingcolor()
2565 && ps_global
->hdr_colors
){
2566 gf_link_filter(gf_line_test
,
2567 gf_line_test_opt(color_headers
, NULL
));
2568 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2571 if(prefix
&& *prefix
)
2572 column
= MAX(column
-strlen(prefix
), 50);
2574 margin
= format_view_margin();
2576 if(!(flags
& FM_NOWRAP
))
2577 gf_link_filter(gf_wrap
,
2578 gf_wrap_filter_opt(column
, column
,
2579 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2582 if(prefix
&& *prefix
)
2583 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2585 if((flags
& FM_DISPLAY
)
2586 && !(flags
& FM_NOCOLOR
)
2587 && pico_usingcolor()
2588 && ((VAR_NORM_FORE_COLOR
2589 && VAR_HEADER_GENERAL_FORE_COLOR
2590 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2592 (VAR_NORM_BACK_COLOR
2593 && VAR_HEADER_GENERAL_BACK_COLOR
2594 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2598 right_margin
= margin
? margin
[1] : 0;
2599 total_wid
= column
- right_margin
;
2601 gf_link_filter(gf_line_test
,
2602 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2603 wrapflags
|= GFW_HANDLES
;
2606 gf_link_filter(gf_nvtnl_local
, NULL
);
2608 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2610 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2611 "Can't build header : %.200s", errstr
);
2616 gf_clear_so_readc(df_store
);
2620 gf_clear_so_readc(tmp_store
);
2623 so_give(&tmp_store
);
2626 fs_give((void **)&h
);
2629 fs_give((void **)&fields
);
2635 /*----------------------------------------------------------------------
2636 Format RAW header text for display
2638 Args: stream -- mail stream for various header text fetches
2639 rawno -- sequence number in stream of message we're interested in
2640 section -- which section of message
2641 pc -- function to write header text with
2643 Result: 0 if all's well, -1 if write error, 1 if fetch error
2645 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2646 in the local convention.
2650 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_o_t pc
)
2652 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2659 if(!gf_puts(NEWLINE
, pc
))
2662 if(ISRFCEOL(h
)) /* all done! */
2665 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2666 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2667 c
= (unsigned char) *h
++;
2668 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2669 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2672 else if(!(*pc
)(*h
++))
2677 return(FHT_FTCHERR
);
2684 /*----------------------------------------------------------------------
2685 Format c-client envelope data suitable for display
2687 Args: s -- mail stream for various header text fetches
2688 n -- raw sequence number in stream of message we're interested in
2689 sect -- which section of message
2690 e -- pointer to msg's envelope
2691 pc -- function to write header text with
2692 which -- which header lines to write
2694 flags -- FM_ flags, see pith/mailview.h
2696 Result: 0 if all's well, -1 if write error, 1 if fetch error
2698 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2699 in the local convention.
2703 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_o_t pc
,
2704 long int which
, char *oacs
, int flags
)
2706 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2711 if((which
& FE_DATE
) && e
->date
) {
2713 snprintf(buftmp
, sizeof(buftmp
), "%s",
2714 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2715 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2716 buftmp
[sizeof(buftmp
)-1] = '\0';
2717 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2718 SIZEOF_20KBUF
, buftmp
);
2720 format_env_puts(p2
, pc
);
2721 gf_puts(NEWLINE
, pc
);
2724 if((which
& FE_FROM
) && e
->from
)
2725 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2727 if((which
& FE_REPLYTO
) && e
->reply_to
2728 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2729 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2731 if((which
& FE_TO
) && e
->to
)
2732 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2734 if((which
& FE_CC
) && e
->cc
)
2735 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2737 if((which
& FE_BCC
) && e
->bcc
)
2738 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2740 if((which
& FE_RETURNPATH
) && e
->return_path
)
2741 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2744 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2746 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2747 if (!e
->ngpathexists
&& e
->message_id
&&
2748 strncmp (e
->message_id
,"<alpine.",8) &&
2749 strncmp (e
->message_id
,"<Pine.",6) &&
2750 strncmp (e
->message_id
,"<MS-C.",6) &&
2751 strncmp (e
->message_id
,"<MailManager.",13) &&
2752 strncmp (e
->message_id
,"<EasyMail.",11) &&
2753 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2756 q_status_message(SM_ORDER
, 0, 3,
2757 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2760 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2761 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2763 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2764 char *freeme
= NULL
;
2769 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2771 if(flags
& FM_DISPLAY
2772 && (ps_global
->display_keywords_in_subject
2773 || ps_global
->display_keywordinits_in_subject
)){
2775 /* don't bother if no keywords are defined */
2776 if(some_user_flags_defined(s
))
2777 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2778 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2779 NULL
, ps_global
->VAR_KW_BRACES
);
2782 format_env_puts(p2
, pc
);
2785 fs_give((void **) &freeme
);
2787 gf_puts(NEWLINE
, pc
);
2790 if((which
& FE_SENDER
) && e
->sender
2791 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2792 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2794 if((which
& FE_MESSAGEID
) && e
->message_id
){
2797 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2798 format_env_puts(p2
, pc
);
2799 gf_puts(NEWLINE
, pc
);
2802 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2803 q
= "In-Reply-To: ";
2805 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);
2806 format_env_puts(p2
, pc
);
2807 gf_puts(NEWLINE
, pc
);
2810 if((which
& FE_REFERENCES
) && e
->references
) {
2813 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2814 format_env_puts(p2
, pc
);
2815 gf_puts(NEWLINE
, pc
);
2823 * The argument fieldname is something like "Subject:..." or "Subject".
2824 * Look through the specs in speccolor for a match of the fieldname,
2825 * and return the color that goes with any match, or NULL.
2826 * Caller should free the color.
2829 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2831 SPEC_COLOR_S
*hc
= NULL
;
2832 COLOR_PAIR
*color_pair
= NULL
;
2833 char *colon
, *fname
;
2834 char fbuf
[FBUF_LEN
+1];
2838 colon
= strindex(fieldname
, ':');
2840 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2841 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2848 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2849 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2854 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2855 if(srchstr(value
, pat
->substring
))
2862 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2863 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2870 * The argument fieldname is something like "Subject:..." or "Subject".
2871 * Look through the specs in hdr_colors for a match of the fieldname,
2872 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2875 any_hdr_color(char *fieldname
)
2877 SPEC_COLOR_S
*hc
= NULL
;
2878 char *colon
, *fname
;
2879 char fbuf
[FBUF_LEN
+1];
2881 colon
= strindex(fieldname
, ':');
2883 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2884 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2891 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2892 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2899 /*----------------------------------------------------------------------
2900 Format an address field, wrapping lines nicely at commas
2902 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2903 addr -- ADDRESS structure to format
2905 Result: A formatted, malloced string is returned.
2906 ----------------------------------------------------------------------*/
2908 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2909 struct mail_address
*addr
, int flags
, char *oacs
, gf_o_t pc
)
2911 char *ptmp
, *mtmp
= NULL
;
2912 int trailing
= 0, group
= 0;
2919 * quickly run down address list to make sure none are patently bogus.
2920 * If so, just blat raw field out.
2922 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2923 if(atmp
->host
&& atmp
->host
[0] == '.'){
2924 char *field
, *fields
[2];
2927 fields
[0] = cpystr(field_name
);
2928 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2931 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2934 for(t
= h
= field
; *h
; t
++)
2935 if(*t
== '\015' && *(t
+1) == '\012'){
2936 *t
= '\0'; /* tie off line */
2937 format_env_puts(h
, pc
);
2938 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2939 gf_puts(NEWLINE
, pc
); /* more to write */
2943 else if(!*t
){ /* shouldn't happen much */
2945 format_env_puts(h
, pc
);
2950 fs_give((void **)&field
);
2953 fs_give((void **)&fields
[0]);
2954 gf_puts(NEWLINE
, pc
);
2955 dprint((2, "Error in \"%s\" field address\n",
2956 field_name
? field_name
: "?"));
2960 gf_puts(field_name
, pc
);
2963 atmp
= addr
->next
; /* remember what's next */
2965 if(!addr
->host
&& addr
->mailbox
){
2966 mtmp
= addr
->mailbox
;
2967 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2968 (unsigned char *)tmp_20k_buf
,
2969 SIZEOF_20KBUF
, addr
->mailbox
));
2972 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2973 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2974 tmp_20k_buf
[10000-1] = '\0';
2976 if(!trailing
) /* 1st pass, just address */
2978 else{ /* else comma, unless */
2979 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2980 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2983 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2991 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2993 addr
->personal
= ptmp
; /* restore old personal ptr */
2994 if(!addr
->host
&& addr
->mailbox
){
2995 fs_give((void **)&addr
->mailbox
);
2996 addr
->mailbox
= mtmp
;
3003 gf_puts(NEWLINE
, pc
);
3009 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3010 /* RFC822 continuation, must start with CRLF */
3011 #define RFC822CONT "\015\012 "
3013 /* Write RFC822 address with some quoting turned off.
3015 * address to interpret
3017 * (This is a copy of c-client's rfc822_write_address except
3018 * we don't quote double quote and dot in personal names. It writes
3019 * to a gf_io_t instead of to a buffer so that we don't have to worry
3020 * about fixed sized buffer overflowing. It's also special cased to deal
3021 * with only a single address.)
3023 * The idea is that there are some places where we'd just like to display
3024 * the personal name as is before applying confusing quoting. However,
3025 * we do want to be careful not to break things that should be quoted so
3026 * we'll only use this where we are sure. Quoting may look ugly but it
3027 * doesn't usually break anything.
3030 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_o_t pc
, int *group
)
3032 extern const char *rspecials
;
3034 if (adr
->host
) { /* ordinary address? */
3035 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3036 else { /* no, must use phrase <route-addr> form */
3038 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3040 gf_puts(" <", pc
); /* write address delimiter */
3041 pine_rfc822_address(adr
, pc
);
3042 gf_puts (">", pc
); /* closing delimiter */
3048 else if (adr
->mailbox
) { /* start of group? */
3049 /* yes, write group name */
3050 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3052 gf_puts (": ", pc
); /* write group identifier */
3053 *group
= 1; /* in a group */
3055 else if (*group
) { /* must be end of group (but be paranoid) */
3057 *group
= 0; /* no longer in that group */
3062 /* Write RFC822 route-address to string
3064 * address to interpret
3068 pine_rfc822_address(struct mail_address
*adr
, gf_o_t pc
)
3070 extern char *wspecials
;
3072 if (adr
&& adr
->host
) { /* no-op if no address */
3073 if (adr
->adl
) { /* have an A-D-L? */
3074 gf_puts (adr
->adl
, pc
);
3077 /* write mailbox name */
3078 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3079 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3080 gf_puts ("@", pc
); /* host delimiter */
3081 gf_puts (adr
->host
, pc
); /* write host name */
3087 /* Concatenate RFC822 string
3089 * pointer to string to concatenate
3090 * list of special characters
3094 pine_rfc822_cat(char *src
, const char *specials
, gf_o_t pc
)
3098 if (strpbrk (src
,specials
)) { /* any specials present? */
3099 gf_puts ("\"", pc
); /* opening quote */
3100 /* truly bizarre characters in there? */
3101 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3104 /* turn it into a null-terminated piece */
3108 gf_puts (src
, pc
); /* yes, output leader */
3110 gf_puts ("\\", pc
); /* quoting */
3111 gf_puts (save
, pc
); /* output the bizarre character */
3112 src
= ++s
; /* continue after the bizarre character */
3114 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3115 gf_puts ("\"", pc
); /* closing quote */
3117 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3121 /*----------------------------------------------------------------------
3122 Format an address field, wrapping lines nicely at commas
3124 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3125 newsgrps -- ADDRESS structure to format
3127 Result: A formatted, malloced string is returned.
3129 The resulting lines formatted are 80 columns wide.
3130 ----------------------------------------------------------------------*/
3132 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_o_t pc
)
3134 char buf
[MAILTMPLEN
];
3135 int trailing
= 0, llen
, alen
;
3138 if(!newsgrps
|| !*newsgrps
)
3141 gf_puts(field_name
, pc
);
3143 llen
= strlen(field_name
);
3145 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3146 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3147 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3152 if(!trailing
){ /* first time thru, just address */
3156 else{ /* else preceding comma */
3160 if(alen
+ llen
+ 1 > 76){
3161 gf_puts(NEWLINE
, pc
);
3171 if(alen
&& llen
> 76){ /* handle long addresses */
3172 register char *q
, *p
= &buf
[alen
-1];
3175 if(isspace((unsigned char)*p
)
3176 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3177 for(q
= buf
; q
< p
; q
++)
3178 (*pc
)(*q
); /* write character */
3180 gf_puts(NEWLINE
, pc
);
3189 if(p
== buf
) /* no reasonable break point */
3196 gf_puts(NEWLINE
, pc
);
3201 /*----------------------------------------------------------------------
3202 Format a text field that's part of some raw (non-envelope) message header
3208 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3210 ----------------------------------------------------------------------*/
3212 format_raw_hdr_string(char *start
, char *finish
, gf_o_t pc
, char *oacs
, int flags
)
3214 register char *current
;
3215 unsigned char *p
, *tmp
= NULL
, c
;
3223 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3225 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3228 len
= SIZEOF_20KBUF
;
3229 p
= (unsigned char *) tmp_20k_buf
;
3232 if(islower((unsigned char)(*start
)))
3233 *start
= toupper((unsigned char)(*start
));
3235 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3237 /* output from start to finish */
3238 while(*current
&& rv
== FHT_OK
)
3239 if(ISRFCEOL(current
)){
3240 if(!gf_puts(NEWLINE
, pc
))
3245 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3246 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3247 c
= (unsigned char) *current
++;
3248 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3249 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3252 else if(!(*pc
)(*current
++))
3256 fs_give((void **) &tmp
);
3266 /*----------------------------------------------------------------------
3267 Format a text field that's part of some raw (non-envelope) message header
3274 ----------------------------------------------------------------------*/
3276 format_env_puts(char *s
, gf_o_t pc
)
3278 if(ps_global
->pass_ctrl_chars
)
3279 return(gf_puts(s
, pc
));
3282 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3283 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3284 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3295 display_parameters(PARAMETER
*params
)
3300 PARMLIST_S
*parmlist
;
3302 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3303 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3304 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3307 tmp_20k_buf
[0] = '\0';
3308 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3309 n
= 0; /* n overloaded */
3310 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3312 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3313 tmp_20k_buf
[10000-1] = '\0';
3317 if(parmlist
->value
){
3318 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3319 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3323 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3328 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3329 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3331 tmp_20k_buf
[10000-1] = '\0';
3335 rfc2231_free_parmlist(&parmlist
);
3338 return(tmp_20k_buf
);
3342 /*----------------------------------------------------------------------
3343 Fetch the requested header fields from the msgno specified
3345 Args: stream -- mail stream of open folder
3346 msgno -- number of message to get header lines from
3347 fields -- array of pointers to desired fields
3349 Returns: allocated string containing matched header lines,
3353 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3356 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3357 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3360 * If the user misconfigures it is possible to have one of the fields
3361 * set to the empty string instead of a header name. We want to catch
3362 * that here instead of asking the server the nonsensical question.
3364 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3368 if(pp
&& *pp
){ /* found an empty header field, fix it */
3369 pflds
= copy_list_array(fields
);
3370 for(pp
= pflds
; pp
&& *pp
; pp
++){
3371 if(!**pp
){ /* scoot rest of the lines up */
3373 for(qq
= pp
; *qq
; qq
++)
3377 fs_give((void **) &free_this
);
3381 /* no headers to look for, return NULL */
3382 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3383 free_list_array(&pflds
);
3390 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3391 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3396 if(pflds
&& pflds
!= fields
)
3397 free_list_array(&pflds
);
3402 while(find_field(&h
, tmp
, sizeof(tmp
))){
3403 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3406 /* interesting field? */
3407 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3409 * Hold off allocating space for matching fields until
3410 * we at least find one to copy...
3413 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3415 while(*p
) /* copy field name */
3418 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3419 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3422 *m
= '\0'; /* tie off match string */
3424 else{ /* no match, pass this field */
3425 while(*h
&& !(*h
++ == '\n'
3426 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3431 if(pflds
&& pflds
!= fields
)
3432 free_list_array(&pflds
);
3434 return(match
? match
: cpystr(""));
3439 find_field(char **h
, char *tmp
, size_t ntmp
)
3443 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3446 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3454 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3458 embed_color(COLOR_PAIR
*cp
, gf_o_t pc
)
3461 if(_last_embedded_fg_color
)
3462 fs_give((void **)&_last_embedded_fg_color
);
3464 _last_embedded_fg_color
= cpystr(cp
->fg
);
3466 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3467 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3472 if(_last_embedded_bg_color
)
3473 fs_give((void **)&_last_embedded_bg_color
);
3475 _last_embedded_bg_color
= cpystr(cp
->bg
);
3477 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3478 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3487 get_cur_embedded_color(void)
3491 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3492 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3494 ret
= pico_get_cur_color();
3501 clear_cur_embedded_color(void)
3503 if(_last_embedded_fg_color
)
3504 fs_give((void **)&_last_embedded_fg_color
);
3506 if(_last_embedded_bg_color
)
3507 fs_give((void **)&_last_embedded_bg_color
);
3512 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3516 if(pico_usingcolor()){
3517 char *fg
= NULL
, *bg
= NULL
, *s
;
3519 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3520 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3521 ps_global
->VAR_NORM_FORE_COLOR
))
3522 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3524 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3525 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3526 ps_global
->VAR_NORM_BACK_COLOR
))
3527 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3533 * The blacks are just known good colors for
3534 * testing whether the other color is good.
3536 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3537 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3538 if(pico_is_good_colorpair(tmp
))
3539 for(s
= color_embed(fg
, bg
);
3540 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3544 free_color_pair(&tmp
);
3548 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3549 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3554 colorstring
[buflen
-1] = '\0';
3561 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3564 if(pico_usingcolor()){
3565 char *fg
= NULL
, *bg
= NULL
, *s
;
3566 char *basefg
= NULL
, *basebg
= NULL
;
3568 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3569 : ps_global
->VAR_NORM_FORE_COLOR
;
3570 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3571 : ps_global
->VAR_NORM_BACK_COLOR
;
3574 * We need to change the fg and bg colors back even if they
3575 * are the same as the Normal Colors so that color_a_quote
3576 * will have a chance to pick up those colors as the ones to
3577 * switch to. We don't do this before the handle above so that
3578 * the quote color will flow into the selectable item when
3579 * the selectable item color is partly the same as the
3580 * normal color. That is, suppose the normal color was black on
3581 * cyan and the selectable color was blue on cyan, only a fg color
3582 * change. We preserve the only-a-fg-color-change in a quote by
3583 * letting the quote background color flow into the selectable text.
3585 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3588 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3591 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3592 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3597 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3601 colorstring
[buflen
-1] = '\0';
3608 * Helper routine that is of limited use.
3609 * We need to tally up the screen width of
3610 * a UTF-8 string as we go through the string.
3611 * We just want the width of the character starting
3612 * at str (and no longer than remaining_octets).
3613 * If we're plopped into the middle of a UTF-8
3614 * character we just want to return width zero.
3617 width_at_this_position(unsigned char *str
, unsigned long n
)
3619 unsigned char *inputp
= str
;
3620 unsigned long remaining_octets
= n
;
3624 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3625 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3626 width
= wcellwidth(ucs
);
3627 /* Writechar will print a '?' */