/* * ======================================================================== * Copyright 2013-2022 Eduardo Chappa * Copyright 2006-2009 University of Washington * * 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/ical.h" #include "../pith/body.h" #include "../pith/mailpart.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); void format_calendar_vevent(VCALENDAR_S *, ATTACH_S *, HANDLE_S **, int, int, gf_io_t, int); /*---------------------------------------------------------------------- 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, rv = 0; 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); format_calendar(msgno, body, handlesp, flgs, width, pc); } /* write delimiter and body */ if(gf_puts(NEWLINE, pc)){ if((decode_err = format_body(msgno, body, handlesp, &h, flgs, width, pc)) == NULL) rv = 1; else rv = 0; } clear_cur_embedded_color(); return(rv); write_error: if(!(flgs & FM_DISPLAY)) q_status_message1(SM_ORDER, 3, 4, _("Error writing message: %s"), decode_err ? decode_err : error_description(errno)); clear_cur_embedded_color(); return(0); } void format_calendar_vevent(VCALENDAR_S *vcal, ATTACH_S *a, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc, int cflags) { int avail, m1, m2, hwid, i, partwid, padwid; int s1, s2, dwid, minkey; int *margin; char padding[1024]; VEVENT_SUMMARY_S *vesy, *vesummary; /* vevent summary */ vesummary = vesy = ical_vevent_summary(vcal); if(vesy == NULL) return; if((cflags & FC_SUMMARY) && (cflags & FC_FULL)) cflags |= ~FC_FULL; minkey = -1; /* initialize to something negative */ for(; vesy != NULL ; vesy = vesy->next){ avail = width; margin = (cflags & FC_FULL) ? NULL : (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; hwid = MAX(avail, 0); padwid = 0; if(ps_global->atmts[1].description == NULL){ avail = width - m1 -2; dwid = MAX(MIN(40, avail), 0); snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "", repeat_char(dwid, '-')); if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc)) return; } if(cflags & FC_SUMMARY){ i = utf8_width(_("Calendar Entry:")); partwid = MIN(i, hwid); if(m1 > 0){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, ""); if(!gf_puts(tmp_20k_buf, pc)) return; } utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%-*.*w%*.*s", partwid, partwid, _("Calendar Entry:"), padwid, padwid, ""); if(!gf_puts(tmp_20k_buf, pc) || !gf_puts(NEWLINE, pc)) return; } else partwid = 0; if(m1 > 0){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", m1, m1, ""); if(!gf_puts(tmp_20k_buf, pc)) return; } 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; if(cflags & FC_SUMMARY) utf8_snprintf(padding, sizeof(padding), "%*.*s%*.*w%*.*s", s1, s1, "", dwid, dwid, "", s2, s2, ""); else padding[0] = '\0'; if(vesy->cancel){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", padding, _("This event was cancelled!")); if((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)){ gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); (*pc)(TAG_EMBED); (*pc)(TAG_BOLDOFF); } } if(vesy->organizer){ if(vesy->sender != NULL){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Sent-by: "), vesy->sender); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Organizer: "), vesy->organizer); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } /* end of if(organizer) */ if(vesy->location){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Location: "), vesy->location); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } /* end of if location */ if(vesy->evstart){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Start Date: "), vesy->evstart); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } /* end of if dtstart */ if(vesy->duration){ int i; for(i = 0; vesy->duration[i] != NULL; i++){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Duration: "), vesy->duration[i]); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } } /* end of DURATION */ else if(vesy->evend){ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("End Date: "), vesy->evend); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } else { /* end of if dtend */ utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s", padding, _("No duration nor end time found for this event")); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } /* end of else for if (duration) and if (dtend) */ if(vesy->attendee){ #define MAX_DISPLAYED 3 int i; for(i = 0; vesy->attendee[i] != NULL; i++){ if((cflags & FC_SUMMARY) && i >= MAX_DISPLAYED) break; utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s%s%s", padding, _("Attendee: "), vesy->attendee[i]); gf_puts(tmp_20k_buf, pc); gf_puts(NEWLINE, pc); } } /* end of ATTENDEES */ if(cflags & FC_SUMMARY){ COLOR_PAIR *lastc = NULL; 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); } } if(!(!hdrcolor || embed_color(hdrcolor, pc))) return; gf_puts(padding, pc); utf8_snprintf(tmp_20k_buf, SIZEOF_20KBUF, "[%s]", _("More Details")); if(handlesp){ char buf[16], color[64]; int l; HANDLE_S *h; h = new_handle(handlesp); if(minkey < 0) minkey = h->key; h->type = iCal; h->h.ical.attach = a; h->h.ical.depth = h->key - minkey; 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; } else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global) && (!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDON)))) return; if(!((*pc)(TAG_EMBED) && (*pc)(TAG_HANDLE) && (*pc)(strlen(buf)) && gf_puts(buf, pc))) return; } else tmp_20k_buf[0] = '\0'; if(!format_env_puts(tmp_20k_buf, pc)) return; if(handlesp){ if(lastc){ if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){ if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF))) return; } if(!embed_color(lastc, pc)) return; free_color_pair(&lastc); } else if(!((*pc)(TAG_EMBED) && (*pc)(TAG_BOLDOFF))) return; if(!((*pc)(TAG_EMBED) && (*pc)(TAG_INVOFF))) return; } if(padwid > 0){ snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s", padwid, padwid, ""); if(!gf_puts(tmp_20k_buf, pc)) return; } if(!gf_puts(NEWLINE, pc)) return; } avail = width - m1 -2; dwid = MAX(MIN(40, avail), 0); avail -= dwid; snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*.*s%s", m1, m1, "", repeat_char(dwid, '-')); gf_puts(tmp_20k_buf, pc); if(vesy->next) gf_puts(NEWLINE, pc); } free_vevent_summary(&vesummary); } int format_calendar(long int msgno, BODY *body, HANDLE_S **handlesp, int flgs, int width, gf_io_t pc) { char *rawtext, *caltext; unsigned long callen; VCALENDAR_S *vcal = NULL; ATTACH_S *a; BODY *b; if(flgs & FM_NEW_MESS) { zero_atmts(ps_global->atmts); describe_mime(body, "", 1, 1, 0, flgs); } for(a = ps_global->atmts; a->description != NULL; a++){ if(MIME_VCALENDAR(a->body->type, a->body->subtype)){ b = mail_body (ps_global->mail_stream, msgno, (unsigned char *) a->number); if(b == NULL){ gf_puts(_("Error fetching calendar body part"), pc); gf_puts(NEWLINE, pc); continue; } if(b->sparep == NULL){ rawtext = mail_fetch_body(ps_global->mail_stream, msgno, a->number, &callen, 0); if(rawtext == NULL || *rawtext == '\0'){ gf_puts(_("Error fetching calendar text"), pc); gf_puts(NEWLINE, pc); continue; } rawtext[callen] = '\0'; /* chop off cookie */ switch(b->encoding){ case ENCBASE64: caltext = rfc822_base64((unsigned char *) rawtext, strlen(rawtext), &callen); if(caltext == NULL){ gf_puts(_("Error in calendar base64 encoding"), pc); gf_puts(NEWLINE, pc); continue; } caltext[callen] = '\0'; break; case ENCQUOTEDPRINTABLE: caltext = rfc822_qprint ((unsigned char *) rawtext,strlen(rawtext),&callen); if(caltext == NULL){ gf_puts(_("Error in calendar quoted printable encoding"), pc); gf_puts(NEWLINE, pc); continue; } caltext[callen] = '\0'; break; default: caltext = cpystr(rawtext); } if(caltext != NULL){ vcal = ical_parse_text(caltext); if(vcal != NULL) vcal->encoding = b->encoding; b->sparep = create_body_sparep(iCalType, (void *) vcal); fs_give((void **) &caltext); } } else if(get_body_sparep_type(b->sparep) == iCalType) vcal = (VCALENDAR_S *) get_body_sparep_data(b->sparep); if(vcal != NULL && vcal->comp != NULL){ if(vcal->comp[VEvent] != NULL){ format_calendar_vevent(vcal, a, handlesp, flgs, width, pc, FC_SUMMARY); } /* else another type of entry in the calendar */ } gf_puts(NEWLINE, pc); } } 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 = cpystr(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)); } if (charset) fs_give((void **) &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(MIME_VCALENDAR(a->body->type, a->body->subtype)) continue; 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)){ if(MIME_VCALENDAR(a->body->type, a->body->subtype)) tmp1 = "Calendar entry"; else 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"); } /* skip calendar */ if(!MIME_VCALENDAR(a->body->type, a->body->subtype)) 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(MIME_VCALENDAR(a->body->type, a->body->subtype)) continue; 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; if(MIME_VCALENDAR(a->body->type, a->body->subtype)) continue; #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 occurrence 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, "* * * HANDLER 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 : "", 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 occurrences 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,"message_id,"message_id,"message_id,"message_id,"message_id,"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 = NULL; 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 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 resulting 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-otmpfg){ 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); }