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"));
396 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
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
))
427 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
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
);
467 // gf_puts(NEWLINE, pc);
471 format_calendar(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
473 char *rawtext
, *caltext
;
474 unsigned long callen
;
475 VCALENDAR_S
*vcal
= NULL
;
479 if(flgs
& FM_NEW_MESS
) {
480 zero_atmts(ps_global
->atmts
);
481 describe_mime(body
, "", 1, 1, 0, flgs
);
484 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
485 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
)){
486 b
= mail_body (ps_global
->mail_stream
, msgno
, a
->number
);
488 gf_puts(_("Error fetching calendar body part"), pc
);
489 gf_puts(NEWLINE
, pc
);
492 if(b
->sparep
== NULL
){
493 rawtext
= mail_fetch_body(ps_global
->mail_stream
, msgno
, a
->number
, &callen
, 0);
495 gf_puts(_("Error fetching calendar text"), pc
);
496 gf_puts(NEWLINE
, pc
);
499 rawtext
[callen
] = '\0'; /* chop off cookie */
502 caltext
= rfc822_base64(rawtext
, strlen(rawtext
), &callen
);
504 gf_puts(_("Error in calendar base64 encoding"), pc
);
505 gf_puts(NEWLINE
, pc
);
510 case ENCQUOTEDPRINTABLE
:
511 caltext
= rfc822_qprint ((unsigned char *) rawtext
,strlen(rawtext
),&callen
);
513 gf_puts(_("Error in calendar quoted printable encoding"), pc
);
514 gf_puts(NEWLINE
, pc
);
519 vcal
= ical_parse_text(caltext
);
520 if(vcal
!= NULL
) vcal
->encoding
= b
->encoding
;
521 b
->sparep
= create_body_sparep(iCalType
, (void *) vcal
);
523 else if(get_body_sparep_type(b
->sparep
) == iCalType
)
524 vcal
= (VCALENDAR_S
*) get_body_sparep_data(b
->sparep
);
525 if(vcal
!= NULL
&& vcal
->comp
!= NULL
){
526 if(vcal
->comp
[VEvent
] != NULL
){
527 format_calendar_vevent(vcal
, a
, handlesp
, flgs
, width
, pc
, FC_SUMMARY
);
528 } /* else another type of entry in the calendar */
530 gf_puts(NEWLINE
, pc
);
538 format_body(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, HEADER_S
*hp
, int flgs
, int width
, gf_io_t pc
)
540 int filt_only_c0
= 0, wrapflags
, error_found
= 0;
541 int is_in_sig
= OUT_SIG_BLOCK
;
542 char *charset
, *decode_err
= NULL
, *tmp1
, *description
;
548 || (ps_global
->full_header
== 2
549 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))) {
551 /*--- Server is not an IMAP2bis, It can't parse MIME
552 so we just show the text here. Hopefully the
553 message isn't a MIME message
557 if((text2
= (void *)pine_mail_fetch_text(ps_global
->mail_stream
,
558 msgno
, NULL
, NULL
, NIL
)) != NULL
){
560 if(!gf_puts(NEWLINE
, pc
)) /* write delimiter */
561 return("Write Error");
563 gf_set_readc(&gc
, text2
, (unsigned long)strlen(text2
), CharStar
, 0);
567 * We need to translate the message
568 * into UTF-8, but that's trouble in the full header case
569 * because we don't know what to translate from. We'll just
570 * take a guess by looking for the first text part and
573 if(body
&& body
->type
== TYPETEXT
)
574 charset
= parameter_val(body
->parameter
, "charset");
575 else if(body
&& body
->type
== TYPEMULTIPART
&& body
->nested
.part
576 && body
->nested
.part
->body
.type
== TYPETEXT
)
577 charset
= parameter_val(body
->nested
.part
->body
.parameter
, "charset");
579 charset
= ps_global
->display_charmap
;
581 if(strucmp(charset
, "us-ascii") && strucmp(charset
, "utf-8")){
582 /* transliterate message text to UTF-8 */
583 gf_link_filter(gf_utf8
, gf_utf8_opt(charset
));
586 /* link in filters, similar to what is done in decode_text() */
587 if(!ps_global
->pass_ctrl_chars
){
588 gf_link_filter(gf_escape_filter
, NULL
);
590 gf_link_filter(gf_control_filter
,
591 gf_control_filter_opt(&filt_only_c0
));
594 gf_link_filter(gf_tag_filter
, NULL
);
596 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
597 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
598 || F_ON(F_SCAN_ADDR
, ps_global
))
600 gf_link_filter(gf_line_test
,
601 gf_line_test_opt(url_hilite
,
602 gf_url_hilite_opt(&uh
,handlesp
,0)));
605 if((flgs
& FM_DISPLAY
)
606 && !(flgs
& FM_NOCOLOR
)
608 && ps_global
->VAR_SIGNATURE_FORE_COLOR
609 && ps_global
->VAR_SIGNATURE_BACK_COLOR
){
610 gf_link_filter(gf_line_test
, gf_line_test_opt(color_signature
, &is_in_sig
));
613 if((flgs
& FM_DISPLAY
)
614 && !(flgs
& FM_NOCOLOR
)
616 && ps_global
->VAR_QUOTE1_FORE_COLOR
617 && ps_global
->VAR_QUOTE1_BACK_COLOR
){
618 gf_link_filter(gf_line_test
, gf_line_test_opt(color_a_quote
, NULL
));
621 if(!(flgs
& FM_NOWRAP
)){
622 wrapflags
= (flgs
& FM_DISPLAY
) ? (GFW_HANDLES
|GFW_SOFTHYPHEN
) : GFW_NONE
;
624 && !(flgs
& FM_NOCOLOR
)
625 && pico_usingcolor())
626 wrapflags
|= GFW_USECOLOR
;
627 gf_link_filter(gf_wrap
, gf_wrap_filter_opt(width
, width
,
629 ? NULL
: format_view_margin(),
634 gf_link_filter(gf_nvtnl_local
, NULL
);
635 if((decode_err
= gf_pipe(gc
, pc
)) != NULL
){
636 /* TRANSLATORS: There was an error putting together a message for
637 viewing. The arg is the description of the error. */
638 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, _("Formatting error: %s"), decode_err
);
639 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
640 if(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
641 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
642 && gf_puts(NEWLINE
, pc
))
650 if(!gf_puts(NEWLINE
, pc
)
651 || !gf_puts(_(" [ERROR fetching text of message]"), pc
)
652 || !gf_puts(NEWLINE
, pc
)
653 || !gf_puts(NEWLINE
, pc
))
654 return("Write Error");
660 /*======== Now loop through formatting all the parts =======*/
661 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++) {
662 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
665 if(a
->body
->type
== TYPEMULTIPART
){
667 if(strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0){
669 if(!(!format_editorial(a
->description
, width
, flgs
, handlesp
, pc
)
670 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
671 return("Write Error");
679 if(a
->suppress_editorial
)
682 if(!(flgs
& FM_NOEDITORIAL
)
683 && (!gf_puts(NEWLINE
, pc
)
684 || (decode_err
= part_desc(a
->number
, a
->body
,
686 ? (a
->can_display
!= MCD_NONE
)
688 : 3, width
, flgs
, pc
))))
689 return("Write Error");
694 switch(a
->body
->type
){
698 * If a message is multipart *and* the first part of it
699 * is text *and that text is empty, there is a good chance that
700 * there was actually something there that c-client was
701 * unable to parse. Here we report the empty message body
702 * and insert the raw RFC822.TEXT (if full-headers are
705 if(body
->type
== TYPEMULTIPART
706 && a
== ps_global
->atmts
707 && a
->body
->size
.bytes
== 0
708 && F_ON(F_ENABLE_FULL_HDR
, ps_global
)){
711 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
712 "Empty or malformed message%s.",
713 ps_global
->full_header
== 2
714 ? ". Displaying raw text"
715 : ". Use \"H\" to see raw text");
716 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
718 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
719 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
720 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
721 return("Write Error");
723 if(ps_global
->full_header
== 2
724 && (err
= detach_raw(ps_global
->mail_stream
, msgno
,
725 a
->number
, pc
, flgs
))){
726 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
,
727 "%s%s [ Formatting error: %s ]%s%s",
728 NEWLINE
, NEWLINE
, err
, NEWLINE
, NEWLINE
);
729 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
730 if(!gf_puts(tmp_20k_buf
, pc
))
731 return("Write Error");
738 * Don't write our delimiter if this text part is
739 * the first part of a message/rfc822 segment...
741 if(show_parts
&& a
!= ps_global
->atmts
742 && !((a
[-1].body
&& a
[-1].body
->type
== TYPEMESSAGE
)
744 || (a
[-1].body
->type
== TYPEMULTIPART
745 && a
[-1].body
->subtype
746 && (strucmp(a
[-1].body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0)
747 && &a
[-1] != ps_global
->atmts
748 && a
[-2].body
&& a
[-2].body
->type
== TYPEMESSAGE
)
751 && !(flgs
& FM_NOEDITORIAL
)){
752 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
753 tmp1
= "Calendar entry";
755 tmp1
= a
->body
->description
? a
->body
->description
757 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
758 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
760 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
762 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
763 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
764 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
765 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
766 return("Write Error");
769 if(!MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
770 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
771 (flgs
& FM_DISPLAY
) ? InLine
: QStatus
,
776 tmp1
= a
->body
->description
? a
->body
->description
777 : (strucmp(a
->body
->subtype
, "delivery-status") == 0)
779 : "Included Message";
780 description
= iutf8ncpy((char *)(tmp_20k_buf
+10000),
781 (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+15000), 5000, tmp1
), 5000);
783 snprintf(tmp_20k_buf
, SIZEOF_20KBUF
, "Part %s: \"%.1024s\"", a
->number
,
785 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
787 if(!(gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)
788 && !format_editorial(tmp_20k_buf
, width
, flgs
, handlesp
, pc
)
789 && gf_puts(NEWLINE
, pc
) && gf_puts(NEWLINE
, pc
)))
790 return("Write Error");
792 if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "rfc822") == 0){
793 /* imapenvonly, we may not have all the headers we need */
794 if(a
->body
->nested
.msg
->env
->imapenvonly
)
795 mail_fetch_header(ps_global
->mail_stream
, msgno
,
796 a
->number
, NULL
, NULL
, FT_PEEK
);
797 switch(format_header(ps_global
->mail_stream
, msgno
, a
->number
,
798 a
->body
->nested
.msg
->env
, hp
,
799 NULL
, handlesp
, flgs
, NULL
, pc
)){
800 case -1 : /* write error */
801 return("Write Error");
803 case 1 : /* fetch error */
804 if(!(gf_puts("[ Error fetching header ]", pc
)
805 && !gf_puts(NEWLINE
, pc
)))
806 return("Write Error");
811 else if(a
->body
->subtype
&& strucmp(a
->body
->subtype
, "external-body") == 0){
812 int *margin
, avail
, m1
, m2
;
815 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
817 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
820 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
823 if(format_editorial("This part is not included and can be fetched as follows:", avail
, flgs
, handlesp
, pc
)
824 || !gf_puts(NEWLINE
, pc
)
825 || format_editorial(display_parameters(a
->body
->parameter
), avail
, flgs
, handlesp
, pc
))
826 return("Write Error");
829 error_found
+= decode_text(a
, msgno
, pc
, handlesp
,
830 (flgs
&FM_DISPLAY
) ? InLine
: QStatus
,
833 if(!gf_puts(NEWLINE
, pc
))
834 return("Write Error");
839 if((decode_err
= part_desc(a
->number
, a
->body
,
840 (flgs
& FM_DISPLAY
) ? 1 : 3,
841 width
, flgs
, pc
)) != NULL
)
842 return("Write Error");
849 && (pith_opt_rfc2369_editorial
? (*pith_opt_rfc2369_editorial
)(msgno
, handlesp
, flgs
, width
, pc
) : 1)
850 && format_blip_seen(msgno
)))
851 return("Cannot format body.");
859 format_attachment_list(long int msgno
, BODY
*body
, HANDLE_S
**handlesp
, int flgs
, int width
, gf_io_t pc
)
863 if(flgs
& FM_NEW_MESS
) {
864 zero_atmts(ps_global
->atmts
);
865 describe_mime(body
, "", 1, 1, 0, flgs
);
868 /*----- First do the list of parts/attachments if needed ----*/
869 if((flgs
& FM_DISPLAY
)
870 && (ps_global
->atmts
[1].description
871 || (ps_global
->atmts
[0].body
872 && ps_global
->atmts
[0].body
->type
!= TYPETEXT
))){
873 char tmp
[6*MAX_SCREEN_COLS
+ 1], *tmpp
;
874 int i
, n
, maxnumwid
= 0, maxsizewid
= 0, *margin
;
875 int avail
, m1
, m2
, hwid
, s1
, s2
, s3
, s4
, s5
, dwid
, shownwid
;
876 int sizewid
, descwid
, dashwid
, partwid
, padwid
;
877 COLOR_PAIR
*hdrcolor
= NULL
;
879 if((flgs
& FM_DISPLAY
)
880 && !(flgs
& FM_NOCOLOR
)
882 && ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
883 && ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
884 && ps_global
->VAR_NORM_FORE_COLOR
885 && ps_global
->VAR_NORM_BACK_COLOR
886 && (colorcmp(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
887 ps_global
->VAR_NORM_FORE_COLOR
)
888 || colorcmp(ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
,
889 ps_global
->VAR_NORM_BACK_COLOR
))){
891 if((hdrcolor
= new_color_pair(ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
,
892 ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
)) != NULL
){
893 if(!pico_is_good_colorpair(hdrcolor
))
894 free_color_pair(&hdrcolor
);
898 margin
= (flgs
& FM_NOINDENT
) ? NULL
: format_view_margin();
901 * Attachment list header
906 m1
= MAX(MIN(margin
? margin
[0] : 0, avail
), 0);
909 m2
= MAX(MIN(margin
? margin
[1] : 0, avail
), 0);
912 hwid
= MAX(avail
, 0);
914 i
= utf8_width(_("Parts/Attachments:"));
915 partwid
= MIN(i
, hwid
);
916 padwid
= hdrcolor
? (hwid
-partwid
) : 0;
919 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
920 if(!gf_puts(tmp
, pc
))
924 utf8_snprintf(tmp
, sizeof(tmp
),
926 /* TRANSLATORS: A label */
927 partwid
, partwid
, _("Parts/Attachments:"),
930 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
934 /*----- Figure max display widths -----*/
935 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
936 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
939 if((n
= utf8_width(a
->number
)) > maxnumwid
)
942 if((n
= utf8_width(a
->size
)) > maxsizewid
)
947 * ----- adjust max lengths for nice display -----
949 * marg _ D _ number _ Shown _ _ _ size _ _ description marg
953 avail
= width
- m1
- m2
;
955 s1
= MAX(MIN(1, avail
), 0);
958 dwid
= MAX(MIN(1, avail
), 0);
961 s2
= MAX(MIN(1, avail
), 0);
964 maxnumwid
= MIN(maxnumwid
, width
/3);
965 maxnumwid
= MAX(MIN(maxnumwid
, avail
), 0);
968 s3
= MAX(MIN(1, avail
), 0);
971 shownwid
= MAX(MIN(5, avail
), 0);
974 s4
= MAX(MIN(3, avail
), 0);
977 sizewid
= MAX(MIN(maxsizewid
, avail
), 0);
980 s5
= MAX(MIN(2, avail
), 0);
983 descwid
= MAX(0, avail
);
985 /*----- Format the list of attachments -----*/
986 for(a
= ps_global
->atmts
; a
->description
!= NULL
; a
++){
987 COLOR_PAIR
*lastc
= NULL
;
989 int thisdescwid
, padwid
;
991 if(MIME_VCALENDAR(a
->body
->type
, a
->body
->subtype
))
994 if(a
->body
->type
== TYPEMULTIPART
995 && (strucmp(a
->body
->subtype
, OUR_PKCS7_ENCLOSURE_SUBTYPE
)==0))
999 i
= utf8_width((descwid
> 2 && a
->description
) ? a
->description
: "");
1000 thisdescwid
= MIN(i
, descwid
);
1001 padwid
= hdrcolor
? (descwid
-thisdescwid
) : 0;
1004 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1005 if(!gf_puts(tmp
, pc
))
1009 utf8_snprintf(tmp
, sizeof(tmp
),
1010 "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
1013 msgno_part_deleted(ps_global
->mail_stream
, msgno
, a
->number
) ? "D" : "",
1015 maxnumwid
, maxnumwid
,
1017 ? short_str(a
->number
, numbuf
, sizeof(numbuf
), maxnumwid
, FrontDots
)
1021 a
->shown
? "Shown" :
1022 (a
->can_display
!= MCD_NONE
&& !(a
->can_display
& MCD_EXT_PROMPT
))
1026 a
->size
? a
->size
: "",
1028 thisdescwid
, thisdescwid
,
1029 (descwid
> 2 && a
->description
) ? a
->description
: "");
1031 if(!(!hdrcolor
|| embed_color(hdrcolor
, pc
)))
1034 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1035 char buf
[16], color
[64];
1039 for(tmpp
= tmp
; *tmpp
&& *tmpp
== ' '; tmpp
++)
1043 h
= new_handle(handlesp
);
1047 snprintf(buf
, sizeof(buf
), "%d", h
->key
);
1048 buf
[sizeof(buf
)-1] = '\0';
1050 if(!(flgs
& FM_NOCOLOR
)
1051 && handle_start_color(color
, sizeof(color
), &l
, 1)){
1052 lastc
= get_cur_embedded_color();
1053 if(!gf_nputs(color
, (long) l
, pc
))
1056 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)
1057 && (!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDON
))))
1060 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_HANDLE
)
1061 && (*pc
)(strlen(buf
)) && gf_puts(buf
, pc
)))
1067 if(!format_env_puts(tmpp
, pc
))
1070 if(F_ON(F_VIEW_SEL_ATTACH
, ps_global
) && handlesp
){
1072 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1073 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1077 if(!embed_color(lastc
, pc
))
1080 free_color_pair(&lastc
);
1082 else if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_BOLDOFF
)))
1085 if(!((*pc
)(TAG_EMBED
) && (*pc
)(TAG_INVOFF
)))
1090 snprintf(tmp
, sizeof(tmp
), "%*.*s", padwid
, padwid
, "");
1091 if(!gf_puts(tmp
, pc
))
1095 if(!gf_puts(NEWLINE
, pc
))
1100 * Dashed line after list
1104 avail
= width
- m1
- m2
;
1105 hwid
= MAX(avail
, 0);
1107 dashwid
= MAX(MIN(40, hwid
-2), 0);
1108 padwid
= hwid
- dashwid
;
1110 snprintf(tmp
, sizeof(tmp
), "%*.*s", m1
, m1
, "");
1111 if(!gf_puts(tmp
, pc
))
1115 snprintf(tmp
, sizeof(tmp
),
1117 repeat_char(dashwid
, '-'),
1118 padwid
, padwid
, "");
1121 avail
= width
- m1
-2;
1123 dashwid
= MAX(MIN(40, avail
), 0);
1126 snprintf(tmp
, sizeof(tmp
),
1129 repeat_char(dashwid
, '-'));
1132 if(!((!hdrcolor
|| embed_color(hdrcolor
, pc
)) && gf_puts(tmp
, pc
) && gf_puts(NEWLINE
, pc
)))
1136 free_color_pair(&hdrcolor
);
1145 * format_blip_seen - if seen bit (which is usually cleared as a side-effect
1146 * of body part fetches as we're formatting) for the
1147 * given message isn't set (likely because there
1148 * weren't any parts suitable for display), then make
1149 * sure to set it here.
1152 format_blip_seen(long int msgno
)
1156 if(msgno
> 0L && ps_global
->mail_stream
1157 && msgno
<= ps_global
->mail_stream
->nmsgs
1158 && (mc
= mail_elt(ps_global
->mail_stream
, msgno
))
1160 && !ps_global
->mail_stream
->rdonly
)
1161 mail_flag(ps_global
->mail_stream
, long2string(msgno
), "\\SEEN", ST_SET
);
1168 * is_an_env_hdr - is this name a header in the envelope structure?
1170 * name - the header name to check
1173 is_an_env_hdr(char *name
)
1177 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1178 if(!strucmp(name
, envelope_hdrs
[i
].name
))
1188 * is_an_addr_hdr - is this an address header?
1190 * name - the header name to check
1193 is_an_addr_hdr(char *fieldname
)
1195 char fbuf
[FBUF_LEN
+1];
1196 char *colon
, *fname
;
1197 static char *addr_headers
[] = {
1213 /* so it is pointing to NULL */
1214 char **p
= addr_headers
+ sizeof(addr_headers
)/sizeof(*addr_headers
) - 1;
1216 if((colon
= strindex(fieldname
, ':')) != NULL
){
1217 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,sizeof(fbuf
)));
1218 fbuf
[MIN(colon
-fieldname
,sizeof(fbuf
)-1)] = '\0';
1224 if(fname
&& *fname
){
1225 for(p
= addr_headers
; *p
; p
++)
1226 if(!strucmp(fname
, *p
))
1230 return((*p
) ? 1 : 0);
1235 * Format a single field from the envelope
1238 format_env_hdr(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
1239 fmt_env_t fmt_env
, gf_io_t pc
, char *field
, char *oacs
, int flags
)
1244 fmt_env
= format_envelope
;
1246 for(i
= 0; envelope_hdrs
[i
].name
; i
++)
1247 if(!strucmp(field
, envelope_hdrs
[i
].name
)){
1248 (*fmt_env
)(stream
, msgno
, section
, env
, pc
, envelope_hdrs
[i
].val
, oacs
, flags
);
1255 * Look through header string beginning with "begin", for the next
1256 * occurrence of header "field". Set "start" to that. Set "end" to point one
1257 * position past all of the continuation lines that go with "field".
1258 * That is, if "end" is converted to a null
1259 * character then the string "start" will be the next occurence of header
1260 * "field" including all of its continuation lines. Assume we
1261 * have CRLF's as end of lines.
1263 * If "field" is NULL, then we just leave "start" pointing to "begin" and
1264 * make "end" the end of that header.
1266 * Returns 1 if found, 0 if not.
1269 delineate_this_header(char *field
, char *begin
, char **start
, char **end
)
1271 char tmpfield
[MAILTMPLEN
+2]; /* copy of field with colon appended */
1276 if(!begin
|| !*begin
|| isspace((unsigned char)*begin
))
1282 strncpy(tmpfield
, field
, sizeof(tmpfield
)-2);
1283 tmpfield
[sizeof(tmpfield
)-2] = '\0';
1284 strncat(tmpfield
, ":", sizeof(tmpfield
)-strlen(tmpfield
)-1);
1285 tmpfield
[sizeof(tmpfield
)-1] = '\0';
1288 * We require that start is at the beginning of a line, so
1289 * either it equals begin (which we assume is the beginning of a
1290 * line) or it is preceded by a CRLF.
1293 *start
= srchstr(begin_srch
, tmpfield
);
1294 while(*start
&& *start
!= begin
1295 && !(*start
- 2 >= begin
&& ISRFCEOL(*start
- 2))){
1296 begin_srch
= *start
+ 1;
1297 *start
= srchstr(begin_srch
, tmpfield
);
1304 for(p
= *start
; *p
; p
++){
1306 && (!isspace((unsigned char)*(p
+2)) || *(p
+2) == '\015')){
1308 * The final 015 in the test above is to test for the end
1325 handle_start_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
1329 if(pico_usingcolor()){
1330 char *fg
= NULL
, *bg
= NULL
, *s
;
1331 char *basefg
= NULL
, *basebg
= NULL
;
1333 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
1334 : ps_global
->VAR_NORM_FORE_COLOR
;
1335 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
1336 : ps_global
->VAR_NORM_BACK_COLOR
;
1338 if(ps_global
->VAR_SLCTBL_FORE_COLOR
1339 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
, basefg
))
1340 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
1342 if(ps_global
->VAR_SLCTBL_BACK_COLOR
1343 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
, basebg
))
1344 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
1350 * The blacks are just known good colors for
1351 * testing whether the other color is good.
1353 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
1354 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
1355 if(pico_is_good_colorpair(tmp
))
1356 for(s
= color_embed(fg
, bg
);
1357 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
1361 free_color_pair(&tmp
);
1365 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1366 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
1371 colorstring
[buflen
-1] = '\0';
1378 handle_end_color(char *colorstring
, size_t buflen
, int *len
)
1381 if(pico_usingcolor()){
1382 char *fg
= NULL
, *bg
= NULL
, *s
;
1385 * We need to change the fg and bg colors back even if they
1386 * are the same as the Normal Colors so that color_a_quote
1387 * will have a chance to pick up those colors as the ones to
1388 * switch to. We don't do this before the handle above so that
1389 * the quote color will flow into the selectable item when
1390 * the selectable item color is partly the same as the
1391 * normal color. That is, suppose the normal color was black on
1392 * cyan and the selectable color was blue on cyan, only a fg color
1393 * change. We preserve the only-a-fg-color-change in a quote by
1394 * letting the quote background color flow into the selectable text.
1396 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
1397 fg
= ps_global
->VAR_NORM_FORE_COLOR
;
1399 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
1400 bg
= ps_global
->VAR_NORM_BACK_COLOR
;
1402 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
1403 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
1408 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
1412 colorstring
[buflen
-1] = '\0';
1419 url_embed(int embed
)
1421 static char buf
[3] = {TAG_EMBED
};
1429 * Paint the signature.
1432 color_signature(long int linenum
, char *line
, LT_INS_S
**ins
, void *is_in_sig
)
1434 struct variable
*vars
= ps_global
->vars
;
1436 COLOR_PAIR
*col
= NULL
;
1438 if(is_in_sig
== NULL
)
1441 in_sig_block
= (int *) is_in_sig
;
1443 if(!strcmp(line
, SIGDASHES
))
1444 *in_sig_block
= START_SIG_BLOCK
;
1445 else if(*line
== '\0')
1447 * Suggested by Eduardo: allow for a blank line right after
1450 *in_sig_block
= (*in_sig_block
== START_SIG_BLOCK
)
1451 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1453 *in_sig_block
= (*in_sig_block
!= OUT_SIG_BLOCK
)
1454 ? IN_SIG_BLOCK
: OUT_SIG_BLOCK
;
1456 if(*in_sig_block
!= OUT_SIG_BLOCK
1457 && VAR_SIGNATURE_FORE_COLOR
&& VAR_SIGNATURE_BACK_COLOR
1458 && (col
= new_color_pair(VAR_SIGNATURE_FORE_COLOR
,
1459 VAR_SIGNATURE_BACK_COLOR
))){
1460 if(!pico_is_good_colorpair(col
))
1461 free_color_pair(&col
);
1465 char *p
, fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1467 ins
= gf_line_test_new_ins(ins
, line
,
1468 color_embed(col
->fg
, col
->bg
),
1471 strncpy(fg
, color_to_asciirgb(VAR_NORM_FORE_COLOR
), sizeof(fg
));
1472 fg
[sizeof(fg
)-1] = '\0';
1473 strncpy(bg
, color_to_asciirgb(VAR_NORM_BACK_COLOR
), sizeof(bg
));
1474 bg
[sizeof(bg
)-1] = '\0';
1477 * Loop watching colors, and override with
1478 * signature color whenever the normal foreground and background
1479 * colors are in force.
1483 if(*p
++ == TAG_EMBED
){
1487 p
+= *p
+ 1; /* skip handle key */
1491 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1492 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1493 p
+= RGBLEN
; /* advance past color value */
1495 if(!colorcmp(rgbbuf
, VAR_NORM_FORE_COLOR
)
1496 && !colorcmp(bg
, VAR_NORM_BACK_COLOR
))
1497 ins
= gf_line_test_new_ins(ins
, p
,
1498 color_embed(col
->fg
,NULL
),
1503 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1504 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1505 p
+= RGBLEN
; /* advance past color value */
1507 if(!colorcmp(rgbbuf
, VAR_NORM_BACK_COLOR
)
1508 && !colorcmp(fg
, VAR_NORM_FORE_COLOR
))
1509 ins
= gf_line_test_new_ins(ins
, p
,
1510 color_embed(NULL
,col
->bg
),
1520 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1521 color_embed(VAR_NORM_FORE_COLOR
,
1522 VAR_NORM_BACK_COLOR
),
1524 free_color_pair(&col
);
1532 * Line filter to add color to displayed headers.
1535 color_headers(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1537 static char field
[FBUF_LEN
+ 1];
1538 char fg
[RGBLEN
+ 1], bg
[RGBLEN
+ 1], rgbbuf
[RGBLEN
+ 1];
1539 char *p
, *q
, *value
, *beg
;
1541 int in_quote
= 0, in_comment
= 0, did_color
= 0;
1542 struct variable
*vars
= ps_global
->vars
;
1544 field
[FBUF_LEN
] = '\0';
1546 if(isspace((unsigned char)*line
)) /* continuation line */
1549 if(!(value
= strindex(line
, ':')))
1552 memset(field
, 0, sizeof(field
));
1553 strncpy(field
, line
, MIN(value
-line
, sizeof(field
)-1));
1556 for(value
++; isspace((unsigned char)*value
); value
++)
1559 strncpy(fg
, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR
), sizeof(fg
));
1560 fg
[sizeof(fg
)-1] = '\0';
1561 strncpy(bg
, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR
), sizeof(bg
));
1562 bg
[sizeof(bg
)-1] = '\0';
1565 * Split into two cases depending on whether this is a header which
1566 * contains addresses or not. We may color addresses separately.
1568 if(is_an_addr_hdr(field
)){
1571 * If none of the patterns are for this header, don't bother parsing
1572 * and checking each address.
1574 if(!any_hdr_color(field
))
1578 * First check for patternless patterns which color whole line.
1580 if((color
= hdr_color(field
, NULL
, ps_global
->hdr_colors
)) != NULL
){
1581 if(pico_is_good_colorpair(color
)){
1582 ins
= gf_line_test_new_ins(ins
, value
,
1583 color_embed(color
->fg
, color
->bg
),
1585 strncpy(fg
, color_to_asciirgb(color
->fg
), sizeof(fg
));
1586 fg
[sizeof(fg
)-1] = '\0';
1587 strncpy(bg
, color_to_asciirgb(color
->bg
), sizeof(bg
));
1588 bg
[sizeof(bg
)-1] = '\0';
1592 free_color_pair(&color
);
1596 * Then go through checking address by address.
1597 * Keep track of quotes and watch for color changes, and override
1598 * with most recent header color whenever the normal foreground
1599 * and background colors are in force.
1605 /* skip next character */
1606 if(*(p
+1) && (in_comment
|| in_quote
))
1615 in_quote
= 1 - in_quote
;
1633 if(!(in_quote
|| in_comment
)){
1634 /* we reached the end of this address */
1637 free_color_pair(&color
);
1639 if((color
= hdr_color(field
, beg
,
1640 ps_global
->hdr_colors
)) != NULL
){
1641 if(pico_is_good_colorpair(color
)){
1643 ins
= gf_line_test_new_ins(ins
, beg
,
1644 color_embed(color
->fg
,
1648 for(q
= p
; q
> beg
&&
1649 isspace((unsigned char)*(q
-1)); q
--)
1652 ins
= gf_line_test_new_ins(ins
, q
,
1653 color_embed(fg
, bg
),
1657 free_color_pair(&color
);
1662 for(p
++; isspace((unsigned char)*p
); p
++)
1676 p
+= *p
+ 1; /* skip handle key */
1681 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1682 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1683 p
+= RGBLEN
; /* advance past color value */
1685 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
))
1686 ins
= gf_line_test_new_ins(ins
, p
,
1687 color_embed(color
->fg
,NULL
),
1693 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1694 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1695 p
+= RGBLEN
; /* advance past color value */
1697 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
))
1698 ins
= gf_line_test_new_ins(ins
, p
,
1699 color_embed(NULL
,color
->bg
),
1716 for(q
= beg
; *q
&& isspace((unsigned char)*q
); q
++)
1719 if(*q
&& !(in_quote
|| in_comment
)){
1720 /* we reached the end of this address */
1722 free_color_pair(&color
);
1724 if((color
= hdr_color(field
, beg
, ps_global
->hdr_colors
)) != NULL
){
1725 if(pico_is_good_colorpair(color
)){
1727 ins
= gf_line_test_new_ins(ins
, beg
,
1728 color_embed(color
->fg
,
1731 for(q
= p
; q
> beg
&& isspace((unsigned char)*(q
-1)); q
--)
1734 ins
= gf_line_test_new_ins(ins
, q
,
1735 color_embed(fg
, bg
),
1739 free_color_pair(&color
);
1744 free_color_pair(&color
);
1747 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1748 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1749 VAR_HEADER_GENERAL_BACK_COLOR
),
1754 color
= hdr_color(field
, value
, ps_global
->hdr_colors
);
1757 if(pico_is_good_colorpair(color
)){
1758 ins
= gf_line_test_new_ins(ins
, value
,
1759 color_embed(color
->fg
, color
->bg
),
1763 * Loop watching colors, and override with header
1764 * color whenever the normal foreground and background
1765 * colors are in force.
1769 if(*p
++ == TAG_EMBED
){
1773 p
+= *p
+ 1; /* skip handle key */
1777 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1778 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1779 p
+= RGBLEN
; /* advance past color value */
1781 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_FORE_COLOR
)
1782 && !colorcmp(bg
, VAR_HEADER_GENERAL_BACK_COLOR
))
1783 ins
= gf_line_test_new_ins(ins
, p
,
1784 color_embed(color
->fg
,NULL
),
1789 snprintf(rgbbuf
, sizeof(rgbbuf
), "%.*s", RGBLEN
, p
);
1790 rgbbuf
[sizeof(rgbbuf
)-1] = '\0';
1791 p
+= RGBLEN
; /* advance past color value */
1793 if(!colorcmp(rgbbuf
, VAR_HEADER_GENERAL_BACK_COLOR
)
1794 && !colorcmp(fg
, VAR_HEADER_GENERAL_FORE_COLOR
))
1795 ins
= gf_line_test_new_ins(ins
, p
,
1796 color_embed(NULL
,color
->bg
),
1806 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
1807 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
1808 VAR_HEADER_GENERAL_BACK_COLOR
),
1812 free_color_pair(&color
);
1821 url_hilite(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1823 register char *lp
, *up
= NULL
, *urlp
= NULL
,
1824 *weburlp
= NULL
, *mailurlp
= NULL
;
1825 int n
, n1
, n2
, n3
, l
;
1826 char buf
[256], color
[256];
1830 for(lp
= line
; ; lp
= up
+ n
){
1831 /* scan for all of them so we can choose the first */
1832 if(F_ON(F_VIEW_SEL_URL
,ps_global
))
1833 urlp
= rfc1738_scan(lp
, &n1
);
1834 if(F_ON(F_VIEW_SEL_URL_HOST
,ps_global
))
1835 weburlp
= web_host_scan(lp
, &n2
);
1836 if(F_ON(F_SCAN_ADDR
,ps_global
))
1837 mailurlp
= mail_addr_scan(lp
, &n3
);
1839 if(urlp
|| weburlp
|| mailurlp
){
1841 weburlp
? weburlp
: mailurlp
;
1842 if(up
== urlp
&& weburlp
&& weburlp
< up
)
1844 if(mailurlp
&& mailurlp
< up
)
1849 weburlp
= mailurlp
= NULL
;
1851 else if(up
== weburlp
){
1863 uh
= (URL_HILITE_S
*) local
;
1865 h
= new_handle(uh
->handlesp
);
1867 h
->h
.url
.path
= (char *) fs_get((n
+ 10) * sizeof(char));
1868 snprintf(h
->h
.url
.path
, n
+10, "%s%.*s",
1869 weburlp
? "http://" : (mailurlp
? "mailto:" : ""), n
, up
);
1870 h
->h
.url
.path
[n
+10-1] = '\0';
1872 if(handle_start_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1873 ins
= gf_line_test_new_ins(ins
, up
, color
, l
);
1874 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
))
1875 ins
= gf_line_test_new_ins(ins
, up
, url_embed(TAG_BOLDON
), 2);
1878 buf
[1] = TAG_HANDLE
;
1879 snprintf(&buf
[3], sizeof(buf
)-3, "%d", h
->key
);
1880 buf
[sizeof(buf
)-1] = '\0';
1881 buf
[2] = strlen(&buf
[3]);
1882 ins
= gf_line_test_new_ins(ins
, up
, buf
, (int) buf
[2] + 3);
1884 /* in case it was the current selection */
1885 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_INVOFF
), 2);
1887 if(scroll_handle_end_color(color
, sizeof(color
), &l
, uh
->hdr_color
))
1888 ins
= gf_line_test_new_ins(ins
, up
+ n
, color
, l
);
1890 ins
= gf_line_test_new_ins(ins
, up
+ n
, url_embed(TAG_BOLDOFF
), 2);
1892 urlp
= weburlp
= mailurlp
= NULL
;
1900 url_hilite_hdr(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1902 static int check_for_urls
= 0;
1905 if(isspace((unsigned char)*line
)) /* continuation, check or not
1906 depending on last line */
1910 if((lp
= strchr(line
, ':')) != NULL
){ /* there ought to always be a colon */
1915 if(((ft
= pine_header_standard(line
)) == FreeText
1917 || ft
== TypeUnknown
)
1918 && strucmp(line
, "message-id")
1919 && strucmp(line
, "newsgroups")
1920 && strucmp(line
, "references")
1921 && strucmp(line
, "in-reply-to")
1922 && strucmp(line
, "received")
1923 && strucmp(line
, "date")){
1932 (void) url_hilite(linenum
, lp
+ 1, ins
, local
);
1939 pad_to_right_edge(long int linenum
, char *line
, LT_INS_S
**ins
, void *local
)
1944 struct variable
*vars
= ps_global
->vars
;
1949 total_wid
= *((int *) local
);
1951 /* calculate width of line */
1961 p
+= *p
+ 1; /* skip handle key */
1966 p
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1978 default: /* literal embed char */
1986 while(((++wid
) & 0x07) != 0) /* add tab's spaces */
1992 wid
+= width_at_this_position((unsigned char *) p
, strlen(p
));
1998 if(total_wid
> wid
){
1999 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2000 color_embed(VAR_HEADER_GENERAL_FORE_COLOR
,
2001 VAR_HEADER_GENERAL_BACK_COLOR
),
2003 ins
= gf_line_test_new_ins(ins
, line
+strlen(line
),
2004 repeat_char(total_wid
-wid
, ' '), total_wid
-wid
);
2005 ins
= gf_line_test_new_ins(ins
, line
+ strlen(line
),
2006 color_embed(VAR_NORM_FORE_COLOR
,
2007 VAR_NORM_BACK_COLOR
),
2019 url_external_specific_handler(char *url
, int len
)
2021 static char list
[UES_LEN
* UES_MAX
];
2027 for(i
= 0; i
< UES_MAX
&& *(p
= &list
[i
* UES_LEN
]); i
++)
2028 if(strlen(p
) == len
&& !struncmp(p
, url
, len
))
2031 else{ /* initialize! */
2032 char **l
, *test
, *cmd
, *p
, *p2
;
2035 memset(list
, 0, sizeof(list
));
2036 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
2037 get_pair(*l
, &test
, &cmd
, 1, 1);
2039 if((p
= srchstr(test
, "_scheme(")) && (p2
= strstr(p
+8, ")_"))){
2042 for(p
+= 8; *p
&& i
< UES_MAX
; p
+= n
)
2043 if((p2
= strchr(p
, ',')) != NULL
){
2044 if((n
= p2
- p
) < UES_LEN
){
2045 strncpy(&list
[i
* UES_LEN
], p
, MIN(n
, sizeof(list
)-(i
* UES_LEN
)));
2050 "* * * HANLDER TOO LONG: %.*s\n", n
,
2056 if(strlen(p
) <= UES_LEN
){
2057 strncpy(&list
[i
* UES_LEN
], p
, sizeof(list
)-(i
* UES_LEN
));
2066 fs_give((void **) &test
);
2069 fs_give((void **) &cmd
);
2078 url_imap_folder(char *true_url
, char **folder
, imapuid_t
*uid_val
,
2079 imapuid_t
*uid
, char **search
, int silent
)
2081 char *url
, *scheme
, *p
, *cmd
, *server
= NULL
,
2082 *user
= NULL
, *auth
= NULL
, *mailbox
= NULL
,
2085 int rv
= URL_IMAP_ERROR
;
2088 * Since we're planting nulls, operate on a temporary copy...
2090 scheme
= silent
? NULL
: "IMAP";
2091 url
= cpystr(true_url
+ 7);
2093 /* Try to pick apart the "iserver" portion */
2094 if((cmd
= strchr(url
, '/')) != NULL
){ /* iserver "/" [mailbox] ? */
2098 dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
2100 cmd
= &url
[strlen(url
)-1]; /* assume only iserver */
2103 if((p
= strchr(url
, '@')) != NULL
){ /* user | auth | pass? */
2105 server
= rfc1738_str(p
);
2107 /* only ";auth=*" supported (and also ";auth=anonymous") */
2108 if((p
= srchstr(url
, ";auth=")) != NULL
){
2110 auth
= rfc1738_str(p
+ 6);
2114 user
= rfc1738_str(url
);
2117 server
= rfc1738_str(url
);
2120 return(url_bogus_imap(&url
, scheme
, "No server specified"));
2123 * "iserver" in hand, pick apart the "icommand"...
2126 if(!*cmd
|| (p
= srchstr(cmd
, ";type="))){
2130 * No "icommand" (all top-level folders) or "imailboxlist"...
2133 *p
= '\0'; /* tie off criteria */
2134 criteria
= rfc1738_str(cmd
); /* get "enc_list_mailbox" */
2135 if(!strucmp(p
= rfc1738_str(p
+6), "lsub"))
2136 rv
|= URL_IMAP_IMBXLSTLSUB
;
2137 else if(strucmp(p
, "list"))
2138 return(url_bogus_imap(&url
, scheme
,
2139 "Invalid list type specified"));
2142 rv
|= URL_IMAP_ISERVERONLY
;
2146 /* build folder list from specified server/criteria/list-method */
2147 l
= strlen(server
) + strlen(criteria
) + 10 + (user
? (strlen(user
)+2) : 9);
2148 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2149 snprintf(*folder
, l
+1, "{%s/%s%s%s}%s%s%s", server
,
2150 user
? "user=\"" : "Anonymous",
2153 *criteria
? "[" : "", criteria
, *criteria
? "[" : "");
2154 (*folder
)[l
] = '\0';
2155 rv
|= URL_IMAP_IMAILBOXLIST
;
2158 if((p
= srchstr(cmd
, "/;uid=")) != NULL
){ /* "imessagepart" */
2159 *p
= '\0'; /* tie off mailbox [uidvalidity] */
2160 if((section
= srchstr(p
+= 6, "/;section=")) != NULL
){
2161 *section
= '\0'; /* tie off UID */
2162 section
= rfc1738_str(section
+ 10);
2163 /* BUG: verify valid section spec ala rfc 2060 */
2165 "-- URL IMAP FOLDER: section not used: %s\n",
2166 section
? section
: "?"));
2169 if(!(*uid
= rfc1738_num(&p
)) || *p
) /* decode UID */
2170 return(url_bogus_imap(&url
, scheme
, "Invalid data in UID"));
2172 /* optional "uidvalidity"? */
2173 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2176 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2177 return(url_bogus_imap(&url
, scheme
,
2178 "Invalid UIDVALIDITY"));
2181 mailbox
= rfc1738_str(cmd
);
2182 rv
= URL_IMAP_IMESSAGEPART
;
2184 else{ /* "imessagelist" */
2185 /* optional "uidvalidity"? */
2186 if((p
= srchstr(cmd
, ";uidvalidity=")) != NULL
){
2189 if(!(*uid_val
= rfc1738_num(&p
)) || *p
)
2190 return(url_bogus_imap(&url
, scheme
,
2191 "Invalid UIDVALIDITY"));
2194 /* optional "enc_search"? */
2195 if((p
= strchr(cmd
, '?')) != NULL
){
2198 *search
= cpystr(rfc1738_str(p
+ 1));
2199 /* BUG: verify valid search spec ala rfc 2060 */
2202 mailbox
= rfc1738_str(cmd
);
2203 rv
= URL_IMAP_IMESSAGELIST
;
2206 if(auth
&& *auth
!= '*' && strucmp(auth
, "anonymous"))
2207 q_status_message(SM_ORDER
, 3, 3,
2208 "Unsupported authentication method. Using standard login.");
2211 * At this point our structure should contain the
2212 * digested url. Now put it together for c-client...
2214 l
= strlen(server
) + 8 + (mailbox
? strlen(mailbox
) : 0)
2215 + (user
? (strlen(user
)+2) : 9);
2216 *folder
= (char *) fs_get((l
+1) * sizeof(char));
2217 snprintf(*folder
, l
+1, "{%s%s%s%s%s}%s", server
,
2218 (user
|| !(auth
&& strucmp(auth
, "anonymous"))) ? "/" : "",
2219 user
? "user=\"" : ((auth
&& strucmp(auth
, "anonymous")) ? "" : "Anonymous"),
2223 (*folder
)[l
] = '\0';
2226 fs_give((void **) &url
);
2232 url_bogus_imap(char **freeme
, char *url
, char *problem
)
2234 fs_give((void **) freeme
);
2235 (void) url_bogus(url
, problem
);
2236 return(URL_IMAP_ERROR
);
2241 * url_bogus - report url syntax errors and such
2244 url_bogus(char *url
, char *reason
)
2246 dprint((2, "-- bogus url \"%s\": %s\n",
2247 url
? url
: "<NULL URL>", reason
? reason
: "?"));
2249 q_status_message3(SM_ORDER
|SM_DING
, 2, 3,
2250 "Malformed \"%.*s\" URL: %.200s",
2251 (void *) (strchr(url
, ':') - url
), url
, reason
);
2258 /*----------------------------------------------------------------------
2259 Format header text suitable for display
2261 Args: stream -- mail stream for various header text fetches
2262 msgno -- sequence number in stream of message we're interested in
2263 section -- which section of message
2264 env -- pointer to msg's envelope
2265 hdrs -- struct containing what's to get formatted
2266 prefix -- prefix to append to each output line
2267 handlesp -- address of pointer to the message's handles
2268 flags -- FM_ flags, see pith/mailview.h
2269 final_pc -- function to write header text with
2271 Result: 0 if all's well, -1 if write error, 1 if fetch error
2273 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2274 in the local convention.
2278 #define FHT_WRTERR -1
2279 #define FHT_FTCHERR 1
2281 format_header(MAILSTREAM
*stream
, long int msgno
, char *section
, ENVELOPE
*env
,
2282 HEADER_S
*hdrs
, char *prefix
, HANDLE_S
**handlesp
, int flags
,
2283 fmt_env_t fmt_env
, gf_io_t final_pc
)
2287 char *h
= NULL
, **fields
= NULL
, **v
, *q
, *start
,
2291 gf_io_t tmp_pc
, tmp_gc
;
2292 struct variable
*vars
= ps_global
->vars
;
2294 if((tmp_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
)
2295 gf_set_so_writec(&tmp_pc
, tmp_store
);
2300 fmt_env
= format_envelope
;
2302 if(ps_global
->full_header
== 2){
2303 rv
= format_raw_header(stream
, msgno
, section
, tmp_pc
);
2307 * First, calculate how big a fields array we need.
2310 /* Custom header viewing list specified */
2311 if(hdrs
->type
== HD_LIST
){
2312 /* view all these headers */
2314 for(nfields
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2315 if(!is_an_env_hdr(q
))
2318 /* view all except these headers */
2320 for(nfields
= 0, v
= hdrs
->h
.l
; *v
!= NULL
; v
++)
2324 nfields
--; /* subtract one for ALL_EXCEPT field */
2328 nfields
= 6; /* default view */
2330 /* allocate pointer space */
2332 fields
= (char **)fs_get((size_t)(nfields
+1) * sizeof(char *));
2333 memset(fields
, 0, (size_t)(nfields
+1) * sizeof(char *));
2336 if(hdrs
->type
== HD_LIST
){
2337 /* view all these headers */
2339 /* put the non-envelope headers in fields */
2341 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2342 if(!is_an_env_hdr(q
))
2345 /* view all except these headers */
2347 /* put the list of headers not to view in fields */
2349 for(i
= 0, v
= hdrs
->h
.l
; (q
= *v
) != NULL
; v
++)
2350 if(strucmp(ALL_EXCEPT
, q
))
2358 fields
[i
= 0] = "Resent-Date";
2359 fields
[++i
] = "Resent-From";
2360 fields
[++i
] = "Resent-To";
2361 fields
[++i
] = "Resent-cc";
2362 fields
[++i
] = "Resent-Subject";
2368 /* custom view with exception list */
2369 if(hdrs
->type
== HD_LIST
&& hdrs
->except
){
2371 * Go through each header in h and print it.
2372 * First we check to see if it is an envelope header so we
2373 * can print our envelope version of it instead of the raw version.
2376 /* fetch all the other headers */
2378 h
= pine_fetchheader_lines_not(stream
, msgno
, section
, fields
);
2381 h
&& delineate_this_header(NULL
, current
, &start
, &finish
);
2383 char tmp
[MAILTMPLEN
+1];
2386 colon_loc
= strindex(start
, ':');
2387 if(colon_loc
&& colon_loc
< finish
){
2388 strncpy(tmp
, start
, MIN(colon_loc
-start
, sizeof(tmp
)-1));
2389 tmp
[MIN(colon_loc
-start
, sizeof(tmp
)-1)] = '\0';
2394 if(colon_loc
&& is_an_env_hdr(tmp
)){
2395 char *dummystart
, *dummyfinish
;
2398 * Pretty format for env hdrs.
2399 * If the same header appears more than once, only
2400 * print the last to avoid duplicates.
2401 * They should have been combined in the env when parsed.
2403 if(!delineate_this_header(tmp
, current
+1, &dummystart
,
2405 format_env_hdr(stream
, msgno
, section
, env
,
2406 fmt_env
, tmp_pc
, tmp
, hdrs
->charset
, flags
);
2409 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2410 hdrs
->charset
, flags
)) != 0)
2417 /* custom view or default */
2419 /* fetch the non-envelope headers */
2421 h
= pine_fetchheader_lines(stream
, msgno
, section
, fields
);
2423 /* default envelope for default view */
2424 if(hdrs
->type
== HD_BFIELD
)
2425 (*fmt_env
)(stream
, msgno
, section
, env
, tmp_pc
, hdrs
->h
.b
, hdrs
->charset
, flags
);
2427 /* go through each header in list, v initialized above */
2428 for(; (q
= *v
) != NULL
; v
++){
2429 if(is_an_env_hdr(q
)){
2430 /* pretty format for env hdrs */
2431 format_env_hdr(stream
, msgno
, section
, env
,
2432 fmt_env
, tmp_pc
, q
, hdrs
->charset
, flags
);
2436 * Go through h finding all occurences of this header
2437 * and all continuation lines, and output.
2440 h
&& delineate_this_header(q
,current
,&start
,&finish
);
2442 if((rv
= format_raw_hdr_string(start
, finish
, tmp_pc
,
2443 hdrs
->charset
, flags
)) != 0)
2456 gf_clear_so_writec(tmp_store
);
2458 if(!rv
){ /* valid data? Do wrapping and filtering... */
2460 char *errstr
, *display_filter
= NULL
, trigger
[MAILTMPLEN
];
2461 STORE_S
*df_store
= NULL
;
2463 so_seek(tmp_store
, 0L, 0);
2465 column
= (flags
& FM_DISPLAY
) ? ps_global
->ttyo
->screen_cols
: 80;
2468 * Test for and act on any display filter
2469 * This barely makes sense. The display filter is going
2470 * to be getting UTF-8'ized headers here. In pre-alpine
2471 * pine the display filter was being fed already decoded
2472 * headers in whatever character set they were in.
2473 * The good news is that that didn't make much
2474 * sense either, so this shouldn't break anything.
2475 * It seems unlikely that anybody is doing anything useful
2476 * with the header part of display filters.
2478 if(ps_global
->tools
.display_filter
2479 && ps_global
->tools
.display_filter_trigger
2480 && (display_filter
= (*ps_global
->tools
.display_filter_trigger
)(NULL
, trigger
, sizeof(trigger
)))){
2481 if((df_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
)) != NULL
){
2483 gf_set_so_writec(&tmp_pc
, df_store
);
2484 gf_set_so_readc(&tmp_gc
, df_store
);
2485 if((errstr
= (*ps_global
->tools
.display_filter
)(display_filter
, tmp_store
, tmp_pc
, NULL
)) != NULL
){
2486 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2487 _("Formatting error: %s"), errstr
);
2491 so_seek(df_store
, 0L, 0);
2493 gf_clear_so_writec(df_store
);
2496 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2497 "No space for filtered text.");
2502 so_seek(tmp_store
, 0L, 0);
2503 gf_set_so_readc(&tmp_gc
, tmp_store
);
2507 int *margin
, wrapflags
= GFW_ONCOMMA
;
2510 gf_link_filter(gf_local_nvtnl
, NULL
);
2512 if((F_ON(F_VIEW_SEL_URL
, ps_global
)
2513 || F_ON(F_VIEW_SEL_URL_HOST
, ps_global
)
2514 || F_ON(F_SCAN_ADDR
, ps_global
))
2516 gf_link_filter(gf_line_test
,
2517 gf_line_test_opt(url_hilite_hdr
,
2518 gf_url_hilite_opt(&uh
,handlesp
,1)));
2519 wrapflags
|= GFW_HANDLES
;
2522 if((flags
& FM_DISPLAY
)
2523 && !(flags
& FM_NOCOLOR
)
2524 && pico_usingcolor()
2525 && ((VAR_NORM_FORE_COLOR
2526 && VAR_HEADER_GENERAL_FORE_COLOR
2527 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2529 (VAR_NORM_BACK_COLOR
2530 && VAR_HEADER_GENERAL_BACK_COLOR
2531 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
))))
2532 wrapflags
|= GFW_HDRCOLOR
;
2534 if((flags
& FM_DISPLAY
)
2535 && !(flags
& FM_NOCOLOR
)
2536 && pico_usingcolor()
2537 && ps_global
->hdr_colors
){
2538 gf_link_filter(gf_line_test
,
2539 gf_line_test_opt(color_headers
, NULL
));
2540 wrapflags
|= (GFW_HANDLES
| GFW_HDRCOLOR
);
2543 if(prefix
&& *prefix
)
2544 column
= MAX(column
-strlen(prefix
), 50);
2546 margin
= format_view_margin();
2548 if(!(flags
& FM_NOWRAP
))
2549 gf_link_filter(gf_wrap
,
2550 gf_wrap_filter_opt(column
, column
,
2551 (flags
& FM_NOINDENT
) ? NULL
: margin
,
2554 if(prefix
&& *prefix
)
2555 gf_link_filter(gf_prefix
, gf_prefix_opt(prefix
));
2557 if((flags
& FM_DISPLAY
)
2558 && !(flags
& FM_NOCOLOR
)
2559 && pico_usingcolor()
2560 && ((VAR_NORM_FORE_COLOR
2561 && VAR_HEADER_GENERAL_FORE_COLOR
2562 && colorcmp(VAR_NORM_FORE_COLOR
, VAR_HEADER_GENERAL_FORE_COLOR
))
2564 (VAR_NORM_BACK_COLOR
2565 && VAR_HEADER_GENERAL_BACK_COLOR
2566 && colorcmp(VAR_NORM_BACK_COLOR
, VAR_HEADER_GENERAL_BACK_COLOR
)))){
2570 right_margin
= margin
? margin
[1] : 0;
2571 total_wid
= column
- right_margin
;
2573 gf_link_filter(gf_line_test
,
2574 gf_line_test_opt(pad_to_right_edge
, (void *) &total_wid
));
2575 wrapflags
|= GFW_HANDLES
;
2578 gf_link_filter(gf_nvtnl_local
, NULL
);
2580 if((errstr
= gf_pipe(tmp_gc
, final_pc
)) != NULL
){
2582 q_status_message1(SM_ORDER
| SM_DING
, 3, 3,
2583 "Can't build header : %.200s", errstr
);
2588 gf_clear_so_readc(df_store
);
2592 gf_clear_so_readc(tmp_store
);
2595 so_give(&tmp_store
);
2598 fs_give((void **)&h
);
2601 fs_give((void **)&fields
);
2607 /*----------------------------------------------------------------------
2608 Format RAW header text for display
2610 Args: stream -- mail stream for various header text fetches
2611 rawno -- sequence number in stream of message we're interested in
2612 section -- which section of message
2613 pc -- function to write header text with
2615 Result: 0 if all's well, -1 if write error, 1 if fetch error
2617 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2618 in the local convention.
2622 format_raw_header(MAILSTREAM
*stream
, long int msgno
, char *section
, gf_io_t pc
)
2624 char *h
= mail_fetch_header(stream
, msgno
, section
, NULL
, NULL
, FT_PEEK
);
2631 if(!gf_puts(NEWLINE
, pc
))
2634 if(ISRFCEOL(h
)) /* all done! */
2637 else if((unsigned char)(*h
) < 0x80 && FILTER_THIS(*h
) &&
2638 !(*(h
+1) && *h
== ESCAPE
&& match_escapes(h
+1))){
2639 c
= (unsigned char) *h
++;
2640 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
2641 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
2644 else if(!(*pc
)(*h
++))
2649 return(FHT_FTCHERR
);
2656 /*----------------------------------------------------------------------
2657 Format c-client envelope data suitable for display
2659 Args: s -- mail stream for various header text fetches
2660 n -- raw sequence number in stream of message we're interested in
2661 sect -- which section of message
2662 e -- pointer to msg's envelope
2663 pc -- function to write header text with
2664 which -- which header lines to write
2666 flags -- FM_ flags, see pith/mailview.h
2668 Result: 0 if all's well, -1 if write error, 1 if fetch error
2670 NOTE: Blank-line delimiter is NOT written here. Newlines are written
2671 in the local convention.
2675 format_envelope(MAILSTREAM
*s
, long int n
, char *sect
, ENVELOPE
*e
, gf_io_t pc
,
2676 long int which
, char *oacs
, int flags
)
2678 char *q
, *p2
, buftmp
[MAILTMPLEN
];
2683 if((which
& FE_DATE
) && e
->date
) {
2685 snprintf(buftmp
, sizeof(buftmp
), "%s",
2686 F_ON(F_DATES_TO_LOCAL
,ps_global
)
2687 ? convert_date_to_local((char *) e
->date
) : (char *) e
->date
);
2688 buftmp
[sizeof(buftmp
)-1] = '\0';
2689 p2
= (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf
,
2690 SIZEOF_20KBUF
, buftmp
);
2692 format_env_puts(p2
, pc
);
2693 gf_puts(NEWLINE
, pc
);
2696 if((which
& FE_FROM
) && e
->from
)
2697 format_addr_string(s
, n
, sect
, "From: ", e
->from
, flags
, oacs
, pc
);
2699 if((which
& FE_REPLYTO
) && e
->reply_to
2700 && (!e
->from
|| !address_is_same(e
->reply_to
, e
->from
)))
2701 format_addr_string(s
, n
, sect
, "Reply-To: ", e
->reply_to
, flags
, oacs
, pc
);
2703 if((which
& FE_TO
) && e
->to
)
2704 format_addr_string(s
, n
, sect
, "To: ", e
->to
, flags
, oacs
, pc
);
2706 if((which
& FE_CC
) && e
->cc
)
2707 format_addr_string(s
, n
, sect
, "Cc: ", e
->cc
, flags
, oacs
, pc
);
2709 if((which
& FE_BCC
) && e
->bcc
)
2710 format_addr_string(s
, n
, sect
, "Bcc: ", e
->bcc
, flags
, oacs
, pc
);
2712 if((which
& FE_RETURNPATH
) && e
->return_path
)
2713 format_addr_string(s
, n
, sect
, "Return-Path: ", e
->return_path
,
2716 if((which
& FE_NEWSGROUPS
) && e
->newsgroups
){
2718 format_newsgroup_string("Newsgroups: ", e
->newsgroups
, flags
, pc
);
2719 if (!e
->ngpathexists
&& e
->message_id
&&
2720 strncmp (e
->message_id
,"<alpine.",8) &&
2721 strncmp (e
->message_id
,"<Pine.",6) &&
2722 strncmp (e
->message_id
,"<MS-C.",6) &&
2723 strncmp (e
->message_id
,"<MailManager.",13) &&
2724 strncmp (e
->message_id
,"<EasyMail.",11) &&
2725 strncmp (e
->message_id
,"<ML-",4)) bogus
= T
;
2728 q_status_message(SM_ORDER
, 0, 3,
2729 "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
2732 if((which
& FE_FOLLOWUPTO
) && e
->followup_to
)
2733 format_newsgroup_string("Followup-To: ", e
->followup_to
, flags
, pc
);
2735 if((which
& FE_SUBJECT
) && e
->subject
&& e
->subject
[0]){
2736 char *freeme
= NULL
;
2741 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->subject
), SIZEOF_20KBUF
-10000);
2743 if(flags
& FM_DISPLAY
2744 && (ps_global
->display_keywords_in_subject
2745 || ps_global
->display_keywordinits_in_subject
)){
2747 /* don't bother if no keywords are defined */
2748 if(some_user_flags_defined(s
))
2749 p2
= freeme
= prepend_keyword_subject(s
, n
, p2
,
2750 ps_global
->display_keywords_in_subject
? KW
: KWInit
,
2751 NULL
, ps_global
->VAR_KW_BRACES
);
2754 format_env_puts(p2
, pc
);
2757 fs_give((void **) &freeme
);
2759 gf_puts(NEWLINE
, pc
);
2762 if((which
& FE_SENDER
) && e
->sender
2763 && (!e
->from
|| !address_is_same(e
->sender
, e
->from
)))
2764 format_addr_string(s
, n
, sect
, "Sender: ", e
->sender
, flags
, oacs
, pc
);
2766 if((which
& FE_MESSAGEID
) && e
->message_id
){
2769 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->message_id
), SIZEOF_20KBUF
-10000);
2770 format_env_puts(p2
, pc
);
2771 gf_puts(NEWLINE
, pc
);
2774 if((which
& FE_INREPLYTO
) && e
->in_reply_to
){
2775 q
= "In-Reply-To: ";
2777 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);
2778 format_env_puts(p2
, pc
);
2779 gf_puts(NEWLINE
, pc
);
2782 if((which
& FE_REFERENCES
) && e
->references
) {
2785 p2
= iutf8ncpy((char *)(tmp_20k_buf
+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf
, 10000, e
->references
), SIZEOF_20KBUF
-10000);
2786 format_env_puts(p2
, pc
);
2787 gf_puts(NEWLINE
, pc
);
2795 * The argument fieldname is something like "Subject:..." or "Subject".
2796 * Look through the specs in speccolor for a match of the fieldname,
2797 * and return the color that goes with any match, or NULL.
2798 * Caller should free the color.
2801 hdr_color(char *fieldname
, char *value
, SPEC_COLOR_S
*speccolor
)
2803 SPEC_COLOR_S
*hc
= NULL
;
2804 COLOR_PAIR
*color_pair
= NULL
;
2805 char *colon
, *fname
;
2806 char fbuf
[FBUF_LEN
+1];
2810 colon
= strindex(fieldname
, ':');
2812 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2813 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2820 for(hc
= speccolor
; hc
; hc
= hc
->next
)
2821 if(hc
->spec
&& !strucmp(fname
, hc
->spec
)){
2826 for(pat
= hc
->val
; !gotit
&& pat
; pat
= pat
->next
)
2827 if(srchstr(value
, pat
->substring
))
2834 if(hc
&& hc
->fg
&& hc
->fg
[0] && hc
->bg
&& hc
->bg
[0])
2835 color_pair
= new_color_pair(hc
->fg
, hc
->bg
);
2842 * The argument fieldname is something like "Subject:..." or "Subject".
2843 * Look through the specs in hdr_colors for a match of the fieldname,
2844 * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
2847 any_hdr_color(char *fieldname
)
2849 SPEC_COLOR_S
*hc
= NULL
;
2850 char *colon
, *fname
;
2851 char fbuf
[FBUF_LEN
+1];
2853 colon
= strindex(fieldname
, ':');
2855 strncpy(fbuf
, fieldname
, MIN(colon
-fieldname
,FBUF_LEN
));
2856 fbuf
[MIN(colon
-fieldname
,FBUF_LEN
)] = '\0';
2863 for(hc
= ps_global
->hdr_colors
; hc
; hc
= hc
->next
)
2864 if(hc
->spec
&& !strucmp(fname
, hc
->spec
))
2871 /*----------------------------------------------------------------------
2872 Format an address field, wrapping lines nicely at commas
2874 Args: field_name -- The name of the field we're formatting ("TO: ", ...)
2875 addr -- ADDRESS structure to format
2877 Result: A formatted, malloced string is returned.
2878 ----------------------------------------------------------------------*/
2880 format_addr_string(MAILSTREAM
*stream
, long int msgno
, char *section
, char *field_name
,
2881 struct mail_address
*addr
, int flags
, char *oacs
, gf_io_t pc
)
2884 int trailing
= 0, group
= 0;
2891 * quickly run down address list to make sure none are patently bogus.
2892 * If so, just blat raw field out.
2894 for(atmp
= addr
; stream
&& atmp
; atmp
= atmp
->next
)
2895 if(atmp
->host
&& atmp
->host
[0] == '.'){
2896 char *field
, *fields
[2];
2899 fields
[0] = cpystr(field_name
);
2900 if((ptmp
= strchr(fields
[0], ':')) != NULL
)
2903 if((field
= pine_fetchheader_lines(stream
, msgno
, section
, fields
)) != NULL
){
2906 for(t
= h
= field
; *h
; t
++)
2907 if(*t
== '\015' && *(t
+1) == '\012'){
2908 *t
= '\0'; /* tie off line */
2909 format_env_puts(h
, pc
);
2910 if(*(h
= (++t
) + 1)) /* set new h and skip CRLF */
2911 gf_puts(NEWLINE
, pc
); /* more to write */
2915 else if(!*t
){ /* shouldn't happen much */
2917 format_env_puts(h
, pc
);
2922 fs_give((void **)&field
);
2925 fs_give((void **)&fields
[0]);
2926 gf_puts(NEWLINE
, pc
);
2927 dprint((2, "Error in \"%s\" field address\n",
2928 field_name
? field_name
: "?"));
2932 gf_puts(field_name
, pc
);
2935 atmp
= addr
->next
; /* remember what's next */
2937 if(!addr
->host
&& addr
->mailbox
){
2938 mtmp
= addr
->mailbox
;
2939 addr
->mailbox
= cpystr((char *)rfc1522_decode_to_utf8(
2940 (unsigned char *)tmp_20k_buf
,
2941 SIZEOF_20KBUF
, addr
->mailbox
));
2944 ptmp
= addr
->personal
; /* RFC 1522 personal name? */
2945 addr
->personal
= iutf8ncpy((char *)tmp_20k_buf
, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf
+10000), SIZEOF_20KBUF
-10000, addr
->personal
), 10000);
2946 tmp_20k_buf
[10000-1] = '\0';
2948 if(!trailing
) /* 1st pass, just address */
2950 else{ /* else comma, unless */
2951 if(!((group
== 1 && addr
->host
) /* 1st addr in group, */
2952 || (!addr
->host
&& !addr
->mailbox
))){ /* or end of group */
2955 gf_puts(NEWLINE
, pc
); /* ONE address/line please */
2963 pine_rfc822_write_address_noquote(addr
, pc
, &group
);
2965 addr
->personal
= ptmp
; /* restore old personal ptr */
2966 if(!addr
->host
&& addr
->mailbox
){
2967 fs_give((void **)&addr
->mailbox
);
2968 addr
->mailbox
= mtmp
;
2975 gf_puts(NEWLINE
, pc
);
2981 const char *rspecials_minus_quote_and_dot
= "()<>@,;:\\[]";
2982 /* RFC822 continuation, must start with CRLF */
2983 #define RFC822CONT "\015\012 "
2985 /* Write RFC822 address with some quoting turned off.
2987 * address to interpret
2989 * (This is a copy of c-client's rfc822_write_address except
2990 * we don't quote double quote and dot in personal names. It writes
2991 * to a gf_io_t instead of to a buffer so that we don't have to worry
2992 * about fixed sized buffer overflowing. It's also special cased to deal
2993 * with only a single address.)
2995 * The idea is that there are some places where we'd just like to display
2996 * the personal name as is before applying confusing quoting. However,
2997 * we do want to be careful not to break things that should be quoted so
2998 * we'll only use this where we are sure. Quoting may look ugly but it
2999 * doesn't usually break anything.
3002 pine_rfc822_write_address_noquote(struct mail_address
*adr
, gf_io_t pc
, int *group
)
3004 extern const char *rspecials
;
3006 if (adr
->host
) { /* ordinary address? */
3007 if (!(adr
->personal
|| adr
->adl
)) pine_rfc822_address (adr
, pc
);
3008 else { /* no, must use phrase <route-addr> form */
3010 pine_rfc822_cat (adr
->personal
, rspecials_minus_quote_and_dot
, pc
);
3012 gf_puts(" <", pc
); /* write address delimiter */
3013 pine_rfc822_address(adr
, pc
);
3014 gf_puts (">", pc
); /* closing delimiter */
3020 else if (adr
->mailbox
) { /* start of group? */
3021 /* yes, write group name */
3022 pine_rfc822_cat (adr
->mailbox
, rspecials
, pc
);
3024 gf_puts (": ", pc
); /* write group identifier */
3025 *group
= 1; /* in a group */
3027 else if (*group
) { /* must be end of group (but be paranoid) */
3029 *group
= 0; /* no longer in that group */
3034 /* Write RFC822 route-address to string
3036 * address to interpret
3040 pine_rfc822_address(struct mail_address
*adr
, gf_io_t pc
)
3042 extern char *wspecials
;
3044 if (adr
&& adr
->host
) { /* no-op if no address */
3045 if (adr
->adl
) { /* have an A-D-L? */
3046 gf_puts (adr
->adl
, pc
);
3049 /* write mailbox name */
3050 pine_rfc822_cat (adr
->mailbox
, wspecials
, pc
);
3051 if (*adr
->host
!= '@') { /* unless null host (HIGHLY discouraged!) */
3052 gf_puts ("@", pc
); /* host delimiter */
3053 gf_puts (adr
->host
, pc
); /* write host name */
3059 /* Concatenate RFC822 string
3061 * pointer to string to concatenate
3062 * list of special characters
3066 pine_rfc822_cat(char *src
, const char *specials
, gf_io_t pc
)
3070 if (strpbrk (src
,specials
)) { /* any specials present? */
3071 gf_puts ("\"", pc
); /* opening quote */
3072 /* truly bizarre characters in there? */
3073 while ((s
= strpbrk (src
,"\\\"")) != NULL
) {
3076 /* turn it into a null-terminated piece */
3080 gf_puts (src
, pc
); /* yes, output leader */
3082 gf_puts ("\\", pc
); /* quoting */
3083 gf_puts (save
, pc
); /* output the bizarre character */
3084 src
= ++s
; /* continue after the bizarre character */
3086 if (*src
) gf_puts (src
, pc
);/* output non-bizarre string */
3087 gf_puts ("\"", pc
); /* closing quote */
3089 else gf_puts (src
, pc
); /* otherwise it's the easy case */
3093 /*----------------------------------------------------------------------
3094 Format an address field, wrapping lines nicely at commas
3096 Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
3097 newsgrps -- ADDRESS structure to format
3099 Result: A formatted, malloced string is returned.
3101 The resuling lines formatted are 80 columns wide.
3102 ----------------------------------------------------------------------*/
3104 format_newsgroup_string(char *field_name
, char *newsgrps
, int flags
, gf_io_t pc
)
3106 char buf
[MAILTMPLEN
];
3107 int trailing
= 0, llen
, alen
;
3110 if(!newsgrps
|| !*newsgrps
)
3113 gf_puts(field_name
, pc
);
3115 llen
= strlen(field_name
);
3117 for(next_ng
= newsgrps
; *next_ng
&& *next_ng
!= ','; next_ng
++);
3118 strncpy(buf
, newsgrps
, MIN(next_ng
- newsgrps
, sizeof(buf
)-1));
3119 buf
[MIN(next_ng
- newsgrps
, sizeof(buf
)-1)] = '\0';
3124 if(!trailing
){ /* first time thru, just address */
3128 else{ /* else preceding comma */
3132 if(alen
+ llen
+ 1 > 76){
3133 gf_puts(NEWLINE
, pc
);
3143 if(alen
&& llen
> 76){ /* handle long addresses */
3144 register char *q
, *p
= &buf
[alen
-1];
3147 if(isspace((unsigned char)*p
)
3148 && (llen
- (alen
- (int)(p
- buf
))) < 76){
3149 for(q
= buf
; q
< p
; q
++)
3150 (*pc
)(*q
); /* write character */
3152 gf_puts(NEWLINE
, pc
);
3161 if(p
== buf
) /* no reasonable break point */
3168 gf_puts(NEWLINE
, pc
);
3173 /*----------------------------------------------------------------------
3174 Format a text field that's part of some raw (non-envelope) message header
3180 Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
3182 ----------------------------------------------------------------------*/
3184 format_raw_hdr_string(char *start
, char *finish
, gf_io_t pc
, char *oacs
, int flags
)
3186 register char *current
;
3187 unsigned char *p
, *tmp
= NULL
, c
;
3195 if((n
= 4*(finish
-start
)) > SIZEOF_20KBUF
-1){
3197 p
= tmp
= (unsigned char *) fs_get(len
* sizeof(unsigned char));
3200 len
= SIZEOF_20KBUF
;
3201 p
= (unsigned char *) tmp_20k_buf
;
3204 if(islower((unsigned char)(*start
)))
3205 *start
= toupper((unsigned char)(*start
));
3207 current
= (char *) rfc1522_decode_to_utf8(p
, len
, start
);
3209 /* output from start to finish */
3210 while(*current
&& rv
== FHT_OK
)
3211 if(ISRFCEOL(current
)){
3212 if(!gf_puts(NEWLINE
, pc
))
3217 else if((unsigned char)(*current
) < 0x80 && FILTER_THIS(*current
) &&
3218 !(*(current
+1) && *current
== ESCAPE
&& match_escapes(current
+1))){
3219 c
= (unsigned char) *current
++;
3220 if(!((*pc
)(c
>= 0x80 ? '~' : '^')
3221 && (*pc
)((c
== 0x7f) ? '?' : (c
& 0x1f) + '@')))
3224 else if(!(*pc
)(*current
++))
3228 fs_give((void **) &tmp
);
3238 /*----------------------------------------------------------------------
3239 Format a text field that's part of some raw (non-envelope) message header
3246 ----------------------------------------------------------------------*/
3248 format_env_puts(char *s
, gf_io_t pc
)
3250 if(ps_global
->pass_ctrl_chars
)
3251 return(gf_puts(s
, pc
));
3254 if((unsigned char)(*s
) < 0x80 && FILTER_THIS(*s
) && !(*(s
+1) && *s
== ESCAPE
&& match_escapes(s
+1))){
3255 if(!((*pc
)((unsigned char) (*s
) >= 0x80 ? '~' : '^')
3256 && (*pc
)((*s
== 0x7f) ? '?' : (*s
& 0x1f) + '@')))
3267 display_parameters(PARAMETER
*params
)
3272 PARMLIST_S
*parmlist
;
3274 for(p
= params
; p
; p
= p
->next
) /* ok if we include *'s */
3275 if(p
->attribute
&& (n
= strlen(p
->attribute
)) > longest
)
3276 longest
= MIN(32, n
); /* shouldn't be any bigger than 32 */
3279 tmp_20k_buf
[0] = '\0';
3280 if((parmlist
= rfc2231_newparmlist(params
)) != NULL
){
3281 n
= 0; /* n overloaded */
3282 while(rfc2231_list_params(parmlist
) && d
< tmp_20k_buf
+ 10000){
3284 snprintf(d
, 10000-(d
-tmp_20k_buf
), "\n");
3285 tmp_20k_buf
[10000-1] = '\0';
3289 if(parmlist
->value
){
3290 if(parmlist
->attrib
&& strucmp(parmlist
->attrib
, "url") == 0){
3291 snprintf(printme
= tmp_20k_buf
+ 11000, 1000, "%s", parmlist
->value
);
3295 printme
= strsquish(tmp_20k_buf
+ 11000, 1000, parmlist
->value
, 100);
3300 snprintf(d
, 10000-(d
-tmp_20k_buf
), "%-*s: %s", longest
,
3301 parmlist
->attrib
? parmlist
->attrib
: "", printme
);
3303 tmp_20k_buf
[10000-1] = '\0';
3307 rfc2231_free_parmlist(&parmlist
);
3310 return(tmp_20k_buf
);
3314 /*----------------------------------------------------------------------
3315 Fetch the requested header fields from the msgno specified
3317 Args: stream -- mail stream of open folder
3318 msgno -- number of message to get header lines from
3319 fields -- array of pointers to desired fields
3321 Returns: allocated string containing matched header lines,
3325 pine_fetch_header(MAILSTREAM
*stream
, long int msgno
, char *section
, char **fields
, long int flags
)
3328 char *p
, *m
, *h
= NULL
, *match
= NULL
, *free_this
, tmp
[MAILTMPLEN
];
3329 char **pflds
= NULL
, **pp
= NULL
, **qq
;
3332 * If the user misconfigures it is possible to have one of the fields
3333 * set to the empty string instead of a header name. We want to catch
3334 * that here instead of asking the server the nonsensical question.
3336 for(pp
= fields
? &fields
[0] : NULL
; pp
&& *pp
; pp
++)
3340 if(pp
&& *pp
){ /* found an empty header field, fix it */
3341 pflds
= copy_list_array(fields
);
3342 for(pp
= pflds
; pp
&& *pp
; pp
++){
3343 if(!**pp
){ /* scoot rest of the lines up */
3345 for(qq
= pp
; *qq
; qq
++)
3349 fs_give((void **) &free_this
);
3353 /* no headers to look for, return NULL */
3354 if(pflds
&& !*pflds
&& !(flags
& FT_NOT
)){
3355 free_list_array(&pflds
);
3362 sl
= (pflds
&& *pflds
) ? new_strlst(pflds
) : NULL
; /* package up fields */
3363 h
= mail_fetch_header(stream
, msgno
, section
, sl
, NULL
, flags
| FT_PEEK
);
3368 if(pflds
&& pflds
!= fields
)
3369 free_list_array(&pflds
);
3374 while(find_field(&h
, tmp
, sizeof(tmp
))){
3375 for(pp
= &pflds
[0]; *pp
&& strucmp(tmp
, *pp
); pp
++)
3378 /* interesting field? */
3379 if((p
= (flags
& FT_NOT
) ? ((*pp
) ? NULL
: tmp
) : *pp
) != NULL
){
3381 * Hold off allocating space for matching fields until
3382 * we at least find one to copy...
3385 match
= m
= fs_get(strlen(h
) + strlen(p
) + 1);
3387 while(*p
) /* copy field name */
3390 while(*h
&& (*m
++ = *h
++)) /* header includes colon */
3391 if(*(m
-1) == '\n' && (*h
== '\r' || !isspace((unsigned char)*h
)))
3394 *m
= '\0'; /* tie off match string */
3396 else{ /* no match, pass this field */
3397 while(*h
&& !(*h
++ == '\n'
3398 && (*h
== '\r' || !isspace((unsigned char)*h
))))
3403 if(pflds
&& pflds
!= fields
)
3404 free_list_array(&pflds
);
3406 return(match
? match
: cpystr(""));
3411 find_field(char **h
, char *tmp
, size_t ntmp
)
3415 if(!h
|| !*h
|| !**h
|| isspace((unsigned char)**h
))
3418 while(tmp
-otmp
<ntmp
-1 && **h
&& **h
!= ':' && !isspace((unsigned char)**h
))
3426 static char *_last_embedded_fg_color
, *_last_embedded_bg_color
;
3430 embed_color(COLOR_PAIR
*cp
, gf_io_t pc
)
3433 if(_last_embedded_fg_color
)
3434 fs_give((void **)&_last_embedded_fg_color
);
3436 _last_embedded_fg_color
= cpystr(cp
->fg
);
3438 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_FGCOLOR
) &&
3439 gf_puts(color_to_asciirgb(cp
->fg
), pc
)))
3444 if(_last_embedded_bg_color
)
3445 fs_give((void **)&_last_embedded_bg_color
);
3447 _last_embedded_bg_color
= cpystr(cp
->bg
);
3449 if(!(pc
&& (*pc
)(TAG_EMBED
) && (*pc
)(TAG_BGCOLOR
) &&
3450 gf_puts(color_to_asciirgb(cp
->bg
), pc
)))
3459 get_cur_embedded_color(void)
3463 if(_last_embedded_fg_color
&& _last_embedded_bg_color
)
3464 ret
= new_color_pair(_last_embedded_fg_color
, _last_embedded_bg_color
);
3466 ret
= pico_get_cur_color();
3473 clear_cur_embedded_color(void)
3475 if(_last_embedded_fg_color
)
3476 fs_give((void **)&_last_embedded_fg_color
);
3478 if(_last_embedded_bg_color
)
3479 fs_give((void **)&_last_embedded_bg_color
);
3484 scroll_handle_start_color(char *colorstring
, size_t buflen
, int *len
)
3488 if(pico_usingcolor()){
3489 char *fg
= NULL
, *bg
= NULL
, *s
;
3491 if(ps_global
->VAR_SLCTBL_FORE_COLOR
3492 && colorcmp(ps_global
->VAR_SLCTBL_FORE_COLOR
,
3493 ps_global
->VAR_NORM_FORE_COLOR
))
3494 fg
= ps_global
->VAR_SLCTBL_FORE_COLOR
;
3496 if(ps_global
->VAR_SLCTBL_BACK_COLOR
3497 && colorcmp(ps_global
->VAR_SLCTBL_BACK_COLOR
,
3498 ps_global
->VAR_NORM_BACK_COLOR
))
3499 bg
= ps_global
->VAR_SLCTBL_BACK_COLOR
;
3505 * The blacks are just known good colors for
3506 * testing whether the other color is good.
3508 if((tmp
= new_color_pair(fg
? fg
: colorx(COL_BLACK
),
3509 bg
? bg
: colorx(COL_BLACK
))) != NULL
){
3510 if(pico_is_good_colorpair(tmp
))
3511 for(s
= color_embed(fg
, bg
);
3512 (*len
) < buflen
&& (colorstring
[*len
] = *s
);
3516 free_color_pair(&tmp
);
3520 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3521 strncpy(colorstring
+ (*len
), url_embed(TAG_BOLDON
), MIN(3,buflen
-(*len
)));
3526 colorstring
[buflen
-1] = '\0';
3533 scroll_handle_end_color(char *colorstring
, size_t buflen
, int *len
, int use_hdr_color
)
3536 if(pico_usingcolor()){
3537 char *fg
= NULL
, *bg
= NULL
, *s
;
3538 char *basefg
= NULL
, *basebg
= NULL
;
3540 basefg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_FORE_COLOR
3541 : ps_global
->VAR_NORM_FORE_COLOR
;
3542 basebg
= use_hdr_color
? ps_global
->VAR_HEADER_GENERAL_BACK_COLOR
3543 : ps_global
->VAR_NORM_BACK_COLOR
;
3546 * We need to change the fg and bg colors back even if they
3547 * are the same as the Normal Colors so that color_a_quote
3548 * will have a chance to pick up those colors as the ones to
3549 * switch to. We don't do this before the handle above so that
3550 * the quote color will flow into the selectable item when
3551 * the selectable item color is partly the same as the
3552 * normal color. That is, suppose the normal color was black on
3553 * cyan and the selectable color was blue on cyan, only a fg color
3554 * change. We preserve the only-a-fg-color-change in a quote by
3555 * letting the quote background color flow into the selectable text.
3557 if(ps_global
->VAR_SLCTBL_FORE_COLOR
)
3560 if(ps_global
->VAR_SLCTBL_BACK_COLOR
)
3563 if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
3564 strncpy(colorstring
, url_embed(TAG_BOLDOFF
), MIN(3,buflen
));
3569 for(s
= color_embed(fg
, bg
); (*len
) < buflen
&& (colorstring
[*len
] = *s
); s
++, (*len
)++)
3573 colorstring
[buflen
-1] = '\0';
3580 * Helper routine that is of limited use.
3581 * We need to tally up the screen width of
3582 * a UTF-8 string as we go through the string.
3583 * We just want the width of the character starting
3584 * at str (and no longer than remaining_octets).
3585 * If we're plopped into the middle of a UTF-8
3586 * character we just want to return width zero.
3589 width_at_this_position(unsigned char *str
, unsigned long n
)
3591 unsigned char *inputp
= str
;
3592 unsigned long remaining_octets
= n
;
3596 ucs
= (UCS
) utf8_get(&inputp
, &remaining_octets
);
3597 if(!(ucs
& U8G_ERROR
|| ucs
== UBOGON
)){
3598 width
= wcellwidth(ucs
);
3599 /* Writechar will print a '?' */