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-2017 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(cflags
& FC_SUMMARY
){
248 i
= utf8_width(_("Calendar Entry:"));
249 partwid
= MIN(i
, hwid
);
251 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
252 if(!gf_puts(tmp_20k_buf
, pc
))
256 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%-*.*w%*.*s",
257 partwid
, partwid
, _("Calendar Entry:"),
260 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
267 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
268 if(!gf_puts(tmp_20k_buf
, pc
))
272 avail
= width
- m1
- m2
;
274 s1
= MAX(MIN(1, avail
), 0);
277 dwid
= MAX(MIN(1, avail
), 0);
280 s2
= MAX(MIN(1, avail
), 0);
283 if(cflags
& FC_SUMMARY
)
284 utf8_snprintf(padding
, sizeof(padding
), "%*.*s%*.*w%*.*s",
285 s1
, s1
, "", dwid
, dwid
, "", s2
, s2
, "");
290 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
291 padding
, _("This event was cancelled!"));
292 if((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
)){
293 gf_puts(tmp_20k_buf
, pc
);
294 gf_puts(NEWLINE
, pc
);
301 if(vesy
->sender
!= NULL
){
302 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
303 padding
, _("Sent-by: "), vesy
->sender
);
304 gf_puts(tmp_20k_buf
, pc
);
305 gf_puts(NEWLINE
, pc
);
308 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
309 padding
, _("Organizer: "), vesy
->organizer
);
310 gf_puts(tmp_20k_buf
, pc
);
311 gf_puts(NEWLINE
, pc
);
312 } /* end of if(organizer) */
315 ical_remove_escapes(&vesy
->location
);
316 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
317 padding
, _("Location: "), vesy
->location
);
318 gf_puts(tmp_20k_buf
, pc
);
319 gf_puts(NEWLINE
, pc
);
320 } /* end of if location */
323 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
324 padding
, _("Start Date: "), vesy
->evstart
);
325 gf_puts(tmp_20k_buf
, pc
);
326 gf_puts(NEWLINE
, pc
);
327 } /* end of if dtstart */
332 for(i
= 0; vesy
->duration
[i
] != NULL
; i
++){
333 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
334 padding
, _("Duration: "), vesy
->duration
[i
]);
335 gf_puts(tmp_20k_buf
, pc
);
336 gf_puts(NEWLINE
, pc
);
338 } /* end of DURATION */
339 else if(vesy
->evend
){
340 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
341 padding
, _("End Date: "), vesy
->evend
);
342 gf_puts(tmp_20k_buf
, pc
);
343 gf_puts(NEWLINE
, pc
);
344 } else { /* end of if dtend */
345 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
346 padding
, _("No duration nor end time found for this event"));
347 gf_puts(tmp_20k_buf
, pc
);
348 gf_puts(NEWLINE
, pc
);
349 } /* end of else for if (duration) and if (dtend) */
352 #define MAX_DISPLAYED 3
355 for(i
= 0; vesy
->attendee
[i
] != NULL
; i
++){
356 if((cflags
& FC_SUMMARY
) && i
>= MAX_DISPLAYED
)
359 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
361 _("Attendee: "), vesy
->attendee
[i
]);
362 gf_puts(tmp_20k_buf
, pc
);
363 gf_puts(NEWLINE
, pc
);
365 if(cflags
& FC_SUMMARY
){
366 COLOR_PAIR
*lastc
= NULL
;
369 COLOR_PAIR
*hdrcolor
= NULL
;
371 if((flgs
& FM_DISPLAY
)
372 && !(flgs
& FM_NOCOLOR
)
374 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
375 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
376 && ps_global
->VAR_NORM_FORE_COLOR
377 && ps_global
->VAR_NORM_BACK_COLOR
378 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
379 ps_global
->VAR_NORM_FORE_COLOR
)
380 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
381 ps_global
->VAR_NORM_BACK_COLOR
))){
383 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
384 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
385 if(!pico_is_good_colorpair(hdrcolor
))
386 free_color_pair(&hdrcolor
);
390 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
393 gf_puts(padding
, pc
);
394 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
397 char buf
[16], color
[64];
401 h
= new_handle(handlesp
);
405 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
406 buf
[sizeof(buf
)-1] = '\0';
408 if(!(flgs
& FM_NOCOLOR
)
409 && handle_start_color(color
, sizeof(color
), &l
, 1)){
410 lastc
= get_cur_embedded_color();
411 if(!gf_nputs(color
, (long) l
, pc
))
414 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
415 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
418 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
419 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
422 tmp_20k_buf
[0] = '\0';
424 if(!format_env_puts(tmp_20k_buf
, pc
))
429 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
430 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
434 if(!embed_color(lastc
, pc
))
437 free_color_pair(&lastc
);
439 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
442 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
447 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
448 if(!gf_puts(tmp_20k_buf
, pc
))
452 if(!gf_puts(NEWLINE
, pc
))
455 } /* end of ATTENDEES */
457 free_vevent_summary(&vesy
);
459 avail
= width
- m1
-2;
461 dwid
= MAX(MIN(40, avail
), 0);
464 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
465 repeat_char(dwid
, '-'));
466 gf_puts(tmp_20k_buf
, pc
);
470 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
472 char *rawtext
, *caltext
;
473 unsigned long callen
;
474 VCALENDAR_S
*vcal
= NULL
;
478 if(flgs
& FM_NEW_MESS
) {
479 zero_atmts(ps_global
->atmts
);
480 describe_mime(body
, "", 1, 1, 0, flgs
);
483 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
484 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
485 b
= mail_body (ps_global
->mail_stream
, msgno
, a
->number
);
487 gf_puts(_("Error fetching calendar body part"), pc
);
488 gf_puts(NEWLINE
, pc
);
491 if(b
->sparep
== NULL
){
492 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
493 if(rawtext
== NULL
|| *rawtext
== '\0'){
494 gf_puts(_("Error fetching calendar text"), pc
);
495 gf_puts(NEWLINE
, pc
);
498 rawtext
[callen
] = '\0'; /* chop off cookie */
501 caltext
= rfc822_base64(rawtext
, strlen(rawtext
), &callen
);
503 gf_puts(_("Error in calendar base64 encoding"), pc
);
504 gf_puts(NEWLINE
, pc
);
509 case ENCQUOTEDPRINTABLE
:
510 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
512 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
513 gf_puts(NEWLINE
, pc
);
518 default: caltext
= cpystr(rawtext
);
521 vcal
= ical_parse_text(caltext
);
522 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
523 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
524 fs_give((void **) &caltext
);
527 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
528 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
529 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
530 if(vcal
->comp
[VEvent
] != NULL
){
531 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
532 } /* else another type of entry in the calendar */
534 gf_puts(NEWLINE
, pc
);
542 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
544 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
545 int is_in_sig
= OUT_SIG_BLOCK
;
546 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
552 || (ps_global
->full_header
== 2
553 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
555 /*--- Server is not an IMAP2bis, It can't parse MIME
556 so we just show the text here. Hopefully the
557 message isn't a MIME message
561 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
562 msgno
, NULL
, NULL
, NIL
)) != NULL
){
564 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
565 return("Write Error");
567 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
571 * We need to translate the message
572 * into UTF-8, but that's trouble in the full header case
573 * because we don't know what to translate from. We'll just
574 * take a guess by looking for the first text part and
577 if(body
&& body
->type
== TYPETEXT
)
578 charset
= parameter_val(body
->parameter
, "charset");
579 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
580 && body
->nested
.part
->body
.type
== TYPETEXT
)
581 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
583 charset
= ps_global
->display_charmap
;
585 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
586 /* transliterate message text to UTF-8 */
587 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
590 /* link in filters, similar to what is done in decode_text() */
591 if(!ps_global
->pass_ctrl_chars
){
592 gf_link_filter(gf_escape_filter
, NULL
);
594 gf_link_filter(gf_control_filter
,
595 gf_control_filter_opt(&filt_only_c0
));
598 gf_link_filter(gf_tag_filter
, NULL
);
600 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
601 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
602 || F_ON(F_SCAN_ADDR
, ps_global
))
604 gf_link_filter(gf_line_test
,
605 gf_line_test_opt(url_hilite
,
606 gf_url_hilite_opt(&uh
,handlesp
,0)));
609 if((flgs
& FM_DISPLAY
)
610 && !(flgs
& FM_NOCOLOR
)
612 && ps_global
->VAR_SIGNATURE_FORE_COLOR
613 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
614 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
617 if((flgs
& FM_DISPLAY
)
618 && !(flgs
& FM_NOCOLOR
)
620 && ps_global
->VAR_QUOTE1_FORE_COLOR
621 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
622 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
625 if(!(flgs
& FM_NOWRAP
)){
626 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
628 && !(flgs
& FM_NOCOLOR
)
629 && pico_usingcolor())
630 wrapflags
|= GFW_USECOLOR
;
631 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
633 ? NULL
: format_view_margin(),
638 gf_link_filter(gf_nvtnl_local
, NULL
);
639 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
640 /* TRANSLATORS: There was an error putting together a message for
641 viewing. The arg is the description of the error. */
642 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
643 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
644 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
645 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
646 && gf_puts(NEWLINE
, pc
))
654 if(!gf_puts(NEWLINE
, pc
)
655 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
656 || !gf_puts(NEWLINE
, pc
)
657 || !gf_puts(NEWLINE
, pc
))
658 return("Write Error");
664 /*======== Now loop through formatting all the parts =======*/
665 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
666 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
669 if(a
->body
->type
== TYPEMULTIPART
){
671 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
673 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
674 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
675 return("Write Error");
683 if(a
->suppress_editorial
)
686 if(!(flgs
& FM_NOEDITORIAL
)
687 && (!gf_puts(NEWLINE
, pc
)
688 || (decode_err
= part_desc(a
->number
, a
->body
,
690 ? (a
->can_display
!= MCD_NONE
)
692 : 3, width
, flgs
, pc
))))
693 return("Write Error");
698 switch(a
->body
->type
){
702 * If a message is multipart *and* the first part of it
703 * is text *and that text is empty, there is a good chance that
704 * there was actually something there that c-client was
705 * unable to parse. Here we report the empty message body
706 * and insert the raw RFC822.TEXT (if full-headers are
709 if(body
->type
== TYPEMULTIPART
710 && a
== ps_global
->atmts
711 && a
->body
->size
.bytes
== 0
712 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
715 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
716 "Empty or malformed message%s.",
717 ps_global
->full_header
== 2
718 ? ". Displaying raw text"
719 : ". Use \"H\" to see raw text");
720 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
722 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
723 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
724 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
725 return("Write Error");
727 if(ps_global
->full_header
== 2
728 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
729 a
->number
, pc
, flgs
))){
730 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
731 "%s%s [ Formatting error: %s ]%s%s",
732 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
733 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
734 if(!gf_puts(tmp_20k_buf
, pc
))
735 return("Write Error");
742 * Don't write our delimiter if this text part is
743 * the first part of a message/rfc822 segment...
745 if(show_parts
&& a
!= ps_global
->atmts
746 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
748 || (a
[-1].body
->type
== TYPEMULTIPART
749 && a
[-1].body
->subtype
750 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
751 && &a
[-1] != ps_global
->atmts
752 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
755 && !(flgs
& FM_NOEDITORIAL
)){
756 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
757 tmp1
= "Calendar entry";
759 tmp1
= a
->body
->description
? a
->body
->description
761 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
762 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
764 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
766 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
767 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
768 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
769 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
770 return("Write Error");
773 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
774 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
775 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
780 tmp1
= a
->body
->description
? a
->body
->description
781 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
783 : "Included Message";
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';
791 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
792 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
793 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
794 return("Write Error");
796 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
797 /* imapenvonly, we may not have all the headers we need */
798 if(a
->body
->nested
.msg
->env
->imapenvonly
)
799 mail_fetch_header(ps_global
->mail_stream
, msgno
,
800 a
->number
, NULL
, NULL
, FT_PEEK
);
801 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
802 a
->body
->nested
.msg
->env
, hp
,
803 NULL
, handlesp
, flgs
, NULL
, pc
)){
804 case -1 : /* write error */
805 return("Write Error");
807 case 1 : /* fetch error */
808 if(!(gf_puts("[ Error fetching header ]", pc
)
809 && !gf_puts(NEWLINE
, pc
)))
810 return("Write Error");
815 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
816 int *margin
, avail
, m1
, m2
;
819 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
821 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
824 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
827 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
828 || !gf_puts(NEWLINE
, pc
)
829 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
830 return("Write Error");
833 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
834 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
837 if(!gf_puts(NEWLINE
, pc
))
838 return("Write Error");
843 if((decode_err
= part_desc(a
->number
, a
->body
,
844 (flgs
& FM_DISPLAY
) ? 1 : 3,
845 width
, flgs
, pc
)) != NULL
)
846 return("Write Error");
853 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
854 && format_blip_seen(msgno
)))
855 return("Cannot format body.");
863 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
867 if(flgs
& FM_NEW_MESS
) {
868 zero_atmts(ps_global
->atmts
);
869 describe_mime(body
, "", 1, 1, 0, flgs
);
872 /*----- First do the list of parts/attachments if needed ----*/
873 if((flgs
& FM_DISPLAY
)
874 && (ps_global
->atmts
[1].description
875 || (ps_global
->atmts
[0].body
876 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
877 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
878 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
879 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
880 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
881 COLOR_PAIR
*hdrcolor
= NULL
;
883 if((flgs
& FM_DISPLAY
)
884 && !(flgs
& FM_NOCOLOR
)
886 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
887 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
888 && ps_global
->VAR_NORM_FORE_COLOR
889 && ps_global
->VAR_NORM_BACK_COLOR
890 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
891 ps_global
->VAR_NORM_FORE_COLOR
)
892 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
893 ps_global
->VAR_NORM_BACK_COLOR
))){
895 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
896 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
897 if(!pico_is_good_colorpair(hdrcolor
))
898 free_color_pair(&hdrcolor
);
902 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
905 * Attachment list header
910 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
913 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
916 hwid
= MAX(avail
, 0);
918 i
= utf8_width(_("Parts/Attachments:"));
919 partwid
= MIN(i
, hwid
);
920 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
923 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
924 if(!gf_puts(tmp
, pc
))
928 utf8_snprintf(tmp
, sizeof(tmp
),
930 /* TRANSLATORS: A label */
931 partwid
, partwid
, _("Parts/Attachments:"),
934 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
938 /*----- Figure max display widths -----*/
939 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
940 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
943 if((n
= utf8_width(a
->number
)) > maxnumwid
)
946 if((n
= utf8_width(a
->size
)) > maxsizewid
)
951 * ----- adjust max lengths for nice display -----
953 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
957 avail
= width
- m1
- m2
;
959 s1
= MAX(MIN(1, avail
), 0);
962 dwid
= MAX(MIN(1, avail
), 0);
965 s2
= MAX(MIN(1, avail
), 0);
968 maxnumwid
= MIN(maxnumwid
, width
/3);
969 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
972 s3
= MAX(MIN(1, avail
), 0);
975 shownwid
= MAX(MIN(5, avail
), 0);
978 s4
= MAX(MIN(3, avail
), 0);
981 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
984 s5
= MAX(MIN(2, avail
), 0);
987 descwid
= MAX(0, avail
);
989 /*----- Format the list of attachments -----*/
990 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
991 COLOR_PAIR
*lastc
= NULL
;
993 int thisdescwid
, padwid
;
995 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
998 if(a
->body
->type
== TYPEMULTIPART
999 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1003 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1004 thisdescwid
= MIN(i
, descwid
);
1005 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1008 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1009 if(!gf_puts(tmp
, pc
))
1013 utf8_snprintf(tmp
, sizeof(tmp
),
1014 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1017 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1019 maxnumwid
, maxnumwid
,
1021 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1025 a
->shown
? "Shown" :
1026 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1030 a
->size
? a
->size
: "",
1032 thisdescwid
, thisdescwid
,
1033 (descwid
> 2 && a
->description
) ? a
->description
: "");
1035 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1038 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1039 char buf
[16], color
[64];
1043 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1047 h
= new_handle(handlesp
);
1051 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1052 buf
[sizeof(buf
)-1] = '\0';
1054 if(!(flgs
& FM_NOCOLOR
)
1055 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1056 lastc
= get_cur_embedded_color();
1057 if(!gf_nputs(color
, (long) l
, pc
))
1060 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1061 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1064 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1065 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1071 if(!format_env_puts(tmpp
, pc
))
1074 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1076 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1077 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1081 if(!embed_color(lastc
, pc
))
1084 free_color_pair(&lastc
);
1086 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1089 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1094 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1095 if(!gf_puts(tmp
, pc
))
1099 if(!gf_puts(NEWLINE
, pc
))
1104 * Dashed line after list
1108 avail
= width
- m1
- m2
;
1109 hwid
= MAX(avail
, 0);
1111 dashwid
= MAX(MIN(40, hwid
-2), 0);
1112 padwid
= hwid
- dashwid
;
1114 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1115 if(!gf_puts(tmp
, pc
))
1119 snprintf(tmp
, sizeof(tmp
),
1121 repeat_char(dashwid
, '-'),
1122 padwid
, padwid
, "");
1125 avail
= width
- m1
-2;
1127 dashwid
= MAX(MIN(40, avail
), 0);
1130 snprintf(tmp
, sizeof(tmp
),
1133 repeat_char(dashwid
, '-'));
1136 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1140 free_color_pair(&hdrcolor
);
1149 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1150 * of body part fetches as we're formatting) for the
1151 * given message isn't set (likely because there
1152 * weren't any parts suitable for display), then make
1153 * sure to set it here.
1156 format_blip_seen(long int msgno
)
1160 if(msgno
> 0L && ps_global
->mail_stream
1161 && msgno
<= ps_global
->mail_stream
->nmsgs
1162 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1164 && !ps_global
->mail_stream
->rdonly
)
1165 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1172 * is_an_env_hdr - is this name a header in the envelope structure?
1174 * name - the header name to check
1177 is_an_env_hdr(char *name
)
1181 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1182 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1192 * is_an_addr_hdr - is this an address header?
1194 * name - the header name to check
1197 is_an_addr_hdr(char *fieldname
)
1199 char fbuf
[FBUF_LEN
+1];
1200 char *colon
, *fname
;
1201 static char *addr_headers
[] = {
1217 /* so it is pointing to NULL */
1218 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1220 if((colon
= strindex(fieldname
, ':')) != NULL
){
1221 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1222 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1228 if(fname
&& *fname
){
1229 for(p
= addr_headers
; *p
; p
++)
1230 if(!strucmp(fname
, *p
))
1234 return((*p
) ? 1 : 0);
1239 * Format a single field from the envelope
1242 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1243 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1248 fmt_env
= format_envelope
;
1250 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1251 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1252 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1259 * Look through header string beginning with "begin", for the next
1260 * occurrence of header "field". Set "start" to that. Set "end" to point one
1261 * position past all of the continuation lines that go with "field".
1262 * That is, if "end" is converted to a null
1263 * character then the string "start" will be the next occurence of header
1264 * "field" including all of its continuation lines. Assume we
1265 * have CRLF's as end of lines.
1267 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1268 * make "end" the end of that header.
1270 * Returns 1 if found, 0 if not.
1273 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1275 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1280 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1286 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1287 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1288 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1289 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1292 * We require that start is at the beginning of a line, so
1293 * either it equals begin (which we assume is the beginning of a
1294 * line) or it is preceded by a CRLF.
1297 *start
= srchstr(begin_srch
, tmpfield
);
1298 while(*start
&& *start
!= begin
1299 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1300 begin_srch
= *start
+ 1;
1301 *start
= srchstr(begin_srch
, tmpfield
);
1308 for(p
= *start
; *p
; p
++){
1310 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1312 * The final 015 in the test above is to test for the end
1329 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1333 if(pico_usingcolor()){
1334 char *fg
= NULL
, *bg
= NULL
, *s
;
1335 char *basefg
= NULL
, *basebg
= NULL
;
1337 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1338 : ps_global
->VAR_NORM_FORE_COLOR
;
1339 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1340 : ps_global
->VAR_NORM_BACK_COLOR
;
1342 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1343 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1344 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1346 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1347 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1348 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1354 * The blacks are just known good colors for
1355 * testing whether the other color is good.
1357 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1358 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1359 if(pico_is_good_colorpair(tmp
))
1360 for(s
= color_embed(fg
, bg
);
1361 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1365 free_color_pair(&tmp
);
1369 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1370 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1375 colorstring
[buflen
-1] = '\0';
1382 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1385 if(pico_usingcolor()){
1386 char *fg
= NULL
, *bg
= NULL
, *s
;
1389 * We need to change the fg and bg colors back even if they
1390 * are the same as the Normal Colors so that color_a_quote
1391 * will have a chance to pick up those colors as the ones to
1392 * switch to. We don't do this before the handle above so that
1393 * the quote color will flow into the selectable item when
1394 * the selectable item color is partly the same as the
1395 * normal color. That is, suppose the normal color was black on
1396 * cyan and the selectable color was blue on cyan, only a fg color
1397 * change. We preserve the only-a-fg-color-change in a quote by
1398 * letting the quote background color flow into the selectable text.
1400 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1401 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1403 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1404 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1406 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1407 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1412 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1416 colorstring
[buflen
-1] = '\0';
1423 url_embed(int embed
)
1425 static char buf
[3] = {TAG_EMBED
};
1433 * Paint the signature.
1436 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1438 struct variable
*vars
= ps_global
->vars
;
1440 COLOR_PAIR
*col
= NULL
;
1442 if(is_in_sig
== NULL
)
1445 in_sig_block
= (int *) is_in_sig
;
1447 if(!strcmp(line
, SIGDASHES
))
1448 *in_sig_block
= START_SIG_BLOCK
;
1449 else if(*line
== '\0')
1451 * Suggested by Eduardo: allow for a blank line right after
1454 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1455 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1457 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1458 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1460 if(*in_sig_block
!= OUT_SIG_BLOCK
1461 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1462 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1463 VAR_SIGNATURE_BACK_COLOR
))){
1464 if(!pico_is_good_colorpair(col
))
1465 free_color_pair(&col
);
1469 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1471 ins
= gf_line_test_new_ins(ins
, line
,
1472 color_embed(col
->fg
, col
->bg
),
1475 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1476 fg
[sizeof(fg
)-1] = '\0';
1477 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1478 bg
[sizeof(bg
)-1] = '\0';
1481 * Loop watching colors, and override with
1482 * signature color whenever the normal foreground and background
1483 * colors are in force.
1487 if(*p
++ == TAG_EMBED
){
1491 p
+= *p
+ 1; /* skip handle key */
1495 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1496 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1497 p
+= RGBLEN
; /* advance past color value */
1499 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1500 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1501 ins
= gf_line_test_new_ins(ins
, p
,
1502 color_embed(col
->fg
,NULL
),
1507 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1508 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1509 p
+= RGBLEN
; /* advance past color value */
1511 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1512 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1513 ins
= gf_line_test_new_ins(ins
, p
,
1514 color_embed(NULL
,col
->bg
),
1524 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1525 color_embed(VAR_NORM_FORE_COLOR
,
1526 VAR_NORM_BACK_COLOR
),
1528 free_color_pair(&col
);
1536 * Line filter to add color to displayed headers.
1539 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1541 static char field
[FBUF_LEN
+ 1];
1542 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1543 char *p
, *q
, *value
, *beg
;
1545 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1546 struct variable
*vars
= ps_global
->vars
;
1548 field
[FBUF_LEN
] = '\0';
1550 if(isspace((unsigned char)*line
)) /* continuation line */
1553 if(!(value
= strindex(line
, ':')))
1556 memset(field
, 0, sizeof(field
));
1557 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1560 for(value
++; isspace((unsigned char)*value
); value
++)
1563 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1564 fg
[sizeof(fg
)-1] = '\0';
1565 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1566 bg
[sizeof(bg
)-1] = '\0';
1569 * Split into two cases depending on whether this is a header which
1570 * contains addresses or not. We may color addresses separately.
1572 if(is_an_addr_hdr(field
)){
1575 * If none of the patterns are for this header, don't bother parsing
1576 * and checking each address.
1578 if(!any_hdr_color(field
))
1582 * First check for patternless patterns which color whole line.
1584 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1585 if(pico_is_good_colorpair(color
)){
1586 ins
= gf_line_test_new_ins(ins
, value
,
1587 color_embed(color
->fg
, color
->bg
),
1589 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1590 fg
[sizeof(fg
)-1] = '\0';
1591 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1592 bg
[sizeof(bg
)-1] = '\0';
1596 free_color_pair(&color
);
1600 * Then go through checking address by address.
1601 * Keep track of quotes and watch for color changes, and override
1602 * with most recent header color whenever the normal foreground
1603 * and background colors are in force.
1609 /* skip next character */
1610 if(*(p
+1) && (in_comment
|| in_quote
))
1619 in_quote
= 1 - in_quote
;
1637 if(!(in_quote
|| in_comment
)){
1638 /* we reached the end of this address */
1641 free_color_pair(&color
);
1643 if((color
= hdr_color(field
, beg
,
1644 ps_global
->hdr_colors
)) != NULL
){
1645 if(pico_is_good_colorpair(color
)){
1647 ins
= gf_line_test_new_ins(ins
, beg
,
1648 color_embed(color
->fg
,
1652 for(q
= p
; q
> beg
&&
1653 isspace((unsigned char)*(q
-1)); q
--)
1656 ins
= gf_line_test_new_ins(ins
, q
,
1657 color_embed(fg
, bg
),
1661 free_color_pair(&color
);
1666 for(p
++; isspace((unsigned char)*p
); p
++)
1680 p
+= *p
+ 1; /* skip handle key */
1685 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1686 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1687 p
+= RGBLEN
; /* advance past color value */
1689 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1690 ins
= gf_line_test_new_ins(ins
, p
,
1691 color_embed(color
->fg
,NULL
),
1697 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1698 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1699 p
+= RGBLEN
; /* advance past color value */
1701 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1702 ins
= gf_line_test_new_ins(ins
, p
,
1703 color_embed(NULL
,color
->bg
),
1720 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1723 if(*q
&& !(in_quote
|| in_comment
)){
1724 /* we reached the end of this address */
1726 free_color_pair(&color
);
1728 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1729 if(pico_is_good_colorpair(color
)){
1731 ins
= gf_line_test_new_ins(ins
, beg
,
1732 color_embed(color
->fg
,
1735 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1738 ins
= gf_line_test_new_ins(ins
, q
,
1739 color_embed(fg
, bg
),
1743 free_color_pair(&color
);
1748 free_color_pair(&color
);
1751 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1752 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1753 VAR_HEADER_GENERAL_BACK_COLOR
),
1758 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1761 if(pico_is_good_colorpair(color
)){
1762 ins
= gf_line_test_new_ins(ins
, value
,
1763 color_embed(color
->fg
, color
->bg
),
1767 * Loop watching colors, and override with header
1768 * color whenever the normal foreground and background
1769 * colors are in force.
1773 if(*p
++ == TAG_EMBED
){
1777 p
+= *p
+ 1; /* skip handle key */
1781 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1782 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1783 p
+= RGBLEN
; /* advance past color value */
1785 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1786 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1787 ins
= gf_line_test_new_ins(ins
, p
,
1788 color_embed(color
->fg
,NULL
),
1793 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1794 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1795 p
+= RGBLEN
; /* advance past color value */
1797 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1798 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1799 ins
= gf_line_test_new_ins(ins
, p
,
1800 color_embed(NULL
,color
->bg
),
1810 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1811 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1812 VAR_HEADER_GENERAL_BACK_COLOR
),
1816 free_color_pair(&color
);
1825 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1827 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1828 *weburlp
= NULL
, *mailurlp
= NULL
;
1829 int n
, n1
, n2
, n3
, l
;
1830 char buf
[256], color
[256];
1834 for(lp
= line
; ; lp
= up
+ n
){
1835 /* scan for all of them so we can choose the first */
1836 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1837 urlp
= rfc1738_scan(lp
, &n1
);
1838 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1839 weburlp
= web_host_scan(lp
, &n2
);
1840 if(F_ON(F_SCAN_ADDR
,ps_global
))
1841 mailurlp
= mail_addr_scan(lp
, &n3
);
1843 if(urlp
|| weburlp
|| mailurlp
){
1845 weburlp
? weburlp
: mailurlp
;
1846 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1848 if(mailurlp
&& mailurlp
< up
)
1853 weburlp
= mailurlp
= NULL
;
1855 else if(up
== weburlp
){
1867 uh
= (URL_HILITE_S
*) local
;
1869 h
= new_handle(uh
->handlesp
);
1871 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1872 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1873 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1874 h
->h
.url
.path
[n
+10-1] = '\0';
1876 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1877 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1878 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1879 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1882 buf
[1] = TAG_HANDLE
;
1883 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1884 buf
[sizeof(buf
)-1] = '\0';
1885 buf
[2] = strlen(&buf
[3]);
1886 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1888 /* in case it was the current selection */
1889 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1891 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1892 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1894 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1896 urlp
= weburlp
= mailurlp
= NULL
;
1904 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1906 static int check_for_urls
= 0;
1909 if(isspace((unsigned char)*line
)) /* continuation, check or not
1910 depending on last line */
1914 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1919 if(((ft
= pine_header_standard(line
)) == FreeText
1921 || ft
== TypeUnknown
)
1922 && strucmp(line
, "message-id")
1923 && strucmp(line
, "newsgroups")
1924 && strucmp(line
, "references")
1925 && strucmp(line
, "in-reply-to")
1926 && strucmp(line
, "received")
1927 && strucmp(line
, "date")){
1936 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1943 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1948 struct variable
*vars
= ps_global
->vars
;
1953 total_wid
= *((int *) local
);
1955 /* calculate width of line */
1965 p
+= *p
+ 1; /* skip handle key */
1970 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1982 default: /* literal embed char */
1990 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
1996 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2002 if(total_wid
> wid
){
2003 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2004 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2005 VAR_HEADER_GENERAL_BACK_COLOR
),
2007 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2008 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2009 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2010 color_embed(VAR_NORM_FORE_COLOR
,
2011 VAR_NORM_BACK_COLOR
),
2023 url_external_specific_handler(char *url
, int len
)
2025 static char list
[UES_LEN
* UES_MAX
];
2031 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2032 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2035 else{ /* initialize! */
2036 char **l
, *test
, *cmd
, *p
, *p2
;
2039 memset(list
, 0, sizeof(list
));
2040 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2041 get_pair(*l
, &test
, &cmd
, 1, 1);
2043 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2046 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2047 if((p2
= strchr(p
, ',')) != NULL
){
2048 if((n
= p2
- p
) < UES_LEN
){
2049 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2054 "* * * HANLDER TOO LONG: %.*s\n", n
,
2060 if(strlen(p
) <= UES_LEN
){
2061 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2070 fs_give((void **) &test
);
2073 fs_give((void **) &cmd
);
2082 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2083 imapuid_t
*uid
, char **search
, int silent
)
2085 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2086 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2089 int rv
= URL_IMAP_ERROR
;
2092 * Since we're planting nulls, operate on a temporary copy...
2094 scheme
= silent
? NULL
: "IMAP";
2095 url
= cpystr(true_url
+ 7);
2097 /* Try to pick apart the "iserver" portion */
2098 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2102 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2104 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2107 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2109 server
= rfc1738_str(p
);
2111 /* only ";auth=*" supported (and also ";auth=anonymous") */
2112 if((p
= srchstr(url
, ";auth=")) != NULL
){
2114 auth
= rfc1738_str(p
+ 6);
2118 user
= rfc1738_str(url
);
2121 server
= rfc1738_str(url
);
2124 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2127 * "iserver" in hand, pick apart the "icommand"...
2130 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2134 * No "icommand" (all top-level folders) or "imailboxlist"...
2137 *p
= '\0'; /* tie off criteria */
2138 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2139 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2140 rv
|= URL_IMAP_IMBXLSTLSUB
;
2141 else if(strucmp(p
, "list"))
2142 return(url_bogus_imap(&url
, scheme
,
2143 "Invalid list type specified"));
2146 rv
|= URL_IMAP_ISERVERONLY
;
2150 /* build folder list from specified server/criteria/list-method */
2151 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2152 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2153 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2154 user
? "user=\"" : "Anonymous",
2157 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2158 (*folder
)[l
] = '\0';
2159 rv
|= URL_IMAP_IMAILBOXLIST
;
2162 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2163 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2164 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2165 *section
= '\0'; /* tie off UID */
2166 section
= rfc1738_str(section
+ 10);
2167 /* BUG: verify valid section spec ala rfc 2060 */
2169 "-- URL IMAP FOLDER: section not used: %s\n",
2170 section
? section
: "?"));
2173 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2174 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2176 /* optional "uidvalidity"? */
2177 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2180 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2181 return(url_bogus_imap(&url
, scheme
,
2182 "Invalid UIDVALIDITY"));
2185 mailbox
= rfc1738_str(cmd
);
2186 rv
= URL_IMAP_IMESSAGEPART
;
2188 else{ /* "imessagelist" */
2189 /* optional "uidvalidity"? */
2190 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2193 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2194 return(url_bogus_imap(&url
, scheme
,
2195 "Invalid UIDVALIDITY"));
2198 /* optional "enc_search"? */
2199 if((p
= strchr(cmd
, '?')) != NULL
){
2202 *search
= cpystr(rfc1738_str(p
+ 1));
2203 /* BUG: verify valid search spec ala rfc 2060 */
2206 mailbox
= rfc1738_str(cmd
);
2207 rv
= URL_IMAP_IMESSAGELIST
;
2210 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2211 q_status_message(SM_ORDER
, 3, 3,
2212 "Unsupported authentication method. Using standard login.");
2215 * At this point our structure should contain the
2216 * digested url. Now put it together for c-client...
2218 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2219 + (user
? (strlen(user
)+2) : 9);
2220 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2221 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2222 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2223 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2227 (*folder
)[l
] = '\0';
2230 fs_give((void **) &url
);
2236 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2238 fs_give((void **) freeme
);
2239 (void) url_bogus(url
, problem
);
2240 return(URL_IMAP_ERROR
);
2245 * url_bogus - report url syntax errors and such
2248 url_bogus(char *url
, char *reason
)
2250 dprint((2, "-- bogus url \"%s\": %s\n",
2251 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2253 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2254 "Malformed \"%.*s\" URL: %.200s",
2255 (void *) (strchr(url
, ':') - url
), url
, reason
);
2262 /*----------------------------------------------------------------------
2263 Format header text suitable for display
2265 Args: stream -- mail stream for various header text fetches
2266 msgno -- sequence number in stream of message we're interested in
2267 section -- which section of message
2268 env -- pointer to msg's envelope
2269 hdrs -- struct containing what's to get formatted
2270 prefix -- prefix to append to each output line
2271 handlesp -- address of pointer to the message's handles
2272 flags -- FM_ flags, see pith/mailview.h
2273 final_pc -- function to write header text with
2275 Result: 0 if all's well, -1 if write error, 1 if fetch error
2277 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2278 in the local convention.
2282 #define FHT_WRTERR -1
2283 #define FHT_FTCHERR 1
2285 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2286 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2287 fmt_env_t fmt_env
, gf_io_t final_pc
)
2291 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2295 gf_io_t tmp_pc
, tmp_gc
;
2296 struct variable
*vars
= ps_global
->vars
;
2298 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2299 gf_set_so_writec(&tmp_pc
, tmp_store
);
2304 fmt_env
= format_envelope
;
2306 if(ps_global
->full_header
== 2){
2307 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2311 * First, calculate how big a fields array we need.
2314 /* Custom header viewing list specified */
2315 if(hdrs
->type
== HD_LIST
){
2316 /* view all these headers */
2318 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2319 if(!is_an_env_hdr(q
))
2322 /* view all except these headers */
2324 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2328 nfields
--; /* subtract one for ALL_EXCEPT field */
2332 nfields
= 6; /* default view */
2334 /* allocate pointer space */
2336 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2337 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2340 if(hdrs
->type
== HD_LIST
){
2341 /* view all these headers */
2343 /* put the non-envelope headers in fields */
2345 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2346 if(!is_an_env_hdr(q
))
2349 /* view all except these headers */
2351 /* put the list of headers not to view in fields */
2353 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2354 if(strucmp(ALL_EXCEPT
, q
))
2362 fields
[i
= 0] = "Resent-Date";
2363 fields
[++i
] = "Resent-From";
2364 fields
[++i
] = "Resent-To";
2365 fields
[++i
] = "Resent-cc";
2366 fields
[++i
] = "Resent-Subject";
2372 /* custom view with exception list */
2373 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2375 * Go through each header in h and print it.
2376 * First we check to see if it is an envelope header so we
2377 * can print our envelope version of it instead of the raw version.
2380 /* fetch all the other headers */
2382 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2385 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2387 char tmp
[MAILTMPLEN
+1];
2390 colon_loc
= strindex(start
, ':');
2391 if(colon_loc
&& colon_loc
< finish
){
2392 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2393 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2398 if(colon_loc
&& is_an_env_hdr(tmp
)){
2399 char *dummystart
, *dummyfinish
;
2402 * Pretty format for env hdrs.
2403 * If the same header appears more than once, only
2404 * print the last to avoid duplicates.
2405 * They should have been combined in the env when parsed.
2407 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2409 format_env_hdr(stream
, msgno
, section
, env
,
2410 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2413 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2414 hdrs
->charset
, flags
)) != 0)
2421 /* custom view or default */
2423 /* fetch the non-envelope headers */
2425 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2427 /* default envelope for default view */
2428 if(hdrs
->type
== HD_BFIELD
)
2429 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2431 /* go through each header in list, v initialized above */
2432 for(; (q
= *v
) != NULL
; v
++){
2433 if(is_an_env_hdr(q
)){
2434 /* pretty format for env hdrs */
2435 format_env_hdr(stream
, msgno
, section
, env
,
2436 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2440 * Go through h finding all occurences of this header
2441 * and all continuation lines, and output.
2444 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2446 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2447 hdrs
->charset
, flags
)) != 0)
2460 gf_clear_so_writec(tmp_store
);
2462 if(!rv
){ /* valid data? Do wrapping and filtering... */
2464 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2465 STORE_S
*df_store
= NULL
;
2467 so_seek(tmp_store
, 0L, 0);
2469 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2472 * Test for and act on any display filter
2473 * This barely makes sense. The display filter is going
2474 * to be getting UTF-8'ized headers here. In pre-alpine
2475 * pine the display filter was being fed already decoded
2476 * headers in whatever character set they were in.
2477 * The good news is that that didn't make much
2478 * sense either, so this shouldn't break anything.
2479 * It seems unlikely that anybody is doing anything useful
2480 * with the header part of display filters.
2482 if(ps_global
->tools
.display_filter
2483 && ps_global
->tools
.display_filter_trigger
2484 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2485 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2487 gf_set_so_writec(&tmp_pc
, df_store
);
2488 gf_set_so_readc(&tmp_gc
, df_store
);
2489 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2490 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2491 _("Formatting error: %s"), errstr
);
2495 so_seek(df_store
, 0L, 0);
2497 gf_clear_so_writec(df_store
);
2500 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2501 "No space for filtered text.");
2506 so_seek(tmp_store
, 0L, 0);
2507 gf_set_so_readc(&tmp_gc
, tmp_store
);
2511 int *margin
, wrapflags
= GFW_ONCOMMA
;
2514 gf_link_filter(gf_local_nvtnl
, NULL
);
2516 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2517 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2518 || F_ON(F_SCAN_ADDR
, ps_global
))
2520 gf_link_filter(gf_line_test
,
2521 gf_line_test_opt(url_hilite_hdr
,
2522 gf_url_hilite_opt(&uh
,handlesp
,1)));
2523 wrapflags
|= GFW_HANDLES
;
2526 if((flags
& FM_DISPLAY
)
2527 && !(flags
& FM_NOCOLOR
)
2528 && pico_usingcolor()
2529 && ((VAR_NORM_FORE_COLOR
2530 && VAR_HEADER_GENERAL_FORE_COLOR
2531 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2533 (VAR_NORM_BACK_COLOR
2534 && VAR_HEADER_GENERAL_BACK_COLOR
2535 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2536 wrapflags
|= GFW_HDRCOLOR
;
2538 if((flags
& FM_DISPLAY
)
2539 && !(flags
& FM_NOCOLOR
)
2540 && pico_usingcolor()
2541 && ps_global
->hdr_colors
){
2542 gf_link_filter(gf_line_test
,
2543 gf_line_test_opt(color_headers
, NULL
));
2544 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2547 if(prefix
&& *prefix
)
2548 column
= MAX(column
-strlen(prefix
), 50);
2550 margin
= format_view_margin();
2552 if(!(flags
& FM_NOWRAP
))
2553 gf_link_filter(gf_wrap
,
2554 gf_wrap_filter_opt(column
, column
,
2555 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2558 if(prefix
&& *prefix
)
2559 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2561 if((flags
& FM_DISPLAY
)
2562 && !(flags
& FM_NOCOLOR
)
2563 && pico_usingcolor()
2564 && ((VAR_NORM_FORE_COLOR
2565 && VAR_HEADER_GENERAL_FORE_COLOR
2566 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2568 (VAR_NORM_BACK_COLOR
2569 && VAR_HEADER_GENERAL_BACK_COLOR
2570 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2574 right_margin
= margin
? margin
[1] : 0;
2575 total_wid
= column
- right_margin
;
2577 gf_link_filter(gf_line_test
,
2578 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2579 wrapflags
|= GFW_HANDLES
;
2582 gf_link_filter(gf_nvtnl_local
, NULL
);
2584 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2586 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2587 "Can't build header : %.200s", errstr
);
2592 gf_clear_so_readc(df_store
);
2596 gf_clear_so_readc(tmp_store
);
2599 so_give(&tmp_store
);
2602 fs_give((void **)&h
);
2605 fs_give((void **)&fields
);
2611 /*----------------------------------------------------------------------
2612 Format RAW header text for display
2614 Args: stream -- mail stream for various header text fetches
2615 rawno -- sequence number in stream of message we're interested in
2616 section -- which section of message
2617 pc -- function to write header text with
2619 Result: 0 if all's well, -1 if write error, 1 if fetch error
2621 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2622 in the local convention.
2626 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2628 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2635 if(!gf_puts(NEWLINE
, pc
))
2638 if(ISRFCEOL(h
)) /* all done! */
2641 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2642 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2643 c
= (unsigned char) *h
++;
2644 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2645 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2648 else if(!(*pc
)(*h
++))
2653 return(FHT_FTCHERR
);
2660 /*----------------------------------------------------------------------
2661 Format c-client envelope data suitable for display
2663 Args: s -- mail stream for various header text fetches
2664 n -- raw sequence number in stream of message we're interested in
2665 sect -- which section of message
2666 e -- pointer to msg's envelope
2667 pc -- function to write header text with
2668 which -- which header lines to write
2670 flags -- FM_ flags, see pith/mailview.h
2672 Result: 0 if all's well, -1 if write error, 1 if fetch error
2674 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2675 in the local convention.
2679 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2680 long int which
, char *oacs
, int flags
)
2682 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2687 if((which
& FE_DATE
) && e
->date
) {
2689 snprintf(buftmp
, sizeof(buftmp
), "%s",
2690 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2691 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2692 buftmp
[sizeof(buftmp
)-1] = '\0';
2693 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2694 SIZEOF_20KBUF
, buftmp
);
2696 format_env_puts(p2
, pc
);
2697 gf_puts(NEWLINE
, pc
);
2700 if((which
& FE_FROM
) && e
->from
)
2701 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2703 if((which
& FE_REPLYTO
) && e
->reply_to
2704 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2705 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2707 if((which
& FE_TO
) && e
->to
)
2708 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2710 if((which
& FE_CC
) && e
->cc
)
2711 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2713 if((which
& FE_BCC
) && e
->bcc
)
2714 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2716 if((which
& FE_RETURNPATH
) && e
->return_path
)
2717 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2720 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2722 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2723 if (!e
->ngpathexists
&& e
->message_id
&&
2724 strncmp (e
->message_id
,"<alpine.",8) &&
2725 strncmp (e
->message_id
,"<Pine.",6) &&
2726 strncmp (e
->message_id
,"<MS-C.",6) &&
2727 strncmp (e
->message_id
,"<MailManager.",13) &&
2728 strncmp (e
->message_id
,"<EasyMail.",11) &&
2729 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2732 q_status_message(SM_ORDER
, 0, 3,
2733 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2736 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2737 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2739 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2740 char *freeme
= NULL
;
2745 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2747 if(flags
& FM_DISPLAY
2748 && (ps_global
->display_keywords_in_subject
2749 || ps_global
->display_keywordinits_in_subject
)){
2751 /* don't bother if no keywords are defined */
2752 if(some_user_flags_defined(s
))
2753 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2754 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2755 NULL
, ps_global
->VAR_KW_BRACES
);
2758 format_env_puts(p2
, pc
);
2761 fs_give((void **) &freeme
);
2763 gf_puts(NEWLINE
, pc
);
2766 if((which
& FE_SENDER
) && e
->sender
2767 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2768 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2770 if((which
& FE_MESSAGEID
) && e
->message_id
){
2773 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2774 format_env_puts(p2
, pc
);
2775 gf_puts(NEWLINE
, pc
);
2778 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2779 q
= "In-Reply-To: ";
2781 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);
2782 format_env_puts(p2
, pc
);
2783 gf_puts(NEWLINE
, pc
);
2786 if((which
& FE_REFERENCES
) && e
->references
) {
2789 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2790 format_env_puts(p2
, pc
);
2791 gf_puts(NEWLINE
, pc
);
2799 * The argument fieldname is something like "Subject:..." or "Subject".
2800 * Look through the specs in speccolor for a match of the fieldname,
2801 * and return the color that goes with any match, or NULL.
2802 * Caller should free the color.
2805 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2807 SPEC_COLOR_S
*hc
= NULL
;
2808 COLOR_PAIR
*color_pair
= NULL
;
2809 char *colon
, *fname
;
2810 char fbuf
[FBUF_LEN
+1];
2814 colon
= strindex(fieldname
, ':');
2816 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2817 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2824 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2825 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2830 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2831 if(srchstr(value
, pat
->substring
))
2838 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2839 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2846 * The argument fieldname is something like "Subject:..." or "Subject".
2847 * Look through the specs in hdr_colors for a match of the fieldname,
2848 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2851 any_hdr_color(char *fieldname
)
2853 SPEC_COLOR_S
*hc
= NULL
;
2854 char *colon
, *fname
;
2855 char fbuf
[FBUF_LEN
+1];
2857 colon
= strindex(fieldname
, ':');
2859 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2860 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2867 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2868 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2875 /*----------------------------------------------------------------------
2876 Format an address field, wrapping lines nicely at commas
2878 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2879 addr -- ADDRESS structure to format
2881 Result: A formatted, malloced string is returned.
2882 ----------------------------------------------------------------------*/
2884 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2885 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2888 int trailing
= 0, group
= 0;
2895 * quickly run down address list to make sure none are patently bogus.
2896 * If so, just blat raw field out.
2898 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2899 if(atmp
->host
&& atmp
->host
[0] == '.'){
2900 char *field
, *fields
[2];
2903 fields
[0] = cpystr(field_name
);
2904 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2907 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2910 for(t
= h
= field
; *h
; t
++)
2911 if(*t
== '\015' && *(t
+1) == '\012'){
2912 *t
= '\0'; /* tie off line */
2913 format_env_puts(h
, pc
);
2914 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2915 gf_puts(NEWLINE
, pc
); /* more to write */
2919 else if(!*t
){ /* shouldn't happen much */
2921 format_env_puts(h
, pc
);
2926 fs_give((void **)&field
);
2929 fs_give((void **)&fields
[0]);
2930 gf_puts(NEWLINE
, pc
);
2931 dprint((2, "Error in \"%s\" field address\n",
2932 field_name
? field_name
: "?"));
2936 gf_puts(field_name
, pc
);
2939 atmp
= addr
->next
; /* remember what's next */
2941 if(!addr
->host
&& addr
->mailbox
){
2942 mtmp
= addr
->mailbox
;
2943 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2944 (unsigned char *)tmp_20k_buf
,
2945 SIZEOF_20KBUF
, addr
->mailbox
));
2948 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2949 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2950 tmp_20k_buf
[10000-1] = '\0';
2952 if(!trailing
) /* 1st pass, just address */
2954 else{ /* else comma, unless */
2955 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2956 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2959 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2967 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2969 addr
->personal
= ptmp
; /* restore old personal ptr */
2970 if(!addr
->host
&& addr
->mailbox
){
2971 fs_give((void **)&addr
->mailbox
);
2972 addr
->mailbox
= mtmp
;
2979 gf_puts(NEWLINE
, pc
);
2985 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
2986 /* RFC822 continuation, must start with CRLF */
2987 #define RFC822CONT "\015\012 "
2989 /* Write RFC822 address with some quoting turned off.
2991 * address to interpret
2993 * (This is a copy of c-client's rfc822_write_address except
2994 * we don't quote double quote and dot in personal names. It writes
2995 * to a gf_io_t instead of to a buffer so that we don't have to worry
2996 * about fixed sized buffer overflowing. It's also special cased to deal
2997 * with only a single address.)
2999 * The idea is that there are some places where we'd just like to display
3000 * the personal name as is before applying confusing quoting. However,
3001 * we do want to be careful not to break things that should be quoted so
3002 * we'll only use this where we are sure. Quoting may look ugly but it
3003 * doesn't usually break anything.
3006 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3008 extern const char *rspecials
;
3010 if (adr
->host
) { /* ordinary address? */
3011 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3012 else { /* no, must use phrase <route-addr> form */
3014 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3016 gf_puts(" <", pc
); /* write address delimiter */
3017 pine_rfc822_address(adr
, pc
);
3018 gf_puts (">", pc
); /* closing delimiter */
3024 else if (adr
->mailbox
) { /* start of group? */
3025 /* yes, write group name */
3026 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3028 gf_puts (": ", pc
); /* write group identifier */
3029 *group
= 1; /* in a group */
3031 else if (*group
) { /* must be end of group (but be paranoid) */
3033 *group
= 0; /* no longer in that group */
3038 /* Write RFC822 route-address to string
3040 * address to interpret
3044 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3046 extern char *wspecials
;
3048 if (adr
&& adr
->host
) { /* no-op if no address */
3049 if (adr
->adl
) { /* have an A-D-L? */
3050 gf_puts (adr
->adl
, pc
);
3053 /* write mailbox name */
3054 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3055 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3056 gf_puts ("@", pc
); /* host delimiter */
3057 gf_puts (adr
->host
, pc
); /* write host name */
3063 /* Concatenate RFC822 string
3065 * pointer to string to concatenate
3066 * list of special characters
3070 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3074 if (strpbrk (src
,specials
)) { /* any specials present? */
3075 gf_puts ("\"", pc
); /* opening quote */
3076 /* truly bizarre characters in there? */
3077 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3080 /* turn it into a null-terminated piece */
3084 gf_puts (src
, pc
); /* yes, output leader */
3086 gf_puts ("\\", pc
); /* quoting */
3087 gf_puts (save
, pc
); /* output the bizarre character */
3088 src
= ++s
; /* continue after the bizarre character */
3090 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3091 gf_puts ("\"", pc
); /* closing quote */
3093 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3097 /*----------------------------------------------------------------------
3098 Format an address field, wrapping lines nicely at commas
3100 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3101 newsgrps -- ADDRESS structure to format
3103 Result: A formatted, malloced string is returned.
3105 The resuling lines formatted are 80 columns wide.
3106 ----------------------------------------------------------------------*/
3108 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3110 char buf
[MAILTMPLEN
];
3111 int trailing
= 0, llen
, alen
;
3114 if(!newsgrps
|| !*newsgrps
)
3117 gf_puts(field_name
, pc
);
3119 llen
= strlen(field_name
);
3121 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3122 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3123 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3128 if(!trailing
){ /* first time thru, just address */
3132 else{ /* else preceding comma */
3136 if(alen
+ llen
+ 1 > 76){
3137 gf_puts(NEWLINE
, pc
);
3147 if(alen
&& llen
> 76){ /* handle long addresses */
3148 register char *q
, *p
= &buf
[alen
-1];
3151 if(isspace((unsigned char)*p
)
3152 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3153 for(q
= buf
; q
< p
; q
++)
3154 (*pc
)(*q
); /* write character */
3156 gf_puts(NEWLINE
, pc
);
3165 if(p
== buf
) /* no reasonable break point */
3172 gf_puts(NEWLINE
, pc
);
3177 /*----------------------------------------------------------------------
3178 Format a text field that's part of some raw (non-envelope) message header
3184 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3186 ----------------------------------------------------------------------*/
3188 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3190 register char *current
;
3191 unsigned char *p
, *tmp
= NULL
, c
;
3199 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3201 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3204 len
= SIZEOF_20KBUF
;
3205 p
= (unsigned char *) tmp_20k_buf
;
3208 if(islower((unsigned char)(*start
)))
3209 *start
= toupper((unsigned char)(*start
));
3211 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3213 /* output from start to finish */
3214 while(*current
&& rv
== FHT_OK
)
3215 if(ISRFCEOL(current
)){
3216 if(!gf_puts(NEWLINE
, pc
))
3221 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3222 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3223 c
= (unsigned char) *current
++;
3224 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3225 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3228 else if(!(*pc
)(*current
++))
3232 fs_give((void **) &tmp
);
3242 /*----------------------------------------------------------------------
3243 Format a text field that's part of some raw (non-envelope) message header
3250 ----------------------------------------------------------------------*/
3252 format_env_puts(char *s
, gf_io_t pc
)
3254 if(ps_global
->pass_ctrl_chars
)
3255 return(gf_puts(s
, pc
));
3258 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3259 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3260 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3271 display_parameters(PARAMETER
*params
)
3276 PARMLIST_S
*parmlist
;
3278 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3279 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3280 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3283 tmp_20k_buf
[0] = '\0';
3284 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3285 n
= 0; /* n overloaded */
3286 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3288 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3289 tmp_20k_buf
[10000-1] = '\0';
3293 if(parmlist
->value
){
3294 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3295 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3299 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3304 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3305 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3307 tmp_20k_buf
[10000-1] = '\0';
3311 rfc2231_free_parmlist(&parmlist
);
3314 return(tmp_20k_buf
);
3318 /*----------------------------------------------------------------------
3319 Fetch the requested header fields from the msgno specified
3321 Args: stream -- mail stream of open folder
3322 msgno -- number of message to get header lines from
3323 fields -- array of pointers to desired fields
3325 Returns: allocated string containing matched header lines,
3329 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3332 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3333 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3336 * If the user misconfigures it is possible to have one of the fields
3337 * set to the empty string instead of a header name. We want to catch
3338 * that here instead of asking the server the nonsensical question.
3340 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3344 if(pp
&& *pp
){ /* found an empty header field, fix it */
3345 pflds
= copy_list_array(fields
);
3346 for(pp
= pflds
; pp
&& *pp
; pp
++){
3347 if(!**pp
){ /* scoot rest of the lines up */
3349 for(qq
= pp
; *qq
; qq
++)
3353 fs_give((void **) &free_this
);
3357 /* no headers to look for, return NULL */
3358 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3359 free_list_array(&pflds
);
3366 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3367 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3372 if(pflds
&& pflds
!= fields
)
3373 free_list_array(&pflds
);
3378 while(find_field(&h
, tmp
, sizeof(tmp
))){
3379 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3382 /* interesting field? */
3383 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3385 * Hold off allocating space for matching fields until
3386 * we at least find one to copy...
3389 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3391 while(*p
) /* copy field name */
3394 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3395 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3398 *m
= '\0'; /* tie off match string */
3400 else{ /* no match, pass this field */
3401 while(*h
&& !(*h
++ == '\n'
3402 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3407 if(pflds
&& pflds
!= fields
)
3408 free_list_array(&pflds
);
3410 return(match
? match
: cpystr(""));
3415 find_field(char **h
, char *tmp
, size_t ntmp
)
3419 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3422 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3430 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3434 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3437 if(_last_embedded_fg_color
)
3438 fs_give((void **)&_last_embedded_fg_color
);
3440 _last_embedded_fg_color
= cpystr(cp
->fg
);
3442 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3443 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3448 if(_last_embedded_bg_color
)
3449 fs_give((void **)&_last_embedded_bg_color
);
3451 _last_embedded_bg_color
= cpystr(cp
->bg
);
3453 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3454 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3463 get_cur_embedded_color(void)
3467 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3468 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3470 ret
= pico_get_cur_color();
3477 clear_cur_embedded_color(void)
3479 if(_last_embedded_fg_color
)
3480 fs_give((void **)&_last_embedded_fg_color
);
3482 if(_last_embedded_bg_color
)
3483 fs_give((void **)&_last_embedded_bg_color
);
3488 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3492 if(pico_usingcolor()){
3493 char *fg
= NULL
, *bg
= NULL
, *s
;
3495 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3496 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3497 ps_global
->VAR_NORM_FORE_COLOR
))
3498 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3500 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3501 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3502 ps_global
->VAR_NORM_BACK_COLOR
))
3503 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3509 * The blacks are just known good colors for
3510 * testing whether the other color is good.
3512 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3513 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3514 if(pico_is_good_colorpair(tmp
))
3515 for(s
= color_embed(fg
, bg
);
3516 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3520 free_color_pair(&tmp
);
3524 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3525 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3530 colorstring
[buflen
-1] = '\0';
3537 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3540 if(pico_usingcolor()){
3541 char *fg
= NULL
, *bg
= NULL
, *s
;
3542 char *basefg
= NULL
, *basebg
= NULL
;
3544 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3545 : ps_global
->VAR_NORM_FORE_COLOR
;
3546 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3547 : ps_global
->VAR_NORM_BACK_COLOR
;
3550 * We need to change the fg and bg colors back even if they
3551 * are the same as the Normal Colors so that color_a_quote
3552 * will have a chance to pick up those colors as the ones to
3553 * switch to. We don't do this before the handle above so that
3554 * the quote color will flow into the selectable item when
3555 * the selectable item color is partly the same as the
3556 * normal color. That is, suppose the normal color was black on
3557 * cyan and the selectable color was blue on cyan, only a fg color
3558 * change. We preserve the only-a-fg-color-change in a quote by
3559 * letting the quote background color flow into the selectable text.
3561 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3564 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3567 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3568 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3573 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3577 colorstring
[buflen
-1] = '\0';
3584 * Helper routine that is of limited use.
3585 * We need to tally up the screen width of
3586 * a UTF-8 string as we go through the string.
3587 * We just want the width of the character starting
3588 * at str (and no longer than remaining_octets).
3589 * If we're plopped into the middle of a UTF-8
3590 * character we just want to return width zero.
3593 width_at_this_position(unsigned char *str
, unsigned long n
)
3595 unsigned char *inputp
= str
;
3596 unsigned long remaining_octets
= n
;
3600 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3601 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3602 width
= wcellwidth(ucs
);
3603 /* Writechar will print a '?' */