summaryrefslogtreecommitdiff
path: root/pith/mailview.c
diff options
context:
space:
mode:
authorEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
committerEduardo Chappa <echappa@gmx.com>2013-02-03 00:59:38 -0700
commit094ca96844842928810f14844413109fc6cdd890 (patch)
treee60efbb980f38ba9308ccb4fb2b77b87bbc115f3 /pith/mailview.c
downloadalpine-094ca96844842928810f14844413109fc6cdd890.tar.xz
Initial Alpine Version
Diffstat (limited to 'pith/mailview.c')
-rw-r--r--pith/mailview.c3271
1 files changed, 3271 insertions, 0 deletions
diff --git a/pith/mailview.c b/pith/mailview.c
new file mode 100644
index 00000000..40728aab
--- /dev/null
+++ b/pith/mailview.c
@@ -0,0 +1,3271 @@
+#if !defined(lint) && !defined(DOS)
+static char rcsid[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
+#endif
+
+/*
+ * ========================================================================
+ * Copyright 2006-2009 University of Washington
+ * Copyright 2013 Eduardo Chappa
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * ========================================================================
+ */
+
+/*======================================================================
+
+ mailview.c
+ Implements message data gathering and formatting
+
+ ====*/
+
+
+#include "headers.h"
+#include "../pith/mailview.h"
+#include "../pith/conf.h"
+#include "../pith/msgno.h"
+#include "../pith/editorial.h"
+#include "../pith/mimedesc.h"
+#include "../pith/margin.h"
+#include "../pith/color.h"
+#include "../pith/strlst.h"
+#include "../pith/charset.h"
+#include "../pith/status.h"
+#include "../pith/maillist.h"
+#include "../pith/mailcmd.h"
+#include "../pith/mailindx.h"
+#include "../pith/imap.h"
+#include "../pith/detach.h"
+#include "../pith/text.h"
+#include "../pith/url.h"
+#include "../pith/rfc2231.h"
+#include "../pith/list.h"
+#include "../pith/stream.h"
+#include "../pith/send.h"
+#include "../pith/filter.h"
+#include "../pith/string.h"
+#include "../pith/ablookup.h"
+#include "../pith/escapes.h"
+#include "../pith/keyword.h"
+#include "../pith/smime.h"
+
+
+#define FBUF_LEN (50)
+
+#define ISRFCEOL(S) (*(S) == '\015' && *((S)+1) == '\012')
+
+
+/*
+ * This is a list of header fields that are represented canonically
+ * by the c-client's ENVELOPE structure. The list is used by the
+ * two functions below to decide if a given field is included in this
+ * set.
+ */
+static struct envelope_s {
+ char *name;
+ long val;
+} envelope_hdrs[] = {
+ {"from", FE_FROM},
+ {"sender", FE_SENDER},
+ {"date", FE_DATE},
+ {"to", FE_TO},
+ {"cc", FE_CC},
+ {"bcc", FE_BCC},
+ {"newsgroups", FE_NEWSGROUPS},
+ {"subject", FE_SUBJECT},
+ {"message-id", FE_MESSAGEID},
+ {"reply-to", FE_REPLYTO},
+ {"followup-to", FE_FOLLOWUPTO},
+ {"in-reply-to", FE_INREPLYTO},
+/* {"return-path", FE_RETURNPATH}, not usually filled in */
+ {"references", FE_REFERENCES},
+ {NULL, 0}
+};
+
+
+/*
+ * Hook for optional display of rfc2369 content
+ */
+int (*pith_opt_rfc2369_editorial)(long, HANDLE_S **, int, int, gf_io_t);
+
+
+
+
+/*
+ * Internal prototypes
+ */
+int format_blip_seen(long);
+int is_an_env_hdr(char *);
+int is_an_addr_hdr(char *);
+void format_env_hdr(MAILSTREAM *, long, char *, ENVELOPE *,
+ fmt_env_t, gf_io_t, char *, char *, int);
+int delineate_this_header(char *, char *, char **, char **);
+char *url_embed(int);
+int color_headers(long, char *, LT_INS_S **, void *);
+int url_hilite_hdr(long, char *, LT_INS_S **, void *);
+int pad_to_right_edge(long, char *, LT_INS_S **, void *);
+int url_bogus_imap(char **, char *, char *);
+int format_raw_header(MAILSTREAM *, long, char *, gf_io_t);
+void format_envelope(MAILSTREAM *, long, char *, ENVELOPE *,
+ gf_io_t, long, char *, int);
+int any_hdr_color(char *);
+void format_addr_string(MAILSTREAM *, long, char *, char *,
+ ADDRESS *, int, char *, gf_io_t);
+void pine_rfc822_write_address_noquote(ADDRESS *, gf_io_t, int *);
+void format_newsgroup_string(char *, char *, int, gf_io_t);
+int format_raw_hdr_string(char *, char *, gf_io_t, char *, int);
+int format_env_puts(char *, gf_io_t);
+int find_field(char **, char *, size_t);
+int embed_color(COLOR_PAIR *, gf_io_t);
+COLOR_PAIR *get_cur_embedded_color(void);
+void clear_cur_embedded_color(void);
+
+
+
+
+/*----------------------------------------------------------------------
+ Format a message message for viewing
+
+ Args: msgno -- The number of the message to view
+ env -- pointer to the message's envelope
+ body -- pointer to the message's body
+ handlesp -- address of pointer to the message's handles
+ flgs -- possible flags listed in pith/mailview.h with
+ prefix FM_
+ pc -- write to this function
+
+Result: Returns true if no problems encountered, else false.
+
+First the envelope is formatted; next a list of all attachments is
+formatted if there is more than one. Then all the body parts are
+formatted, fetching them as needed. This includes headers of included
+message. Richtext is also formatted. An entry is made in the text for
+parts that are not displayed or can't be displayed.
+
+ ----*/
+int
+format_message(long int msgno, ENVELOPE *env, struct mail_bodystruct *body,
+ HANDLE_S **handlesp, int flgs, gf_io_t pc)
+{
+ char *decode_err = NULL;
+ HEADER_S h;
+ int width;
+
+ clear_cur_embedded_color();
+
+ if(!(flgs & FM_DISPLAY))
+ flgs |= FM_NOINDENT;
+
+ width = (flgs & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ /*---- format and copy envelope ----*/
+ if(!(flgs & FM_NOEDITORIAL)){
+ if(ps_global->full_header == 1)
+ /* TRANSLATORS: User is viewing a message and all the quoted text is
+ being shown. */
+ q_status_message(SM_INFO, 0, 3, _("All quoted text being included"));
+ else if(ps_global->full_header == 2)
+ q_status_message(SM_INFO, 0, 3,
+ /* TRANSLATORS: User is viewing a message and all of
+ the header text is being shown. */
+ _("Full header mode ON. All header text being included"));
+ }
+
+ HD_INIT(&h, ps_global->VAR_VIEW_HEADERS, ps_global->view_all_except, FE_DEFAULT);
+ switch(format_header(ps_global->mail_stream, msgno, NULL,
+ env, &h, NULL, handlesp, flgs, NULL, pc)){
+
+ case -1 : /* write error */
+ goto write_error;
+
+ case 1 : /* fetch error */
+ if(!(gf_puts("[ Error fetching header ]", pc)
+ && !gf_puts(NEWLINE, pc)))
+ goto write_error;
+
+ break;
+ }
+
+ if(!(body == NULL
+ || (ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))))
+ format_attachment_list(msgno, body, handlesp, flgs, width, pc);
+
+ /* write delimiter and body */
+ if(gf_puts(NEWLINE, pc)
+ && (decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL)
+ return(1);
+
+
+ write_error:
+
+ if(!(flgs & FM_DISPLAY))
+ q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"),
+ decode_err ? decode_err : error_description(errno));
+
+ return(0);
+}
+
+
+char *
+format_body(long int msgno, BODY *body, HANDLE_S **handlesp, HEADER_S *hp, int flgs, int width, gf_io_t pc)
+{
+ int filt_only_c0 = 0, wrapflags, error_found = 0;
+ int is_in_sig = OUT_SIG_BLOCK;
+ char *charset, *decode_err = NULL, *tmp1, *description;
+ ATTACH_S *a;
+ URL_HILITE_S uh;
+ gf_io_t gc;
+
+ if(body == NULL
+ || (ps_global->full_header == 2
+ && F_ON(F_ENABLE_FULL_HDR_AND_TEXT, ps_global))) {
+
+ /*--- Server is not an IMAP2bis, It can't parse MIME
+ so we just show the text here. Hopefully the
+ message isn't a MIME message
+ ---*/
+ void *text2;
+
+ if((text2 = (void *)pine_mail_fetch_text(ps_global->mail_stream,
+ msgno, NULL, NULL, NIL)) != NULL){
+
+ if(!gf_puts(NEWLINE, pc)) /* write delimiter */
+ return("Write Error");
+
+ gf_set_readc(&gc, text2, (unsigned long)strlen(text2), CharStar, 0);
+ gf_filter_init();
+
+ /*
+ * We need to translate the message
+ * into UTF-8, but that's trouble in the full header case
+ * because we don't know what to translate from. We'll just
+ * take a guess by looking for the first text part and
+ * using its charset.
+ */
+ if(body && body->type == TYPETEXT)
+ charset = parameter_val(body->parameter, "charset");
+ else if(body && body->type == TYPEMULTIPART && body->nested.part
+ && body->nested.part->body.type == TYPETEXT)
+ charset = parameter_val(body->nested.part->body.parameter, "charset");
+ else
+ charset = ps_global->display_charmap;
+
+ if(strucmp(charset, "us-ascii") && strucmp(charset, "utf-8")){
+ /* transliterate message text to UTF-8 */
+ gf_link_filter(gf_utf8, gf_utf8_opt(charset));
+ }
+
+ /* link in filters, similar to what is done in decode_text() */
+ if(!ps_global->pass_ctrl_chars){
+ gf_link_filter(gf_escape_filter, NULL);
+ filt_only_c0 = 1;
+ gf_link_filter(gf_control_filter,
+ gf_control_filter_opt(&filt_only_c0));
+ }
+
+ gf_link_filter(gf_tag_filter, NULL);
+
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite,
+ gf_url_hilite_opt(&uh,handlesp,0)));
+ }
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_SIGNATURE_FORE_COLOR
+ && ps_global->VAR_SIGNATURE_BACK_COLOR){
+ gf_link_filter(gf_line_test, gf_line_test_opt(color_signature, &is_in_sig));
+ }
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_QUOTE1_FORE_COLOR
+ && ps_global->VAR_QUOTE1_BACK_COLOR){
+ gf_link_filter(gf_line_test, gf_line_test_opt(color_a_quote, NULL));
+ }
+
+ if(!(flgs & FM_NOWRAP)){
+ wrapflags = (flgs & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) : GFW_NONE;
+ if(flgs & FM_DISPLAY
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor())
+ wrapflags |= GFW_USECOLOR;
+ gf_link_filter(gf_wrap, gf_wrap_filter_opt(width, width,
+ (flgs & FM_NOINDENT)
+ ? NULL : format_view_margin(),
+ 0,
+ wrapflags));
+ }
+
+ gf_link_filter(gf_nvtnl_local, NULL);
+ if((decode_err = gf_pipe(gc, pc)) != NULL){
+ /* TRANSLATORS: There was an error putting together a message for
+ viewing. The arg is the description of the error. */
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, _("Formatting error: %s"), decode_err);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc))
+ decode_err = NULL;
+ else
+ return(decode_err);
+ }
+ }
+
+ if(!text2){
+ if(!gf_puts(NEWLINE, pc)
+ || !gf_puts(_(" [ERROR fetching text of message]"), pc)
+ || !gf_puts(NEWLINE, pc)
+ || !gf_puts(NEWLINE, pc))
+ return("Write Error");
+ }
+ }
+ else{
+ int show_parts = 0;
+
+ /*======== Now loop through formatting all the parts =======*/
+ for(a = ps_global->atmts; a->description != NULL; a++) {
+
+ if(a->body->type == TYPEMULTIPART){
+#ifdef SMIME
+ if(strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0){
+ if(a->description){
+ if(!(!format_editorial(a->description, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+ }
+ }
+#endif /* SMIME */
+ continue;
+ }
+
+ if(!a->shown) {
+ if(a->suppress_editorial)
+ continue;
+
+ if(!(flgs & FM_NOEDITORIAL)
+ && (!gf_puts(NEWLINE, pc)
+ || (decode_err = part_desc(a->number, a->body,
+ (flgs & FM_DISPLAY)
+ ? (a->can_display != MCD_NONE)
+ ? 1 : 2
+ : 3, width, flgs, pc))))
+ return("Write Error");
+
+ continue;
+ }
+
+ switch(a->body->type){
+
+ case TYPETEXT:
+ /*
+ * If a message is multipart *and* the first part of it
+ * is text *and that text is empty, there is a good chance that
+ * there was actually something there that c-client was
+ * unable to parse. Here we report the empty message body
+ * and insert the raw RFC822.TEXT (if full-headers are
+ * on).
+ */
+ if(body->type == TYPEMULTIPART
+ && a == ps_global->atmts
+ && a->body->size.bytes == 0
+ && F_ON(F_ENABLE_FULL_HDR, ps_global)){
+ char *err = NULL;
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "Empty or malformed message%s.",
+ ps_global->full_header == 2
+ ? ". Displaying raw text"
+ : ". Use \"H\" to see raw text");
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ if(ps_global->full_header == 2
+ && (err = detach_raw(ps_global->mail_stream, msgno,
+ a->number, pc, flgs))){
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF,
+ "%s%s [ Formatting error: %s ]%s%s",
+ NEWLINE, NEWLINE, err, NEWLINE, NEWLINE);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(!gf_puts(tmp_20k_buf, pc))
+ return("Write Error");
+ }
+
+ break;
+ }
+
+ /*
+ * Don't write our delimiter if this text part is
+ * the first part of a message/rfc822 segment...
+ */
+ if(show_parts && a != ps_global->atmts
+ && !((a[-1].body && a[-1].body->type == TYPEMESSAGE)
+#ifdef SMIME
+ || (a[-1].body->type == TYPEMULTIPART
+ && a[-1].body->subtype
+ && (strucmp(a[-1].body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0)
+ && &a[-1] != ps_global->atmts
+ && a[-2].body && a[-2].body->type == TYPEMESSAGE)
+#endif /* SMIME */
+ )
+ && !(flgs & FM_NOEDITORIAL)){
+ tmp1 = a->body->description ? a->body->description
+ : "Attached Text";
+ description = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
+ description);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+ }
+
+ error_found += decode_text(a, msgno, pc, handlesp,
+ (flgs & FM_DISPLAY) ? InLine : QStatus,
+ flgs);
+ break;
+
+ case TYPEMESSAGE:
+ tmp1 = a->body->description ? a->body->description
+ : (strucmp(a->body->subtype, "delivery-status") == 0)
+ ? "Delivery Status"
+ : "Included Message";
+ description = iutf8ncpy((char *)(tmp_20k_buf+10000),
+ (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+15000), 5000, tmp1), 5000);
+
+ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Part %s: \"%.1024s\"", a->number,
+ description);
+ tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
+
+ if(!(gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)
+ && !format_editorial(tmp_20k_buf, width, flgs, handlesp, pc)
+ && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ if(a->body->subtype && strucmp(a->body->subtype, "rfc822") == 0){
+ /* imapenvonly, we may not have all the headers we need */
+ if(a->body->nested.msg->env->imapenvonly)
+ mail_fetch_header(ps_global->mail_stream, msgno,
+ a->number, NULL, NULL, FT_PEEK);
+ switch(format_header(ps_global->mail_stream, msgno, a->number,
+ a->body->nested.msg->env, hp,
+ NULL, handlesp, flgs, NULL, pc)){
+ case -1 : /* write error */
+ return("Write Error");
+
+ case 1 : /* fetch error */
+ if(!(gf_puts("[ Error fetching header ]", pc)
+ && !gf_puts(NEWLINE, pc)))
+ return("Write Error");
+
+ break;
+ }
+ }
+ else if(a->body->subtype && strucmp(a->body->subtype, "external-body") == 0){
+ int *margin, avail, m1, m2;
+
+ avail = width;
+ margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
+
+ m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
+ avail -= m1;
+
+ m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
+ avail -= m2;
+
+ if(format_editorial("This part is not included and can be fetched as follows:", avail, flgs, handlesp, pc)
+ || !gf_puts(NEWLINE, pc)
+ || format_editorial(display_parameters(a->body->parameter), avail, flgs, handlesp, pc))
+ return("Write Error");
+ }
+ else
+ error_found += decode_text(a, msgno, pc, handlesp,
+ (flgs&FM_DISPLAY) ? InLine : QStatus,
+ flgs);
+
+ if(!gf_puts(NEWLINE, pc))
+ return("Write Error");
+
+ break;
+
+ default:
+ if((decode_err = part_desc(a->number, a->body,
+ (flgs & FM_DISPLAY) ? 1 : 3,
+ width, flgs, pc)) != NULL)
+ return("Write Error");
+ }
+
+ show_parts++;
+ }
+
+ if(!(!error_found
+ && (pith_opt_rfc2369_editorial ? (*pith_opt_rfc2369_editorial)(msgno, handlesp, flgs, width, pc) : 1)
+ && format_blip_seen(msgno)))
+ return("Cannot format body.");
+ }
+
+ return(NULL);
+}
+
+
+int
+format_attachment_list(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc)
+{
+ ATTACH_S *a;
+
+ if(flgs & FM_NEW_MESS) {
+ zero_atmts(ps_global->atmts);
+ describe_mime(body, "", 1, 1, 0, flgs);
+ }
+
+ /*----- First do the list of parts/attachments if needed ----*/
+ if((flgs & FM_DISPLAY)
+ && (ps_global->atmts[1].description
+ || (ps_global->atmts[0].body
+ && ps_global->atmts[0].body->type != TYPETEXT))){
+ char tmp[6*MAX_SCREEN_COLS + 1], *tmpp;
+ int i, n, maxnumwid = 0, maxsizewid = 0, *margin;
+ int avail, m1, m2, hwid, s1, s2, s3, s4, s5, dwid, shownwid;
+ int sizewid, descwid, dashwid, partwid, padwid;
+ COLOR_PAIR *hdrcolor = NULL;
+
+ if((flgs & FM_DISPLAY)
+ && !(flgs & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ && ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ && ps_global->VAR_NORM_FORE_COLOR
+ && ps_global->VAR_NORM_BACK_COLOR
+ && (colorcmp(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR)
+ || colorcmp(ps_global->VAR_HEADER_GENERAL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))){
+
+ if((hdrcolor = new_color_pair(ps_global->VAR_HEADER_GENERAL_FORE_COLOR,
+ ps_global->VAR_HEADER_GENERAL_BACK_COLOR)) != NULL){
+ if(!pico_is_good_colorpair(hdrcolor))
+ free_color_pair(&hdrcolor);
+ }
+ }
+
+ margin = (flgs & FM_NOINDENT) ? NULL : format_view_margin();
+
+ /*
+ * Attachment list header
+ */
+
+ avail = width;
+
+ m1 = MAX(MIN(margin ? margin[0] : 0, avail), 0);
+ avail -= m1;
+
+ m2 = MAX(MIN(margin ? margin[1] : 0, avail), 0);
+ avail -= m2;
+
+ hwid = MAX(avail, 0);
+
+ i = utf8_width(_("Parts/Attachments:"));
+ partwid = MIN(i, hwid);
+ padwid = hdrcolor ? (hwid-partwid) : 0;
+
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ utf8_snprintf(tmp, sizeof(tmp),
+ "%-*.*w%*.*s",
+ /* TRANSLATORS: A label */
+ partwid, partwid, _("Parts/Attachments:"),
+ padwid, padwid, "");
+
+ if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
+ return(0);
+
+
+ /*----- Figure max display widths -----*/
+ for(a = ps_global->atmts; a->description != NULL; a++){
+ if((n = utf8_width(a->number)) > maxnumwid)
+ maxnumwid = n;
+
+ if((n = utf8_width(a->size)) > maxsizewid)
+ maxsizewid = n;
+ }
+
+ /*
+ * ----- adjust max lengths for nice display -----
+ *
+ * marg _ D _ number _ Shown _ _ _ size _ _ description marg
+ *
+ */
+
+ avail = width - m1 - m2;
+
+ s1 = MAX(MIN(1, avail), 0);
+ avail -= s1;
+
+ dwid = MAX(MIN(1, avail), 0);
+ avail -= dwid;
+
+ s2 = MAX(MIN(1, avail), 0);
+ avail -= s2;
+
+ maxnumwid = MIN(maxnumwid, width/3);
+ maxnumwid = MAX(MIN(maxnumwid, avail), 0);
+ avail -= maxnumwid;
+
+ s3 = MAX(MIN(1, avail), 0);
+ avail -= s3;
+
+ shownwid = MAX(MIN(5, avail), 0);
+ avail -= shownwid;
+
+ s4 = MAX(MIN(3, avail), 0);
+ avail -= s4;
+
+ sizewid = MAX(MIN(maxsizewid, avail), 0);
+ avail -= sizewid;
+
+ s5 = MAX(MIN(2, avail), 0);
+ avail -= s5;
+
+ descwid = MAX(0, avail);
+
+ /*----- Format the list of attachments -----*/
+ for(a = ps_global->atmts; a->description != NULL; a++){
+ COLOR_PAIR *lastc = NULL;
+ char numbuf[50];
+ int thisdescwid, padwid;
+
+#ifdef SMIME
+ if(a->body->type == TYPEMULTIPART
+ && (strucmp(a->body->subtype, OUR_PKCS7_ENCLOSURE_SUBTYPE)==0))
+ continue;
+#endif /* SMIME */
+
+ i = utf8_width((descwid > 2 && a->description) ? a->description : "");
+ thisdescwid = MIN(i, descwid);
+ padwid = hdrcolor ? (descwid-thisdescwid) : 0;
+
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ utf8_snprintf(tmp, sizeof(tmp),
+ "%*.*s%*.*w%*.*s%-*.*w%*.*s%*.*w%*.*s%*.*w%*.*s%-*.*w",
+ s1, s1, "",
+ dwid, dwid,
+ msgno_part_deleted(ps_global->mail_stream, msgno, a->number) ? "D" : "",
+ s2, s2, "",
+ maxnumwid, maxnumwid,
+ a->number
+ ? short_str(a->number, numbuf, sizeof(numbuf), maxnumwid, FrontDots)
+ : "",
+ s3, s3, "",
+ shownwid, shownwid,
+ a->shown ? "Shown" :
+ (a->can_display != MCD_NONE && !(a->can_display & MCD_EXT_PROMPT))
+ ? "OK " : "",
+ s4, s4, "",
+ sizewid, sizewid,
+ a->size ? a->size : "",
+ s5, s5, "",
+ thisdescwid, thisdescwid,
+ (descwid > 2 && a->description) ? a->description : "");
+
+ if(!(!hdrcolor || embed_color(hdrcolor, pc)))
+ return(0);
+
+ if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
+ char buf[16], color[64];
+ int l;
+ HANDLE_S *h;
+
+ for(tmpp = tmp; *tmpp && *tmpp == ' '; tmpp++)
+ if(!(*pc)(' '))
+ return(0);
+
+ h = new_handle(handlesp);
+ h->type = Attach;
+ h->h.attach = a;
+
+ snprintf(buf, sizeof(buf), "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+
+ if(!(flgs & FM_NOCOLOR)
+ && handle_start_color(color, sizeof(color), &l, 1)){
+ lastc = get_cur_embedded_color();
+ if(!gf_nputs(color, (long) l, pc))
+ return(0);
+ }
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)
+ && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON))))
+ return(0);
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE)
+ && (*pc)(strlen(buf)) && gf_puts(buf, pc)))
+ return(0);
+ }
+ else
+ tmpp = tmp;
+
+ if(!format_env_puts(tmpp, pc))
+ return(0);
+
+ if(F_ON(F_VIEW_SEL_ATTACH, ps_global) && handlesp){
+ if(lastc){
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return(0);
+ }
+
+ if(!embed_color(lastc, pc))
+ return(0);
+
+ free_color_pair(&lastc);
+ }
+ else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF)))
+ return(0);
+
+ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF)))
+ return(0);
+ }
+
+ if(padwid > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", padwid, padwid, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ if(!gf_puts(NEWLINE, pc))
+ return(0);
+ }
+
+ /*
+ * Dashed line after list
+ */
+
+ if(hdrcolor){
+ avail = width - m1 - m2;
+ hwid = MAX(avail, 0);
+
+ dashwid = MAX(MIN(40, hwid-2), 0);
+ padwid = hwid - dashwid;
+ if(m1 > 0){
+ snprintf(tmp, sizeof(tmp), "%*.*s", m1, m1, "");
+ if(!gf_puts(tmp, pc))
+ return(0);
+ }
+
+ snprintf(tmp, sizeof(tmp),
+ "%s%*.*s",
+ repeat_char(dashwid, '-'),
+ padwid, padwid, "");
+ }
+ else{
+ avail = width - m1 -2;
+
+ dashwid = MAX(MIN(40, avail), 0);
+ avail -= dashwid;
+
+ snprintf(tmp, sizeof(tmp),
+ "%*.*s%s",
+ m1, m1, "",
+ repeat_char(dashwid, '-'));
+ }
+
+ if(!((!hdrcolor || embed_color(hdrcolor, pc)) && gf_puts(tmp, pc) && gf_puts(NEWLINE, pc)))
+ return(0);
+
+ if(hdrcolor)
+ free_color_pair(&hdrcolor);
+ }
+
+ return(1);
+}
+
+
+
+/*
+ * format_blip_seen - if seen bit (which is usually cleared as a side-effect
+ * of body part fetches as we're formatting) for the
+ * given message isn't set (likely because there
+ * weren't any parts suitable for display), then make
+ * sure to set it here.
+ */
+int
+format_blip_seen(long int msgno)
+{
+ MESSAGECACHE *mc;
+
+ if(msgno > 0L && ps_global->mail_stream
+ && msgno <= ps_global->mail_stream->nmsgs
+ && (mc = mail_elt(ps_global->mail_stream, msgno))
+ && !mc->seen
+ && !ps_global->mail_stream->rdonly)
+ mail_flag(ps_global->mail_stream, long2string(msgno), "\\SEEN", ST_SET);
+
+ return(1);
+}
+
+
+/*
+ * is_an_env_hdr - is this name a header in the envelope structure?
+ *
+ * name - the header name to check
+ */
+int
+is_an_env_hdr(char *name)
+{
+ register int i;
+
+ for(i = 0; envelope_hdrs[i].name; i++)
+ if(!strucmp(name, envelope_hdrs[i].name))
+ return(1);
+
+ return(0);
+}
+
+
+
+
+/*
+ * is_an_addr_hdr - is this an address header?
+ *
+ * name - the header name to check
+ */
+int
+is_an_addr_hdr(char *fieldname)
+{
+ char fbuf[FBUF_LEN+1];
+ char *colon, *fname;
+ static char *addr_headers[] = {
+ "from",
+ "reply-to",
+ "to",
+ "cc",
+ "bcc",
+ "return-path",
+ "sender",
+ "x-sender",
+ "x-x-sender",
+ "resent-from",
+ "resent-to",
+ "resent-cc",
+ NULL
+ };
+
+ /* so it is pointing to NULL */
+ char **p = addr_headers + sizeof(addr_headers)/sizeof(*addr_headers) - 1;
+
+ if((colon = strindex(fieldname, ':')) != NULL){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,sizeof(fbuf)));
+ fbuf[MIN(colon-fieldname,sizeof(fbuf)-1)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname){
+ for(p = addr_headers; *p; p++)
+ if(!strucmp(fname, *p))
+ break;
+ }
+
+ return((*p) ? 1 : 0);
+}
+
+
+/*
+ * Format a single field from the envelope
+ */
+void
+format_env_hdr(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
+ fmt_env_t fmt_env, gf_io_t pc, char *field, char *oacs, int flags)
+{
+ register int i;
+
+ if(!fmt_env)
+ fmt_env = format_envelope;
+
+ for(i = 0; envelope_hdrs[i].name; i++)
+ if(!strucmp(field, envelope_hdrs[i].name)){
+ (*fmt_env)(stream, msgno, section, env, pc, envelope_hdrs[i].val, oacs, flags);
+ return;
+ }
+}
+
+
+/*
+ * Look through header string beginning with "begin", for the next
+ * occurrence of header "field". Set "start" to that. Set "end" to point one
+ * position past all of the continuation lines that go with "field".
+ * That is, if "end" is converted to a null
+ * character then the string "start" will be the next occurence of header
+ * "field" including all of its continuation lines. Assume we
+ * have CRLF's as end of lines.
+ *
+ * If "field" is NULL, then we just leave "start" pointing to "begin" and
+ * make "end" the end of that header.
+ *
+ * Returns 1 if found, 0 if not.
+ */
+int
+delineate_this_header(char *field, char *begin, char **start, char **end)
+{
+ char tmpfield[MAILTMPLEN+2]; /* copy of field with colon appended */
+ char *p;
+ char *begin_srch;
+
+ if(field == NULL){
+ if(!begin || !*begin || isspace((unsigned char)*begin))
+ return 0;
+ else
+ *start = begin;
+ }
+ else{
+ strncpy(tmpfield, field, sizeof(tmpfield)-2);
+ tmpfield[sizeof(tmpfield)-2] = '\0';
+ strncat(tmpfield, ":", sizeof(tmpfield)-strlen(tmpfield)-1);
+ tmpfield[sizeof(tmpfield)-1] = '\0';
+
+ /*
+ * We require that start is at the beginning of a line, so
+ * either it equals begin (which we assume is the beginning of a
+ * line) or it is preceded by a CRLF.
+ */
+ begin_srch = begin;
+ *start = srchstr(begin_srch, tmpfield);
+ while(*start && *start != begin
+ && !(*start - 2 >= begin && ISRFCEOL(*start - 2))){
+ begin_srch = *start + 1;
+ *start = srchstr(begin_srch, tmpfield);
+ }
+
+ if(!*start)
+ return 0;
+ }
+
+ for(p = *start; *p; p++){
+ if(ISRFCEOL(p)
+ && (!isspace((unsigned char)*(p+2)) || *(p+2) == '\015')){
+ /*
+ * The final 015 in the test above is to test for the end
+ * of the headers.
+ */
+ *end = p+2;
+ break;
+ }
+ }
+
+ if(!*p)
+ *end = p;
+
+ return 1;
+}
+
+
+
+int
+handle_start_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
+{
+ *len = 0;
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+ char *basefg = NULL, *basebg = NULL;
+
+ basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ : ps_global->VAR_NORM_FORE_COLOR;
+ basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ : ps_global->VAR_NORM_BACK_COLOR;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR, basefg))
+ fg = ps_global->VAR_SLCTBL_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR, basebg))
+ bg = ps_global->VAR_SLCTBL_BACK_COLOR;
+
+ if(bg || fg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for
+ * testing whether the other color is good.
+ */
+ if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK))) != NULL){
+ if(pico_is_good_colorpair(tmp))
+ for(s = color_embed(fg, bg);
+ (*len) < buflen && (colorstring[*len] = *s);
+ s++, (*len)++)
+ ;
+
+ free_color_pair(&tmp);
+ }
+ }
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
+ *len += 2;
+ }
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+int
+handle_end_color(char *colorstring, size_t buflen, int *len)
+{
+ *len = 0;
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+
+ /*
+ * We need to change the fg and bg colors back even if they
+ * are the same as the Normal Colors so that color_a_quote
+ * will have a chance to pick up those colors as the ones to
+ * switch to. We don't do this before the handle above so that
+ * the quote color will flow into the selectable item when
+ * the selectable item color is partly the same as the
+ * normal color. That is, suppose the normal color was black on
+ * cyan and the selectable color was blue on cyan, only a fg color
+ * change. We preserve the only-a-fg-color-change in a quote by
+ * letting the quote background color flow into the selectable text.
+ */
+ if(ps_global->VAR_SLCTBL_FORE_COLOR)
+ fg = ps_global->VAR_NORM_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR)
+ bg = ps_global->VAR_NORM_BACK_COLOR;
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
+ *len = 2;
+ }
+
+ if(fg || bg)
+ for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
+ ;
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+char *
+url_embed(int embed)
+{
+ static char buf[3] = {TAG_EMBED};
+ buf[1] = embed;
+ buf[2] = '\0';
+ return(buf);
+}
+
+
+/*
+ * Paint the signature.
+ */
+int
+color_signature(long int linenum, char *line, LT_INS_S **ins, void *is_in_sig)
+{
+ struct variable *vars = ps_global->vars;
+ int *in_sig_block;
+ COLOR_PAIR *col = NULL;
+
+ if(is_in_sig == NULL)
+ return 0;
+
+ in_sig_block = (int *) is_in_sig;
+
+ if(!strcmp(line, SIGDASHES))
+ *in_sig_block = START_SIG_BLOCK;
+ else if(*line == '\0')
+ /*
+ * Suggested by Eduardo: allow for a blank line right after
+ * the sigdashes.
+ */
+ *in_sig_block = (*in_sig_block == START_SIG_BLOCK)
+ ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+ else
+ *in_sig_block = (*in_sig_block != OUT_SIG_BLOCK)
+ ? IN_SIG_BLOCK : OUT_SIG_BLOCK;
+
+ if(*in_sig_block != OUT_SIG_BLOCK
+ && VAR_SIGNATURE_FORE_COLOR && VAR_SIGNATURE_BACK_COLOR
+ && (col = new_color_pair(VAR_SIGNATURE_FORE_COLOR,
+ VAR_SIGNATURE_BACK_COLOR))){
+ if(!pico_is_good_colorpair(col))
+ free_color_pair(&col);
+ }
+
+ if(col){
+ char *p, fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
+
+ ins = gf_line_test_new_ins(ins, line,
+ color_embed(col->fg, col->bg),
+ (2 * RGBLEN) + 4);
+
+ strncpy(fg, color_to_asciirgb(VAR_NORM_FORE_COLOR), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(VAR_NORM_BACK_COLOR), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+
+ /*
+ * Loop watching colors, and override with
+ * signature color whenever the normal foreground and background
+ * colors are in force.
+ */
+
+ for(p = line; *p; )
+ if(*p++ == TAG_EMBED){
+
+ switch(*p++){
+ case TAG_HANDLE :
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_FORE_COLOR)
+ && !colorcmp(bg, VAR_NORM_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(col->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR :
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_NORM_BACK_COLOR)
+ && !colorcmp(fg, VAR_NORM_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,col->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default :
+ break;
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ free_color_pair(&col);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Line filter to add color to displayed headers.
+ */
+int
+color_headers(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ static char field[FBUF_LEN + 1];
+ char fg[RGBLEN + 1], bg[RGBLEN + 1], rgbbuf[RGBLEN + 1];
+ char *p, *q, *value, *beg;
+ COLOR_PAIR *color;
+ int in_quote = 0, in_comment = 0, did_color = 0;
+ struct variable *vars = ps_global->vars;
+
+ field[FBUF_LEN] = '\0';
+
+ if(isspace((unsigned char)*line)) /* continuation line */
+ value = line;
+ else{
+ if(!(value = strindex(line, ':')))
+ return(0);
+
+ memset(field, 0, sizeof(field));
+ strncpy(field, line, MIN(value-line, sizeof(field)-1));
+ }
+
+ for(value++; isspace((unsigned char)*value); value++)
+ ;
+
+ strncpy(fg, color_to_asciirgb(VAR_HEADER_GENERAL_FORE_COLOR), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(VAR_HEADER_GENERAL_BACK_COLOR), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+
+ /*
+ * Split into two cases depending on whether this is a header which
+ * contains addresses or not. We may color addresses separately.
+ */
+ if(is_an_addr_hdr(field)){
+
+ /*
+ * If none of the patterns are for this header, don't bother parsing
+ * and checking each address.
+ */
+ if(!any_hdr_color(field))
+ return(0);
+
+ /*
+ * First check for patternless patterns which color whole line.
+ */
+ if((color = hdr_color(field, NULL, ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ ins = gf_line_test_new_ins(ins, value,
+ color_embed(color->fg, color->bg),
+ (2 * RGBLEN) + 4);
+ strncpy(fg, color_to_asciirgb(color->fg), sizeof(fg));
+ fg[sizeof(fg)-1] = '\0';
+ strncpy(bg, color_to_asciirgb(color->bg), sizeof(bg));
+ bg[sizeof(bg)-1] = '\0';
+ did_color++;
+ }
+ else
+ free_color_pair(&color);
+ }
+
+ /*
+ * Then go through checking address by address.
+ * Keep track of quotes and watch for color changes, and override
+ * with most recent header color whenever the normal foreground
+ * and background colors are in force.
+ */
+ beg = p = value;
+ while(*p){
+ switch(*p){
+ case '\\':
+ /* skip next character */
+ if(*(p+1) && (in_comment || in_quote))
+ p += 2;
+ else
+ p++;
+
+ break;
+
+ case '"':
+ if(!in_comment)
+ in_quote = 1 - in_quote;
+
+ p++;
+ break;
+
+ case '(':
+ in_comment++;
+ p++;
+ break;
+
+ case ')':
+ if(in_comment > 0)
+ in_comment--;
+
+ p++;
+ break;
+
+ case ',':
+ if(!(in_quote || in_comment)){
+ /* we reached the end of this address */
+ *p = '\0';
+ if(color)
+ free_color_pair(&color);
+
+ if((color = hdr_color(field, beg,
+ ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ did_color++;
+ ins = gf_line_test_new_ins(ins, beg,
+ color_embed(color->fg,
+ color->bg),
+ (2 * RGBLEN) + 4);
+ *p = ',';
+ for(q = p; q > beg &&
+ isspace((unsigned char)*(q-1)); q--)
+ ;
+
+ ins = gf_line_test_new_ins(ins, q,
+ color_embed(fg, bg),
+ (2 * RGBLEN) + 4);
+ }
+ else
+ free_color_pair(&color);
+ }
+ else
+ *p = ',';
+
+ for(p++; isspace((unsigned char)*p); p++)
+ ;
+
+ beg = p;
+ }
+ else
+ p++;
+
+ break;
+
+ case TAG_EMBED:
+ switch(*(++p)){
+ case TAG_HANDLE:
+ p++;
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR:
+ p++;
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(color->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR:
+ p++;
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,color->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default:
+ break;
+ }
+
+ break;
+
+ default:
+ p++;
+ break;
+ }
+ }
+
+ for(q = beg; *q && isspace((unsigned char)*q); q++)
+ ;
+
+ if(*q && !(in_quote || in_comment)){
+ /* we reached the end of this address */
+ if(color)
+ free_color_pair(&color);
+
+ if((color = hdr_color(field, beg, ps_global->hdr_colors)) != NULL){
+ if(pico_is_good_colorpair(color)){
+ did_color++;
+ ins = gf_line_test_new_ins(ins, beg,
+ color_embed(color->fg,
+ color->bg),
+ (2 * RGBLEN) + 4);
+ for(q = p; q > beg && isspace((unsigned char)*(q-1)); q--)
+ ;
+
+ ins = gf_line_test_new_ins(ins, q,
+ color_embed(fg, bg),
+ (2 * RGBLEN) + 4);
+ }
+ else
+ free_color_pair(&color);
+ }
+ }
+
+ if(color)
+ free_color_pair(&color);
+
+ if(did_color)
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+ else{
+
+ color = hdr_color(field, value, ps_global->hdr_colors);
+
+ if(color){
+ if(pico_is_good_colorpair(color)){
+ ins = gf_line_test_new_ins(ins, value,
+ color_embed(color->fg, color->bg),
+ (2 * RGBLEN) + 4);
+
+ /*
+ * Loop watching colors, and override with header
+ * color whenever the normal foreground and background
+ * colors are in force.
+ */
+ p = value;
+ while(*p)
+ if(*p++ == TAG_EMBED){
+
+ switch(*p++){
+ case TAG_HANDLE:
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR:
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_FORE_COLOR)
+ && !colorcmp(bg, VAR_HEADER_GENERAL_BACK_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(color->fg,NULL),
+ RGBLEN + 2);
+ break;
+
+ case TAG_BGCOLOR:
+ snprintf(rgbbuf, sizeof(rgbbuf), "%.*s", RGBLEN, p);
+ rgbbuf[sizeof(rgbbuf)-1] = '\0';
+ p += RGBLEN; /* advance past color value */
+
+ if(!colorcmp(rgbbuf, VAR_HEADER_GENERAL_BACK_COLOR)
+ && !colorcmp(fg, VAR_HEADER_GENERAL_FORE_COLOR))
+ ins = gf_line_test_new_ins(ins, p,
+ color_embed(NULL,color->bg),
+ RGBLEN + 2);
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+
+ free_color_pair(&color);
+ }
+ }
+
+ return(0);
+}
+
+
+int
+url_hilite(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ register char *lp, *up = NULL, *urlp = NULL,
+ *weburlp = NULL, *mailurlp = NULL;
+ int n, n1, n2, n3, l;
+ char buf[256], color[256];
+ HANDLE_S *h;
+ URL_HILITE_S *uh;
+
+ for(lp = line; ; lp = up + n){
+ /* scan for all of them so we can choose the first */
+ if(F_ON(F_VIEW_SEL_URL,ps_global))
+ urlp = rfc1738_scan(lp, &n1);
+ if(F_ON(F_VIEW_SEL_URL_HOST,ps_global))
+ weburlp = web_host_scan(lp, &n2);
+ if(F_ON(F_SCAN_ADDR,ps_global))
+ mailurlp = mail_addr_scan(lp, &n3);
+
+ if(urlp || weburlp || mailurlp){
+ up = urlp ? urlp :
+ weburlp ? weburlp : mailurlp;
+ if(up == urlp && weburlp && weburlp < up)
+ up = weburlp;
+ if(mailurlp && mailurlp < up)
+ up = mailurlp;
+
+ if(up == urlp){
+ n = n1;
+ weburlp = mailurlp = NULL;
+ }
+ else if(up == weburlp){
+ n = n2;
+ mailurlp = NULL;
+ }
+ else{
+ n = n3;
+ weburlp = NULL;
+ }
+ }
+ else
+ break;
+
+ uh = (URL_HILITE_S *) local;
+
+ h = new_handle(uh->handlesp);
+ h->type = URL;
+ h->h.url.path = (char *) fs_get((n + 10) * sizeof(char));
+ snprintf(h->h.url.path, n+10, "%s%.*s",
+ weburlp ? "http://" : (mailurlp ? "mailto:" : ""), n, up);
+ h->h.url.path[n+10-1] = '\0';
+
+ if(handle_start_color(color, sizeof(color), &l, uh->hdr_color))
+ ins = gf_line_test_new_ins(ins, up, color, l);
+ else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global))
+ ins = gf_line_test_new_ins(ins, up, url_embed(TAG_BOLDON), 2);
+
+ buf[0] = TAG_EMBED;
+ buf[1] = TAG_HANDLE;
+ snprintf(&buf[3], sizeof(buf)-3, "%d", h->key);
+ buf[sizeof(buf)-1] = '\0';
+ buf[2] = strlen(&buf[3]);
+ ins = gf_line_test_new_ins(ins, up, buf, (int) buf[2] + 3);
+
+ /* in case it was the current selection */
+ ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_INVOFF), 2);
+
+ if(scroll_handle_end_color(color, sizeof(color), &l, uh->hdr_color))
+ ins = gf_line_test_new_ins(ins, up + n, color, l);
+ else
+ ins = gf_line_test_new_ins(ins, up + n, url_embed(TAG_BOLDOFF), 2);
+
+ urlp = weburlp = mailurlp = NULL;
+ }
+
+ return(0);
+}
+
+
+int
+url_hilite_hdr(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ static int check_for_urls = 0;
+ register char *lp;
+
+ if(isspace((unsigned char)*line)) /* continuation, check or not
+ depending on last line */
+ lp = line;
+ else{
+ check_for_urls = 0;
+ if((lp = strchr(line, ':')) != NULL){ /* there ought to always be a colon */
+ FieldType ft;
+
+ *lp = '\0';
+
+ if(((ft = pine_header_standard(line)) == FreeText
+ || ft == Subject
+ || ft == TypeUnknown)
+ && strucmp(line, "message-id")
+ && strucmp(line, "newsgroups")
+ && strucmp(line, "references")
+ && strucmp(line, "in-reply-to")
+ && strucmp(line, "received")
+ && strucmp(line, "date")){
+ check_for_urls = 1;
+ }
+
+ *lp = ':';
+ }
+ }
+
+ if(check_for_urls)
+ (void) url_hilite(linenum, lp + 1, ins, local);
+
+ return(0);
+}
+
+
+int
+pad_to_right_edge(long int linenum, char *line, LT_INS_S **ins, void *local)
+{
+ char *p;
+ int wid = 0;
+ int total_wid;
+ struct variable *vars = ps_global->vars;
+
+ if(!line[0])
+ return 0;
+
+ total_wid = *((int *) local);
+
+ /* calculate width of line */
+ p = line;
+ while(*p){
+
+ switch(*p){
+ case TAG_EMBED:
+ p++;
+ switch(*p){
+ case TAG_HANDLE:
+ p++;
+ p += *p + 1; /* skip handle key */
+ break;
+
+ case TAG_FGCOLOR :
+ case TAG_BGCOLOR :
+ p += (RGBLEN + 1); /* 1 for TAG, RGBLEN for color */
+ break;
+
+ case TAG_INVON:
+ case TAG_INVOFF:
+ case TAG_BOLDON:
+ case TAG_BOLDOFF:
+ case TAG_ULINEON:
+ case TAG_ULINEOFF:
+ p++;
+ break;
+
+ default: /* literal embed char */
+ break;
+ }
+
+ break;
+
+ case TAB:
+ p++;
+ while(((++wid) & 0x07) != 0) /* add tab's spaces */
+ ;
+
+ break;
+
+ default:
+ wid += width_at_this_position((unsigned char *) p, strlen(p));
+ p++;
+ break;
+ }
+ }
+
+ if(total_wid > wid){
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_HEADER_GENERAL_FORE_COLOR,
+ VAR_HEADER_GENERAL_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ ins = gf_line_test_new_ins(ins, line+strlen(line),
+ repeat_char(total_wid-wid, ' '), total_wid-wid);
+ ins = gf_line_test_new_ins(ins, line + strlen(line),
+ color_embed(VAR_NORM_FORE_COLOR,
+ VAR_NORM_BACK_COLOR),
+ (2 * RGBLEN) + 4);
+ }
+
+ return(0);
+}
+
+
+
+#define UES_LEN 12
+#define UES_MAX 32
+int
+url_external_specific_handler(char *url, int len)
+{
+ static char list[UES_LEN * UES_MAX];
+
+ if(url){
+ char *p;
+ int i;
+
+ for(i = 0; i < UES_MAX && *(p = &list[i * UES_LEN]); i++)
+ if(strlen(p) == len && !struncmp(p, url, len))
+ return(1);
+ }
+ else{ /* initialize! */
+ char **l, *test, *cmd, *p, *p2;
+ int i = 0, n;
+
+ memset(list, 0, sizeof(list));
+ for(l = ps_global->VAR_BROWSER ; l && *l; l++){
+ get_pair(*l, &test, &cmd, 1, 1);
+
+ if((p = srchstr(test, "_scheme(")) && (p2 = strstr(p+8, ")_"))){
+ *p2 = '\0';
+
+ for(p += 8; *p && i < UES_MAX; p += n)
+ if((p2 = strchr(p, ',')) != NULL){
+ if((n = p2 - p) < UES_LEN){
+ strncpy(&list[i * UES_LEN], p, MIN(n, sizeof(list)-(i * UES_LEN)));
+ i++;
+ }
+ else
+ dprint((1,
+ "* * * HANLDER TOO LONG: %.*s\n", n,
+ p ? p : "?"));
+
+ n++;
+ }
+ else{
+ if(strlen(p) <= UES_LEN){
+ strncpy(&list[i * UES_LEN], p, sizeof(list)-(i * UES_LEN));
+ i++;
+ }
+
+ break;
+ }
+ }
+
+ if(test)
+ fs_give((void **) &test);
+
+ if(cmd)
+ fs_give((void **) &cmd);
+ }
+ }
+
+ return(0);
+}
+
+
+int
+url_imap_folder(char *true_url, char **folder, imapuid_t *uid_val,
+ imapuid_t *uid, char **search, int silent)
+{
+ char *url, *scheme, *p, *cmd, *server = NULL,
+ *user = NULL, *auth = NULL, *mailbox = NULL,
+ *section = NULL;
+ size_t l;
+ int rv = URL_IMAP_ERROR;
+
+ /*
+ * Since we're planting nulls, operate on a temporary copy...
+ */
+ scheme = silent ? NULL : "IMAP";
+ url = cpystr(true_url + 7);
+
+ /* Try to pick apart the "iserver" portion */
+ if((cmd = strchr(url, '/')) != NULL){ /* iserver "/" [mailbox] ? */
+ *cmd++ = '\0';
+ }
+ else{
+ dprint((2, "-- URL IMAP FOLDER: missing: %s\n",
+ url ? url : "?"));
+ cmd = &url[strlen(url)-1]; /* assume only iserver */
+ }
+
+ if((p = strchr(url, '@')) != NULL){ /* user | auth | pass? */
+ *p++ = '\0';
+ server = rfc1738_str(p);
+
+ /* only ";auth=*" supported (and also ";auth=anonymous") */
+ if((p = srchstr(url, ";auth=")) != NULL){
+ *p = '\0';
+ auth = rfc1738_str(p + 6);
+ }
+
+ if(*url)
+ user = rfc1738_str(url);
+ }
+ else
+ server = rfc1738_str(url);
+
+ if(!*server)
+ return(url_bogus_imap(&url, scheme, "No server specified"));
+
+ /*
+ * "iserver" in hand, pick apart the "icommand"...
+ */
+ p = NULL;
+ if(!*cmd || (p = srchstr(cmd, ";type="))){
+ char *criteria;
+
+ /*
+ * No "icommand" (all top-level folders) or "imailboxlist"...
+ */
+ if(p){
+ *p = '\0'; /* tie off criteria */
+ criteria = rfc1738_str(cmd); /* get "enc_list_mailbox" */
+ if(!strucmp(p = rfc1738_str(p+6), "lsub"))
+ rv |= URL_IMAP_IMBXLSTLSUB;
+ else if(strucmp(p, "list"))
+ return(url_bogus_imap(&url, scheme,
+ "Invalid list type specified"));
+ }
+ else{
+ rv |= URL_IMAP_ISERVERONLY;
+ criteria = "";
+ }
+
+ /* build folder list from specified server/criteria/list-method */
+ l = strlen(server) + strlen(criteria) + 10 + (user ? (strlen(user)+2) : 9);
+ *folder = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(*folder, l+1, "{%s/%s%s%s}%s%s%s", server,
+ user ? "user=\"" : "Anonymous",
+ user ? user : "",
+ user ? "\"" : "",
+ *criteria ? "[" : "", criteria, *criteria ? "[" : "");
+ (*folder)[l] = '\0';
+ rv |= URL_IMAP_IMAILBOXLIST;
+ }
+ else{
+ if((p = srchstr(cmd, "/;uid=")) != NULL){ /* "imessagepart" */
+ *p = '\0'; /* tie off mailbox [uidvalidity] */
+ if((section = srchstr(p += 6, "/;section=")) != NULL){
+ *section = '\0'; /* tie off UID */
+ section = rfc1738_str(section + 10);
+/* BUG: verify valid section spec ala rfc 2060 */
+ dprint((2,
+ "-- URL IMAP FOLDER: section not used: %s\n",
+ section ? section : "?"));
+ }
+
+ if(!(*uid = rfc1738_num(&p)) || *p) /* decode UID */
+ return(url_bogus_imap(&url, scheme, "Invalid data in UID"));
+
+ /* optional "uidvalidity"? */
+ if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
+ *p = '\0';
+ p += 13;
+ if(!(*uid_val = rfc1738_num(&p)) || *p)
+ return(url_bogus_imap(&url, scheme,
+ "Invalid UIDVALIDITY"));
+ }
+
+ mailbox = rfc1738_str(cmd);
+ rv = URL_IMAP_IMESSAGEPART;
+ }
+ else{ /* "imessagelist" */
+ /* optional "uidvalidity"? */
+ if((p = srchstr(cmd, ";uidvalidity=")) != NULL){
+ *p = '\0';
+ p += 13;
+ if(!(*uid_val = rfc1738_num(&p)) || *p)
+ return(url_bogus_imap(&url, scheme,
+ "Invalid UIDVALIDITY"));
+ }
+
+ /* optional "enc_search"? */
+ if((p = strchr(cmd, '?')) != NULL){
+ *p = '\0';
+ if(search)
+ *search = cpystr(rfc1738_str(p + 1));
+/* BUG: verify valid search spec ala rfc 2060 */
+ }
+
+ mailbox = rfc1738_str(cmd);
+ rv = URL_IMAP_IMESSAGELIST;
+ }
+
+ if(auth && *auth != '*' && strucmp(auth, "anonymous"))
+ q_status_message(SM_ORDER, 3, 3,
+ "Unsupported authentication method. Using standard login.");
+
+ /*
+ * At this point our structure should contain the
+ * digested url. Now put it together for c-client...
+ */
+ l = strlen(server) + 8 + (mailbox ? strlen(mailbox) : 0)
+ + (user ? (strlen(user)+2) : 9);
+ *folder = (char *) fs_get((l+1) * sizeof(char));
+ snprintf(*folder, l+1, "{%s%s%s%s%s}%s", server,
+ (user || !(auth && strucmp(auth, "anonymous"))) ? "/" : "",
+ user ? "user=\"" : ((auth && strucmp(auth, "anonymous")) ? "" : "Anonymous"),
+ user ? user : "",
+ user ? "\"" : "",
+ mailbox);
+ (*folder)[l] = '\0';
+ }
+
+ fs_give((void **) &url);
+ return(rv);
+}
+
+
+int
+url_bogus_imap(char **freeme, char *url, char *problem)
+{
+ fs_give((void **) freeme);
+ (void) url_bogus(url, problem);
+ return(URL_IMAP_ERROR);
+}
+
+
+/*
+ * url_bogus - report url syntax errors and such
+ */
+int
+url_bogus(char *url, char *reason)
+{
+ dprint((2, "-- bogus url \"%s\": %s\n",
+ url ? url : "<NULL URL>", reason ? reason : "?"));
+ if(url)
+ q_status_message3(SM_ORDER|SM_DING, 2, 3,
+ "Malformed \"%.*s\" URL: %.200s",
+ (void *) (strchr(url, ':') - url), url, reason);
+
+ return(0);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format header text suitable for display
+
+ Args: stream -- mail stream for various header text fetches
+ msgno -- sequence number in stream of message we're interested in
+ section -- which section of message
+ env -- pointer to msg's envelope
+ hdrs -- struct containing what's to get formatted
+ prefix -- prefix to append to each output line
+ handlesp -- address of pointer to the message's handles
+ flags -- FM_ flags, see pith/mailview.h
+ final_pc -- function to write header text with
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+#define FHT_OK 0
+#define FHT_WRTERR -1
+#define FHT_FTCHERR 1
+int
+format_header(MAILSTREAM *stream, long int msgno, char *section, ENVELOPE *env,
+ HEADER_S *hdrs, char *prefix, HANDLE_S **handlesp, int flags,
+ fmt_env_t fmt_env, gf_io_t final_pc)
+{
+ int rv = FHT_OK;
+ int nfields, i;
+ char *h = NULL, **fields = NULL, **v, *q, *start,
+ *finish, *current;
+ STORE_S *tmp_store;
+ URL_HILITE_S uh;
+ gf_io_t tmp_pc, tmp_gc;
+ struct variable *vars = ps_global->vars;
+
+ if((tmp_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL)
+ gf_set_so_writec(&tmp_pc, tmp_store);
+ else
+ return(FHT_WRTERR);
+
+ if(!fmt_env)
+ fmt_env = format_envelope;
+
+ if(ps_global->full_header == 2){
+ rv = format_raw_header(stream, msgno, section, tmp_pc);
+ }
+ else{
+ /*
+ * First, calculate how big a fields array we need.
+ */
+
+ /* Custom header viewing list specified */
+ if(hdrs->type == HD_LIST){
+ /* view all these headers */
+ if(!hdrs->except){
+ for(nfields = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(!is_an_env_hdr(q))
+ nfields++;
+ }
+ /* view all except these headers */
+ else{
+ for(nfields = 0, v = hdrs->h.l; *v != NULL; v++)
+ nfields++;
+
+ if(nfields > 1)
+ nfields--; /* subtract one for ALL_EXCEPT field */
+ }
+ }
+ else
+ nfields = 6; /* default view */
+
+ /* allocate pointer space */
+ if(nfields){
+ fields = (char **)fs_get((size_t)(nfields+1) * sizeof(char *));
+ memset(fields, 0, (size_t)(nfields+1) * sizeof(char *));
+ }
+
+ if(hdrs->type == HD_LIST){
+ /* view all these headers */
+ if(!hdrs->except){
+ /* put the non-envelope headers in fields */
+ if(nfields)
+ for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(!is_an_env_hdr(q))
+ fields[i++] = q;
+ }
+ /* view all except these headers */
+ else{
+ /* put the list of headers not to view in fields */
+ if(nfields)
+ for(i = 0, v = hdrs->h.l; (q = *v) != NULL; v++)
+ if(strucmp(ALL_EXCEPT, q))
+ fields[i++] = q;
+ }
+
+ v = hdrs->h.l;
+ }
+ else{
+ if(nfields){
+ fields[i = 0] = "Resent-Date";
+ fields[++i] = "Resent-From";
+ fields[++i] = "Resent-To";
+ fields[++i] = "Resent-cc";
+ fields[++i] = "Resent-Subject";
+ }
+
+ v = fields;
+ }
+
+ /* custom view with exception list */
+ if(hdrs->type == HD_LIST && hdrs->except){
+ /*
+ * Go through each header in h and print it.
+ * First we check to see if it is an envelope header so we
+ * can print our envelope version of it instead of the raw version.
+ */
+
+ /* fetch all the other headers */
+ if(nfields)
+ h = pine_fetchheader_lines_not(stream, msgno, section, fields);
+
+ for(current = h;
+ h && delineate_this_header(NULL, current, &start, &finish);
+ current = finish){
+ char tmp[MAILTMPLEN+1];
+ char *colon_loc;
+
+ colon_loc = strindex(start, ':');
+ if(colon_loc && colon_loc < finish){
+ strncpy(tmp, start, MIN(colon_loc-start, sizeof(tmp)-1));
+ tmp[MIN(colon_loc-start, sizeof(tmp)-1)] = '\0';
+ }
+ else
+ colon_loc = NULL;
+
+ if(colon_loc && is_an_env_hdr(tmp)){
+ char *dummystart, *dummyfinish;
+
+ /*
+ * Pretty format for env hdrs.
+ * If the same header appears more than once, only
+ * print the last to avoid duplicates.
+ * They should have been combined in the env when parsed.
+ */
+ if(!delineate_this_header(tmp, current+1, &dummystart,
+ &dummyfinish))
+ format_env_hdr(stream, msgno, section, env,
+ fmt_env, tmp_pc, tmp, hdrs->charset, flags);
+ }
+ else{
+ if((rv = format_raw_hdr_string(start, finish, tmp_pc,
+ hdrs->charset, flags)) != 0)
+ goto write_error;
+ else
+ start = finish;
+ }
+ }
+ }
+ /* custom view or default */
+ else{
+ /* fetch the non-envelope headers */
+ if(nfields)
+ h = pine_fetchheader_lines(stream, msgno, section, fields);
+
+ /* default envelope for default view */
+ if(hdrs->type == HD_BFIELD)
+ (*fmt_env)(stream, msgno, section, env, tmp_pc, hdrs->h.b, hdrs->charset, flags);
+
+ /* go through each header in list, v initialized above */
+ for(; (q = *v) != NULL; v++){
+ if(is_an_env_hdr(q)){
+ /* pretty format for env hdrs */
+ format_env_hdr(stream, msgno, section, env,
+ fmt_env, tmp_pc, q, hdrs->charset, flags);
+ }
+ else{
+ /*
+ * Go through h finding all occurences of this header
+ * and all continuation lines, and output.
+ */
+ for(current = h;
+ h && delineate_this_header(q,current,&start,&finish);
+ current = finish){
+ if((rv = format_raw_hdr_string(start, finish, tmp_pc,
+ hdrs->charset, flags)) != 0)
+ goto write_error;
+ else
+ start = finish;
+ }
+ }
+ }
+ }
+ }
+
+
+ write_error:
+
+ gf_clear_so_writec(tmp_store);
+
+ if(!rv){ /* valid data? Do wrapping and filtering... */
+ int column;
+ char *errstr, *display_filter = NULL, trigger[MAILTMPLEN];
+ STORE_S *df_store = NULL;
+
+ so_seek(tmp_store, 0L, 0);
+
+ column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80;
+
+ /*
+ * Test for and act on any display filter
+ * This barely makes sense. The display filter is going
+ * to be getting UTF-8'ized headers here. In pre-alpine
+ * pine the display filter was being fed already decoded
+ * headers in whatever character set they were in.
+ * The good news is that that didn't make much
+ * sense either, so this shouldn't break anything.
+ * It seems unlikely that anybody is doing anything useful
+ * with the header part of display filters.
+ */
+ if(ps_global->tools.display_filter
+ && ps_global->tools.display_filter_trigger
+ && (display_filter = (*ps_global->tools.display_filter_trigger)(NULL, trigger, sizeof(trigger)))){
+ if((df_store = so_get(CharStar, NULL, EDIT_ACCESS)) != NULL){
+
+ gf_set_so_writec(&tmp_pc, df_store);
+ gf_set_so_readc(&tmp_gc, df_store);
+ if((errstr = (*ps_global->tools.display_filter)(display_filter, tmp_store, tmp_pc, NULL)) != NULL){
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ _("Formatting error: %s"), errstr);
+ rv = FHT_WRTERR;
+ }
+ else
+ so_seek(df_store, 0L, 0);
+
+ gf_clear_so_writec(df_store);
+ }
+ else{
+ q_status_message(SM_ORDER | SM_DING, 3, 3,
+ "No space for filtered text.");
+ rv = FHT_WRTERR;
+ }
+ }
+ else{
+ so_seek(tmp_store, 0L, 0);
+ gf_set_so_readc(&tmp_gc, tmp_store);
+ }
+
+ if(!rv){
+ int *margin, wrapflags = GFW_ONCOMMA;
+
+ gf_filter_init();
+ gf_link_filter(gf_local_nvtnl, NULL);
+
+ if((F_ON(F_VIEW_SEL_URL, ps_global)
+ || F_ON(F_VIEW_SEL_URL_HOST, ps_global)
+ || F_ON(F_SCAN_ADDR, ps_global))
+ && handlesp){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(url_hilite_hdr,
+ gf_url_hilite_opt(&uh,handlesp,1)));
+ wrapflags |= GFW_HANDLES;
+ }
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ((VAR_NORM_FORE_COLOR
+ && VAR_HEADER_GENERAL_FORE_COLOR
+ && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
+ ||
+ (VAR_NORM_BACK_COLOR
+ && VAR_HEADER_GENERAL_BACK_COLOR
+ && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR))))
+ wrapflags |= GFW_HDRCOLOR;
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ps_global->hdr_colors){
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(color_headers, NULL));
+ wrapflags |= (GFW_HANDLES | GFW_HDRCOLOR);
+ }
+
+ if(prefix && *prefix)
+ column = MAX(column-strlen(prefix), 50);
+
+ margin = format_view_margin();
+
+ if(!(flags & FM_NOWRAP))
+ gf_link_filter(gf_wrap,
+ gf_wrap_filter_opt(column, column,
+ (flags & FM_NOINDENT) ? NULL : margin,
+ 4, wrapflags));
+
+ if(prefix && *prefix)
+ gf_link_filter(gf_prefix, gf_prefix_opt(prefix));
+
+ if((flags & FM_DISPLAY)
+ && !(flags & FM_NOCOLOR)
+ && pico_usingcolor()
+ && ((VAR_NORM_FORE_COLOR
+ && VAR_HEADER_GENERAL_FORE_COLOR
+ && colorcmp(VAR_NORM_FORE_COLOR, VAR_HEADER_GENERAL_FORE_COLOR))
+ ||
+ (VAR_NORM_BACK_COLOR
+ && VAR_HEADER_GENERAL_BACK_COLOR
+ && colorcmp(VAR_NORM_BACK_COLOR, VAR_HEADER_GENERAL_BACK_COLOR)))){
+ int right_margin;
+ int total_wid;
+
+ right_margin = margin ? margin[1] : 0;
+ total_wid = column - right_margin;
+
+ gf_link_filter(gf_line_test,
+ gf_line_test_opt(pad_to_right_edge, (void *) &total_wid));
+ wrapflags |= GFW_HANDLES;
+ }
+
+ gf_link_filter(gf_nvtnl_local, NULL);
+
+ if((errstr = gf_pipe(tmp_gc, final_pc)) != NULL){
+ rv = FHT_WRTERR;
+ q_status_message1(SM_ORDER | SM_DING, 3, 3,
+ "Can't build header : %.200s", errstr);
+ }
+ }
+
+ if(df_store){
+ gf_clear_so_readc(df_store);
+ so_give(&df_store);
+ }
+ else
+ gf_clear_so_readc(tmp_store);
+ }
+
+ so_give(&tmp_store);
+
+ if(h)
+ fs_give((void **)&h);
+
+ if(fields)
+ fs_give((void **)&fields);
+
+ return(rv);
+}
+
+
+/*----------------------------------------------------------------------
+ Format RAW header text for display
+
+ Args: stream -- mail stream for various header text fetches
+ rawno -- sequence number in stream of message we're interested in
+ section -- which section of message
+ pc -- function to write header text with
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+int
+format_raw_header(MAILSTREAM *stream, long int msgno, char *section, gf_io_t pc)
+{
+ char *h = mail_fetch_header(stream, msgno, section, NULL, NULL, FT_PEEK);
+ unsigned char c;
+
+ if(h){
+ while(*h){
+ if(ISRFCEOL(h)){
+ h += 2;
+ if(!gf_puts(NEWLINE, pc))
+ return(FHT_WRTERR);
+
+ if(ISRFCEOL(h)) /* all done! */
+ return(FHT_OK);
+ }
+ else if((unsigned char)(*h) < 0x80 && FILTER_THIS(*h) &&
+ !(*(h+1) && *h == ESCAPE && match_escapes(h+1))){
+ c = (unsigned char) *h++;
+ if(!((*pc)(c >= 0x80 ? '~' : '^')
+ && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
+ return(FHT_WRTERR);
+ }
+ else if(!(*pc)(*h++))
+ return(FHT_WRTERR);
+ }
+ }
+ else
+ return(FHT_FTCHERR);
+
+ return(FHT_OK);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format c-client envelope data suitable for display
+
+ Args: s -- mail stream for various header text fetches
+ n -- raw sequence number in stream of message we're interested in
+ sect -- which section of message
+ e -- pointer to msg's envelope
+ pc -- function to write header text with
+ which -- which header lines to write
+ oacs --
+ flags -- FM_ flags, see pith/mailview.h
+
+ Result: 0 if all's well, -1 if write error, 1 if fetch error
+
+ NOTE: Blank-line delimiter is NOT written here. Newlines are written
+ in the local convention.
+
+ ----*/
+void
+format_envelope(MAILSTREAM *s, long int n, char *sect, ENVELOPE *e, gf_io_t pc,
+ long int which, char *oacs, int flags)
+{
+ char *q, *p2, buftmp[MAILTMPLEN];
+
+ if(!e)
+ return;
+
+ if((which & FE_DATE) && e->date) {
+ q = "Date: ";
+ snprintf(buftmp, sizeof(buftmp), "%s",
+ F_ON(F_DATES_TO_LOCAL,ps_global)
+ ? convert_date_to_local((char *) e->date) : (char *) e->date);
+ buftmp[sizeof(buftmp)-1] = '\0';
+ p2 = (char *)rfc1522_decode_to_utf8((unsigned char *) tmp_20k_buf,
+ SIZEOF_20KBUF, buftmp);
+ gf_puts(q, pc);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_FROM) && e->from)
+ format_addr_string(s, n, sect, "From: ", e->from, flags, oacs, pc);
+
+ if((which & FE_REPLYTO) && e->reply_to
+ && (!e->from || !address_is_same(e->reply_to, e->from)))
+ format_addr_string(s, n, sect, "Reply-To: ", e->reply_to, flags, oacs, pc);
+
+ if((which & FE_TO) && e->to)
+ format_addr_string(s, n, sect, "To: ", e->to, flags, oacs, pc);
+
+ if((which & FE_CC) && e->cc)
+ format_addr_string(s, n, sect, "Cc: ", e->cc, flags, oacs, pc);
+
+ if((which & FE_BCC) && e->bcc)
+ format_addr_string(s, n, sect, "Bcc: ", e->bcc, flags, oacs, pc);
+
+ if((which & FE_RETURNPATH) && e->return_path)
+ format_addr_string(s, n, sect, "Return-Path: ", e->return_path,
+ flags, oacs, pc);
+
+ if((which & FE_NEWSGROUPS) && e->newsgroups){
+ int bogus = NIL;
+ format_newsgroup_string("Newsgroups: ", e->newsgroups, flags, pc);
+ if (!e->ngpathexists && e->message_id &&
+ strncmp (e->message_id,"<alpine.",8) &&
+ strncmp (e->message_id,"<Pine.",6) &&
+ strncmp (e->message_id,"<MS-C.",6) &&
+ strncmp (e->message_id,"<MailManager.",13) &&
+ strncmp (e->message_id,"<EasyMail.",11) &&
+ strncmp (e->message_id,"<ML-",4)) bogus = T;
+
+ if(bogus)
+ q_status_message(SM_ORDER, 0, 3,
+ "Unverified Newsgroup header -- Message MAY or MAY NOT have been posted");
+ }
+
+ if((which & FE_FOLLOWUPTO) && e->followup_to)
+ format_newsgroup_string("Followup-To: ", e->followup_to, flags, pc);
+
+ if((which & FE_SUBJECT) && e->subject && e->subject[0]){
+ char *freeme = NULL;
+
+ q = "Subject: ";
+ gf_puts(q, pc);
+
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->subject), SIZEOF_20KBUF-10000);
+
+ if(flags & FM_DISPLAY
+ && (ps_global->display_keywords_in_subject
+ || ps_global->display_keywordinits_in_subject)){
+
+ /* don't bother if no keywords are defined */
+ if(some_user_flags_defined(s))
+ p2 = freeme = prepend_keyword_subject(s, n, p2,
+ ps_global->display_keywords_in_subject ? KW : KWInit,
+ NULL, ps_global->VAR_KW_BRACES);
+ }
+
+ format_env_puts(p2, pc);
+
+ if(freeme)
+ fs_give((void **) &freeme);
+
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_SENDER) && e->sender
+ && (!e->from || !address_is_same(e->sender, e->from)))
+ format_addr_string(s, n, sect, "Sender: ", e->sender, flags, oacs, pc);
+
+ if((which & FE_MESSAGEID) && e->message_id){
+ q = "Message-ID: ";
+ gf_puts(q, pc);
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->message_id), SIZEOF_20KBUF-10000);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_INREPLYTO) && e->in_reply_to){
+ q = "In-Reply-To: ";
+ gf_puts(q, pc);
+ 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);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+
+ if((which & FE_REFERENCES) && e->references) {
+ q = "References: ";
+ gf_puts(q, pc);
+ p2 = iutf8ncpy((char *)(tmp_20k_buf+10000), (char *)rfc1522_decode_to_utf8((unsigned char *)tmp_20k_buf, 10000, e->references), SIZEOF_20KBUF-10000);
+ format_env_puts(p2, pc);
+ gf_puts(NEWLINE, pc);
+ }
+}
+
+
+
+
+/*
+ * The argument fieldname is something like "Subject:..." or "Subject".
+ * Look through the specs in speccolor for a match of the fieldname,
+ * and return the color that goes with any match, or NULL.
+ * Caller should free the color.
+ */
+COLOR_PAIR *
+hdr_color(char *fieldname, char *value, SPEC_COLOR_S *speccolor)
+{
+ SPEC_COLOR_S *hc = NULL;
+ COLOR_PAIR *color_pair = NULL;
+ char *colon, *fname;
+ char fbuf[FBUF_LEN+1];
+ int gotit;
+ PATTERN_S *pat;
+
+ colon = strindex(fieldname, ':');
+ if(colon){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
+ fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname)
+ for(hc = speccolor; hc; hc = hc->next)
+ if(hc->spec && !strucmp(fname, hc->spec)){
+ if(!hc->val)
+ break;
+
+ gotit = 0;
+ for(pat = hc->val; !gotit && pat; pat = pat->next)
+ if(srchstr(value, pat->substring))
+ gotit++;
+
+ if(gotit)
+ break;
+ }
+
+ if(hc && hc->fg && hc->fg[0] && hc->bg && hc->bg[0])
+ color_pair = new_color_pair(hc->fg, hc->bg);
+
+ return(color_pair);
+}
+
+
+/*
+ * The argument fieldname is something like "Subject:..." or "Subject".
+ * Look through the specs in hdr_colors for a match of the fieldname,
+ * and return 1 if that fieldname is in one of the patterns, 0 otherwise.
+ */
+int
+any_hdr_color(char *fieldname)
+{
+ SPEC_COLOR_S *hc = NULL;
+ char *colon, *fname;
+ char fbuf[FBUF_LEN+1];
+
+ colon = strindex(fieldname, ':');
+ if(colon){
+ strncpy(fbuf, fieldname, MIN(colon-fieldname,FBUF_LEN));
+ fbuf[MIN(colon-fieldname,FBUF_LEN)] = '\0';
+ fname = fbuf;
+ }
+ else
+ fname = fieldname;
+
+ if(fname && *fname)
+ for(hc = ps_global->hdr_colors; hc; hc = hc->next)
+ if(hc->spec && !strucmp(fname, hc->spec))
+ break;
+
+ return(hc ? 1 : 0);
+}
+
+
+/*----------------------------------------------------------------------
+ Format an address field, wrapping lines nicely at commas
+
+ Args: field_name -- The name of the field we're formatting ("TO: ", ...)
+ addr -- ADDRESS structure to format
+
+ Result: A formatted, malloced string is returned.
+ ----------------------------------------------------------------------*/
+void
+format_addr_string(MAILSTREAM *stream, long int msgno, char *section, char *field_name,
+ struct mail_address *addr, int flags, char *oacs, gf_io_t pc)
+{
+ char *ptmp, *mtmp;
+ int trailing = 0, group = 0;
+ ADDRESS *atmp;
+
+ if(!addr)
+ return;
+
+ /*
+ * quickly run down address list to make sure none are patently bogus.
+ * If so, just blat raw field out.
+ */
+ for(atmp = addr; stream && atmp; atmp = atmp->next)
+ if(atmp->host && atmp->host[0] == '.'){
+ char *field, *fields[2];
+
+ fields[1] = NULL;
+ fields[0] = cpystr(field_name);
+ if((ptmp = strchr(fields[0], ':')) != NULL)
+ *ptmp = '\0';
+
+ if((field = pine_fetchheader_lines(stream, msgno, section, fields)) != NULL){
+ char *h, *t;
+
+ for(t = h = field; *h ; t++)
+ if(*t == '\015' && *(t+1) == '\012'){
+ *t = '\0'; /* tie off line */
+ format_env_puts(h, pc);
+ if(*(h = (++t) + 1)) /* set new h and skip CRLF */
+ gf_puts(NEWLINE, pc); /* more to write */
+ else
+ break;
+ }
+ else if(!*t){ /* shouldn't happen much */
+ if(h != t)
+ format_env_puts(h, pc);
+
+ break;
+ }
+
+ fs_give((void **)&field);
+ }
+
+ fs_give((void **)&fields[0]);
+ gf_puts(NEWLINE, pc);
+ dprint((2, "Error in \"%s\" field address\n",
+ field_name ? field_name : "?"));
+ return;
+ }
+
+ gf_puts(field_name, pc);
+
+ while(addr){
+ atmp = addr->next; /* remember what's next */
+ addr->next = NULL;
+ if(!addr->host && addr->mailbox){
+ mtmp = addr->mailbox;
+ addr->mailbox = cpystr((char *)rfc1522_decode_to_utf8(
+ (unsigned char *)tmp_20k_buf,
+ SIZEOF_20KBUF, addr->mailbox));
+ }
+
+ ptmp = addr->personal; /* RFC 1522 personal name? */
+ addr->personal = iutf8ncpy((char *)tmp_20k_buf, (char *)rfc1522_decode_to_utf8((unsigned char *)(tmp_20k_buf+10000), SIZEOF_20KBUF-10000, addr->personal), 10000);
+ tmp_20k_buf[10000-1] = '\0';
+
+ if(!trailing) /* 1st pass, just address */
+ trailing++;
+ else{ /* else comma, unless */
+ if(!((group == 1 && addr->host) /* 1st addr in group, */
+ || (!addr->host && !addr->mailbox))){ /* or end of group */
+ gf_puts(",", pc);
+#if 0
+ gf_puts(NEWLINE, pc); /* ONE address/line please */
+ gf_puts(" ", pc);
+#endif
+ }
+
+ gf_puts(" ", pc);
+ }
+
+ pine_rfc822_write_address_noquote(addr, pc, &group);
+
+ addr->personal = ptmp; /* restore old personal ptr */
+ if(!addr->host && addr->mailbox){
+ fs_give((void **)&addr->mailbox);
+ addr->mailbox = mtmp;
+ }
+
+ addr->next = atmp;
+ addr = atmp;
+ }
+
+ gf_puts(NEWLINE, pc);
+}
+
+
+
+
+const char *rspecials_minus_quote_and_dot = "()<>@,;:\\[]";
+ /* RFC822 continuation, must start with CRLF */
+#define RFC822CONT "\015\012 "
+
+/* Write RFC822 address with some quoting turned off.
+ * Accepts:
+ * address to interpret
+ *
+ * (This is a copy of c-client's rfc822_write_address except
+ * we don't quote double quote and dot in personal names. It writes
+ * to a gf_io_t instead of to a buffer so that we don't have to worry
+ * about fixed sized buffer overflowing. It's also special cased to deal
+ * with only a single address.)
+ *
+ * The idea is that there are some places where we'd just like to display
+ * the personal name as is before applying confusing quoting. However,
+ * we do want to be careful not to break things that should be quoted so
+ * we'll only use this where we are sure. Quoting may look ugly but it
+ * doesn't usually break anything.
+ */
+void
+pine_rfc822_write_address_noquote(struct mail_address *adr, gf_io_t pc, int *group)
+{
+ extern const char *rspecials;
+
+ if (adr->host) { /* ordinary address? */
+ if (!(adr->personal || adr->adl)) pine_rfc822_address (adr, pc);
+ else { /* no, must use phrase <route-addr> form */
+ if (adr->personal)
+ pine_rfc822_cat (adr->personal, rspecials_minus_quote_and_dot, pc);
+
+ gf_puts(" <", pc); /* write address delimiter */
+ pine_rfc822_address(adr, pc);
+ gf_puts (">", pc); /* closing delimiter */
+ }
+
+ if(*group)
+ (*group)++;
+ }
+ else if (adr->mailbox) { /* start of group? */
+ /* yes, write group name */
+ pine_rfc822_cat (adr->mailbox, rspecials, pc);
+
+ gf_puts (": ", pc); /* write group identifier */
+ *group = 1; /* in a group */
+ }
+ else if (*group) { /* must be end of group (but be paranoid) */
+ gf_puts (";", pc);
+ *group = 0; /* no longer in that group */
+ }
+}
+
+
+/* Write RFC822 route-address to string
+ * Accepts:
+ * address to interpret
+ */
+
+void
+pine_rfc822_address(struct mail_address *adr, gf_io_t pc)
+{
+ extern char *wspecials;
+
+ if (adr && adr->host) { /* no-op if no address */
+ if (adr->adl) { /* have an A-D-L? */
+ gf_puts (adr->adl, pc);
+ gf_puts (":", pc);
+ }
+ /* write mailbox name */
+ pine_rfc822_cat (adr->mailbox, wspecials, pc);
+ if (*adr->host != '@') { /* unless null host (HIGHLY discouraged!) */
+ gf_puts ("@", pc); /* host delimiter */
+ gf_puts (adr->host, pc); /* write host name */
+ }
+ }
+}
+
+
+/* Concatenate RFC822 string
+ * Accepts:
+ * pointer to string to concatenate
+ * list of special characters
+ */
+
+void
+pine_rfc822_cat(char *src, const char *specials, gf_io_t pc)
+{
+ char *s;
+
+ if (strpbrk (src,specials)) { /* any specials present? */
+ gf_puts ("\"", pc); /* opening quote */
+ /* truly bizarre characters in there? */
+ while ((s = strpbrk (src,"\\\"")) != NULL) {
+ char save[2];
+
+ /* turn it into a null-terminated piece */
+ save[0] = *s;
+ save[1] = '\0';
+ *s = '\0';
+ gf_puts (src, pc); /* yes, output leader */
+ *s = save[0];
+ gf_puts ("\\", pc); /* quoting */
+ gf_puts (save, pc); /* output the bizarre character */
+ src = ++s; /* continue after the bizarre character */
+ }
+ if (*src) gf_puts (src, pc);/* output non-bizarre string */
+ gf_puts ("\"", pc); /* closing quote */
+ }
+ else gf_puts (src, pc); /* otherwise it's the easy case */
+}
+
+
+/*----------------------------------------------------------------------
+ Format an address field, wrapping lines nicely at commas
+
+ Args: field_name -- The name of the field we're formatting ("TO:", Cc:...)
+ newsgrps -- ADDRESS structure to format
+
+ Result: A formatted, malloced string is returned.
+
+The resuling lines formatted are 80 columns wide.
+ ----------------------------------------------------------------------*/
+void
+format_newsgroup_string(char *field_name, char *newsgrps, int flags, gf_io_t pc)
+{
+ char buf[MAILTMPLEN];
+ int trailing = 0, llen, alen;
+ char *next_ng;
+
+ if(!newsgrps || !*newsgrps)
+ return;
+
+ gf_puts(field_name, pc);
+
+ llen = strlen(field_name);
+ while(*newsgrps){
+ for(next_ng = newsgrps; *next_ng && *next_ng != ','; next_ng++);
+ strncpy(buf, newsgrps, MIN(next_ng - newsgrps, sizeof(buf)-1));
+ buf[MIN(next_ng - newsgrps, sizeof(buf)-1)] = '\0';
+ newsgrps = next_ng;
+ if(*newsgrps)
+ newsgrps++;
+ alen = strlen(buf);
+ if(!trailing){ /* first time thru, just address */
+ llen += alen;
+ trailing++;
+ }
+ else{ /* else preceding comma */
+ gf_puts(",", pc);
+ llen++;
+
+ if(alen + llen + 1 > 76){
+ gf_puts(NEWLINE, pc);
+ gf_puts(" ", pc);
+ llen = alen + 5;
+ }
+ else{
+ gf_puts(" ", pc);
+ llen += alen + 1;
+ }
+ }
+
+ if(alen && llen > 76){ /* handle long addresses */
+ register char *q, *p = &buf[alen-1];
+
+ while(p > buf){
+ if(isspace((unsigned char)*p)
+ && (llen - (alen - (int)(p - buf))) < 76){
+ for(q = buf; q < p; q++)
+ (*pc)(*q); /* write character */
+
+ gf_puts(NEWLINE, pc);
+ gf_puts(" ", pc);
+ gf_puts(p, pc);
+ break;
+ }
+ else
+ p--;
+ }
+
+ if(p == buf) /* no reasonable break point */
+ gf_puts(buf, pc);
+ }
+ else
+ gf_puts(buf, pc);
+ }
+
+ gf_puts(NEWLINE, pc);
+}
+
+
+
+/*----------------------------------------------------------------------
+ Format a text field that's part of some raw (non-envelope) message header
+
+ Args: start --
+ finish --
+ pc --
+
+ Result: Semi-digested text (RFC 1522 decoded, anyway) written with "pc"
+
+ ----------------------------------------------------------------------*/
+int
+format_raw_hdr_string(char *start, char *finish, gf_io_t pc, char *oacs, int flags)
+{
+ register char *current;
+ unsigned char *p, *tmp = NULL, c;
+ size_t n, len;
+ char ch;
+ int rv = FHT_OK;
+
+ ch = *finish;
+ *finish = '\0';
+
+ if((n = 4*(finish-start)) > SIZEOF_20KBUF-1){
+ len = n+1;
+ p = tmp = (unsigned char *) fs_get(len * sizeof(unsigned char));
+ }
+ else{
+ len = SIZEOF_20KBUF;
+ p = (unsigned char *) tmp_20k_buf;
+ }
+
+ if(islower((unsigned char)(*start)))
+ *start = toupper((unsigned char)(*start));
+
+ current = (char *) rfc1522_decode_to_utf8(p, len, start);
+
+ /* output from start to finish */
+ while(*current && rv == FHT_OK)
+ if(ISRFCEOL(current)){
+ if(!gf_puts(NEWLINE, pc))
+ rv = FHT_WRTERR;
+
+ current += 2;
+ }
+ else if((unsigned char)(*current) < 0x80 && FILTER_THIS(*current) &&
+ !(*(current+1) && *current == ESCAPE && match_escapes(current+1))){
+ c = (unsigned char) *current++;
+ if(!((*pc)(c >= 0x80 ? '~' : '^')
+ && (*pc)((c == 0x7f) ? '?' : (c & 0x1f) + '@')))
+ rv = FHT_WRTERR;
+ }
+ else if(!(*pc)(*current++))
+ rv = FHT_WRTERR;
+
+ if(tmp)
+ fs_give((void **) &tmp);
+
+ *finish = ch;
+
+ return(rv);
+}
+
+
+
+
+/*----------------------------------------------------------------------
+ Format a text field that's part of some raw (non-envelope) message header
+
+ Args: s --
+ pc --
+
+ Result: Output
+
+ ----------------------------------------------------------------------*/
+int
+format_env_puts(char *s, gf_io_t pc)
+{
+ if(ps_global->pass_ctrl_chars)
+ return(gf_puts(s, pc));
+
+ for(; *s; s++)
+ if((unsigned char)(*s) < 0x80 && FILTER_THIS(*s) && !(*(s+1) && *s == ESCAPE && match_escapes(s+1))){
+ if(!((*pc)((unsigned char) (*s) >= 0x80 ? '~' : '^')
+ && (*pc)((*s == 0x7f) ? '?' : (*s & 0x1f) + '@')))
+ return(0);
+ }
+ else if(!(*pc)(*s))
+ return(0);
+
+ return(1);
+}
+
+
+char *
+display_parameters(PARAMETER *params)
+{
+ int n, longest = 0;
+ char *d, *printme;
+ PARAMETER *p;
+ PARMLIST_S *parmlist;
+
+ for(p = params; p; p = p->next) /* ok if we include *'s */
+ if(p->attribute && (n = strlen(p->attribute)) > longest)
+ longest = MIN(32, n); /* shouldn't be any bigger than 32 */
+
+ d = tmp_20k_buf;
+ tmp_20k_buf[0] = '\0';
+ if((parmlist = rfc2231_newparmlist(params)) != NULL){
+ n = 0; /* n overloaded */
+ while(rfc2231_list_params(parmlist) && d < tmp_20k_buf + 10000){
+ if(n++){
+ snprintf(d, 10000-(d-tmp_20k_buf), "\n");
+ tmp_20k_buf[10000-1] = '\0';
+ d += strlen(d);
+ }
+
+ if(parmlist->value){
+ if(parmlist->attrib && strucmp(parmlist->attrib, "url") == 0){
+ snprintf(printme = tmp_20k_buf + 11000, 1000, "%s", parmlist->value);
+ sqzspaces(printme);
+ }
+ else
+ printme = strsquish(tmp_20k_buf + 11000, 1000, parmlist->value, 100);
+ }
+ else
+ printme = "";
+
+ snprintf(d, 10000-(d-tmp_20k_buf), "%-*s: %s", longest,
+ parmlist->attrib ? parmlist->attrib : "", printme);
+
+ tmp_20k_buf[10000-1] = '\0';
+ d += strlen(d);
+ }
+
+ rfc2231_free_parmlist(&parmlist);
+ }
+
+ return(tmp_20k_buf);
+}
+
+
+/*----------------------------------------------------------------------
+ Fetch the requested header fields from the msgno specified
+
+ Args: stream -- mail stream of open folder
+ msgno -- number of message to get header lines from
+ fields -- array of pointers to desired fields
+
+ Returns: allocated string containing matched header lines,
+ NULL on error.
+ ----*/
+char *
+pine_fetch_header(MAILSTREAM *stream, long int msgno, char *section, char **fields, long int flags)
+{
+ STRINGLIST *sl;
+ char *p, *m, *h = NULL, *match = NULL, *free_this, tmp[MAILTMPLEN];
+ char **pflds = NULL, **pp = NULL, **qq;
+
+ /*
+ * If the user misconfigures it is possible to have one of the fields
+ * set to the empty string instead of a header name. We want to catch
+ * that here instead of asking the server the nonsensical question.
+ */
+ for(pp = fields ? &fields[0] : NULL; pp && *pp; pp++)
+ if(!**pp)
+ break;
+
+ if(pp && *pp){ /* found an empty header field, fix it */
+ pflds = copy_list_array(fields);
+ for(pp = pflds; pp && *pp; pp++){
+ if(!**pp){ /* scoot rest of the lines up */
+ free_this = *pp;
+ for(qq = pp; *qq; qq++)
+ *qq = *(qq+1);
+
+ if(free_this)
+ fs_give((void **) &free_this);
+ }
+ }
+
+ /* no headers to look for, return NULL */
+ if(pflds && !*pflds && !(flags & FT_NOT)){
+ free_list_array(&pflds);
+ return(NULL);
+ }
+ }
+ else
+ pflds = fields;
+
+ sl = (pflds && *pflds) ? new_strlst(pflds) : NULL; /* package up fields */
+ h = mail_fetch_header(stream, msgno, section, sl, NULL, flags | FT_PEEK);
+ if (sl)
+ free_strlst(&sl);
+
+ if(!h){
+ if(pflds && pflds != fields)
+ free_list_array(&pflds);
+
+ return(NULL);
+ }
+
+ while(find_field(&h, tmp, sizeof(tmp))){
+ for(pp = &pflds[0]; *pp && strucmp(tmp, *pp); pp++)
+ ;
+
+ /* interesting field? */
+ if((p = (flags & FT_NOT) ? ((*pp) ? NULL : tmp) : *pp) != NULL){
+ /*
+ * Hold off allocating space for matching fields until
+ * we at least find one to copy...
+ */
+ if(!match)
+ match = m = fs_get(strlen(h) + strlen(p) + 1);
+
+ while(*p) /* copy field name */
+ *m++ = *p++;
+
+ while(*h && (*m++ = *h++)) /* header includes colon */
+ if(*(m-1) == '\n' && (*h == '\r' || !isspace((unsigned char)*h)))
+ break;
+
+ *m = '\0'; /* tie off match string */
+ }
+ else{ /* no match, pass this field */
+ while(*h && !(*h++ == '\n'
+ && (*h == '\r' || !isspace((unsigned char)*h))))
+ ;
+ }
+ }
+
+ if(pflds && pflds != fields)
+ free_list_array(&pflds);
+
+ return(match ? match : cpystr(""));
+}
+
+
+int
+find_field(char **h, char *tmp, size_t ntmp)
+{
+ char *otmp = tmp;
+
+ if(!h || !*h || !**h || isspace((unsigned char)**h))
+ return(0);
+
+ while(tmp-otmp<ntmp-1 && **h && **h != ':' && !isspace((unsigned char)**h))
+ *tmp++ = *(*h)++;
+
+ *tmp = '\0';
+ return(1);
+}
+
+
+static char *_last_embedded_fg_color, *_last_embedded_bg_color;
+
+
+int
+embed_color(COLOR_PAIR *cp, gf_io_t pc)
+{
+ if(cp && cp->fg){
+ if(_last_embedded_fg_color)
+ fs_give((void **)&_last_embedded_fg_color);
+
+ _last_embedded_fg_color = cpystr(cp->fg);
+
+ if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_FGCOLOR) &&
+ gf_puts(color_to_asciirgb(cp->fg), pc)))
+ return 0;
+ }
+
+ if(cp && cp->bg){
+ if(_last_embedded_bg_color)
+ fs_give((void **)&_last_embedded_bg_color);
+
+ _last_embedded_bg_color = cpystr(cp->bg);
+
+ if(!(pc && (*pc)(TAG_EMBED) && (*pc)(TAG_BGCOLOR) &&
+ gf_puts(color_to_asciirgb(cp->bg), pc)))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+COLOR_PAIR *
+get_cur_embedded_color(void)
+{
+ COLOR_PAIR *ret;
+
+ if(_last_embedded_fg_color && _last_embedded_bg_color)
+ ret = new_color_pair(_last_embedded_fg_color, _last_embedded_bg_color);
+ else
+ ret = pico_get_cur_color();
+
+ return(ret);
+}
+
+
+void
+clear_cur_embedded_color(void)
+{
+ if(_last_embedded_fg_color)
+ fs_give((void **)&_last_embedded_fg_color);
+
+ if(_last_embedded_bg_color)
+ fs_give((void **)&_last_embedded_bg_color);
+}
+
+
+int
+scroll_handle_start_color(char *colorstring, size_t buflen, int *len)
+{
+ *len = 0;
+
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+
+ if(ps_global->VAR_SLCTBL_FORE_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_FORE_COLOR,
+ ps_global->VAR_NORM_FORE_COLOR))
+ fg = ps_global->VAR_SLCTBL_FORE_COLOR;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR
+ && colorcmp(ps_global->VAR_SLCTBL_BACK_COLOR,
+ ps_global->VAR_NORM_BACK_COLOR))
+ bg = ps_global->VAR_SLCTBL_BACK_COLOR;
+
+ if(bg || fg){
+ COLOR_PAIR *tmp;
+
+ /*
+ * The blacks are just known good colors for
+ * testing whether the other color is good.
+ */
+ if((tmp = new_color_pair(fg ? fg : colorx(COL_BLACK),
+ bg ? bg : colorx(COL_BLACK))) != NULL){
+ if(pico_is_good_colorpair(tmp))
+ for(s = color_embed(fg, bg);
+ (*len) < buflen && (colorstring[*len] = *s);
+ s++, (*len)++)
+ ;
+
+ free_color_pair(&tmp);
+ }
+ }
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring + (*len), url_embed(TAG_BOLDON), MIN(3,buflen-(*len)));
+ *len += 2;
+ }
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+int
+scroll_handle_end_color(char *colorstring, size_t buflen, int *len, int use_hdr_color)
+{
+ *len = 0;
+ if(pico_usingcolor()){
+ char *fg = NULL, *bg = NULL, *s;
+ char *basefg = NULL, *basebg = NULL;
+
+ basefg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_FORE_COLOR
+ : ps_global->VAR_NORM_FORE_COLOR;
+ basebg = use_hdr_color ? ps_global->VAR_HEADER_GENERAL_BACK_COLOR
+ : ps_global->VAR_NORM_BACK_COLOR;
+
+ /*
+ * We need to change the fg and bg colors back even if they
+ * are the same as the Normal Colors so that color_a_quote
+ * will have a chance to pick up those colors as the ones to
+ * switch to. We don't do this before the handle above so that
+ * the quote color will flow into the selectable item when
+ * the selectable item color is partly the same as the
+ * normal color. That is, suppose the normal color was black on
+ * cyan and the selectable color was blue on cyan, only a fg color
+ * change. We preserve the only-a-fg-color-change in a quote by
+ * letting the quote background color flow into the selectable text.
+ */
+ if(ps_global->VAR_SLCTBL_FORE_COLOR)
+ fg = basefg;
+
+ if(ps_global->VAR_SLCTBL_BACK_COLOR)
+ bg = basebg;
+
+ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){
+ strncpy(colorstring, url_embed(TAG_BOLDOFF), MIN(3,buflen));
+ *len = 2;
+ }
+
+ if(fg || bg)
+ for(s = color_embed(fg, bg); (*len) < buflen && (colorstring[*len] = *s); s++, (*len)++)
+ ;
+ }
+
+ colorstring[buflen-1] = '\0';
+
+ return(*len != 0);
+}
+
+
+/*
+ * Helper routine that is of limited use.
+ * We need to tally up the screen width of
+ * a UTF-8 string as we go through the string.
+ * We just want the width of the character starting
+ * at str (and no longer than remaining_octets).
+ * If we're plopped into the middle of a UTF-8
+ * character we just want to return width zero.
+ */
+int
+width_at_this_position(unsigned char *str, unsigned long n)
+{
+ unsigned char *inputp = str;
+ unsigned long remaining_octets = n;
+ UCS ucs;
+ int width = 0;
+
+ ucs = (UCS) utf8_get(&inputp, &remaining_octets);
+ if(!(ucs & U8G_ERROR || ucs == UBOGON)){
+ width = wcellwidth(ucs);
+ /* Writechar will print a '?' */
+ if(width < 0)
+ width = 1;
+ }
+
+ return(width);
+}