1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2009 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
22 Implements message data gathering and formatting
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
71 static struct envelope_s
{
76 {"sender", FE_SENDER
},
81 {"newsgroups", FE_NEWSGROUPS
},
82 {"subject", FE_SUBJECT
},
83 {"message-id", FE_MESSAGEID
},
84 {"reply-to", FE_REPLYTO
},
85 {"followup-to", FE_FOLLOWUPTO
},
86 {"in-reply-to", FE_INREPLYTO
},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES
},
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial
)(long, HANDLE_S
**, int, int, gf_io_t
);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM
*, long, char *, ENVELOPE
*,
108 fmt_env_t
, gf_io_t
, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S
**, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S
**, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S
**, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM
*, long, char *, gf_io_t
);
116 void format_envelope(MAILSTREAM
*, long, char *, ENVELOPE
*,
117 gf_io_t
, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM
*, long, char *, char *,
120 ADDRESS
*, int, char *, gf_io_t
);
121 void pine_rfc822_write_address_noquote(ADDRESS
*, gf_io_t
, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t
);
123 int format_raw_hdr_string(char *, char *, gf_io_t
, char *, int);
124 int format_env_puts(char *, gf_io_t
);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR
*, gf_io_t
);
127 COLOR_PAIR
*get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S
*, ATTACH_S
*, HANDLE_S
**, int, int, gf_io_t
, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
154 format_message(long int msgno
, ENVELOPE
*env
, struct mail_bodystruct
*body
,
155 HANDLE_S
**handlesp
, int flgs
, gf_io_t pc
)
157 char *decode_err
= NULL
;
161 clear_cur_embedded_color();
163 if(!(flgs
& FM_DISPLAY
))
166 width
= (flgs
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs
& FM_NOEDITORIAL
)){
170 if(ps_global
->full_header
== 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
173 q_status_message(SM_INFO
, 0, 3, _("All quoted text being included"));
174 else if(ps_global
->full_header
== 2)
175 q_status_message(SM_INFO
, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h
, ps_global
->VAR_VIEW_HEADERS
, ps_global
->view_all_except
, FE_DEFAULT
);
182 switch(format_header(ps_global
->mail_stream
, msgno
, NULL
,
183 env
, &h
, NULL
, handlesp
, flgs
, NULL
, pc
)){
185 case -1 : /* write error */
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc
)
190 && !gf_puts(NEWLINE
, pc
)))
197 || (ps_global
->full_header
== 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
)))){
199 format_attachment_list(msgno
, body
, handlesp
, flgs
, width
, pc
);
200 format_calendar(msgno
, body
, handlesp
, flgs
, width
, pc
);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE
, pc
)
205 && (decode_err
= format_body(msgno
, body
, handlesp
, &h
, flgs
, width
, pc
)) == NULL
)
211 if(!(flgs
& FM_DISPLAY
))
212 q_status_message1(SM_ORDER
, 3, 4, _("Error writing message: %s"),
213 decode_err
? decode_err
: error_description(errno
));
219 format_calendar_vevent(VCALENDAR_S
*vcal
, ATTACH_S
*a
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
, int cflags
)
221 int avail
, m1
, m2
, hwid
, i
, partwid
, padwid
;
225 VEVENT_SUMMARY_S
*vesy
; /* vevent summary */
227 vesy
= ical_vevent_summary(vcal
);
229 if(vesy
== NULL
) return;
231 if((cflags
& FC_SUMMARY
) && (cflags
& FC_FULL
))
235 margin
= (cflags
& FC_FULL
) ? NULL
236 : (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
238 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
241 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
244 hwid
= MAX(avail
, 0);
247 if(ps_global
->atmts
[1].description
== NULL
){
248 // && (!ps_global->atmts[0].body
249 // || (ps_global->atmts[0].body->type == TYPETEXT))){
250 avail
= width
- m1
-2;
252 dwid
= MAX(MIN(40, avail
), 0);
254 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
255 repeat_char(dwid
, '-'));
257 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
262 if(cflags
& FC_SUMMARY
){
263 i
= utf8_width(_("Calendar Entry:"));
264 partwid
= MIN(i
, hwid
);
266 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
267 if(!gf_puts(tmp_20k_buf
, pc
))
271 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%-*.*w%*.*s",
272 partwid
, partwid
, _("Calendar Entry:"),
275 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
282 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
283 if(!gf_puts(tmp_20k_buf
, pc
))
287 avail
= width
- m1
- m2
;
289 s1
= MAX(MIN(1, avail
), 0);
292 dwid
= MAX(MIN(1, avail
), 0);
295 s2
= MAX(MIN(1, avail
), 0);
299 if(cflags
& FC_SUMMARY
)
300 utf8_snprintf(padding
, sizeof(padding
), "%*.*s%*.*w%*.*s",
301 s1
, s1
, "", dwid
, dwid
, "", s2
, s2
, "");
306 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
307 padding
, _("This event was cancelled!"));
308 if((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
)){
309 gf_puts(tmp_20k_buf
, pc
);
310 gf_puts(NEWLINE
, pc
);
317 if(vesy
->sender
!= NULL
){
318 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
319 padding
, _("Sent-by: "), vesy
->sender
);
320 gf_puts(tmp_20k_buf
, pc
);
321 gf_puts(NEWLINE
, pc
);
324 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
325 padding
, _("Organizer: "), vesy
->organizer
);
326 gf_puts(tmp_20k_buf
, pc
);
327 gf_puts(NEWLINE
, pc
);
328 } /* end of if(organizer) */
331 ical_remove_escapes(&vesy
->location
);
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 if(cflags
& FC_SUMMARY
){
382 COLOR_PAIR
*lastc
= NULL
;
385 COLOR_PAIR
*hdrcolor
= NULL
;
387 if((flgs
& FM_DISPLAY
)
388 && !(flgs
& FM_NOCOLOR
)
390 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
391 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
392 && ps_global
->VAR_NORM_FORE_COLOR
393 && ps_global
->VAR_NORM_BACK_COLOR
394 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
395 ps_global
->VAR_NORM_FORE_COLOR
)
396 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
397 ps_global
->VAR_NORM_BACK_COLOR
))){
399 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
400 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
401 if(!pico_is_good_colorpair(hdrcolor
))
402 free_color_pair(&hdrcolor
);
406 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
409 gf_puts(padding
, pc
);
410 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
413 char buf
[16], color
[64];
417 h
= new_handle(handlesp
);
421 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
422 buf
[sizeof(buf
)-1] = '\0';
424 if(!(flgs
& FM_NOCOLOR
)
425 && handle_start_color(color
, sizeof(color
), &l
, 1)){
426 lastc
= get_cur_embedded_color();
427 if(!gf_nputs(color
, (long) l
, pc
))
430 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
431 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
434 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
435 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
438 tmp_20k_buf
[0] = '\0';
440 if(!format_env_puts(tmp_20k_buf
, pc
))
445 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
446 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
450 if(!embed_color(lastc
, pc
))
453 free_color_pair(&lastc
);
455 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
458 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
463 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
464 if(!gf_puts(tmp_20k_buf
, pc
))
468 if(!gf_puts(NEWLINE
, pc
))
471 } /* end of ATTENDEES */
473 free_vevent_summary(&vesy
);
475 avail
= width
- m1
-2;
477 dwid
= MAX(MIN(40, avail
), 0);
480 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
481 repeat_char(dwid
, '-'));
483 gf_puts(tmp_20k_buf
, pc
);
487 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
489 char *rawtext
, *caltext
;
490 unsigned long callen
;
491 VCALENDAR_S
*vcal
= NULL
;
495 if(flgs
& FM_NEW_MESS
) {
496 zero_atmts(ps_global
->atmts
);
497 describe_mime(body
, "", 1, 1, 0, flgs
);
500 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
501 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
502 b
= mail_body (ps_global
->mail_stream
, msgno
, a
->number
);
504 gf_puts(_("Error fetching calendar body part"), pc
);
505 gf_puts(NEWLINE
, pc
);
508 if(b
->sparep
== NULL
){
509 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
510 if(rawtext
== NULL
|| *rawtext
== '\0'){
511 gf_puts(_("Error fetching calendar text"), pc
);
512 gf_puts(NEWLINE
, pc
);
515 rawtext
[callen
] = '\0'; /* chop off cookie */
518 caltext
= rfc822_base64(rawtext
, strlen(rawtext
), &callen
);
520 gf_puts(_("Error in calendar base64 encoding"), pc
);
521 gf_puts(NEWLINE
, pc
);
526 case ENCQUOTEDPRINTABLE
:
527 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
529 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
530 gf_puts(NEWLINE
, pc
);
535 default: caltext
= cpystr(rawtext
);
538 vcal
= ical_parse_text(caltext
);
539 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
540 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
541 fs_give((void **) &caltext
);
544 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
545 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
546 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
547 if(vcal
->comp
[VEvent
] != NULL
){
548 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
549 } /* else another type of entry in the calendar */
551 gf_puts(NEWLINE
, pc
);
559 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
561 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
562 int is_in_sig
= OUT_SIG_BLOCK
;
563 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
569 || (ps_global
->full_header
== 2
570 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
572 /*--- Server is not an IMAP2bis, It can't parse MIME
573 so we just show the text here. Hopefully the
574 message isn't a MIME message
578 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
579 msgno
, NULL
, NULL
, NIL
)) != NULL
){
581 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
582 return("Write Error");
584 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
588 * We need to translate the message
589 * into UTF-8, but that's trouble in the full header case
590 * because we don't know what to translate from. We'll just
591 * take a guess by looking for the first text part and
594 if(body
&& body
->type
== TYPETEXT
)
595 charset
= parameter_val(body
->parameter
, "charset");
596 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
597 && body
->nested
.part
->body
.type
== TYPETEXT
)
598 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
600 charset
= ps_global
->display_charmap
;
602 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
603 /* transliterate message text to UTF-8 */
604 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
607 /* link in filters, similar to what is done in decode_text() */
608 if(!ps_global
->pass_ctrl_chars
){
609 gf_link_filter(gf_escape_filter
, NULL
);
611 gf_link_filter(gf_control_filter
,
612 gf_control_filter_opt(&filt_only_c0
));
615 gf_link_filter(gf_tag_filter
, NULL
);
617 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
618 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
619 || F_ON(F_SCAN_ADDR
, ps_global
))
621 gf_link_filter(gf_line_test
,
622 gf_line_test_opt(url_hilite
,
623 gf_url_hilite_opt(&uh
,handlesp
,0)));
626 if((flgs
& FM_DISPLAY
)
627 && !(flgs
& FM_NOCOLOR
)
629 && ps_global
->VAR_SIGNATURE_FORE_COLOR
630 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
631 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
634 if((flgs
& FM_DISPLAY
)
635 && !(flgs
& FM_NOCOLOR
)
637 && ps_global
->VAR_QUOTE1_FORE_COLOR
638 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
639 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
642 if(!(flgs
& FM_NOWRAP
)){
643 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
645 && !(flgs
& FM_NOCOLOR
)
646 && pico_usingcolor())
647 wrapflags
|= GFW_USECOLOR
;
648 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
650 ? NULL
: format_view_margin(),
655 gf_link_filter(gf_nvtnl_local
, NULL
);
656 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
657 /* TRANSLATORS: There was an error putting together a message for
658 viewing. The arg is the description of the error. */
659 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
660 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
661 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
662 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
663 && gf_puts(NEWLINE
, pc
))
671 if(!gf_puts(NEWLINE
, pc
)
672 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
673 || !gf_puts(NEWLINE
, pc
)
674 || !gf_puts(NEWLINE
, pc
))
675 return("Write Error");
681 /*======== Now loop through formatting all the parts =======*/
682 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
683 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
686 if(a
->body
->type
== TYPEMULTIPART
){
688 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
690 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
691 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
692 return("Write Error");
700 if(a
->suppress_editorial
)
703 if(!(flgs
& FM_NOEDITORIAL
)
704 && (!gf_puts(NEWLINE
, pc
)
705 || (decode_err
= part_desc(a
->number
, a
->body
,
707 ? (a
->can_display
!= MCD_NONE
)
709 : 3, width
, flgs
, pc
))))
710 return("Write Error");
715 switch(a
->body
->type
){
719 * If a message is multipart *and* the first part of it
720 * is text *and that text is empty, there is a good chance that
721 * there was actually something there that c-client was
722 * unable to parse. Here we report the empty message body
723 * and insert the raw RFC822.TEXT (if full-headers are
726 if(body
->type
== TYPEMULTIPART
727 && a
== ps_global
->atmts
728 && a
->body
->size
.bytes
== 0
729 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
732 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
733 "Empty or malformed message%s.",
734 ps_global
->full_header
== 2
735 ? ". Displaying raw text"
736 : ". Use \"H\" to see raw text");
737 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
739 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
740 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
741 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
742 return("Write Error");
744 if(ps_global
->full_header
== 2
745 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
746 a
->number
, pc
, flgs
))){
747 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
748 "%s%s [ Formatting error: %s ]%s%s",
749 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
750 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
751 if(!gf_puts(tmp_20k_buf
, pc
))
752 return("Write Error");
759 * Don't write our delimiter if this text part is
760 * the first part of a message/rfc822 segment...
762 if(show_parts
&& a
!= ps_global
->atmts
763 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
765 || (a
[-1].body
->type
== TYPEMULTIPART
766 && a
[-1].body
->subtype
767 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
768 && &a
[-1] != ps_global
->atmts
769 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
772 && !(flgs
& FM_NOEDITORIAL
)){
773 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
774 tmp1
= "Calendar entry";
776 tmp1
= a
->body
->description
? a
->body
->description
778 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
779 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
781 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
783 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
784 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
785 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
786 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
787 return("Write Error");
790 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
791 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
792 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
797 tmp1
= a
->body
->description
? a
->body
->description
798 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
800 : "Included Message";
801 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
802 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
804 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
806 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
808 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
809 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
810 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
811 return("Write Error");
813 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
814 /* imapenvonly, we may not have all the headers we need */
815 if(a
->body
->nested
.msg
->env
->imapenvonly
)
816 mail_fetch_header(ps_global
->mail_stream
, msgno
,
817 a
->number
, NULL
, NULL
, FT_PEEK
);
818 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
819 a
->body
->nested
.msg
->env
, hp
,
820 NULL
, handlesp
, flgs
, NULL
, pc
)){
821 case -1 : /* write error */
822 return("Write Error");
824 case 1 : /* fetch error */
825 if(!(gf_puts("[ Error fetching header ]", pc
)
826 && !gf_puts(NEWLINE
, pc
)))
827 return("Write Error");
832 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
833 int *margin
, avail
, m1
, m2
;
836 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
838 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
841 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
844 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
845 || !gf_puts(NEWLINE
, pc
)
846 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
847 return("Write Error");
850 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
851 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
854 if(!gf_puts(NEWLINE
, pc
))
855 return("Write Error");
860 if((decode_err
= part_desc(a
->number
, a
->body
,
861 (flgs
& FM_DISPLAY
) ? 1 : 3,
862 width
, flgs
, pc
)) != NULL
)
863 return("Write Error");
870 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
871 && format_blip_seen(msgno
)))
872 return("Cannot format body.");
880 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
884 if(flgs
& FM_NEW_MESS
) {
885 zero_atmts(ps_global
->atmts
);
886 describe_mime(body
, "", 1, 1, 0, flgs
);
889 /*----- First do the list of parts/attachments if needed ----*/
890 if((flgs
& FM_DISPLAY
)
891 && (ps_global
->atmts
[1].description
892 || (ps_global
->atmts
[0].body
893 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
894 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
895 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
896 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
897 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
898 COLOR_PAIR
*hdrcolor
= NULL
;
900 if((flgs
& FM_DISPLAY
)
901 && !(flgs
& FM_NOCOLOR
)
903 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
904 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
905 && ps_global
->VAR_NORM_FORE_COLOR
906 && ps_global
->VAR_NORM_BACK_COLOR
907 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
908 ps_global
->VAR_NORM_FORE_COLOR
)
909 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
910 ps_global
->VAR_NORM_BACK_COLOR
))){
912 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
913 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
914 if(!pico_is_good_colorpair(hdrcolor
))
915 free_color_pair(&hdrcolor
);
919 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
922 * Attachment list header
927 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
930 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
933 hwid
= MAX(avail
, 0);
935 i
= utf8_width(_("Parts/Attachments:"));
936 partwid
= MIN(i
, hwid
);
937 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
940 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
941 if(!gf_puts(tmp
, pc
))
945 utf8_snprintf(tmp
, sizeof(tmp
),
947 /* TRANSLATORS: A label */
948 partwid
, partwid
, _("Parts/Attachments:"),
951 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
955 /*----- Figure max display widths -----*/
956 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
957 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
960 if((n
= utf8_width(a
->number
)) > maxnumwid
)
963 if((n
= utf8_width(a
->size
)) > maxsizewid
)
968 * ----- adjust max lengths for nice display -----
970 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
974 avail
= width
- m1
- m2
;
976 s1
= MAX(MIN(1, avail
), 0);
979 dwid
= MAX(MIN(1, avail
), 0);
982 s2
= MAX(MIN(1, avail
), 0);
985 maxnumwid
= MIN(maxnumwid
, width
/3);
986 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
989 s3
= MAX(MIN(1, avail
), 0);
992 shownwid
= MAX(MIN(5, avail
), 0);
995 s4
= MAX(MIN(3, avail
), 0);
998 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
1001 s5
= MAX(MIN(2, avail
), 0);
1004 descwid
= MAX(0, avail
);
1006 /*----- Format the list of attachments -----*/
1007 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
1008 COLOR_PAIR
*lastc
= NULL
;
1010 int thisdescwid
, padwid
;
1012 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
1015 if(a
->body
->type
== TYPEMULTIPART
1016 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1020 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1021 thisdescwid
= MIN(i
, descwid
);
1022 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1025 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1026 if(!gf_puts(tmp
, pc
))
1030 utf8_snprintf(tmp
, sizeof(tmp
),
1031 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1034 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1036 maxnumwid
, maxnumwid
,
1038 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1042 a
->shown
? "Shown" :
1043 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1047 a
->size
? a
->size
: "",
1049 thisdescwid
, thisdescwid
,
1050 (descwid
> 2 && a
->description
) ? a
->description
: "");
1052 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1055 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1056 char buf
[16], color
[64];
1060 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1064 h
= new_handle(handlesp
);
1068 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1069 buf
[sizeof(buf
)-1] = '\0';
1071 if(!(flgs
& FM_NOCOLOR
)
1072 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1073 lastc
= get_cur_embedded_color();
1074 if(!gf_nputs(color
, (long) l
, pc
))
1077 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1078 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1081 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1082 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1088 if(!format_env_puts(tmpp
, pc
))
1091 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1093 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1094 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1098 if(!embed_color(lastc
, pc
))
1101 free_color_pair(&lastc
);
1103 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1106 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1111 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1112 if(!gf_puts(tmp
, pc
))
1116 if(!gf_puts(NEWLINE
, pc
))
1121 * Dashed line after list
1125 avail
= width
- m1
- m2
;
1126 hwid
= MAX(avail
, 0);
1128 dashwid
= MAX(MIN(40, hwid
-2), 0);
1129 padwid
= hwid
- dashwid
;
1131 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1132 if(!gf_puts(tmp
, pc
))
1136 snprintf(tmp
, sizeof(tmp
),
1138 repeat_char(dashwid
, '-'),
1139 padwid
, padwid
, "");
1142 avail
= width
- m1
-2;
1144 dashwid
= MAX(MIN(40, avail
), 0);
1147 snprintf(tmp
, sizeof(tmp
),
1150 repeat_char(dashwid
, '-'));
1153 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1157 free_color_pair(&hdrcolor
);
1166 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1167 * of body part fetches as we're formatting) for the
1168 * given message isn't set (likely because there
1169 * weren't any parts suitable for display), then make
1170 * sure to set it here.
1173 format_blip_seen(long int msgno
)
1177 if(msgno
> 0L && ps_global
->mail_stream
1178 && msgno
<= ps_global
->mail_stream
->nmsgs
1179 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1181 && !ps_global
->mail_stream
->rdonly
)
1182 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1189 * is_an_env_hdr - is this name a header in the envelope structure?
1191 * name - the header name to check
1194 is_an_env_hdr(char *name
)
1198 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1199 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1209 * is_an_addr_hdr - is this an address header?
1211 * name - the header name to check
1214 is_an_addr_hdr(char *fieldname
)
1216 char fbuf
[FBUF_LEN
+1];
1217 char *colon
, *fname
;
1218 static char *addr_headers
[] = {
1234 /* so it is pointing to NULL */
1235 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1237 if((colon
= strindex(fieldname
, ':')) != NULL
){
1238 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1239 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1245 if(fname
&& *fname
){
1246 for(p
= addr_headers
; *p
; p
++)
1247 if(!strucmp(fname
, *p
))
1251 return((*p
) ? 1 : 0);
1256 * Format a single field from the envelope
1259 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1260 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1265 fmt_env
= format_envelope
;
1267 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1268 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1269 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1276 * Look through header string beginning with "begin", for the next
1277 * occurrence of header "field". Set "start" to that. Set "end" to point one
1278 * position past all of the continuation lines that go with "field".
1279 * That is, if "end" is converted to a null
1280 * character then the string "start" will be the next occurence of header
1281 * "field" including all of its continuation lines. Assume we
1282 * have CRLF's as end of lines.
1284 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1285 * make "end" the end of that header.
1287 * Returns 1 if found, 0 if not.
1290 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1292 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1297 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1303 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1304 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1305 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1306 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1309 * We require that start is at the beginning of a line, so
1310 * either it equals begin (which we assume is the beginning of a
1311 * line) or it is preceded by a CRLF.
1314 *start
= srchstr(begin_srch
, tmpfield
);
1315 while(*start
&& *start
!= begin
1316 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1317 begin_srch
= *start
+ 1;
1318 *start
= srchstr(begin_srch
, tmpfield
);
1325 for(p
= *start
; *p
; p
++){
1327 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1329 * The final 015 in the test above is to test for the end
1346 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1350 if(pico_usingcolor()){
1351 char *fg
= NULL
, *bg
= NULL
, *s
;
1352 char *basefg
= NULL
, *basebg
= NULL
;
1354 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1355 : ps_global
->VAR_NORM_FORE_COLOR
;
1356 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1357 : ps_global
->VAR_NORM_BACK_COLOR
;
1359 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1360 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1361 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1363 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1364 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1365 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1371 * The blacks are just known good colors for
1372 * testing whether the other color is good.
1374 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1375 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1376 if(pico_is_good_colorpair(tmp
))
1377 for(s
= color_embed(fg
, bg
);
1378 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1382 free_color_pair(&tmp
);
1386 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1387 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1392 colorstring
[buflen
-1] = '\0';
1399 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1402 if(pico_usingcolor()){
1403 char *fg
= NULL
, *bg
= NULL
, *s
;
1406 * We need to change the fg and bg colors back even if they
1407 * are the same as the Normal Colors so that color_a_quote
1408 * will have a chance to pick up those colors as the ones to
1409 * switch to. We don't do this before the handle above so that
1410 * the quote color will flow into the selectable item when
1411 * the selectable item color is partly the same as the
1412 * normal color. That is, suppose the normal color was black on
1413 * cyan and the selectable color was blue on cyan, only a fg color
1414 * change. We preserve the only-a-fg-color-change in a quote by
1415 * letting the quote background color flow into the selectable text.
1417 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1418 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1420 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1421 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1423 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1424 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1429 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1433 colorstring
[buflen
-1] = '\0';
1440 url_embed(int embed
)
1442 static char buf
[3] = {TAG_EMBED
};
1450 * Paint the signature.
1453 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1455 struct variable
*vars
= ps_global
->vars
;
1457 COLOR_PAIR
*col
= NULL
;
1459 if(is_in_sig
== NULL
)
1462 in_sig_block
= (int *) is_in_sig
;
1464 if(!strcmp(line
, SIGDASHES
))
1465 *in_sig_block
= START_SIG_BLOCK
;
1466 else if(*line
== '\0')
1468 * Suggested by Eduardo: allow for a blank line right after
1471 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1472 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1474 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1475 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1477 if(*in_sig_block
!= OUT_SIG_BLOCK
1478 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1479 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1480 VAR_SIGNATURE_BACK_COLOR
))){
1481 if(!pico_is_good_colorpair(col
))
1482 free_color_pair(&col
);
1486 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1488 ins
= gf_line_test_new_ins(ins
, line
,
1489 color_embed(col
->fg
, col
->bg
),
1492 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1493 fg
[sizeof(fg
)-1] = '\0';
1494 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1495 bg
[sizeof(bg
)-1] = '\0';
1498 * Loop watching colors, and override with
1499 * signature color whenever the normal foreground and background
1500 * colors are in force.
1504 if(*p
++ == TAG_EMBED
){
1508 p
+= *p
+ 1; /* skip handle key */
1512 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1513 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1514 p
+= RGBLEN
; /* advance past color value */
1516 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1517 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1518 ins
= gf_line_test_new_ins(ins
, p
,
1519 color_embed(col
->fg
,NULL
),
1524 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1525 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1526 p
+= RGBLEN
; /* advance past color value */
1528 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1529 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1530 ins
= gf_line_test_new_ins(ins
, p
,
1531 color_embed(NULL
,col
->bg
),
1541 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1542 color_embed(VAR_NORM_FORE_COLOR
,
1543 VAR_NORM_BACK_COLOR
),
1545 free_color_pair(&col
);
1553 * Line filter to add color to displayed headers.
1556 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1558 static char field
[FBUF_LEN
+ 1];
1559 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1560 char *p
, *q
, *value
, *beg
;
1562 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1563 struct variable
*vars
= ps_global
->vars
;
1565 field
[FBUF_LEN
] = '\0';
1567 if(isspace((unsigned char)*line
)) /* continuation line */
1570 if(!(value
= strindex(line
, ':')))
1573 memset(field
, 0, sizeof(field
));
1574 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1577 for(value
++; isspace((unsigned char)*value
); value
++)
1580 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1581 fg
[sizeof(fg
)-1] = '\0';
1582 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1583 bg
[sizeof(bg
)-1] = '\0';
1586 * Split into two cases depending on whether this is a header which
1587 * contains addresses or not. We may color addresses separately.
1589 if(is_an_addr_hdr(field
)){
1592 * If none of the patterns are for this header, don't bother parsing
1593 * and checking each address.
1595 if(!any_hdr_color(field
))
1599 * First check for patternless patterns which color whole line.
1601 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1602 if(pico_is_good_colorpair(color
)){
1603 ins
= gf_line_test_new_ins(ins
, value
,
1604 color_embed(color
->fg
, color
->bg
),
1606 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1607 fg
[sizeof(fg
)-1] = '\0';
1608 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1609 bg
[sizeof(bg
)-1] = '\0';
1613 free_color_pair(&color
);
1617 * Then go through checking address by address.
1618 * Keep track of quotes and watch for color changes, and override
1619 * with most recent header color whenever the normal foreground
1620 * and background colors are in force.
1626 /* skip next character */
1627 if(*(p
+1) && (in_comment
|| in_quote
))
1636 in_quote
= 1 - in_quote
;
1654 if(!(in_quote
|| in_comment
)){
1655 /* we reached the end of this address */
1658 free_color_pair(&color
);
1660 if((color
= hdr_color(field
, beg
,
1661 ps_global
->hdr_colors
)) != NULL
){
1662 if(pico_is_good_colorpair(color
)){
1664 ins
= gf_line_test_new_ins(ins
, beg
,
1665 color_embed(color
->fg
,
1669 for(q
= p
; q
> beg
&&
1670 isspace((unsigned char)*(q
-1)); q
--)
1673 ins
= gf_line_test_new_ins(ins
, q
,
1674 color_embed(fg
, bg
),
1678 free_color_pair(&color
);
1683 for(p
++; isspace((unsigned char)*p
); p
++)
1697 p
+= *p
+ 1; /* skip handle key */
1702 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1703 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1704 p
+= RGBLEN
; /* advance past color value */
1706 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1707 ins
= gf_line_test_new_ins(ins
, p
,
1708 color_embed(color
->fg
,NULL
),
1714 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1715 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1716 p
+= RGBLEN
; /* advance past color value */
1718 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1719 ins
= gf_line_test_new_ins(ins
, p
,
1720 color_embed(NULL
,color
->bg
),
1737 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1740 if(*q
&& !(in_quote
|| in_comment
)){
1741 /* we reached the end of this address */
1743 free_color_pair(&color
);
1745 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1746 if(pico_is_good_colorpair(color
)){
1748 ins
= gf_line_test_new_ins(ins
, beg
,
1749 color_embed(color
->fg
,
1752 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1755 ins
= gf_line_test_new_ins(ins
, q
,
1756 color_embed(fg
, bg
),
1760 free_color_pair(&color
);
1765 free_color_pair(&color
);
1768 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1769 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1770 VAR_HEADER_GENERAL_BACK_COLOR
),
1775 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1778 if(pico_is_good_colorpair(color
)){
1779 ins
= gf_line_test_new_ins(ins
, value
,
1780 color_embed(color
->fg
, color
->bg
),
1784 * Loop watching colors, and override with header
1785 * color whenever the normal foreground and background
1786 * colors are in force.
1790 if(*p
++ == TAG_EMBED
){
1794 p
+= *p
+ 1; /* skip handle key */
1798 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1799 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1800 p
+= RGBLEN
; /* advance past color value */
1802 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1803 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1804 ins
= gf_line_test_new_ins(ins
, p
,
1805 color_embed(color
->fg
,NULL
),
1810 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1811 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1812 p
+= RGBLEN
; /* advance past color value */
1814 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1815 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1816 ins
= gf_line_test_new_ins(ins
, p
,
1817 color_embed(NULL
,color
->bg
),
1827 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1828 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1829 VAR_HEADER_GENERAL_BACK_COLOR
),
1833 free_color_pair(&color
);
1842 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1844 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1845 *weburlp
= NULL
, *mailurlp
= NULL
;
1846 int n
, n1
, n2
, n3
, l
;
1847 char buf
[256], color
[256];
1851 for(lp
= line
; ; lp
= up
+ n
){
1852 /* scan for all of them so we can choose the first */
1853 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1854 urlp
= rfc1738_scan(lp
, &n1
);
1855 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1856 weburlp
= web_host_scan(lp
, &n2
);
1857 if(F_ON(F_SCAN_ADDR
,ps_global
))
1858 mailurlp
= mail_addr_scan(lp
, &n3
);
1860 if(urlp
|| weburlp
|| mailurlp
){
1862 weburlp
? weburlp
: mailurlp
;
1863 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1865 if(mailurlp
&& mailurlp
< up
)
1870 weburlp
= mailurlp
= NULL
;
1872 else if(up
== weburlp
){
1884 uh
= (URL_HILITE_S
*) local
;
1886 h
= new_handle(uh
->handlesp
);
1888 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1889 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1890 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1891 h
->h
.url
.path
[n
+10-1] = '\0';
1893 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1894 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1895 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1896 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1899 buf
[1] = TAG_HANDLE
;
1900 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1901 buf
[sizeof(buf
)-1] = '\0';
1902 buf
[2] = strlen(&buf
[3]);
1903 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1905 /* in case it was the current selection */
1906 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1908 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1909 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1911 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1913 urlp
= weburlp
= mailurlp
= NULL
;
1921 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1923 static int check_for_urls
= 0;
1926 if(isspace((unsigned char)*line
)) /* continuation, check or not
1927 depending on last line */
1931 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1936 if(((ft
= pine_header_standard(line
)) == FreeText
1938 || ft
== TypeUnknown
)
1939 && strucmp(line
, "message-id")
1940 && strucmp(line
, "newsgroups")
1941 && strucmp(line
, "references")
1942 && strucmp(line
, "in-reply-to")
1943 && strucmp(line
, "received")
1944 && strucmp(line
, "date")){
1953 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1960 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1965 struct variable
*vars
= ps_global
->vars
;
1970 total_wid
= *((int *) local
);
1972 /* calculate width of line */
1982 p
+= *p
+ 1; /* skip handle key */
1987 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1999 default: /* literal embed char */
2007 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
2013 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2019 if(total_wid
> wid
){
2020 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2021 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2022 VAR_HEADER_GENERAL_BACK_COLOR
),
2024 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2025 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2026 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2027 color_embed(VAR_NORM_FORE_COLOR
,
2028 VAR_NORM_BACK_COLOR
),
2040 url_external_specific_handler(char *url
, int len
)
2042 static char list
[UES_LEN
* UES_MAX
];
2048 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2049 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2052 else{ /* initialize! */
2053 char **l
, *test
, *cmd
, *p
, *p2
;
2056 memset(list
, 0, sizeof(list
));
2057 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2058 get_pair(*l
, &test
, &cmd
, 1, 1);
2060 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2063 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2064 if((p2
= strchr(p
, ',')) != NULL
){
2065 if((n
= p2
- p
) < UES_LEN
){
2066 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2071 "* * * HANLDER TOO LONG: %.*s\n", n
,
2077 if(strlen(p
) <= UES_LEN
){
2078 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2087 fs_give((void **) &test
);
2090 fs_give((void **) &cmd
);
2099 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2100 imapuid_t
*uid
, char **search
, int silent
)
2102 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2103 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2106 int rv
= URL_IMAP_ERROR
;
2109 * Since we're planting nulls, operate on a temporary copy...
2111 scheme
= silent
? NULL
: "IMAP";
2112 url
= cpystr(true_url
+ 7);
2114 /* Try to pick apart the "iserver" portion */
2115 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2119 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2121 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2124 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2126 server
= rfc1738_str(p
);
2128 /* only ";auth=*" supported (and also ";auth=anonymous") */
2129 if((p
= srchstr(url
, ";auth=")) != NULL
){
2131 auth
= rfc1738_str(p
+ 6);
2135 user
= rfc1738_str(url
);
2138 server
= rfc1738_str(url
);
2141 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2144 * "iserver" in hand, pick apart the "icommand"...
2147 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2151 * No "icommand" (all top-level folders) or "imailboxlist"...
2154 *p
= '\0'; /* tie off criteria */
2155 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2156 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2157 rv
|= URL_IMAP_IMBXLSTLSUB
;
2158 else if(strucmp(p
, "list"))
2159 return(url_bogus_imap(&url
, scheme
,
2160 "Invalid list type specified"));
2163 rv
|= URL_IMAP_ISERVERONLY
;
2167 /* build folder list from specified server/criteria/list-method */
2168 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2169 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2170 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2171 user
? "user=\"" : "Anonymous",
2174 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2175 (*folder
)[l
] = '\0';
2176 rv
|= URL_IMAP_IMAILBOXLIST
;
2179 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2180 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2181 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2182 *section
= '\0'; /* tie off UID */
2183 section
= rfc1738_str(section
+ 10);
2184 /* BUG: verify valid section spec ala rfc 2060 */
2186 "-- URL IMAP FOLDER: section not used: %s\n",
2187 section
? section
: "?"));
2190 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2191 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2193 /* optional "uidvalidity"? */
2194 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2197 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2198 return(url_bogus_imap(&url
, scheme
,
2199 "Invalid UIDVALIDITY"));
2202 mailbox
= rfc1738_str(cmd
);
2203 rv
= URL_IMAP_IMESSAGEPART
;
2205 else{ /* "imessagelist" */
2206 /* optional "uidvalidity"? */
2207 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2210 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2211 return(url_bogus_imap(&url
, scheme
,
2212 "Invalid UIDVALIDITY"));
2215 /* optional "enc_search"? */
2216 if((p
= strchr(cmd
, '?')) != NULL
){
2219 *search
= cpystr(rfc1738_str(p
+ 1));
2220 /* BUG: verify valid search spec ala rfc 2060 */
2223 mailbox
= rfc1738_str(cmd
);
2224 rv
= URL_IMAP_IMESSAGELIST
;
2227 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2228 q_status_message(SM_ORDER
, 3, 3,
2229 "Unsupported authentication method. Using standard login.");
2232 * At this point our structure should contain the
2233 * digested url. Now put it together for c-client...
2235 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2236 + (user
? (strlen(user
)+2) : 9);
2237 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2238 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2239 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2240 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2244 (*folder
)[l
] = '\0';
2247 fs_give((void **) &url
);
2253 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2255 fs_give((void **) freeme
);
2256 (void) url_bogus(url
, problem
);
2257 return(URL_IMAP_ERROR
);
2262 * url_bogus - report url syntax errors and such
2265 url_bogus(char *url
, char *reason
)
2267 dprint((2, "-- bogus url \"%s\": %s\n",
2268 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2270 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2271 "Malformed \"%.*s\" URL: %.200s",
2272 (void *) (strchr(url
, ':') - url
), url
, reason
);
2279 /*----------------------------------------------------------------------
2280 Format header text suitable for display
2282 Args: stream -- mail stream for various header text fetches
2283 msgno -- sequence number in stream of message we're interested in
2284 section -- which section of message
2285 env -- pointer to msg's envelope
2286 hdrs -- struct containing what's to get formatted
2287 prefix -- prefix to append to each output line
2288 handlesp -- address of pointer to the message's handles
2289 flags -- FM_ flags, see pith/mailview.h
2290 final_pc -- function to write header text with
2292 Result: 0 if all's well, -1 if write error, 1 if fetch error
2294 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2295 in the local convention.
2299 #define FHT_WRTERR -1
2300 #define FHT_FTCHERR 1
2302 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2303 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2304 fmt_env_t fmt_env
, gf_io_t final_pc
)
2308 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2312 gf_io_t tmp_pc
, tmp_gc
;
2313 struct variable
*vars
= ps_global
->vars
;
2315 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2316 gf_set_so_writec(&tmp_pc
, tmp_store
);
2321 fmt_env
= format_envelope
;
2323 if(ps_global
->full_header
== 2){
2324 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2328 * First, calculate how big a fields array we need.
2331 /* Custom header viewing list specified */
2332 if(hdrs
->type
== HD_LIST
){
2333 /* view all these headers */
2335 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2336 if(!is_an_env_hdr(q
))
2339 /* view all except these headers */
2341 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2345 nfields
--; /* subtract one for ALL_EXCEPT field */
2349 nfields
= 6; /* default view */
2351 /* allocate pointer space */
2353 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2354 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2357 if(hdrs
->type
== HD_LIST
){
2358 /* view all these headers */
2360 /* put the non-envelope headers in fields */
2362 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2363 if(!is_an_env_hdr(q
))
2366 /* view all except these headers */
2368 /* put the list of headers not to view in fields */
2370 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2371 if(strucmp(ALL_EXCEPT
, q
))
2379 fields
[i
= 0] = "Resent-Date";
2380 fields
[++i
] = "Resent-From";
2381 fields
[++i
] = "Resent-To";
2382 fields
[++i
] = "Resent-cc";
2383 fields
[++i
] = "Resent-Subject";
2389 /* custom view with exception list */
2390 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2392 * Go through each header in h and print it.
2393 * First we check to see if it is an envelope header so we
2394 * can print our envelope version of it instead of the raw version.
2397 /* fetch all the other headers */
2399 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2402 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2404 char tmp
[MAILTMPLEN
+1];
2407 colon_loc
= strindex(start
, ':');
2408 if(colon_loc
&& colon_loc
< finish
){
2409 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2410 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2415 if(colon_loc
&& is_an_env_hdr(tmp
)){
2416 char *dummystart
, *dummyfinish
;
2419 * Pretty format for env hdrs.
2420 * If the same header appears more than once, only
2421 * print the last to avoid duplicates.
2422 * They should have been combined in the env when parsed.
2424 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2426 format_env_hdr(stream
, msgno
, section
, env
,
2427 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2430 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2431 hdrs
->charset
, flags
)) != 0)
2438 /* custom view or default */
2440 /* fetch the non-envelope headers */
2442 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2444 /* default envelope for default view */
2445 if(hdrs
->type
== HD_BFIELD
)
2446 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2448 /* go through each header in list, v initialized above */
2449 for(; (q
= *v
) != NULL
; v
++){
2450 if(is_an_env_hdr(q
)){
2451 /* pretty format for env hdrs */
2452 format_env_hdr(stream
, msgno
, section
, env
,
2453 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2457 * Go through h finding all occurences of this header
2458 * and all continuation lines, and output.
2461 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2463 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2464 hdrs
->charset
, flags
)) != 0)
2477 gf_clear_so_writec(tmp_store
);
2479 if(!rv
){ /* valid data? Do wrapping and filtering... */
2481 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2482 STORE_S
*df_store
= NULL
;
2484 so_seek(tmp_store
, 0L, 0);
2486 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2489 * Test for and act on any display filter
2490 * This barely makes sense. The display filter is going
2491 * to be getting UTF-8'ized headers here. In pre-alpine
2492 * pine the display filter was being fed already decoded
2493 * headers in whatever character set they were in.
2494 * The good news is that that didn't make much
2495 * sense either, so this shouldn't break anything.
2496 * It seems unlikely that anybody is doing anything useful
2497 * with the header part of display filters.
2499 if(ps_global
->tools
.display_filter
2500 && ps_global
->tools
.display_filter_trigger
2501 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2502 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2504 gf_set_so_writec(&tmp_pc
, df_store
);
2505 gf_set_so_readc(&tmp_gc
, df_store
);
2506 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2507 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2508 _("Formatting error: %s"), errstr
);
2512 so_seek(df_store
, 0L, 0);
2514 gf_clear_so_writec(df_store
);
2517 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2518 "No space for filtered text.");
2523 so_seek(tmp_store
, 0L, 0);
2524 gf_set_so_readc(&tmp_gc
, tmp_store
);
2528 int *margin
, wrapflags
= GFW_ONCOMMA
;
2531 gf_link_filter(gf_local_nvtnl
, NULL
);
2533 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2534 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2535 || F_ON(F_SCAN_ADDR
, ps_global
))
2537 gf_link_filter(gf_line_test
,
2538 gf_line_test_opt(url_hilite_hdr
,
2539 gf_url_hilite_opt(&uh
,handlesp
,1)));
2540 wrapflags
|= GFW_HANDLES
;
2543 if((flags
& FM_DISPLAY
)
2544 && !(flags
& FM_NOCOLOR
)
2545 && pico_usingcolor()
2546 && ((VAR_NORM_FORE_COLOR
2547 && VAR_HEADER_GENERAL_FORE_COLOR
2548 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2550 (VAR_NORM_BACK_COLOR
2551 && VAR_HEADER_GENERAL_BACK_COLOR
2552 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2553 wrapflags
|= GFW_HDRCOLOR
;
2555 if((flags
& FM_DISPLAY
)
2556 && !(flags
& FM_NOCOLOR
)
2557 && pico_usingcolor()
2558 && ps_global
->hdr_colors
){
2559 gf_link_filter(gf_line_test
,
2560 gf_line_test_opt(color_headers
, NULL
));
2561 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2564 if(prefix
&& *prefix
)
2565 column
= MAX(column
-strlen(prefix
), 50);
2567 margin
= format_view_margin();
2569 if(!(flags
& FM_NOWRAP
))
2570 gf_link_filter(gf_wrap
,
2571 gf_wrap_filter_opt(column
, column
,
2572 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2575 if(prefix
&& *prefix
)
2576 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2578 if((flags
& FM_DISPLAY
)
2579 && !(flags
& FM_NOCOLOR
)
2580 && pico_usingcolor()
2581 && ((VAR_NORM_FORE_COLOR
2582 && VAR_HEADER_GENERAL_FORE_COLOR
2583 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2585 (VAR_NORM_BACK_COLOR
2586 && VAR_HEADER_GENERAL_BACK_COLOR
2587 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2591 right_margin
= margin
? margin
[1] : 0;
2592 total_wid
= column
- right_margin
;
2594 gf_link_filter(gf_line_test
,
2595 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2596 wrapflags
|= GFW_HANDLES
;
2599 gf_link_filter(gf_nvtnl_local
, NULL
);
2601 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2603 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2604 "Can't build header : %.200s", errstr
);
2609 gf_clear_so_readc(df_store
);
2613 gf_clear_so_readc(tmp_store
);
2616 so_give(&tmp_store
);
2619 fs_give((void **)&h
);
2622 fs_give((void **)&fields
);
2628 /*----------------------------------------------------------------------
2629 Format RAW header text for display
2631 Args: stream -- mail stream for various header text fetches
2632 rawno -- sequence number in stream of message we're interested in
2633 section -- which section of message
2634 pc -- function to write header text with
2636 Result: 0 if all's well, -1 if write error, 1 if fetch error
2638 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2639 in the local convention.
2643 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2645 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2652 if(!gf_puts(NEWLINE
, pc
))
2655 if(ISRFCEOL(h
)) /* all done! */
2658 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2659 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2660 c
= (unsigned char) *h
++;
2661 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2662 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2665 else if(!(*pc
)(*h
++))
2670 return(FHT_FTCHERR
);
2677 /*----------------------------------------------------------------------
2678 Format c-client envelope data suitable for display
2680 Args: s -- mail stream for various header text fetches
2681 n -- raw sequence number in stream of message we're interested in
2682 sect -- which section of message
2683 e -- pointer to msg's envelope
2684 pc -- function to write header text with
2685 which -- which header lines to write
2687 flags -- FM_ flags, see pith/mailview.h
2689 Result: 0 if all's well, -1 if write error, 1 if fetch error
2691 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2692 in the local convention.
2696 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2697 long int which
, char *oacs
, int flags
)
2699 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2704 if((which
& FE_DATE
) && e
->date
) {
2706 snprintf(buftmp
, sizeof(buftmp
), "%s",
2707 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2708 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2709 buftmp
[sizeof(buftmp
)-1] = '\0';
2710 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2711 SIZEOF_20KBUF
, buftmp
);
2713 format_env_puts(p2
, pc
);
2714 gf_puts(NEWLINE
, pc
);
2717 if((which
& FE_FROM
) && e
->from
)
2718 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2720 if((which
& FE_REPLYTO
) && e
->reply_to
2721 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2722 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2724 if((which
& FE_TO
) && e
->to
)
2725 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2727 if((which
& FE_CC
) && e
->cc
)
2728 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2730 if((which
& FE_BCC
) && e
->bcc
)
2731 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2733 if((which
& FE_RETURNPATH
) && e
->return_path
)
2734 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2737 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2739 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2740 if (!e
->ngpathexists
&& e
->message_id
&&
2741 strncmp (e
->message_id
,"<alpine.",8) &&
2742 strncmp (e
->message_id
,"<Pine.",6) &&
2743 strncmp (e
->message_id
,"<MS-C.",6) &&
2744 strncmp (e
->message_id
,"<MailManager.",13) &&
2745 strncmp (e
->message_id
,"<EasyMail.",11) &&
2746 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2749 q_status_message(SM_ORDER
, 0, 3,
2750 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2753 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2754 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2756 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2757 char *freeme
= NULL
;
2762 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2764 if(flags
& FM_DISPLAY
2765 && (ps_global
->display_keywords_in_subject
2766 || ps_global
->display_keywordinits_in_subject
)){
2768 /* don't bother if no keywords are defined */
2769 if(some_user_flags_defined(s
))
2770 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2771 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2772 NULL
, ps_global
->VAR_KW_BRACES
);
2775 format_env_puts(p2
, pc
);
2778 fs_give((void **) &freeme
);
2780 gf_puts(NEWLINE
, pc
);
2783 if((which
& FE_SENDER
) && e
->sender
2784 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2785 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2787 if((which
& FE_MESSAGEID
) && e
->message_id
){
2790 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2791 format_env_puts(p2
, pc
);
2792 gf_puts(NEWLINE
, pc
);
2795 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2796 q
= "In-Reply-To: ";
2798 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);
2799 format_env_puts(p2
, pc
);
2800 gf_puts(NEWLINE
, pc
);
2803 if((which
& FE_REFERENCES
) && e
->references
) {
2806 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2807 format_env_puts(p2
, pc
);
2808 gf_puts(NEWLINE
, pc
);
2816 * The argument fieldname is something like "Subject:..." or "Subject".
2817 * Look through the specs in speccolor for a match of the fieldname,
2818 * and return the color that goes with any match, or NULL.
2819 * Caller should free the color.
2822 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2824 SPEC_COLOR_S
*hc
= NULL
;
2825 COLOR_PAIR
*color_pair
= NULL
;
2826 char *colon
, *fname
;
2827 char fbuf
[FBUF_LEN
+1];
2831 colon
= strindex(fieldname
, ':');
2833 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2834 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2841 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2842 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2847 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2848 if(srchstr(value
, pat
->substring
))
2855 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2856 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2863 * The argument fieldname is something like "Subject:..." or "Subject".
2864 * Look through the specs in hdr_colors for a match of the fieldname,
2865 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2868 any_hdr_color(char *fieldname
)
2870 SPEC_COLOR_S
*hc
= NULL
;
2871 char *colon
, *fname
;
2872 char fbuf
[FBUF_LEN
+1];
2874 colon
= strindex(fieldname
, ':');
2876 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2877 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2884 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2885 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2892 /*----------------------------------------------------------------------
2893 Format an address field, wrapping lines nicely at commas
2895 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2896 addr -- ADDRESS structure to format
2898 Result: A formatted, malloced string is returned.
2899 ----------------------------------------------------------------------*/
2901 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2902 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2905 int trailing
= 0, group
= 0;
2912 * quickly run down address list to make sure none are patently bogus.
2913 * If so, just blat raw field out.
2915 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2916 if(atmp
->host
&& atmp
->host
[0] == '.'){
2917 char *field
, *fields
[2];
2920 fields
[0] = cpystr(field_name
);
2921 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2924 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2927 for(t
= h
= field
; *h
; t
++)
2928 if(*t
== '\015' && *(t
+1) == '\012'){
2929 *t
= '\0'; /* tie off line */
2930 format_env_puts(h
, pc
);
2931 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2932 gf_puts(NEWLINE
, pc
); /* more to write */
2936 else if(!*t
){ /* shouldn't happen much */
2938 format_env_puts(h
, pc
);
2943 fs_give((void **)&field
);
2946 fs_give((void **)&fields
[0]);
2947 gf_puts(NEWLINE
, pc
);
2948 dprint((2, "Error in \"%s\" field address\n",
2949 field_name
? field_name
: "?"));
2953 gf_puts(field_name
, pc
);
2956 atmp
= addr
->next
; /* remember what's next */
2958 if(!addr
->host
&& addr
->mailbox
){
2959 mtmp
= addr
->mailbox
;
2960 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2961 (unsigned char *)tmp_20k_buf
,
2962 SIZEOF_20KBUF
, addr
->mailbox
));
2965 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2966 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2967 tmp_20k_buf
[10000-1] = '\0';
2969 if(!trailing
) /* 1st pass, just address */
2971 else{ /* else comma, unless */
2972 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2973 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2976 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2984 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2986 addr
->personal
= ptmp
; /* restore old personal ptr */
2987 if(!addr
->host
&& addr
->mailbox
){
2988 fs_give((void **)&addr
->mailbox
);
2989 addr
->mailbox
= mtmp
;
2996 gf_puts(NEWLINE
, pc
);
3002 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3003 /* RFC822 continuation, must start with CRLF */
3004 #define RFC822CONT "\015\012 "
3006 /* Write RFC822 address with some quoting turned off.
3008 * address to interpret
3010 * (This is a copy of c-client's rfc822_write_address except
3011 * we don't quote double quote and dot in personal names. It writes
3012 * to a gf_io_t instead of to a buffer so that we don't have to worry
3013 * about fixed sized buffer overflowing. It's also special cased to deal
3014 * with only a single address.)
3016 * The idea is that there are some places where we'd just like to display
3017 * the personal name as is before applying confusing quoting. However,
3018 * we do want to be careful not to break things that should be quoted so
3019 * we'll only use this where we are sure. Quoting may look ugly but it
3020 * doesn't usually break anything.
3023 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3025 extern const char *rspecials
;
3027 if (adr
->host
) { /* ordinary address? */
3028 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3029 else { /* no, must use phrase <route-addr> form */
3031 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3033 gf_puts(" <", pc
); /* write address delimiter */
3034 pine_rfc822_address(adr
, pc
);
3035 gf_puts (">", pc
); /* closing delimiter */
3041 else if (adr
->mailbox
) { /* start of group? */
3042 /* yes, write group name */
3043 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3045 gf_puts (": ", pc
); /* write group identifier */
3046 *group
= 1; /* in a group */
3048 else if (*group
) { /* must be end of group (but be paranoid) */
3050 *group
= 0; /* no longer in that group */
3055 /* Write RFC822 route-address to string
3057 * address to interpret
3061 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3063 extern char *wspecials
;
3065 if (adr
&& adr
->host
) { /* no-op if no address */
3066 if (adr
->adl
) { /* have an A-D-L? */
3067 gf_puts (adr
->adl
, pc
);
3070 /* write mailbox name */
3071 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3072 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3073 gf_puts ("@", pc
); /* host delimiter */
3074 gf_puts (adr
->host
, pc
); /* write host name */
3080 /* Concatenate RFC822 string
3082 * pointer to string to concatenate
3083 * list of special characters
3087 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3091 if (strpbrk (src
,specials
)) { /* any specials present? */
3092 gf_puts ("\"", pc
); /* opening quote */
3093 /* truly bizarre characters in there? */
3094 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3097 /* turn it into a null-terminated piece */
3101 gf_puts (src
, pc
); /* yes, output leader */
3103 gf_puts ("\\", pc
); /* quoting */
3104 gf_puts (save
, pc
); /* output the bizarre character */
3105 src
= ++s
; /* continue after the bizarre character */
3107 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3108 gf_puts ("\"", pc
); /* closing quote */
3110 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3114 /*----------------------------------------------------------------------
3115 Format an address field, wrapping lines nicely at commas
3117 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3118 newsgrps -- ADDRESS structure to format
3120 Result: A formatted, malloced string is returned.
3122 The resuling lines formatted are 80 columns wide.
3123 ----------------------------------------------------------------------*/
3125 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3127 char buf
[MAILTMPLEN
];
3128 int trailing
= 0, llen
, alen
;
3131 if(!newsgrps
|| !*newsgrps
)
3134 gf_puts(field_name
, pc
);
3136 llen
= strlen(field_name
);
3138 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3139 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3140 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3145 if(!trailing
){ /* first time thru, just address */
3149 else{ /* else preceding comma */
3153 if(alen
+ llen
+ 1 > 76){
3154 gf_puts(NEWLINE
, pc
);
3164 if(alen
&& llen
> 76){ /* handle long addresses */
3165 register char *q
, *p
= &buf
[alen
-1];
3168 if(isspace((unsigned char)*p
)
3169 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3170 for(q
= buf
; q
< p
; q
++)
3171 (*pc
)(*q
); /* write character */
3173 gf_puts(NEWLINE
, pc
);
3182 if(p
== buf
) /* no reasonable break point */
3189 gf_puts(NEWLINE
, pc
);
3194 /*----------------------------------------------------------------------
3195 Format a text field that's part of some raw (non-envelope) message header
3201 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3203 ----------------------------------------------------------------------*/
3205 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3207 register char *current
;
3208 unsigned char *p
, *tmp
= NULL
, c
;
3216 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3218 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3221 len
= SIZEOF_20KBUF
;
3222 p
= (unsigned char *) tmp_20k_buf
;
3225 if(islower((unsigned char)(*start
)))
3226 *start
= toupper((unsigned char)(*start
));
3228 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3230 /* output from start to finish */
3231 while(*current
&& rv
== FHT_OK
)
3232 if(ISRFCEOL(current
)){
3233 if(!gf_puts(NEWLINE
, pc
))
3238 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3239 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3240 c
= (unsigned char) *current
++;
3241 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3242 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3245 else if(!(*pc
)(*current
++))
3249 fs_give((void **) &tmp
);
3259 /*----------------------------------------------------------------------
3260 Format a text field that's part of some raw (non-envelope) message header
3267 ----------------------------------------------------------------------*/
3269 format_env_puts(char *s
, gf_io_t pc
)
3271 if(ps_global
->pass_ctrl_chars
)
3272 return(gf_puts(s
, pc
));
3275 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3276 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3277 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3288 display_parameters(PARAMETER
*params
)
3293 PARMLIST_S
*parmlist
;
3295 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3296 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3297 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3300 tmp_20k_buf
[0] = '\0';
3301 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3302 n
= 0; /* n overloaded */
3303 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3305 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3306 tmp_20k_buf
[10000-1] = '\0';
3310 if(parmlist
->value
){
3311 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3312 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3316 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3321 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3322 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3324 tmp_20k_buf
[10000-1] = '\0';
3328 rfc2231_free_parmlist(&parmlist
);
3331 return(tmp_20k_buf
);
3335 /*----------------------------------------------------------------------
3336 Fetch the requested header fields from the msgno specified
3338 Args: stream -- mail stream of open folder
3339 msgno -- number of message to get header lines from
3340 fields -- array of pointers to desired fields
3342 Returns: allocated string containing matched header lines,
3346 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3349 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3350 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3353 * If the user misconfigures it is possible to have one of the fields
3354 * set to the empty string instead of a header name. We want to catch
3355 * that here instead of asking the server the nonsensical question.
3357 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3361 if(pp
&& *pp
){ /* found an empty header field, fix it */
3362 pflds
= copy_list_array(fields
);
3363 for(pp
= pflds
; pp
&& *pp
; pp
++){
3364 if(!**pp
){ /* scoot rest of the lines up */
3366 for(qq
= pp
; *qq
; qq
++)
3370 fs_give((void **) &free_this
);
3374 /* no headers to look for, return NULL */
3375 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3376 free_list_array(&pflds
);
3383 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3384 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3389 if(pflds
&& pflds
!= fields
)
3390 free_list_array(&pflds
);
3395 while(find_field(&h
, tmp
, sizeof(tmp
))){
3396 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3399 /* interesting field? */
3400 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3402 * Hold off allocating space for matching fields until
3403 * we at least find one to copy...
3406 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3408 while(*p
) /* copy field name */
3411 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3412 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3415 *m
= '\0'; /* tie off match string */
3417 else{ /* no match, pass this field */
3418 while(*h
&& !(*h
++ == '\n'
3419 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3424 if(pflds
&& pflds
!= fields
)
3425 free_list_array(&pflds
);
3427 return(match
? match
: cpystr(""));
3432 find_field(char **h
, char *tmp
, size_t ntmp
)
3436 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3439 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3447 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3451 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3454 if(_last_embedded_fg_color
)
3455 fs_give((void **)&_last_embedded_fg_color
);
3457 _last_embedded_fg_color
= cpystr(cp
->fg
);
3459 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3460 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3465 if(_last_embedded_bg_color
)
3466 fs_give((void **)&_last_embedded_bg_color
);
3468 _last_embedded_bg_color
= cpystr(cp
->bg
);
3470 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3471 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3480 get_cur_embedded_color(void)
3484 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3485 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3487 ret
= pico_get_cur_color();
3494 clear_cur_embedded_color(void)
3496 if(_last_embedded_fg_color
)
3497 fs_give((void **)&_last_embedded_fg_color
);
3499 if(_last_embedded_bg_color
)
3500 fs_give((void **)&_last_embedded_bg_color
);
3505 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3509 if(pico_usingcolor()){
3510 char *fg
= NULL
, *bg
= NULL
, *s
;
3512 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3513 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3514 ps_global
->VAR_NORM_FORE_COLOR
))
3515 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3517 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3518 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3519 ps_global
->VAR_NORM_BACK_COLOR
))
3520 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3526 * The blacks are just known good colors for
3527 * testing whether the other color is good.
3529 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3530 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3531 if(pico_is_good_colorpair(tmp
))
3532 for(s
= color_embed(fg
, bg
);
3533 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3537 free_color_pair(&tmp
);
3541 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3542 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3547 colorstring
[buflen
-1] = '\0';
3554 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3557 if(pico_usingcolor()){
3558 char *fg
= NULL
, *bg
= NULL
, *s
;
3559 char *basefg
= NULL
, *basebg
= NULL
;
3561 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3562 : ps_global
->VAR_NORM_FORE_COLOR
;
3563 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3564 : ps_global
->VAR_NORM_BACK_COLOR
;
3567 * We need to change the fg and bg colors back even if they
3568 * are the same as the Normal Colors so that color_a_quote
3569 * will have a chance to pick up those colors as the ones to
3570 * switch to. We don't do this before the handle above so that
3571 * the quote color will flow into the selectable item when
3572 * the selectable item color is partly the same as the
3573 * normal color. That is, suppose the normal color was black on
3574 * cyan and the selectable color was blue on cyan, only a fg color
3575 * change. We preserve the only-a-fg-color-change in a quote by
3576 * letting the quote background color flow into the selectable text.
3578 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3581 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3584 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3585 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3590 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3594 colorstring
[buflen
-1] = '\0';
3601 * Helper routine that is of limited use.
3602 * We need to tally up the screen width of
3603 * a UTF-8 string as we go through the string.
3604 * We just want the width of the character starting
3605 * at str (and no longer than remaining_octets).
3606 * If we're plopped into the middle of a UTF-8
3607 * character we just want to return width zero.
3610 width_at_this_position(unsigned char *str
, unsigned long n
)
3612 unsigned char *inputp
= str
;
3613 unsigned long remaining_octets
= n
;
3617 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3618 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3619 width
= wcellwidth(ucs
);
3620 /* Writechar will print a '?' */