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_io_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_io_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_io_t
);
112 void format_envelope(MAILSTREAM
*, long, char *, ENVELOPE
*,
113 gf_io_t
, long, char *, int);
114 int any_hdr_color(char *);
115 void format_addr_string(MAILSTREAM
*, long, char *, char *,
116 ADDRESS
*, int, char *, gf_io_t
);
117 void pine_rfc822_write_address_noquote(ADDRESS
*, gf_io_t
, int *);
118 void format_newsgroup_string(char *, char *, int, gf_io_t
);
119 int format_raw_hdr_string(char *, char *, gf_io_t
, char *, int);
120 int format_env_puts(char *, gf_io_t
);
121 int find_field(char **, char *, size_t);
122 int embed_color(COLOR_PAIR
*, gf_io_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_io_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_io_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_io_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_io_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_io_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_io_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_io_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_io_t final_pc
)
2314 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2318 gf_io_t tmp_pc
, tmp_gc
;
2319 struct variable
*vars
= ps_global
->vars
;
2321 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2322 gf_set_so_writec(&tmp_pc
, tmp_store
);
2327 fmt_env
= format_envelope
;
2329 if(ps_global
->full_header
== 2){
2330 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2334 * First, calculate how big a fields array we need.
2337 /* Custom header viewing list specified */
2338 if(hdrs
->type
== HD_LIST
){
2339 /* view all these headers */
2341 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2342 if(!is_an_env_hdr(q
))
2345 /* view all except these headers */
2347 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2351 nfields
--; /* subtract one for ALL_EXCEPT field */
2355 nfields
= 6; /* default view */
2357 /* allocate pointer space */
2359 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2360 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2363 if(hdrs
->type
== HD_LIST
){
2364 /* view all these headers */
2366 /* put the non-envelope headers in fields */
2368 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2369 if(!is_an_env_hdr(q
))
2372 /* view all except these headers */
2374 /* put the list of headers not to view in fields */
2376 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2377 if(strucmp(ALL_EXCEPT
, q
))
2385 fields
[i
= 0] = "Resent-Date";
2386 fields
[++i
] = "Resent-From";
2387 fields
[++i
] = "Resent-To";
2388 fields
[++i
] = "Resent-cc";
2389 fields
[++i
] = "Resent-Subject";
2395 /* custom view with exception list */
2396 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2398 * Go through each header in h and print it.
2399 * First we check to see if it is an envelope header so we
2400 * can print our envelope version of it instead of the raw version.
2403 /* fetch all the other headers */
2405 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2408 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2410 char tmp
[MAILTMPLEN
+1];
2413 colon_loc
= strindex(start
, ':');
2414 if(colon_loc
&& colon_loc
< finish
){
2415 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2416 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2421 if(colon_loc
&& is_an_env_hdr(tmp
)){
2422 char *dummystart
, *dummyfinish
;
2425 * Pretty format for env hdrs.
2426 * If the same header appears more than once, only
2427 * print the last to avoid duplicates.
2428 * They should have been combined in the env when parsed.
2430 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2432 format_env_hdr(stream
, msgno
, section
, env
,
2433 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2436 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2437 hdrs
->charset
, flags
)) != 0)
2444 /* custom view or default */
2446 /* fetch the non-envelope headers */
2448 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2450 /* default envelope for default view */
2451 if(hdrs
->type
== HD_BFIELD
)
2452 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2454 /* go through each header in list, v initialized above */
2455 for(; (q
= *v
) != NULL
; v
++){
2456 if(is_an_env_hdr(q
)){
2457 /* pretty format for env hdrs */
2458 format_env_hdr(stream
, msgno
, section
, env
,
2459 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2463 * Go through h finding all occurrences of this header
2464 * and all continuation lines, and output.
2467 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2469 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2470 hdrs
->charset
, flags
)) != 0)
2483 gf_clear_so_writec(tmp_store
);
2485 if(!rv
){ /* valid data? Do wrapping and filtering... */
2487 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2488 STORE_S
*df_store
= NULL
;
2490 so_seek(tmp_store
, 0L, 0);
2492 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2495 * Test for and act on any display filter
2496 * This barely makes sense. The display filter is going
2497 * to be getting UTF-8'ized headers here. In pre-alpine
2498 * pine the display filter was being fed already decoded
2499 * headers in whatever character set they were in.
2500 * The good news is that that didn't make much
2501 * sense either, so this shouldn't break anything.
2502 * It seems unlikely that anybody is doing anything useful
2503 * with the header part of display filters.
2505 if(ps_global
->tools
.display_filter
2506 && ps_global
->tools
.display_filter_trigger
2507 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2508 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2510 gf_set_so_writec(&tmp_pc
, df_store
);
2511 gf_set_so_readc(&tmp_gc
, df_store
);
2512 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2513 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2514 _("Formatting error: %s"), errstr
);
2518 so_seek(df_store
, 0L, 0);
2520 gf_clear_so_writec(df_store
);
2523 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2524 "No space for filtered text.");
2529 so_seek(tmp_store
, 0L, 0);
2530 gf_set_so_readc(&tmp_gc
, tmp_store
);
2534 int *margin
, wrapflags
= GFW_ONCOMMA
;
2537 gf_link_filter(gf_local_nvtnl
, NULL
);
2539 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2540 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2541 || F_ON(F_SCAN_ADDR
, ps_global
))
2543 gf_link_filter(gf_line_test
,
2544 gf_line_test_opt(url_hilite_hdr
,
2545 gf_url_hilite_opt(&uh
,handlesp
,1)));
2546 wrapflags
|= GFW_HANDLES
;
2549 if((flags
& FM_DISPLAY
)
2550 && !(flags
& FM_NOCOLOR
)
2551 && pico_usingcolor()
2552 && ((VAR_NORM_FORE_COLOR
2553 && VAR_HEADER_GENERAL_FORE_COLOR
2554 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2556 (VAR_NORM_BACK_COLOR
2557 && VAR_HEADER_GENERAL_BACK_COLOR
2558 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2559 wrapflags
|= GFW_HDRCOLOR
;
2561 if((flags
& FM_DISPLAY
)
2562 && !(flags
& FM_NOCOLOR
)
2563 && pico_usingcolor()
2564 && ps_global
->hdr_colors
){
2565 gf_link_filter(gf_line_test
,
2566 gf_line_test_opt(color_headers
, NULL
));
2567 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2570 if(prefix
&& *prefix
)
2571 column
= MAX(column
-strlen(prefix
), 50);
2573 margin
= format_view_margin();
2575 if(!(flags
& FM_NOWRAP
))
2576 gf_link_filter(gf_wrap
,
2577 gf_wrap_filter_opt(column
, column
,
2578 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2581 if(prefix
&& *prefix
)
2582 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2584 if((flags
& FM_DISPLAY
)
2585 && !(flags
& FM_NOCOLOR
)
2586 && pico_usingcolor()
2587 && ((VAR_NORM_FORE_COLOR
2588 && VAR_HEADER_GENERAL_FORE_COLOR
2589 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2591 (VAR_NORM_BACK_COLOR
2592 && VAR_HEADER_GENERAL_BACK_COLOR
2593 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2597 right_margin
= margin
? margin
[1] : 0;
2598 total_wid
= column
- right_margin
;
2600 gf_link_filter(gf_line_test
,
2601 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2602 wrapflags
|= GFW_HANDLES
;
2605 gf_link_filter(gf_nvtnl_local
, NULL
);
2607 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2609 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2610 "Can't build header : %.200s", errstr
);
2615 gf_clear_so_readc(df_store
);
2619 gf_clear_so_readc(tmp_store
);
2622 so_give(&tmp_store
);
2625 fs_give((void **)&h
);
2628 fs_give((void **)&fields
);
2634 /*----------------------------------------------------------------------
2635 Format RAW header text for display
2637 Args: stream -- mail stream for various header text fetches
2638 rawno -- sequence number in stream of message we're interested in
2639 section -- which section of message
2640 pc -- function to write header text with
2642 Result: 0 if all's well, -1 if write error, 1 if fetch error
2644 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2645 in the local convention.
2649 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2651 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2658 if(!gf_puts(NEWLINE
, pc
))
2661 if(ISRFCEOL(h
)) /* all done! */
2664 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2665 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2666 c
= (unsigned char) *h
++;
2667 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2668 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2671 else if(!(*pc
)(*h
++))
2676 return(FHT_FTCHERR
);
2683 /*----------------------------------------------------------------------
2684 Format c-client envelope data suitable for display
2686 Args: s -- mail stream for various header text fetches
2687 n -- raw sequence number in stream of message we're interested in
2688 sect -- which section of message
2689 e -- pointer to msg's envelope
2690 pc -- function to write header text with
2691 which -- which header lines to write
2693 flags -- FM_ flags, see pith/mailview.h
2695 Result: 0 if all's well, -1 if write error, 1 if fetch error
2697 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2698 in the local convention.
2702 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2703 long int which
, char *oacs
, int flags
)
2705 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2710 if((which
& FE_DATE
) && e
->date
) {
2712 snprintf(buftmp
, sizeof(buftmp
), "%s",
2713 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2714 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2715 buftmp
[sizeof(buftmp
)-1] = '\0';
2716 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2717 SIZEOF_20KBUF
, buftmp
);
2719 format_env_puts(p2
, pc
);
2720 gf_puts(NEWLINE
, pc
);
2723 if((which
& FE_FROM
) && e
->from
)
2724 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2726 if((which
& FE_REPLYTO
) && e
->reply_to
2727 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2728 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2730 if((which
& FE_TO
) && e
->to
)
2731 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2733 if((which
& FE_CC
) && e
->cc
)
2734 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2736 if((which
& FE_BCC
) && e
->bcc
)
2737 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2739 if((which
& FE_RETURNPATH
) && e
->return_path
)
2740 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2743 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2745 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2746 if (!e
->ngpathexists
&& e
->message_id
&&
2747 strncmp (e
->message_id
,"<alpine.",8) &&
2748 strncmp (e
->message_id
,"<Pine.",6) &&
2749 strncmp (e
->message_id
,"<MS-C.",6) &&
2750 strncmp (e
->message_id
,"<MailManager.",13) &&
2751 strncmp (e
->message_id
,"<EasyMail.",11) &&
2752 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2755 q_status_message(SM_ORDER
, 0, 3,
2756 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2759 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2760 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2762 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2763 char *freeme
= NULL
;
2768 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2770 if(flags
& FM_DISPLAY
2771 && (ps_global
->display_keywords_in_subject
2772 || ps_global
->display_keywordinits_in_subject
)){
2774 /* don't bother if no keywords are defined */
2775 if(some_user_flags_defined(s
))
2776 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2777 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2778 NULL
, ps_global
->VAR_KW_BRACES
);
2781 format_env_puts(p2
, pc
);
2784 fs_give((void **) &freeme
);
2786 gf_puts(NEWLINE
, pc
);
2789 if((which
& FE_SENDER
) && e
->sender
2790 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2791 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2793 if((which
& FE_MESSAGEID
) && e
->message_id
){
2796 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2797 format_env_puts(p2
, pc
);
2798 gf_puts(NEWLINE
, pc
);
2801 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2802 q
= "In-Reply-To: ";
2804 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);
2805 format_env_puts(p2
, pc
);
2806 gf_puts(NEWLINE
, pc
);
2809 if((which
& FE_REFERENCES
) && e
->references
) {
2812 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2813 format_env_puts(p2
, pc
);
2814 gf_puts(NEWLINE
, pc
);
2822 * The argument fieldname is something like "Subject:..." or "Subject".
2823 * Look through the specs in speccolor for a match of the fieldname,
2824 * and return the color that goes with any match, or NULL.
2825 * Caller should free the color.
2828 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2830 SPEC_COLOR_S
*hc
= NULL
;
2831 COLOR_PAIR
*color_pair
= NULL
;
2832 char *colon
, *fname
;
2833 char fbuf
[FBUF_LEN
+1];
2837 colon
= strindex(fieldname
, ':');
2839 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2840 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2847 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2848 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2853 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2854 if(srchstr(value
, pat
->substring
))
2861 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2862 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2869 * The argument fieldname is something like "Subject:..." or "Subject".
2870 * Look through the specs in hdr_colors for a match of the fieldname,
2871 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2874 any_hdr_color(char *fieldname
)
2876 SPEC_COLOR_S
*hc
= NULL
;
2877 char *colon
, *fname
;
2878 char fbuf
[FBUF_LEN
+1];
2880 colon
= strindex(fieldname
, ':');
2882 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2883 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2890 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2891 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2898 /*----------------------------------------------------------------------
2899 Format an address field, wrapping lines nicely at commas
2901 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2902 addr -- ADDRESS structure to format
2904 Result: A formatted, malloced string is returned.
2905 ----------------------------------------------------------------------*/
2907 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2908 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2910 char *ptmp
, *mtmp
= NULL
;
2911 int trailing
= 0, group
= 0;
2918 * quickly run down address list to make sure none are patently bogus.
2919 * If so, just blat raw field out.
2921 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2922 if(atmp
->host
&& atmp
->host
[0] == '.'){
2923 char *field
, *fields
[2];
2926 fields
[0] = cpystr(field_name
);
2927 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2930 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2933 for(t
= h
= field
; *h
; t
++)
2934 if(*t
== '\015' && *(t
+1) == '\012'){
2935 *t
= '\0'; /* tie off line */
2936 format_env_puts(h
, pc
);
2937 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2938 gf_puts(NEWLINE
, pc
); /* more to write */
2942 else if(!*t
){ /* shouldn't happen much */
2944 format_env_puts(h
, pc
);
2949 fs_give((void **)&field
);
2952 fs_give((void **)&fields
[0]);
2953 gf_puts(NEWLINE
, pc
);
2954 dprint((2, "Error in \"%s\" field address\n",
2955 field_name
? field_name
: "?"));
2959 gf_puts(field_name
, pc
);
2962 atmp
= addr
->next
; /* remember what's next */
2964 if(!addr
->host
&& addr
->mailbox
){
2965 mtmp
= addr
->mailbox
;
2966 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2967 (unsigned char *)tmp_20k_buf
,
2968 SIZEOF_20KBUF
, addr
->mailbox
));
2971 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2972 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2973 tmp_20k_buf
[10000-1] = '\0';
2975 if(!trailing
) /* 1st pass, just address */
2977 else{ /* else comma, unless */
2978 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2979 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2982 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2990 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2992 addr
->personal
= ptmp
; /* restore old personal ptr */
2993 if(!addr
->host
&& addr
->mailbox
){
2994 fs_give((void **)&addr
->mailbox
);
2995 addr
->mailbox
= mtmp
;
3002 gf_puts(NEWLINE
, pc
);
3008 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3009 /* RFC822 continuation, must start with CRLF */
3010 #define RFC822CONT "\015\012 "
3012 /* Write RFC822 address with some quoting turned off.
3014 * address to interpret
3016 * (This is a copy of c-client's rfc822_write_address except
3017 * we don't quote double quote and dot in personal names. It writes
3018 * to a gf_io_t instead of to a buffer so that we don't have to worry
3019 * about fixed sized buffer overflowing. It's also special cased to deal
3020 * with only a single address.)
3022 * The idea is that there are some places where we'd just like to display
3023 * the personal name as is before applying confusing quoting. However,
3024 * we do want to be careful not to break things that should be quoted so
3025 * we'll only use this where we are sure. Quoting may look ugly but it
3026 * doesn't usually break anything.
3029 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3031 extern const char *rspecials
;
3033 if (adr
->host
) { /* ordinary address? */
3034 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3035 else { /* no, must use phrase <route-addr> form */
3037 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3039 gf_puts(" <", pc
); /* write address delimiter */
3040 pine_rfc822_address(adr
, pc
);
3041 gf_puts (">", pc
); /* closing delimiter */
3047 else if (adr
->mailbox
) { /* start of group? */
3048 /* yes, write group name */
3049 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3051 gf_puts (": ", pc
); /* write group identifier */
3052 *group
= 1; /* in a group */
3054 else if (*group
) { /* must be end of group (but be paranoid) */
3056 *group
= 0; /* no longer in that group */
3061 /* Write RFC822 route-address to string
3063 * address to interpret
3067 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3069 extern char *wspecials
;
3071 if (adr
&& adr
->host
) { /* no-op if no address */
3072 if (adr
->adl
) { /* have an A-D-L? */
3073 gf_puts (adr
->adl
, pc
);
3076 /* write mailbox name */
3077 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3078 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3079 gf_puts ("@", pc
); /* host delimiter */
3080 gf_puts (adr
->host
, pc
); /* write host name */
3086 /* Concatenate RFC822 string
3088 * pointer to string to concatenate
3089 * list of special characters
3093 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3097 if (strpbrk (src
,specials
)) { /* any specials present? */
3098 gf_puts ("\"", pc
); /* opening quote */
3099 /* truly bizarre characters in there? */
3100 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3103 /* turn it into a null-terminated piece */
3107 gf_puts (src
, pc
); /* yes, output leader */
3109 gf_puts ("\\", pc
); /* quoting */
3110 gf_puts (save
, pc
); /* output the bizarre character */
3111 src
= ++s
; /* continue after the bizarre character */
3113 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3114 gf_puts ("\"", pc
); /* closing quote */
3116 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3120 /*----------------------------------------------------------------------
3121 Format an address field, wrapping lines nicely at commas
3123 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3124 newsgrps -- ADDRESS structure to format
3126 Result: A formatted, malloced string is returned.
3128 The resulting lines formatted are 80 columns wide.
3129 ----------------------------------------------------------------------*/
3131 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3133 char buf
[MAILTMPLEN
];
3134 int trailing
= 0, llen
, alen
;
3137 if(!newsgrps
|| !*newsgrps
)
3140 gf_puts(field_name
, pc
);
3142 llen
= strlen(field_name
);
3144 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3145 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3146 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3151 if(!trailing
){ /* first time thru, just address */
3155 else{ /* else preceding comma */
3159 if(alen
+ llen
+ 1 > 76){
3160 gf_puts(NEWLINE
, pc
);
3170 if(alen
&& llen
> 76){ /* handle long addresses */
3171 register char *q
, *p
= &buf
[alen
-1];
3174 if(isspace((unsigned char)*p
)
3175 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3176 for(q
= buf
; q
< p
; q
++)
3177 (*pc
)(*q
); /* write character */
3179 gf_puts(NEWLINE
, pc
);
3188 if(p
== buf
) /* no reasonable break point */
3195 gf_puts(NEWLINE
, pc
);
3200 /*----------------------------------------------------------------------
3201 Format a text field that's part of some raw (non-envelope) message header
3207 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3209 ----------------------------------------------------------------------*/
3211 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3213 register char *current
;
3214 unsigned char *p
, *tmp
= NULL
, c
;
3222 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3224 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3227 len
= SIZEOF_20KBUF
;
3228 p
= (unsigned char *) tmp_20k_buf
;
3231 if(islower((unsigned char)(*start
)))
3232 *start
= toupper((unsigned char)(*start
));
3234 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3236 /* output from start to finish */
3237 while(*current
&& rv
== FHT_OK
)
3238 if(ISRFCEOL(current
)){
3239 if(!gf_puts(NEWLINE
, pc
))
3244 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3245 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3246 c
= (unsigned char) *current
++;
3247 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3248 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3251 else if(!(*pc
)(*current
++))
3255 fs_give((void **) &tmp
);
3265 /*----------------------------------------------------------------------
3266 Format a text field that's part of some raw (non-envelope) message header
3273 ----------------------------------------------------------------------*/
3275 format_env_puts(char *s
, gf_io_t pc
)
3277 if(ps_global
->pass_ctrl_chars
)
3278 return(gf_puts(s
, pc
));
3281 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3282 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3283 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3294 display_parameters(PARAMETER
*params
)
3299 PARMLIST_S
*parmlist
;
3301 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3302 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3303 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3306 tmp_20k_buf
[0] = '\0';
3307 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3308 n
= 0; /* n overloaded */
3309 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3311 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3312 tmp_20k_buf
[10000-1] = '\0';
3316 if(parmlist
->value
){
3317 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3318 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3322 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3327 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3328 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3330 tmp_20k_buf
[10000-1] = '\0';
3334 rfc2231_free_parmlist(&parmlist
);
3337 return(tmp_20k_buf
);
3341 /*----------------------------------------------------------------------
3342 Fetch the requested header fields from the msgno specified
3344 Args: stream -- mail stream of open folder
3345 msgno -- number of message to get header lines from
3346 fields -- array of pointers to desired fields
3348 Returns: allocated string containing matched header lines,
3352 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3355 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3356 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3359 * If the user misconfigures it is possible to have one of the fields
3360 * set to the empty string instead of a header name. We want to catch
3361 * that here instead of asking the server the nonsensical question.
3363 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3367 if(pp
&& *pp
){ /* found an empty header field, fix it */
3368 pflds
= copy_list_array(fields
);
3369 for(pp
= pflds
; pp
&& *pp
; pp
++){
3370 if(!**pp
){ /* scoot rest of the lines up */
3372 for(qq
= pp
; *qq
; qq
++)
3376 fs_give((void **) &free_this
);
3380 /* no headers to look for, return NULL */
3381 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3382 free_list_array(&pflds
);
3389 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3390 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3395 if(pflds
&& pflds
!= fields
)
3396 free_list_array(&pflds
);
3401 while(find_field(&h
, tmp
, sizeof(tmp
))){
3402 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3405 /* interesting field? */
3406 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3408 * Hold off allocating space for matching fields until
3409 * we at least find one to copy...
3412 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3414 while(*p
) /* copy field name */
3417 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3418 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3421 *m
= '\0'; /* tie off match string */
3423 else{ /* no match, pass this field */
3424 while(*h
&& !(*h
++ == '\n'
3425 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3430 if(pflds
&& pflds
!= fields
)
3431 free_list_array(&pflds
);
3433 return(match
? match
: cpystr(""));
3438 find_field(char **h
, char *tmp
, size_t ntmp
)
3442 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3445 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3453 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3457 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3460 if(_last_embedded_fg_color
)
3461 fs_give((void **)&_last_embedded_fg_color
);
3463 _last_embedded_fg_color
= cpystr(cp
->fg
);
3465 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3466 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3471 if(_last_embedded_bg_color
)
3472 fs_give((void **)&_last_embedded_bg_color
);
3474 _last_embedded_bg_color
= cpystr(cp
->bg
);
3476 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3477 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3486 get_cur_embedded_color(void)
3490 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3491 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3493 ret
= pico_get_cur_color();
3500 clear_cur_embedded_color(void)
3502 if(_last_embedded_fg_color
)
3503 fs_give((void **)&_last_embedded_fg_color
);
3505 if(_last_embedded_bg_color
)
3506 fs_give((void **)&_last_embedded_bg_color
);
3511 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3515 if(pico_usingcolor()){
3516 char *fg
= NULL
, *bg
= NULL
, *s
;
3518 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3519 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3520 ps_global
->VAR_NORM_FORE_COLOR
))
3521 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3523 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3524 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3525 ps_global
->VAR_NORM_BACK_COLOR
))
3526 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3532 * The blacks are just known good colors for
3533 * testing whether the other color is good.
3535 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3536 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3537 if(pico_is_good_colorpair(tmp
))
3538 for(s
= color_embed(fg
, bg
);
3539 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3543 free_color_pair(&tmp
);
3547 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3548 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3553 colorstring
[buflen
-1] = '\0';
3560 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3563 if(pico_usingcolor()){
3564 char *fg
= NULL
, *bg
= NULL
, *s
;
3565 char *basefg
= NULL
, *basebg
= NULL
;
3567 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3568 : ps_global
->VAR_NORM_FORE_COLOR
;
3569 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3570 : ps_global
->VAR_NORM_BACK_COLOR
;
3573 * We need to change the fg and bg colors back even if they
3574 * are the same as the Normal Colors so that color_a_quote
3575 * will have a chance to pick up those colors as the ones to
3576 * switch to. We don't do this before the handle above so that
3577 * the quote color will flow into the selectable item when
3578 * the selectable item color is partly the same as the
3579 * normal color. That is, suppose the normal color was black on
3580 * cyan and the selectable color was blue on cyan, only a fg color
3581 * change. We preserve the only-a-fg-color-change in a quote by
3582 * letting the quote background color flow into the selectable text.
3584 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3587 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3590 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3591 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3596 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3600 colorstring
[buflen
-1] = '\0';
3607 * Helper routine that is of limited use.
3608 * We need to tally up the screen width of
3609 * a UTF-8 string as we go through the string.
3610 * We just want the width of the character starting
3611 * at str (and no longer than remaining_octets).
3612 * If we're plopped into the middle of a UTF-8
3613 * character we just want to return width zero.
3616 width_at_this_position(unsigned char *str
, unsigned long n
)
3618 unsigned char *inputp
= str
;
3619 unsigned long remaining_octets
= n
;
3623 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3624 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3625 width
= wcellwidth(ucs
);
3626 /* Writechar will print a '?' */