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-2020 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
22 Implements message data gathering and formatting
28 #include "../pith/ical.h"
29 #include "../pith/body.h"
30 #include "../pith/mailpart.h"
31 #include "../pith/mailview.h"
32 #include "../pith/conf.h"
33 #include "../pith/msgno.h"
34 #include "../pith/editorial.h"
35 #include "../pith/mimedesc.h"
36 #include "../pith/margin.h"
37 #include "../pith/color.h"
38 #include "../pith/strlst.h"
39 #include "../pith/charset.h"
40 #include "../pith/status.h"
41 #include "../pith/maillist.h"
42 #include "../pith/mailcmd.h"
43 #include "../pith/mailindx.h"
44 #include "../pith/imap.h"
45 #include "../pith/detach.h"
46 #include "../pith/text.h"
47 #include "../pith/url.h"
48 #include "../pith/rfc2231.h"
49 #include "../pith/list.h"
50 #include "../pith/stream.h"
51 #include "../pith/send.h"
52 #include "../pith/filter.h"
53 #include "../pith/string.h"
54 #include "../pith/ablookup.h"
55 #include "../pith/escapes.h"
56 #include "../pith/keyword.h"
57 #include "../pith/smime.h"
62 #define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
66 * This is a list of header fields that are represented canonically
67 * by the c-client's ENVELOPE structure. The list is used by the
68 * two functions below to decide if a given field is included in this
71 static struct envelope_s
{
76 {"sender", FE_SENDER
},
81 {"newsgroups", FE_NEWSGROUPS
},
82 {"subject", FE_SUBJECT
},
83 {"message-id", FE_MESSAGEID
},
84 {"reply-to", FE_REPLYTO
},
85 {"followup-to", FE_FOLLOWUPTO
},
86 {"in-reply-to", FE_INREPLYTO
},
87 /* {"return-path", FE_RETURNPATH}, not usually filled in */
88 {"references", FE_REFERENCES
},
94 * Hook for optional display of rfc2369 content
96 int (*pith_opt_rfc2369_editorial
)(long, HANDLE_S
**, int, int, gf_io_t
);
102 * Internal prototypes
104 int format_blip_seen(long);
105 int is_an_env_hdr(char *);
106 int is_an_addr_hdr(char *);
107 void format_env_hdr(MAILSTREAM
*, long, char *, ENVELOPE
*,
108 fmt_env_t
, gf_io_t
, char *, char *, int);
109 int delineate_this_header(char *, char *, char **, char **);
110 char *url_embed(int);
111 int color_headers(long, char *, LT_INS_S
**, void *);
112 int url_hilite_hdr(long, char *, LT_INS_S
**, void *);
113 int pad_to_right_edge(long, char *, LT_INS_S
**, void *);
114 int url_bogus_imap(char **, char *, char *);
115 int format_raw_header(MAILSTREAM
*, long, char *, gf_io_t
);
116 void format_envelope(MAILSTREAM
*, long, char *, ENVELOPE
*,
117 gf_io_t
, long, char *, int);
118 int any_hdr_color(char *);
119 void format_addr_string(MAILSTREAM
*, long, char *, char *,
120 ADDRESS
*, int, char *, gf_io_t
);
121 void pine_rfc822_write_address_noquote(ADDRESS
*, gf_io_t
, int *);
122 void format_newsgroup_string(char *, char *, int, gf_io_t
);
123 int format_raw_hdr_string(char *, char *, gf_io_t
, char *, int);
124 int format_env_puts(char *, gf_io_t
);
125 int find_field(char **, char *, size_t);
126 int embed_color(COLOR_PAIR
*, gf_io_t
);
127 COLOR_PAIR
*get_cur_embedded_color(void);
128 void clear_cur_embedded_color(void);
129 void format_calendar_vevent(VCALENDAR_S
*, ATTACH_S
*, HANDLE_S
**, int, int, gf_io_t
, int);
133 /*----------------------------------------------------------------------
134 Format a message message for viewing
136 Args: msgno -- The number of the message to view
137 env -- pointer to the message's envelope
138 body -- pointer to the message's body
139 handlesp -- address of pointer to the message's handles
140 flgs -- possible flags listed in pith/mailview.h with
142 pc -- write to this function
144 Result: Returns true if no problems encountered, else false.
146 First the envelope is formatted; next a list of all attachments is
147 formatted if there is more than one. Then all the body parts are
148 formatted, fetching them as needed. This includes headers of included
149 message. Richtext is also formatted. An entry is made in the text for
150 parts that are not displayed or can't be displayed.
154 format_message(long int msgno
, ENVELOPE
*env
, struct mail_bodystruct
*body
,
155 HANDLE_S
**handlesp
, int flgs
, gf_io_t pc
)
157 char *decode_err
= NULL
;
161 clear_cur_embedded_color();
163 if(!(flgs
& FM_DISPLAY
))
166 width
= (flgs
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
168 /*---- format and copy envelope ----*/
169 if(!(flgs
& FM_NOEDITORIAL
)){
170 if(ps_global
->full_header
== 1)
171 /* TRANSLATORS: User is viewing a message and all the quoted text is
173 q_status_message(SM_INFO
, 0, 3, _("All quoted text being included"));
174 else if(ps_global
->full_header
== 2)
175 q_status_message(SM_INFO
, 0, 3,
176 /* TRANSLATORS: User is viewing a message and all of
177 the header text is being shown. */
178 _("Full header mode ON. All header text being included"));
181 HD_INIT(&h
, ps_global
->VAR_VIEW_HEADERS
, ps_global
->view_all_except
, FE_DEFAULT
);
182 switch(format_header(ps_global
->mail_stream
, msgno
, NULL
,
183 env
, &h
, NULL
, handlesp
, flgs
, NULL
, pc
)){
185 case -1 : /* write error */
188 case 1 : /* fetch error */
189 if(!(gf_puts("[ Error fetching header ]", pc
)
190 && !gf_puts(NEWLINE
, pc
)))
197 || (ps_global
->full_header
== 2
198 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
)))){
199 format_attachment_list(msgno
, body
, handlesp
, flgs
, width
, pc
);
200 format_calendar(msgno
, body
, handlesp
, flgs
, width
, pc
);
203 /* write delimiter and body */
204 if(gf_puts(NEWLINE
, pc
)){
205 if((decode_err
= format_body(msgno
, body
, handlesp
, &h
, flgs
, width
, pc
)) == NULL
)
210 clear_cur_embedded_color();
216 if(!(flgs
& FM_DISPLAY
))
217 q_status_message1(SM_ORDER
, 3, 4, _("Error writing message: %s"),
218 decode_err
? decode_err
: error_description(errno
));
219 clear_cur_embedded_color();
224 format_calendar_vevent(VCALENDAR_S
*vcal
, ATTACH_S
*a
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
, int cflags
)
226 int avail
, m1
, m2
, hwid
, i
, partwid
, padwid
;
227 int s1
, s2
, dwid
, minkey
;
230 VEVENT_SUMMARY_S
*vesy
, *vesummary
; /* vevent summary */
232 vesummary
= vesy
= ical_vevent_summary(vcal
);
234 if(vesy
== NULL
) return;
236 if((cflags
& FC_SUMMARY
) && (cflags
& FC_FULL
))
239 minkey
= -1; /* initialize to something negative */
241 for(; vesy
!= NULL
; vesy
= vesy
->next
){
243 margin
= (cflags
& FC_FULL
) ? NULL
244 : (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
246 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
249 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
252 hwid
= MAX(avail
, 0);
255 if(ps_global
->atmts
[1].description
== NULL
){
256 avail
= width
- m1
-2;
258 dwid
= MAX(MIN(40, avail
), 0);
260 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
261 repeat_char(dwid
, '-'));
263 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
268 if(cflags
& FC_SUMMARY
){
269 i
= utf8_width(_("Calendar Entry:"));
270 partwid
= MIN(i
, hwid
);
272 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
273 if(!gf_puts(tmp_20k_buf
, pc
))
277 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%-*.*w%*.*s",
278 partwid
, partwid
, _("Calendar Entry:"),
281 if(!gf_puts(tmp_20k_buf
, pc
) || !gf_puts(NEWLINE
, pc
))
288 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", m1
, m1
, "");
289 if(!gf_puts(tmp_20k_buf
, pc
))
293 avail
= width
- m1
- m2
;
295 s1
= MAX(MIN(1, avail
), 0);
298 dwid
= MAX(MIN(1, avail
), 0);
301 s2
= MAX(MIN(1, avail
), 0);
304 if(cflags
& FC_SUMMARY
)
305 utf8_snprintf(padding
, sizeof(padding
), "%*.*s%*.*w%*.*s",
306 s1
, s1
, "", dwid
, dwid
, "", s2
, s2
, "");
311 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
312 padding
, _("This event was cancelled!"));
313 if((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
)){
314 gf_puts(tmp_20k_buf
, pc
);
315 gf_puts(NEWLINE
, pc
);
322 if(vesy
->sender
!= NULL
){
323 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
324 padding
, _("Sent-by: "), vesy
->sender
);
325 gf_puts(tmp_20k_buf
, pc
);
326 gf_puts(NEWLINE
, pc
);
329 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
330 padding
, _("Organizer: "), vesy
->organizer
);
331 gf_puts(tmp_20k_buf
, pc
);
332 gf_puts(NEWLINE
, pc
);
333 } /* end of if(organizer) */
336 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
337 padding
, _("Location: "), vesy
->location
);
338 gf_puts(tmp_20k_buf
, pc
);
339 gf_puts(NEWLINE
, pc
);
340 } /* end of if location */
343 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
344 padding
, _("Start Date: "), vesy
->evstart
);
345 gf_puts(tmp_20k_buf
, pc
);
346 gf_puts(NEWLINE
, pc
);
347 } /* end of if dtstart */
352 for(i
= 0; vesy
->duration
[i
] != NULL
; i
++){
353 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
354 padding
, _("Duration: "), vesy
->duration
[i
]);
355 gf_puts(tmp_20k_buf
, pc
);
356 gf_puts(NEWLINE
, pc
);
358 } /* end of DURATION */
359 else if(vesy
->evend
){
360 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
361 padding
, _("End Date: "), vesy
->evend
);
362 gf_puts(tmp_20k_buf
, pc
);
363 gf_puts(NEWLINE
, pc
);
364 } else { /* end of if dtend */
365 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s",
366 padding
, _("No duration nor end time found for this event"));
367 gf_puts(tmp_20k_buf
, pc
);
368 gf_puts(NEWLINE
, pc
);
369 } /* end of else for if (duration) and if (dtend) */
372 #define MAX_DISPLAYED 3
375 for(i
= 0; vesy
->attendee
[i
] != NULL
; i
++){
376 if((cflags
& FC_SUMMARY
) && i
>= MAX_DISPLAYED
)
379 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%s%s%s",
381 _("Attendee: "), vesy
->attendee
[i
]);
382 gf_puts(tmp_20k_buf
, pc
);
383 gf_puts(NEWLINE
, pc
);
385 } /* end of ATTENDEES */
388 if(cflags
& FC_SUMMARY
){
389 COLOR_PAIR
*lastc
= NULL
;
392 COLOR_PAIR
*hdrcolor
= NULL
;
394 if((flgs
& FM_DISPLAY
)
395 && !(flgs
& FM_NOCOLOR
)
397 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
398 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
399 && ps_global
->VAR_NORM_FORE_COLOR
400 && ps_global
->VAR_NORM_BACK_COLOR
401 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
402 ps_global
->VAR_NORM_FORE_COLOR
)
403 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
404 ps_global
->VAR_NORM_BACK_COLOR
))){
406 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
407 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
408 if(!pico_is_good_colorpair(hdrcolor
))
409 free_color_pair(&hdrcolor
);
413 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
416 gf_puts(padding
, pc
);
417 utf8_snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "[%s]", _("More Details"));
420 char buf
[16], color
[64];
424 h
= new_handle(handlesp
);
425 if(minkey
< 0) minkey
= h
->key
;
427 h
->h
.ical
.attach
= a
;
428 h
->h
.ical
.depth
= h
->key
- minkey
;
430 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
431 buf
[sizeof(buf
)-1] = '\0';
433 if(!(flgs
& FM_NOCOLOR
)
434 && handle_start_color(color
, sizeof(color
), &l
, 1)){
435 lastc
= get_cur_embedded_color();
436 if(!gf_nputs(color
, (long) l
, pc
))
439 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
440 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
443 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
444 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
447 tmp_20k_buf
[0] = '\0';
449 if(!format_env_puts(tmp_20k_buf
, pc
))
454 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
455 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
459 if(!embed_color(lastc
, pc
))
462 free_color_pair(&lastc
);
464 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
467 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
472 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s", padwid
, padwid
, "");
473 if(!gf_puts(tmp_20k_buf
, pc
))
477 if(!gf_puts(NEWLINE
, pc
))
480 avail
= width
- m1
-2;
482 dwid
= MAX(MIN(40, avail
), 0);
485 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "%*.*s%s", m1
, m1
, "",
486 repeat_char(dwid
, '-'));
488 gf_puts(tmp_20k_buf
, pc
);
490 gf_puts(NEWLINE
, pc
);
492 free_vevent_summary(&vesummary
);
496 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
498 char *rawtext
, *caltext
;
499 unsigned long callen
;
500 VCALENDAR_S
*vcal
= NULL
;
504 if(flgs
& FM_NEW_MESS
) {
505 zero_atmts(ps_global
->atmts
);
506 describe_mime(body
, "", 1, 1, 0, flgs
);
509 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
510 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
511 b
= mail_body (ps_global
->mail_stream
, msgno
, a
->number
);
513 gf_puts(_("Error fetching calendar body part"), pc
);
514 gf_puts(NEWLINE
, pc
);
517 if(b
->sparep
== NULL
){
518 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
519 if(rawtext
== NULL
|| *rawtext
== '\0'){
520 gf_puts(_("Error fetching calendar text"), pc
);
521 gf_puts(NEWLINE
, pc
);
524 rawtext
[callen
] = '\0'; /* chop off cookie */
527 caltext
= rfc822_base64(rawtext
, strlen(rawtext
), &callen
);
529 gf_puts(_("Error in calendar base64 encoding"), pc
);
530 gf_puts(NEWLINE
, pc
);
533 caltext
[callen
] = '\0';
536 case ENCQUOTEDPRINTABLE
:
537 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
539 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
540 gf_puts(NEWLINE
, pc
);
543 caltext
[callen
] = '\0';
546 default: caltext
= cpystr(rawtext
);
549 vcal
= ical_parse_text(caltext
);
550 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
551 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
552 fs_give((void **) &caltext
);
555 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
556 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
557 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
558 if(vcal
->comp
[VEvent
] != NULL
){
559 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
560 } /* else another type of entry in the calendar */
562 gf_puts(NEWLINE
, pc
);
570 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
572 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
573 int is_in_sig
= OUT_SIG_BLOCK
;
574 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
580 || (ps_global
->full_header
== 2
581 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
583 /*--- Server is not an IMAP2bis, It can't parse MIME
584 so we just show the text here. Hopefully the
585 message isn't a MIME message
589 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
590 msgno
, NULL
, NULL
, NIL
)) != NULL
){
592 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
593 return("Write Error");
595 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
599 * We need to translate the message
600 * into UTF-8, but that's trouble in the full header case
601 * because we don't know what to translate from. We'll just
602 * take a guess by looking for the first text part and
605 if(body
&& body
->type
== TYPETEXT
)
606 charset
= parameter_val(body
->parameter
, "charset");
607 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
608 && body
->nested
.part
->body
.type
== TYPETEXT
)
609 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
611 charset
= cpystr(ps_global
->display_charmap
);
613 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
614 /* transliterate message text to UTF-8 */
615 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
617 if (charset
) fs_give((void **) &charset
);
619 /* link in filters, similar to what is done in decode_text() */
620 if(!ps_global
->pass_ctrl_chars
){
621 gf_link_filter(gf_escape_filter
, NULL
);
623 gf_link_filter(gf_control_filter
,
624 gf_control_filter_opt(&filt_only_c0
));
627 gf_link_filter(gf_tag_filter
, NULL
);
629 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
630 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
631 || F_ON(F_SCAN_ADDR
, ps_global
))
633 gf_link_filter(gf_line_test
,
634 gf_line_test_opt(url_hilite
,
635 gf_url_hilite_opt(&uh
,handlesp
,0)));
638 if((flgs
& FM_DISPLAY
)
639 && !(flgs
& FM_NOCOLOR
)
641 && ps_global
->VAR_SIGNATURE_FORE_COLOR
642 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
643 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
646 if((flgs
& FM_DISPLAY
)
647 && !(flgs
& FM_NOCOLOR
)
649 && ps_global
->VAR_QUOTE1_FORE_COLOR
650 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
651 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
654 if(!(flgs
& FM_NOWRAP
)){
655 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
657 && !(flgs
& FM_NOCOLOR
)
658 && pico_usingcolor())
659 wrapflags
|= GFW_USECOLOR
;
660 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
662 ? NULL
: format_view_margin(),
667 gf_link_filter(gf_nvtnl_local
, NULL
);
668 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
669 /* TRANSLATORS: There was an error putting together a message for
670 viewing. The arg is the description of the error. */
671 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
672 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
673 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
674 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
675 && gf_puts(NEWLINE
, pc
))
683 if(!gf_puts(NEWLINE
, pc
)
684 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
685 || !gf_puts(NEWLINE
, pc
)
686 || !gf_puts(NEWLINE
, pc
))
687 return("Write Error");
693 /*======== Now loop through formatting all the parts =======*/
694 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
695 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
698 if(a
->body
->type
== TYPEMULTIPART
){
700 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
702 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
703 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
704 return("Write Error");
712 if(a
->suppress_editorial
)
715 if(!(flgs
& FM_NOEDITORIAL
)
716 && (!gf_puts(NEWLINE
, pc
)
717 || (decode_err
= part_desc(a
->number
, a
->body
,
719 ? (a
->can_display
!= MCD_NONE
)
721 : 3, width
, flgs
, pc
))))
722 return("Write Error");
727 switch(a
->body
->type
){
731 * If a message is multipart *and* the first part of it
732 * is text *and that text is empty, there is a good chance that
733 * there was actually something there that c-client was
734 * unable to parse. Here we report the empty message body
735 * and insert the raw RFC822.TEXT (if full-headers are
738 if(body
->type
== TYPEMULTIPART
739 && a
== ps_global
->atmts
740 && a
->body
->size
.bytes
== 0
741 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
744 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
745 "Empty or malformed message%s.",
746 ps_global
->full_header
== 2
747 ? ". Displaying raw text"
748 : ". Use \"H\" to see raw text");
749 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
751 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
752 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
753 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
754 return("Write Error");
756 if(ps_global
->full_header
== 2
757 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
758 a
->number
, pc
, flgs
))){
759 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
760 "%s%s [ Formatting error: %s ]%s%s",
761 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
762 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
763 if(!gf_puts(tmp_20k_buf
, pc
))
764 return("Write Error");
771 * Don't write our delimiter if this text part is
772 * the first part of a message/rfc822 segment...
774 if(show_parts
&& a
!= ps_global
->atmts
775 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
777 || (a
[-1].body
->type
== TYPEMULTIPART
778 && a
[-1].body
->subtype
779 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
780 && &a
[-1] != ps_global
->atmts
781 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
784 && !(flgs
& FM_NOEDITORIAL
)){
785 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
786 tmp1
= "Calendar entry";
788 tmp1
= a
->body
->description
? a
->body
->description
790 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
791 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
793 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
795 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
796 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
797 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
798 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
799 return("Write Error");
802 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
803 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
804 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
809 tmp1
= a
->body
->description
? a
->body
->description
810 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
812 : "Included Message";
813 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
814 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
816 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
818 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
820 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
821 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
822 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
823 return("Write Error");
825 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
826 /* imapenvonly, we may not have all the headers we need */
827 if(a
->body
->nested
.msg
->env
->imapenvonly
)
828 mail_fetch_header(ps_global
->mail_stream
, msgno
,
829 a
->number
, NULL
, NULL
, FT_PEEK
);
830 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
831 a
->body
->nested
.msg
->env
, hp
,
832 NULL
, handlesp
, flgs
, NULL
, pc
)){
833 case -1 : /* write error */
834 return("Write Error");
836 case 1 : /* fetch error */
837 if(!(gf_puts("[ Error fetching header ]", pc
)
838 && !gf_puts(NEWLINE
, pc
)))
839 return("Write Error");
844 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
845 int *margin
, avail
, m1
, m2
;
848 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
850 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
853 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
856 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
857 || !gf_puts(NEWLINE
, pc
)
858 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
859 return("Write Error");
862 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
863 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
866 if(!gf_puts(NEWLINE
, pc
))
867 return("Write Error");
872 if((decode_err
= part_desc(a
->number
, a
->body
,
873 (flgs
& FM_DISPLAY
) ? 1 : 3,
874 width
, flgs
, pc
)) != NULL
)
875 return("Write Error");
882 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
883 && format_blip_seen(msgno
)))
884 return("Cannot format body.");
892 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
896 if(flgs
& FM_NEW_MESS
) {
897 zero_atmts(ps_global
->atmts
);
898 describe_mime(body
, "", 1, 1, 0, flgs
);
901 /*----- First do the list of parts/attachments if needed ----*/
902 if((flgs
& FM_DISPLAY
)
903 && (ps_global
->atmts
[1].description
904 || (ps_global
->atmts
[0].body
905 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
906 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
907 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
908 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
909 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
910 COLOR_PAIR
*hdrcolor
= NULL
;
912 if((flgs
& FM_DISPLAY
)
913 && !(flgs
& FM_NOCOLOR
)
915 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
916 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
917 && ps_global
->VAR_NORM_FORE_COLOR
918 && ps_global
->VAR_NORM_BACK_COLOR
919 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
920 ps_global
->VAR_NORM_FORE_COLOR
)
921 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
922 ps_global
->VAR_NORM_BACK_COLOR
))){
924 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
925 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
926 if(!pico_is_good_colorpair(hdrcolor
))
927 free_color_pair(&hdrcolor
);
931 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
934 * Attachment list header
939 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
942 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
945 hwid
= MAX(avail
, 0);
947 i
= utf8_width(_("Parts/Attachments:"));
948 partwid
= MIN(i
, hwid
);
949 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
952 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
953 if(!gf_puts(tmp
, pc
))
957 utf8_snprintf(tmp
, sizeof(tmp
),
959 /* TRANSLATORS: A label */
960 partwid
, partwid
, _("Parts/Attachments:"),
963 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
967 /*----- Figure max display widths -----*/
968 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
969 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
972 if((n
= utf8_width(a
->number
)) > maxnumwid
)
975 if((n
= utf8_width(a
->size
)) > maxsizewid
)
980 * ----- adjust max lengths for nice display -----
982 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
986 avail
= width
- m1
- m2
;
988 s1
= MAX(MIN(1, avail
), 0);
991 dwid
= MAX(MIN(1, avail
), 0);
994 s2
= MAX(MIN(1, avail
), 0);
997 maxnumwid
= MIN(maxnumwid
, width
/3);
998 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
1001 s3
= MAX(MIN(1, avail
), 0);
1004 shownwid
= MAX(MIN(5, avail
), 0);
1007 s4
= MAX(MIN(3, avail
), 0);
1010 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
1013 s5
= MAX(MIN(2, avail
), 0);
1016 descwid
= MAX(0, avail
);
1018 /*----- Format the list of attachments -----*/
1019 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
1020 COLOR_PAIR
*lastc
= NULL
;
1022 int thisdescwid
, padwid
;
1024 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
1027 if(a
->body
->type
== TYPEMULTIPART
1028 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
1032 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1033 thisdescwid
= MIN(i
, descwid
);
1034 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1037 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1038 if(!gf_puts(tmp
, pc
))
1042 utf8_snprintf(tmp
, sizeof(tmp
),
1043 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1046 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1048 maxnumwid
, maxnumwid
,
1050 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1054 a
->shown
? "Shown" :
1055 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1059 a
->size
? a
->size
: "",
1061 thisdescwid
, thisdescwid
,
1062 (descwid
> 2 && a
->description
) ? a
->description
: "");
1064 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1067 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1068 char buf
[16], color
[64];
1072 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1076 h
= new_handle(handlesp
);
1080 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1081 buf
[sizeof(buf
)-1] = '\0';
1083 if(!(flgs
& FM_NOCOLOR
)
1084 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1085 lastc
= get_cur_embedded_color();
1086 if(!gf_nputs(color
, (long) l
, pc
))
1089 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1090 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1093 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1094 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1100 if(!format_env_puts(tmpp
, pc
))
1103 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1105 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1106 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1110 if(!embed_color(lastc
, pc
))
1113 free_color_pair(&lastc
);
1115 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1118 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1123 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1124 if(!gf_puts(tmp
, pc
))
1128 if(!gf_puts(NEWLINE
, pc
))
1133 * Dashed line after list
1137 avail
= width
- m1
- m2
;
1138 hwid
= MAX(avail
, 0);
1140 dashwid
= MAX(MIN(40, hwid
-2), 0);
1141 padwid
= hwid
- dashwid
;
1143 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1144 if(!gf_puts(tmp
, pc
))
1148 snprintf(tmp
, sizeof(tmp
),
1150 repeat_char(dashwid
, '-'),
1151 padwid
, padwid
, "");
1154 avail
= width
- m1
-2;
1156 dashwid
= MAX(MIN(40, avail
), 0);
1159 snprintf(tmp
, sizeof(tmp
),
1162 repeat_char(dashwid
, '-'));
1165 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1169 free_color_pair(&hdrcolor
);
1178 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1179 * of body part fetches as we're formatting) for the
1180 * given message isn't set (likely because there
1181 * weren't any parts suitable for display), then make
1182 * sure to set it here.
1185 format_blip_seen(long int msgno
)
1189 if(msgno
> 0L && ps_global
->mail_stream
1190 && msgno
<= ps_global
->mail_stream
->nmsgs
1191 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1193 && !ps_global
->mail_stream
->rdonly
)
1194 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1201 * is_an_env_hdr - is this name a header in the envelope structure?
1203 * name - the header name to check
1206 is_an_env_hdr(char *name
)
1210 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1211 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1221 * is_an_addr_hdr - is this an address header?
1223 * name - the header name to check
1226 is_an_addr_hdr(char *fieldname
)
1228 char fbuf
[FBUF_LEN
+1];
1229 char *colon
, *fname
;
1230 static char *addr_headers
[] = {
1246 /* so it is pointing to NULL */
1247 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1249 if((colon
= strindex(fieldname
, ':')) != NULL
){
1250 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1251 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1257 if(fname
&& *fname
){
1258 for(p
= addr_headers
; *p
; p
++)
1259 if(!strucmp(fname
, *p
))
1263 return((*p
) ? 1 : 0);
1268 * Format a single field from the envelope
1271 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1272 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1277 fmt_env
= format_envelope
;
1279 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1280 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1281 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1288 * Look through header string beginning with "begin", for the next
1289 * occurrence of header "field". Set "start" to that. Set "end" to point one
1290 * position past all of the continuation lines that go with "field".
1291 * That is, if "end" is converted to a null
1292 * character then the string "start" will be the next occurrence of header
1293 * "field" including all of its continuation lines. Assume we
1294 * have CRLF's as end of lines.
1296 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1297 * make "end" the end of that header.
1299 * Returns 1 if found, 0 if not.
1302 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1304 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1309 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1315 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1316 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1317 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1318 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1321 * We require that start is at the beginning of a line, so
1322 * either it equals begin (which we assume is the beginning of a
1323 * line) or it is preceded by a CRLF.
1326 *start
= srchstr(begin_srch
, tmpfield
);
1327 while(*start
&& *start
!= begin
1328 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1329 begin_srch
= *start
+ 1;
1330 *start
= srchstr(begin_srch
, tmpfield
);
1337 for(p
= *start
; *p
; p
++){
1339 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1341 * The final 015 in the test above is to test for the end
1358 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1362 if(pico_usingcolor()){
1363 char *fg
= NULL
, *bg
= NULL
, *s
;
1364 char *basefg
= NULL
, *basebg
= NULL
;
1366 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1367 : ps_global
->VAR_NORM_FORE_COLOR
;
1368 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1369 : ps_global
->VAR_NORM_BACK_COLOR
;
1371 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1372 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1373 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1375 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1376 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1377 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1383 * The blacks are just known good colors for
1384 * testing whether the other color is good.
1386 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1387 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1388 if(pico_is_good_colorpair(tmp
))
1389 for(s
= color_embed(fg
, bg
);
1390 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1394 free_color_pair(&tmp
);
1398 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1399 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1404 colorstring
[buflen
-1] = '\0';
1411 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1414 if(pico_usingcolor()){
1415 char *fg
= NULL
, *bg
= NULL
, *s
;
1418 * We need to change the fg and bg colors back even if they
1419 * are the same as the Normal Colors so that color_a_quote
1420 * will have a chance to pick up those colors as the ones to
1421 * switch to. We don't do this before the handle above so that
1422 * the quote color will flow into the selectable item when
1423 * the selectable item color is partly the same as the
1424 * normal color. That is, suppose the normal color was black on
1425 * cyan and the selectable color was blue on cyan, only a fg color
1426 * change. We preserve the only-a-fg-color-change in a quote by
1427 * letting the quote background color flow into the selectable text.
1429 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1430 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1432 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1433 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1435 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1436 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1441 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1445 colorstring
[buflen
-1] = '\0';
1452 url_embed(int embed
)
1454 static char buf
[3] = {TAG_EMBED
};
1462 * Paint the signature.
1465 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1467 struct variable
*vars
= ps_global
->vars
;
1469 COLOR_PAIR
*col
= NULL
;
1471 if(is_in_sig
== NULL
)
1474 in_sig_block
= (int *) is_in_sig
;
1476 if(!strcmp(line
, SIGDASHES
))
1477 *in_sig_block
= START_SIG_BLOCK
;
1478 else if(*line
== '\0')
1480 * Suggested by Eduardo: allow for a blank line right after
1483 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1484 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1486 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1487 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1489 if(*in_sig_block
!= OUT_SIG_BLOCK
1490 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1491 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1492 VAR_SIGNATURE_BACK_COLOR
))){
1493 if(!pico_is_good_colorpair(col
))
1494 free_color_pair(&col
);
1498 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1500 ins
= gf_line_test_new_ins(ins
, line
,
1501 color_embed(col
->fg
, col
->bg
),
1504 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1505 fg
[sizeof(fg
)-1] = '\0';
1506 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1507 bg
[sizeof(bg
)-1] = '\0';
1510 * Loop watching colors, and override with
1511 * signature color whenever the normal foreground and background
1512 * colors are in force.
1516 if(*p
++ == TAG_EMBED
){
1520 p
+= *p
+ 1; /* skip handle key */
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_FORE_COLOR
)
1529 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1530 ins
= gf_line_test_new_ins(ins
, p
,
1531 color_embed(col
->fg
,NULL
),
1536 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1537 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1538 p
+= RGBLEN
; /* advance past color value */
1540 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1541 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1542 ins
= gf_line_test_new_ins(ins
, p
,
1543 color_embed(NULL
,col
->bg
),
1553 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1554 color_embed(VAR_NORM_FORE_COLOR
,
1555 VAR_NORM_BACK_COLOR
),
1557 free_color_pair(&col
);
1565 * Line filter to add color to displayed headers.
1568 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1570 static char field
[FBUF_LEN
+ 1];
1571 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1572 char *p
, *q
, *value
, *beg
;
1574 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1575 struct variable
*vars
= ps_global
->vars
;
1577 field
[FBUF_LEN
] = '\0';
1579 if(isspace((unsigned char)*line
)) /* continuation line */
1582 if(!(value
= strindex(line
, ':')))
1585 memset(field
, 0, sizeof(field
));
1586 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1589 for(value
++; isspace((unsigned char)*value
); value
++)
1592 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1593 fg
[sizeof(fg
)-1] = '\0';
1594 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1595 bg
[sizeof(bg
)-1] = '\0';
1598 * Split into two cases depending on whether this is a header which
1599 * contains addresses or not. We may color addresses separately.
1601 if(is_an_addr_hdr(field
)){
1604 * If none of the patterns are for this header, don't bother parsing
1605 * and checking each address.
1607 if(!any_hdr_color(field
))
1611 * First check for patternless patterns which color whole line.
1613 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1614 if(pico_is_good_colorpair(color
)){
1615 ins
= gf_line_test_new_ins(ins
, value
,
1616 color_embed(color
->fg
, color
->bg
),
1618 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1619 fg
[sizeof(fg
)-1] = '\0';
1620 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1621 bg
[sizeof(bg
)-1] = '\0';
1625 free_color_pair(&color
);
1629 * Then go through checking address by address.
1630 * Keep track of quotes and watch for color changes, and override
1631 * with most recent header color whenever the normal foreground
1632 * and background colors are in force.
1638 /* skip next character */
1639 if(*(p
+1) && (in_comment
|| in_quote
))
1648 in_quote
= 1 - in_quote
;
1666 if(!(in_quote
|| in_comment
)){
1667 /* we reached the end of this address */
1670 free_color_pair(&color
);
1672 if((color
= hdr_color(field
, beg
,
1673 ps_global
->hdr_colors
)) != NULL
){
1674 if(pico_is_good_colorpair(color
)){
1676 ins
= gf_line_test_new_ins(ins
, beg
,
1677 color_embed(color
->fg
,
1681 for(q
= p
; q
> beg
&&
1682 isspace((unsigned char)*(q
-1)); q
--)
1685 ins
= gf_line_test_new_ins(ins
, q
,
1686 color_embed(fg
, bg
),
1690 free_color_pair(&color
);
1695 for(p
++; isspace((unsigned char)*p
); p
++)
1709 p
+= *p
+ 1; /* skip handle key */
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_FORE_COLOR
))
1719 ins
= gf_line_test_new_ins(ins
, p
,
1720 color_embed(color
->fg
,NULL
),
1726 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1727 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1728 p
+= RGBLEN
; /* advance past color value */
1730 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1731 ins
= gf_line_test_new_ins(ins
, p
,
1732 color_embed(NULL
,color
->bg
),
1749 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1752 if(*q
&& !(in_quote
|| in_comment
)){
1753 /* we reached the end of this address */
1755 free_color_pair(&color
);
1757 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1758 if(pico_is_good_colorpair(color
)){
1760 ins
= gf_line_test_new_ins(ins
, beg
,
1761 color_embed(color
->fg
,
1764 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1767 ins
= gf_line_test_new_ins(ins
, q
,
1768 color_embed(fg
, bg
),
1772 free_color_pair(&color
);
1777 free_color_pair(&color
);
1780 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1781 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1782 VAR_HEADER_GENERAL_BACK_COLOR
),
1787 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1790 if(pico_is_good_colorpair(color
)){
1791 ins
= gf_line_test_new_ins(ins
, value
,
1792 color_embed(color
->fg
, color
->bg
),
1796 * Loop watching colors, and override with header
1797 * color whenever the normal foreground and background
1798 * colors are in force.
1802 if(*p
++ == TAG_EMBED
){
1806 p
+= *p
+ 1; /* skip handle key */
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_FORE_COLOR
)
1815 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1816 ins
= gf_line_test_new_ins(ins
, p
,
1817 color_embed(color
->fg
,NULL
),
1822 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1823 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1824 p
+= RGBLEN
; /* advance past color value */
1826 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1827 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1828 ins
= gf_line_test_new_ins(ins
, p
,
1829 color_embed(NULL
,color
->bg
),
1839 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1840 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1841 VAR_HEADER_GENERAL_BACK_COLOR
),
1845 free_color_pair(&color
);
1854 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1856 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1857 *weburlp
= NULL
, *mailurlp
= NULL
;
1858 int n
, n1
, n2
, n3
, l
;
1859 char buf
[256], color
[256];
1863 for(lp
= line
; ; lp
= up
+ n
){
1864 /* scan for all of them so we can choose the first */
1865 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1866 urlp
= rfc1738_scan(lp
, &n1
);
1867 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1868 weburlp
= web_host_scan(lp
, &n2
);
1869 if(F_ON(F_SCAN_ADDR
,ps_global
))
1870 mailurlp
= mail_addr_scan(lp
, &n3
);
1872 if(urlp
|| weburlp
|| mailurlp
){
1874 weburlp
? weburlp
: mailurlp
;
1875 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1877 if(mailurlp
&& mailurlp
< up
)
1882 weburlp
= mailurlp
= NULL
;
1884 else if(up
== weburlp
){
1896 uh
= (URL_HILITE_S
*) local
;
1898 h
= new_handle(uh
->handlesp
);
1900 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1901 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1902 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1903 h
->h
.url
.path
[n
+10-1] = '\0';
1905 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1906 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1907 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1908 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1911 buf
[1] = TAG_HANDLE
;
1912 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1913 buf
[sizeof(buf
)-1] = '\0';
1914 buf
[2] = strlen(&buf
[3]);
1915 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1917 /* in case it was the current selection */
1918 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1920 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1921 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1923 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1925 urlp
= weburlp
= mailurlp
= NULL
;
1933 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1935 static int check_for_urls
= 0;
1938 if(isspace((unsigned char)*line
)) /* continuation, check or not
1939 depending on last line */
1943 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1948 if(((ft
= pine_header_standard(line
)) == FreeText
1950 || ft
== TypeUnknown
)
1951 && strucmp(line
, "message-id")
1952 && strucmp(line
, "newsgroups")
1953 && strucmp(line
, "references")
1954 && strucmp(line
, "in-reply-to")
1955 && strucmp(line
, "received")
1956 && strucmp(line
, "date")){
1965 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1972 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1977 struct variable
*vars
= ps_global
->vars
;
1982 total_wid
= *((int *) local
);
1984 /* calculate width of line */
1994 p
+= *p
+ 1; /* skip handle key */
1999 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
2011 default: /* literal embed char */
2019 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
2025 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
2031 if(total_wid
> wid
){
2032 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2033 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2034 VAR_HEADER_GENERAL_BACK_COLOR
),
2036 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2037 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2038 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2039 color_embed(VAR_NORM_FORE_COLOR
,
2040 VAR_NORM_BACK_COLOR
),
2052 url_external_specific_handler(char *url
, int len
)
2054 static char list
[UES_LEN
* UES_MAX
];
2060 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2061 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2064 else{ /* initialize! */
2065 char **l
, *test
, *cmd
, *p
, *p2
;
2068 memset(list
, 0, sizeof(list
));
2069 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2070 get_pair(*l
, &test
, &cmd
, 1, 1);
2072 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2075 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2076 if((p2
= strchr(p
, ',')) != NULL
){
2077 if((n
= p2
- p
) < UES_LEN
){
2078 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2083 "* * * HANDLER TOO LONG: %.*s\n", n
,
2089 if(strlen(p
) <= UES_LEN
){
2090 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2099 fs_give((void **) &test
);
2102 fs_give((void **) &cmd
);
2111 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2112 imapuid_t
*uid
, char **search
, int silent
)
2114 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2115 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2118 int rv
= URL_IMAP_ERROR
;
2121 * Since we're planting nulls, operate on a temporary copy...
2123 scheme
= silent
? NULL
: "IMAP";
2124 url
= cpystr(true_url
+ 7);
2126 /* Try to pick apart the "iserver" portion */
2127 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2131 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2133 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2136 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2138 server
= rfc1738_str(p
);
2140 /* only ";auth=*" supported (and also ";auth=anonymous") */
2141 if((p
= srchstr(url
, ";auth=")) != NULL
){
2143 auth
= rfc1738_str(p
+ 6);
2147 user
= rfc1738_str(url
);
2150 server
= rfc1738_str(url
);
2153 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2156 * "iserver" in hand, pick apart the "icommand"...
2159 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2163 * No "icommand" (all top-level folders) or "imailboxlist"...
2166 *p
= '\0'; /* tie off criteria */
2167 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2168 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2169 rv
|= URL_IMAP_IMBXLSTLSUB
;
2170 else if(strucmp(p
, "list"))
2171 return(url_bogus_imap(&url
, scheme
,
2172 "Invalid list type specified"));
2175 rv
|= URL_IMAP_ISERVERONLY
;
2179 /* build folder list from specified server/criteria/list-method */
2180 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2181 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2182 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2183 user
? "user=\"" : "Anonymous",
2186 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2187 (*folder
)[l
] = '\0';
2188 rv
|= URL_IMAP_IMAILBOXLIST
;
2191 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2192 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2193 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2194 *section
= '\0'; /* tie off UID */
2195 section
= rfc1738_str(section
+ 10);
2196 /* BUG: verify valid section spec ala rfc 2060 */
2198 "-- URL IMAP FOLDER: section not used: %s\n",
2199 section
? section
: "?"));
2202 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2203 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2205 /* optional "uidvalidity"? */
2206 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2209 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2210 return(url_bogus_imap(&url
, scheme
,
2211 "Invalid UIDVALIDITY"));
2214 mailbox
= rfc1738_str(cmd
);
2215 rv
= URL_IMAP_IMESSAGEPART
;
2217 else{ /* "imessagelist" */
2218 /* optional "uidvalidity"? */
2219 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2222 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2223 return(url_bogus_imap(&url
, scheme
,
2224 "Invalid UIDVALIDITY"));
2227 /* optional "enc_search"? */
2228 if((p
= strchr(cmd
, '?')) != NULL
){
2231 *search
= cpystr(rfc1738_str(p
+ 1));
2232 /* BUG: verify valid search spec ala rfc 2060 */
2235 mailbox
= rfc1738_str(cmd
);
2236 rv
= URL_IMAP_IMESSAGELIST
;
2239 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2240 q_status_message(SM_ORDER
, 3, 3,
2241 "Unsupported authentication method. Using standard login.");
2244 * At this point our structure should contain the
2245 * digested url. Now put it together for c-client...
2247 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2248 + (user
? (strlen(user
)+2) : 9);
2249 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2250 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2251 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2252 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2256 (*folder
)[l
] = '\0';
2259 fs_give((void **) &url
);
2265 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2267 fs_give((void **) freeme
);
2268 (void) url_bogus(url
, problem
);
2269 return(URL_IMAP_ERROR
);
2274 * url_bogus - report url syntax errors and such
2277 url_bogus(char *url
, char *reason
)
2279 dprint((2, "-- bogus url \"%s\": %s\n",
2280 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2282 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2283 "Malformed \"%.*s\" URL: %.200s",
2284 (void *) (strchr(url
, ':') - url
), url
, reason
);
2291 /*----------------------------------------------------------------------
2292 Format header text suitable for display
2294 Args: stream -- mail stream for various header text fetches
2295 msgno -- sequence number in stream of message we're interested in
2296 section -- which section of message
2297 env -- pointer to msg's envelope
2298 hdrs -- struct containing what's to get formatted
2299 prefix -- prefix to append to each output line
2300 handlesp -- address of pointer to the message's handles
2301 flags -- FM_ flags, see pith/mailview.h
2302 final_pc -- function to write header text with
2304 Result: 0 if all's well, -1 if write error, 1 if fetch error
2306 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2307 in the local convention.
2311 #define FHT_WRTERR -1
2312 #define FHT_FTCHERR 1
2314 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2315 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2316 fmt_env_t fmt_env
, gf_io_t final_pc
)
2320 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2324 gf_io_t tmp_pc
, tmp_gc
;
2325 struct variable
*vars
= ps_global
->vars
;
2327 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2328 gf_set_so_writec(&tmp_pc
, tmp_store
);
2333 fmt_env
= format_envelope
;
2335 if(ps_global
->full_header
== 2){
2336 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2340 * First, calculate how big a fields array we need.
2343 /* Custom header viewing list specified */
2344 if(hdrs
->type
== HD_LIST
){
2345 /* view all these headers */
2347 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2348 if(!is_an_env_hdr(q
))
2351 /* view all except these headers */
2353 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2357 nfields
--; /* subtract one for ALL_EXCEPT field */
2361 nfields
= 6; /* default view */
2363 /* allocate pointer space */
2365 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2366 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2369 if(hdrs
->type
== HD_LIST
){
2370 /* view all these headers */
2372 /* put the non-envelope headers in fields */
2374 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2375 if(!is_an_env_hdr(q
))
2378 /* view all except these headers */
2380 /* put the list of headers not to view in fields */
2382 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2383 if(strucmp(ALL_EXCEPT
, q
))
2391 fields
[i
= 0] = "Resent-Date";
2392 fields
[++i
] = "Resent-From";
2393 fields
[++i
] = "Resent-To";
2394 fields
[++i
] = "Resent-cc";
2395 fields
[++i
] = "Resent-Subject";
2401 /* custom view with exception list */
2402 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2404 * Go through each header in h and print it.
2405 * First we check to see if it is an envelope header so we
2406 * can print our envelope version of it instead of the raw version.
2409 /* fetch all the other headers */
2411 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2414 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2416 char tmp
[MAILTMPLEN
+1];
2419 colon_loc
= strindex(start
, ':');
2420 if(colon_loc
&& colon_loc
< finish
){
2421 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2422 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2427 if(colon_loc
&& is_an_env_hdr(tmp
)){
2428 char *dummystart
, *dummyfinish
;
2431 * Pretty format for env hdrs.
2432 * If the same header appears more than once, only
2433 * print the last to avoid duplicates.
2434 * They should have been combined in the env when parsed.
2436 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2438 format_env_hdr(stream
, msgno
, section
, env
,
2439 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2442 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2443 hdrs
->charset
, flags
)) != 0)
2450 /* custom view or default */
2452 /* fetch the non-envelope headers */
2454 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2456 /* default envelope for default view */
2457 if(hdrs
->type
== HD_BFIELD
)
2458 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2460 /* go through each header in list, v initialized above */
2461 for(; (q
= *v
) != NULL
; v
++){
2462 if(is_an_env_hdr(q
)){
2463 /* pretty format for env hdrs */
2464 format_env_hdr(stream
, msgno
, section
, env
,
2465 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2469 * Go through h finding all occurrences of this header
2470 * and all continuation lines, and output.
2473 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2475 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2476 hdrs
->charset
, flags
)) != 0)
2489 gf_clear_so_writec(tmp_store
);
2491 if(!rv
){ /* valid data? Do wrapping and filtering... */
2493 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2494 STORE_S
*df_store
= NULL
;
2496 so_seek(tmp_store
, 0L, 0);
2498 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2501 * Test for and act on any display filter
2502 * This barely makes sense. The display filter is going
2503 * to be getting UTF-8'ized headers here. In pre-alpine
2504 * pine the display filter was being fed already decoded
2505 * headers in whatever character set they were in.
2506 * The good news is that that didn't make much
2507 * sense either, so this shouldn't break anything.
2508 * It seems unlikely that anybody is doing anything useful
2509 * with the header part of display filters.
2511 if(ps_global
->tools
.display_filter
2512 && ps_global
->tools
.display_filter_trigger
2513 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2514 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2516 gf_set_so_writec(&tmp_pc
, df_store
);
2517 gf_set_so_readc(&tmp_gc
, df_store
);
2518 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2519 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2520 _("Formatting error: %s"), errstr
);
2524 so_seek(df_store
, 0L, 0);
2526 gf_clear_so_writec(df_store
);
2529 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2530 "No space for filtered text.");
2535 so_seek(tmp_store
, 0L, 0);
2536 gf_set_so_readc(&tmp_gc
, tmp_store
);
2540 int *margin
, wrapflags
= GFW_ONCOMMA
;
2543 gf_link_filter(gf_local_nvtnl
, NULL
);
2545 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2546 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2547 || F_ON(F_SCAN_ADDR
, ps_global
))
2549 gf_link_filter(gf_line_test
,
2550 gf_line_test_opt(url_hilite_hdr
,
2551 gf_url_hilite_opt(&uh
,handlesp
,1)));
2552 wrapflags
|= GFW_HANDLES
;
2555 if((flags
& FM_DISPLAY
)
2556 && !(flags
& FM_NOCOLOR
)
2557 && pico_usingcolor()
2558 && ((VAR_NORM_FORE_COLOR
2559 && VAR_HEADER_GENERAL_FORE_COLOR
2560 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2562 (VAR_NORM_BACK_COLOR
2563 && VAR_HEADER_GENERAL_BACK_COLOR
2564 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2565 wrapflags
|= GFW_HDRCOLOR
;
2567 if((flags
& FM_DISPLAY
)
2568 && !(flags
& FM_NOCOLOR
)
2569 && pico_usingcolor()
2570 && ps_global
->hdr_colors
){
2571 gf_link_filter(gf_line_test
,
2572 gf_line_test_opt(color_headers
, NULL
));
2573 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2576 if(prefix
&& *prefix
)
2577 column
= MAX(column
-strlen(prefix
), 50);
2579 margin
= format_view_margin();
2581 if(!(flags
& FM_NOWRAP
))
2582 gf_link_filter(gf_wrap
,
2583 gf_wrap_filter_opt(column
, column
,
2584 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2587 if(prefix
&& *prefix
)
2588 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2590 if((flags
& FM_DISPLAY
)
2591 && !(flags
& FM_NOCOLOR
)
2592 && pico_usingcolor()
2593 && ((VAR_NORM_FORE_COLOR
2594 && VAR_HEADER_GENERAL_FORE_COLOR
2595 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2597 (VAR_NORM_BACK_COLOR
2598 && VAR_HEADER_GENERAL_BACK_COLOR
2599 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2603 right_margin
= margin
? margin
[1] : 0;
2604 total_wid
= column
- right_margin
;
2606 gf_link_filter(gf_line_test
,
2607 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2608 wrapflags
|= GFW_HANDLES
;
2611 gf_link_filter(gf_nvtnl_local
, NULL
);
2613 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2615 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2616 "Can't build header : %.200s", errstr
);
2621 gf_clear_so_readc(df_store
);
2625 gf_clear_so_readc(tmp_store
);
2628 so_give(&tmp_store
);
2631 fs_give((void **)&h
);
2634 fs_give((void **)&fields
);
2640 /*----------------------------------------------------------------------
2641 Format RAW header text for display
2643 Args: stream -- mail stream for various header text fetches
2644 rawno -- sequence number in stream of message we're interested in
2645 section -- which section of message
2646 pc -- function to write header text with
2648 Result: 0 if all's well, -1 if write error, 1 if fetch error
2650 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2651 in the local convention.
2655 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2657 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2664 if(!gf_puts(NEWLINE
, pc
))
2667 if(ISRFCEOL(h
)) /* all done! */
2670 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2671 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2672 c
= (unsigned char) *h
++;
2673 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2674 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2677 else if(!(*pc
)(*h
++))
2682 return(FHT_FTCHERR
);
2689 /*----------------------------------------------------------------------
2690 Format c-client envelope data suitable for display
2692 Args: s -- mail stream for various header text fetches
2693 n -- raw sequence number in stream of message we're interested in
2694 sect -- which section of message
2695 e -- pointer to msg's envelope
2696 pc -- function to write header text with
2697 which -- which header lines to write
2699 flags -- FM_ flags, see pith/mailview.h
2701 Result: 0 if all's well, -1 if write error, 1 if fetch error
2703 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2704 in the local convention.
2708 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2709 long int which
, char *oacs
, int flags
)
2711 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2716 if((which
& FE_DATE
) && e
->date
) {
2718 snprintf(buftmp
, sizeof(buftmp
), "%s",
2719 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2720 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2721 buftmp
[sizeof(buftmp
)-1] = '\0';
2722 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2723 SIZEOF_20KBUF
, buftmp
);
2725 format_env_puts(p2
, pc
);
2726 gf_puts(NEWLINE
, pc
);
2729 if((which
& FE_FROM
) && e
->from
)
2730 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2732 if((which
& FE_REPLYTO
) && e
->reply_to
2733 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2734 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2736 if((which
& FE_TO
) && e
->to
)
2737 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2739 if((which
& FE_CC
) && e
->cc
)
2740 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2742 if((which
& FE_BCC
) && e
->bcc
)
2743 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2745 if((which
& FE_RETURNPATH
) && e
->return_path
)
2746 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2749 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2751 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2752 if (!e
->ngpathexists
&& e
->message_id
&&
2753 strncmp (e
->message_id
,"<alpine.",8) &&
2754 strncmp (e
->message_id
,"<Pine.",6) &&
2755 strncmp (e
->message_id
,"<MS-C.",6) &&
2756 strncmp (e
->message_id
,"<MailManager.",13) &&
2757 strncmp (e
->message_id
,"<EasyMail.",11) &&
2758 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2761 q_status_message(SM_ORDER
, 0, 3,
2762 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2765 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2766 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2768 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2769 char *freeme
= NULL
;
2774 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2776 if(flags
& FM_DISPLAY
2777 && (ps_global
->display_keywords_in_subject
2778 || ps_global
->display_keywordinits_in_subject
)){
2780 /* don't bother if no keywords are defined */
2781 if(some_user_flags_defined(s
))
2782 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2783 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2784 NULL
, ps_global
->VAR_KW_BRACES
);
2787 format_env_puts(p2
, pc
);
2790 fs_give((void **) &freeme
);
2792 gf_puts(NEWLINE
, pc
);
2795 if((which
& FE_SENDER
) && e
->sender
2796 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2797 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2799 if((which
& FE_MESSAGEID
) && e
->message_id
){
2802 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2803 format_env_puts(p2
, pc
);
2804 gf_puts(NEWLINE
, pc
);
2807 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2808 q
= "In-Reply-To: ";
2810 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);
2811 format_env_puts(p2
, pc
);
2812 gf_puts(NEWLINE
, pc
);
2815 if((which
& FE_REFERENCES
) && e
->references
) {
2818 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2819 format_env_puts(p2
, pc
);
2820 gf_puts(NEWLINE
, pc
);
2828 * The argument fieldname is something like "Subject:..." or "Subject".
2829 * Look through the specs in speccolor for a match of the fieldname,
2830 * and return the color that goes with any match, or NULL.
2831 * Caller should free the color.
2834 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2836 SPEC_COLOR_S
*hc
= NULL
;
2837 COLOR_PAIR
*color_pair
= NULL
;
2838 char *colon
, *fname
;
2839 char fbuf
[FBUF_LEN
+1];
2843 colon
= strindex(fieldname
, ':');
2845 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2846 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2853 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2854 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2859 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2860 if(srchstr(value
, pat
->substring
))
2867 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2868 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2875 * The argument fieldname is something like "Subject:..." or "Subject".
2876 * Look through the specs in hdr_colors for a match of the fieldname,
2877 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2880 any_hdr_color(char *fieldname
)
2882 SPEC_COLOR_S
*hc
= NULL
;
2883 char *colon
, *fname
;
2884 char fbuf
[FBUF_LEN
+1];
2886 colon
= strindex(fieldname
, ':');
2888 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2889 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2896 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2897 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2904 /*----------------------------------------------------------------------
2905 Format an address field, wrapping lines nicely at commas
2907 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2908 addr -- ADDRESS structure to format
2910 Result: A formatted, malloced string is returned.
2911 ----------------------------------------------------------------------*/
2913 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2914 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2917 int trailing
= 0, group
= 0;
2924 * quickly run down address list to make sure none are patently bogus.
2925 * If so, just blat raw field out.
2927 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2928 if(atmp
->host
&& atmp
->host
[0] == '.'){
2929 char *field
, *fields
[2];
2932 fields
[0] = cpystr(field_name
);
2933 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2936 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2939 for(t
= h
= field
; *h
; t
++)
2940 if(*t
== '\015' && *(t
+1) == '\012'){
2941 *t
= '\0'; /* tie off line */
2942 format_env_puts(h
, pc
);
2943 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2944 gf_puts(NEWLINE
, pc
); /* more to write */
2948 else if(!*t
){ /* shouldn't happen much */
2950 format_env_puts(h
, pc
);
2955 fs_give((void **)&field
);
2958 fs_give((void **)&fields
[0]);
2959 gf_puts(NEWLINE
, pc
);
2960 dprint((2, "Error in \"%s\" field address\n",
2961 field_name
? field_name
: "?"));
2965 gf_puts(field_name
, pc
);
2968 atmp
= addr
->next
; /* remember what's next */
2970 if(!addr
->host
&& addr
->mailbox
){
2971 mtmp
= addr
->mailbox
;
2972 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2973 (unsigned char *)tmp_20k_buf
,
2974 SIZEOF_20KBUF
, addr
->mailbox
));
2977 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2978 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2979 tmp_20k_buf
[10000-1] = '\0';
2981 if(!trailing
) /* 1st pass, just address */
2983 else{ /* else comma, unless */
2984 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2985 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2988 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2996 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2998 addr
->personal
= ptmp
; /* restore old personal ptr */
2999 if(!addr
->host
&& addr
->mailbox
){
3000 fs_give((void **)&addr
->mailbox
);
3001 addr
->mailbox
= mtmp
;
3008 gf_puts(NEWLINE
, pc
);
3014 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
3015 /* RFC822 continuation, must start with CRLF */
3016 #define RFC822CONT "\015\012 "
3018 /* Write RFC822 address with some quoting turned off.
3020 * address to interpret
3022 * (This is a copy of c-client's rfc822_write_address except
3023 * we don't quote double quote and dot in personal names. It writes
3024 * to a gf_io_t instead of to a buffer so that we don't have to worry
3025 * about fixed sized buffer overflowing. It's also special cased to deal
3026 * with only a single address.)
3028 * The idea is that there are some places where we'd just like to display
3029 * the personal name as is before applying confusing quoting. However,
3030 * we do want to be careful not to break things that should be quoted so
3031 * we'll only use this where we are sure. Quoting may look ugly but it
3032 * doesn't usually break anything.
3035 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3037 extern const char *rspecials
;
3039 if (adr
->host
) { /* ordinary address? */
3040 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3041 else { /* no, must use phrase <route-addr> form */
3043 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3045 gf_puts(" <", pc
); /* write address delimiter */
3046 pine_rfc822_address(adr
, pc
);
3047 gf_puts (">", pc
); /* closing delimiter */
3053 else if (adr
->mailbox
) { /* start of group? */
3054 /* yes, write group name */
3055 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3057 gf_puts (": ", pc
); /* write group identifier */
3058 *group
= 1; /* in a group */
3060 else if (*group
) { /* must be end of group (but be paranoid) */
3062 *group
= 0; /* no longer in that group */
3067 /* Write RFC822 route-address to string
3069 * address to interpret
3073 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3075 extern char *wspecials
;
3077 if (adr
&& adr
->host
) { /* no-op if no address */
3078 if (adr
->adl
) { /* have an A-D-L? */
3079 gf_puts (adr
->adl
, pc
);
3082 /* write mailbox name */
3083 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3084 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3085 gf_puts ("@", pc
); /* host delimiter */
3086 gf_puts (adr
->host
, pc
); /* write host name */
3092 /* Concatenate RFC822 string
3094 * pointer to string to concatenate
3095 * list of special characters
3099 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3103 if (strpbrk (src
,specials
)) { /* any specials present? */
3104 gf_puts ("\"", pc
); /* opening quote */
3105 /* truly bizarre characters in there? */
3106 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3109 /* turn it into a null-terminated piece */
3113 gf_puts (src
, pc
); /* yes, output leader */
3115 gf_puts ("\\", pc
); /* quoting */
3116 gf_puts (save
, pc
); /* output the bizarre character */
3117 src
= ++s
; /* continue after the bizarre character */
3119 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3120 gf_puts ("\"", pc
); /* closing quote */
3122 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3126 /*----------------------------------------------------------------------
3127 Format an address field, wrapping lines nicely at commas
3129 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3130 newsgrps -- ADDRESS structure to format
3132 Result: A formatted, malloced string is returned.
3134 The resulting lines formatted are 80 columns wide.
3135 ----------------------------------------------------------------------*/
3137 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3139 char buf
[MAILTMPLEN
];
3140 int trailing
= 0, llen
, alen
;
3143 if(!newsgrps
|| !*newsgrps
)
3146 gf_puts(field_name
, pc
);
3148 llen
= strlen(field_name
);
3150 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3151 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3152 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3157 if(!trailing
){ /* first time thru, just address */
3161 else{ /* else preceding comma */
3165 if(alen
+ llen
+ 1 > 76){
3166 gf_puts(NEWLINE
, pc
);
3176 if(alen
&& llen
> 76){ /* handle long addresses */
3177 register char *q
, *p
= &buf
[alen
-1];
3180 if(isspace((unsigned char)*p
)
3181 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3182 for(q
= buf
; q
< p
; q
++)
3183 (*pc
)(*q
); /* write character */
3185 gf_puts(NEWLINE
, pc
);
3194 if(p
== buf
) /* no reasonable break point */
3201 gf_puts(NEWLINE
, pc
);
3206 /*----------------------------------------------------------------------
3207 Format a text field that's part of some raw (non-envelope) message header
3213 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3215 ----------------------------------------------------------------------*/
3217 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3219 register char *current
;
3220 unsigned char *p
, *tmp
= NULL
, c
;
3228 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3230 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3233 len
= SIZEOF_20KBUF
;
3234 p
= (unsigned char *) tmp_20k_buf
;
3237 if(islower((unsigned char)(*start
)))
3238 *start
= toupper((unsigned char)(*start
));
3240 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3242 /* output from start to finish */
3243 while(*current
&& rv
== FHT_OK
)
3244 if(ISRFCEOL(current
)){
3245 if(!gf_puts(NEWLINE
, pc
))
3250 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3251 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3252 c
= (unsigned char) *current
++;
3253 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3254 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3257 else if(!(*pc
)(*current
++))
3261 fs_give((void **) &tmp
);
3271 /*----------------------------------------------------------------------
3272 Format a text field that's part of some raw (non-envelope) message header
3279 ----------------------------------------------------------------------*/
3281 format_env_puts(char *s
, gf_io_t pc
)
3283 if(ps_global
->pass_ctrl_chars
)
3284 return(gf_puts(s
, pc
));
3287 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3288 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3289 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3300 display_parameters(PARAMETER
*params
)
3305 PARMLIST_S
*parmlist
;
3307 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3308 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3309 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3312 tmp_20k_buf
[0] = '\0';
3313 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3314 n
= 0; /* n overloaded */
3315 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3317 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3318 tmp_20k_buf
[10000-1] = '\0';
3322 if(parmlist
->value
){
3323 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3324 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3328 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3333 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3334 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3336 tmp_20k_buf
[10000-1] = '\0';
3340 rfc2231_free_parmlist(&parmlist
);
3343 return(tmp_20k_buf
);
3347 /*----------------------------------------------------------------------
3348 Fetch the requested header fields from the msgno specified
3350 Args: stream -- mail stream of open folder
3351 msgno -- number of message to get header lines from
3352 fields -- array of pointers to desired fields
3354 Returns: allocated string containing matched header lines,
3358 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3361 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3362 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3365 * If the user misconfigures it is possible to have one of the fields
3366 * set to the empty string instead of a header name. We want to catch
3367 * that here instead of asking the server the nonsensical question.
3369 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3373 if(pp
&& *pp
){ /* found an empty header field, fix it */
3374 pflds
= copy_list_array(fields
);
3375 for(pp
= pflds
; pp
&& *pp
; pp
++){
3376 if(!**pp
){ /* scoot rest of the lines up */
3378 for(qq
= pp
; *qq
; qq
++)
3382 fs_give((void **) &free_this
);
3386 /* no headers to look for, return NULL */
3387 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3388 free_list_array(&pflds
);
3395 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3396 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3401 if(pflds
&& pflds
!= fields
)
3402 free_list_array(&pflds
);
3407 while(find_field(&h
, tmp
, sizeof(tmp
))){
3408 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3411 /* interesting field? */
3412 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3414 * Hold off allocating space for matching fields until
3415 * we at least find one to copy...
3418 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3420 while(*p
) /* copy field name */
3423 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3424 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3427 *m
= '\0'; /* tie off match string */
3429 else{ /* no match, pass this field */
3430 while(*h
&& !(*h
++ == '\n'
3431 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3436 if(pflds
&& pflds
!= fields
)
3437 free_list_array(&pflds
);
3439 return(match
? match
: cpystr(""));
3444 find_field(char **h
, char *tmp
, size_t ntmp
)
3448 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3451 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3459 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3463 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3466 if(_last_embedded_fg_color
)
3467 fs_give((void **)&_last_embedded_fg_color
);
3469 _last_embedded_fg_color
= cpystr(cp
->fg
);
3471 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3472 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3477 if(_last_embedded_bg_color
)
3478 fs_give((void **)&_last_embedded_bg_color
);
3480 _last_embedded_bg_color
= cpystr(cp
->bg
);
3482 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3483 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3492 get_cur_embedded_color(void)
3496 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3497 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3499 ret
= pico_get_cur_color();
3506 clear_cur_embedded_color(void)
3508 if(_last_embedded_fg_color
)
3509 fs_give((void **)&_last_embedded_fg_color
);
3511 if(_last_embedded_bg_color
)
3512 fs_give((void **)&_last_embedded_bg_color
);
3517 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3521 if(pico_usingcolor()){
3522 char *fg
= NULL
, *bg
= NULL
, *s
;
3524 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3525 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3526 ps_global
->VAR_NORM_FORE_COLOR
))
3527 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3529 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3530 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3531 ps_global
->VAR_NORM_BACK_COLOR
))
3532 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3538 * The blacks are just known good colors for
3539 * testing whether the other color is good.
3541 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3542 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3543 if(pico_is_good_colorpair(tmp
))
3544 for(s
= color_embed(fg
, bg
);
3545 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3549 free_color_pair(&tmp
);
3553 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3554 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3559 colorstring
[buflen
-1] = '\0';
3566 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3569 if(pico_usingcolor()){
3570 char *fg
= NULL
, *bg
= NULL
, *s
;
3571 char *basefg
= NULL
, *basebg
= NULL
;
3573 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3574 : ps_global
->VAR_NORM_FORE_COLOR
;
3575 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3576 : ps_global
->VAR_NORM_BACK_COLOR
;
3579 * We need to change the fg and bg colors back even if they
3580 * are the same as the Normal Colors so that color_a_quote
3581 * will have a chance to pick up those colors as the ones to
3582 * switch to. We don't do this before the handle above so that
3583 * the quote color will flow into the selectable item when
3584 * the selectable item color is partly the same as the
3585 * normal color. That is, suppose the normal color was black on
3586 * cyan and the selectable color was blue on cyan, only a fg color
3587 * change. We preserve the only-a-fg-color-change in a quote by
3588 * letting the quote background color flow into the selectable text.
3590 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3593 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3596 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3597 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3602 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3606 colorstring
[buflen
-1] = '\0';
3613 * Helper routine that is of limited use.
3614 * We need to tally up the screen width of
3615 * a UTF-8 string as we go through the string.
3616 * We just want the width of the character starting
3617 * at str (and no longer than remaining_octets).
3618 * If we're plopped into the middle of a UTF-8
3619 * character we just want to return width zero.
3622 width_at_this_position(unsigned char *str
, unsigned long n
)
3624 unsigned char *inputp
= str
;
3625 unsigned long remaining_octets
= n
;
3629 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3630 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3631 width
= wcellwidth(ucs
);
3632 /* Writechar will print a '?' */