diff options
Diffstat (limited to 'pith/text.c')
-rw-r--r-- | pith/text.c | 964 |
1 files changed, 964 insertions, 0 deletions
diff --git a/pith/text.c b/pith/text.c new file mode 100644 index 00000000..5de53e51 --- /dev/null +++ b/pith/text.c @@ -0,0 +1,964 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: text.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2008 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 + * + * ======================================================================== + */ + +/*====================================================================== + + text.c + Implements message text fetching and decoding + + ====*/ + + +#include "../pith/headers.h" +#include "../pith/text.h" +#include "../pith/state.h" +#include "../pith/conf.h" +#include "../pith/handle.h" +#include "../pith/margin.h" +#include "../pith/editorial.h" +#include "../pith/color.h" +#include "../pith/filter.h" +#include "../pith/charset.h" +#include "../pith/status.h" +#include "../pith/mailview.h" +#include "../pith/mimedesc.h" +#include "../pith/detach.h" + + +/* internal prototypes */ +int charset_editorial(char *, long, HANDLE_S **, int, int, int, gf_io_t); +int replace_quotes(long, char *, LT_INS_S **, void *); +void mark_handles_in_line(char *, HANDLE_S **, int); +void clear_html_risk(void); +void set_html_risk(void); +int get_html_risk(void); + + +#define CHARSET_DISCLAIMER_1 \ + "The following text is in the \"%.40s\" character set.\015\012Your " +#define CHARSET_DISCLAIMER_2 "display is set" +#define CHARSET_DISCLAIMER_3 \ + " for the \"%.40s\" character set. \015\012Some %.40scharacters may be displayed incorrectly." +#define ENCODING_DISCLAIMER \ + "The following text contains the unknown encoding type \"%.20s\". \015\012Some or all of the text may be displayed incorrectly." +#define HTML_WARNING \ + "The following HTML text may contain deceptive links. Carefully note the destination URL before visiting any links." + + +/* + * HTML risk state holder. + */ +static int _html_risk; + + + +/*---------------------------------------------------------------------- + Handle fetching and filtering a text message segment to be displayed + by scrolltool or printed or exported or piped. + +Args: att -- segment to fetch + msgno -- message number segment is a part of + pc -- function to write characters from segment with + style -- Indicates special handling for error messages + flags -- Indicates special necessary handling + +Returns: 1 if errors encountered, 0 if everything went A-OK + + ----*/ +int +decode_text(ATTACH_S *att, + long int msgno, + gf_io_t pc, + HANDLE_S **handlesp, + DetachErrStyle style, + int flags) +{ + FILTLIST_S filters[14]; + char *err, *charset; + int filtcnt = 0, error_found = 0, column, wrapit; + int is_in_sig = OUT_SIG_BLOCK; + int is_flowed_msg = 0; + int is_delsp_yes = 0; + int filt_only_c0 = 0; + char *parmval; + char *free_this = NULL; + STORE_S *warn_so = NULL; + DELQ_S dq; + URL_HILITE_S uh; + + column = (flags & FM_DISPLAY) ? ps_global->ttyo->screen_cols : 80; + + if(!(flags & FM_DISPLAY)) + flags |= FM_NOINDENT; + + wrapit = column; + + memset(filters, 0, sizeof(filters)); + + /* charset the body part is in */ + charset = parameter_val(att->body->parameter, "charset"); + + /* determined if it's flowed, affects wrapping and quote coloring */ + if(att->body->type == TYPETEXT + && !strucmp(att->body->subtype, "plain") + && (parmval = parameter_val(att->body->parameter, "format"))){ + if(!strucmp(parmval, "flowed")) + is_flowed_msg = 1; + fs_give((void **) &parmval); + + if(is_flowed_msg){ + if((parmval = parameter_val(att->body->parameter, "delsp")) != NULL){ + if(!strucmp(parmval, "yes")) + is_delsp_yes = 1; + + fs_give((void **) &parmval); + } + } + } + + if(!ps_global->pass_ctrl_chars){ + filters[filtcnt++].filter = gf_escape_filter; + filters[filtcnt].filter = gf_control_filter; + + filt_only_c0 = 1; + filters[filtcnt++].data = gf_control_filter_opt(&filt_only_c0); + } + + if(flags & FM_DISPLAY) + filters[filtcnt++].filter = gf_tag_filter; + + /* + * if it's just plain old text, look for url's + */ + if(!(att->body->subtype && strucmp(att->body->subtype, "plain"))){ + struct variable *vars = ps_global->vars; + + 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){ + + /* + * The url_hilite filter really ought to come + * after flowing, because flowing with the DelSp=yes parameter + * can reassemble broken urls back into identifiable urls. + * We add the preflow filter to do only the reassembly part + * of the flowing so that we can spot the urls. + * At this time (2005-03-29) we know that Apple Mail does + * send mail like this sometimes. This filter removes the + * sequence SP CRLF if that seems safe. + */ + if(ps_global->full_header != 2 && is_delsp_yes) + filters[filtcnt++].filter = gf_preflow; + + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(url_hilite, + gf_url_hilite_opt(&uh,handlesp,0)); + } + + /* + * First, paint the signature. + * Disclaimers noted below for coloring quotes apply here as well. + */ + if((flags & FM_DISPLAY) + && !(flags & FM_NOCOLOR) + && pico_usingcolor() + && VAR_SIGNATURE_FORE_COLOR + && VAR_SIGNATURE_BACK_COLOR){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(color_signature, + &is_in_sig); + } + + /* + * Gotta be careful with this. The color_a_quote filter adds color + * to the beginning and end of the line. This will break some + * line_test-style filters which come after it. For example, if they + * are looking for something at the start of a line (like color_a_quote + * itself). I guess we could fix that by ignoring tags at the + * beginning of the line when doing the search. + */ + if((flags & FM_DISPLAY) + && !(flags & FM_NOCOLOR) + && pico_usingcolor() + && VAR_QUOTE1_FORE_COLOR + && VAR_QUOTE1_BACK_COLOR){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(color_a_quote, + &is_flowed_msg); + } + } + else if(!strucmp(att->body->subtype, "richtext")){ + int plain_opt; + + plain_opt = !(flags&FM_DISPLAY); + + /* maybe strip everything! */ + filters[filtcnt].filter = gf_rich2plain; + filters[filtcnt++].data = gf_rich2plain_opt(&plain_opt); + /* width to use for file or printer */ + if(wrapit - 5 > 0) + wrapit -= 5; + } + else if(!strucmp(att->body->subtype, "enriched")){ + int plain_opt; + + plain_opt = !(flags&FM_DISPLAY); + + filters[filtcnt].filter = gf_enriched2plain; + filters[filtcnt++].data = gf_enriched2plain_opt(&plain_opt); + /* width to use for file or printer */ + if(wrapit - 5 > 0) + wrapit -= 5; + } + else if(!strucmp(att->body->subtype, "html") && ps_global->full_header < 2){ +/*BUG: sniff the params for "version=2.0" ala draft-ietf-html-spec-01 */ + int opts = 0; + + clear_html_risk(); + + if(flags & FM_DISPLAY){ + gf_io_t warn_pc; + + if(handlesp){ /* pass on handles awareness */ + opts |= GFHP_HANDLES; + + if(F_OFF(F_QUELL_HOST_AFTER_URL, ps_global) && !(flags & FM_HIDESERVER)) + opts |= GFHP_SHOW_SERVER; + } + + if(!(flags & FM_NOEDITORIAL)){ + warn_so = so_get(CharStar, NULL, EDIT_ACCESS); + gf_set_so_writec(&warn_pc, warn_so); + format_editorial(HTML_WARNING, column, flags, handlesp, warn_pc); + gf_clear_so_writec(warn_so); + so_puts(warn_so, "\015\012"); + so_writec('\0', warn_so); + } + } + else + opts |= GFHP_STRIPPED; /* don't embed anything! */ + + if(flags & FM_NOHTMLREL) + opts |= GFHP_NO_RELATIVE; + + if(flags & FM_HTMLRELATED) + opts |= GFHP_RELATED_CONTENT; + + if(flags & FM_HTML) + opts |= GFHP_HTML; + + if(flags & FM_HTMLIMAGES) + opts |= GFHP_HTML_IMAGES; + + wrapit = 0; /* wrap already handled! */ + filters[filtcnt].filter = gf_html2plain; + filters[filtcnt++].data = gf_html2plain_opt(NULL, column, + (flags & (FM_NOINDENT | FM_HTML)) + ? 0 + : format_view_margin(), + handlesp, set_html_risk, opts); + + if(warn_so){ + filters[filtcnt].filter = gf_prepend_editorial; + filters[filtcnt++].data = gf_prepend_editorial_opt(get_html_risk, + (char *) so_text(warn_so)); + } + } + + /* + * If the message is not flowed, we do the quote suppression before + * the wrapping, because the wrapping does not preserve the quote + * characters at the beginnings of the lines in that case. + * Otherwise, we defer until after the wrapping. + * + * Also, this is a good place to do quote-replacement on nonflowed + * messages because no other filters depend on the "> ". + * Quote-replacement is easier in the flowed case and occurs + * automatically in the flowed wrapping filter. + */ + if(!is_flowed_msg + && ps_global->full_header == 0 + && !(att->body->subtype && strucmp(att->body->subtype, "plain")) + && (flags & FM_DISPLAY)){ + if(ps_global->quote_suppression_threshold != 0){ + memset(&dq, 0, sizeof(dq)); + dq.lines = ps_global->quote_suppression_threshold; + dq.is_flowed = is_flowed_msg; + dq.indent_length = 0; /* indent didn't happen yet */ + dq.saved_line = &free_this; + dq.handlesp = handlesp; + dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); + + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); + } + if(ps_global->VAR_QUOTE_REPLACE_STRING + && F_ON(F_QUOTE_REPLACE_NOFLOW, ps_global)){ + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(replace_quotes, NULL); + } + } + + if(wrapit && !(flags & FM_NOWRAP)){ + int wrapflags = (flags & FM_DISPLAY) ? (GFW_HANDLES|GFW_SOFTHYPHEN) + : GFW_NONE; + + if(flags & FM_DISPLAY + && !(flags & FM_NOCOLOR) + && pico_usingcolor()) + wrapflags |= GFW_USECOLOR; + + /* text/flowed (RFC 2646 + draft)? */ + if(ps_global->full_header != 2 && is_flowed_msg){ + wrapflags |= GFW_FLOWED; + + if(is_delsp_yes) + wrapflags |= GFW_DELSP; + } + + filters[filtcnt].filter = gf_wrap; + filters[filtcnt++].data = gf_wrap_filter_opt(wrapit, column, + (flags & FM_NOINDENT) + ? 0 + : format_view_margin(), + 0, wrapflags); + } + + /* + * This has to come after wrapping has happened because the user tells + * us how many quoted lines to display, and we want that number to be + * the wrapped lines, not the pre-wrapped lines. We do it before the + * wrapping if the message is not flowed, because the wrapping does not + * preserve the quote characters at the beginnings of the lines in that + * case. + */ + if(is_flowed_msg + && ps_global->full_header == 0 + && !(att->body->subtype && strucmp(att->body->subtype, "plain")) + && (flags & FM_DISPLAY) + && ps_global->quote_suppression_threshold != 0){ + memset(&dq, 0, sizeof(dq)); + dq.lines = ps_global->quote_suppression_threshold; + dq.is_flowed = is_flowed_msg; + dq.indent_length = (wrapit && !(flags & FM_NOWRAP) + && !(flags & FM_NOINDENT)) + ? format_view_margin()[0] + : 0; + dq.saved_line = &free_this; + dq.handlesp = handlesp; + dq.do_color = (!(flags & FM_NOCOLOR) && pico_usingcolor()); + + filters[filtcnt].filter = gf_line_test; + filters[filtcnt++].data = gf_line_test_opt(delete_quotes, &dq); + } + + if(charset){ + if(F_OFF(F_QUELL_CHARSET_WARNING, ps_global) + && !(flags & FM_NOEDITORIAL)){ + int rv = TRUE; + CONV_TABLE *ct = NULL; + + /* + * Need editorial if message charset is not ascii + * and the display charset is not either the same + * as the message charset or UTF-8. + */ + if(strucmp(charset, "us-ascii") + && !((ps_global->display_charmap + && !strucmp(charset, ps_global->display_charmap)) + || !strucmp("UTF-8", ps_global->display_charmap))){ + + /* + * This is probably overkill. We're just using this + * conversion_table routine to get at the quality. + */ + if(ps_global->display_charmap) + ct = conversion_table(charset, ps_global->display_charmap); + + rv = charset_editorial(charset, msgno, handlesp, flags, + ct ? ct->quality : CV_NO_TRANSLATE_POSSIBLE, + column, pc); + } + if(!rv) + goto write_error; + } + + fs_give((void **) &charset); + } + + /* Format editorial comment about unknown encoding */ + if(att->body->encoding > ENCQUOTEDPRINTABLE){ + char buf[2048]; + + snprintf(buf, sizeof(buf), ENCODING_DISCLAIMER, body_encodings[att->body->encoding]); + + if(!(format_editorial(buf, column, flags, handlesp, pc) == NULL + && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc))) + goto write_error; + } + + err = detach(ps_global->mail_stream, msgno, att->number, 0L, + NULL, pc, filtcnt ? filters : NULL, 0); + + delete_unused_handles(handlesp); + + if(free_this) + fs_give((void **) &free_this); + + if(warn_so) + so_give(&warn_so); + + if(err) { + error_found++; + if(style == QStatus) { + q_status_message1(SM_ORDER, 3, 4, "%.200s", err); + } else if(style == InLine) { + char buftmp[MAILTMPLEN]; + + snprintf(buftmp, sizeof(buftmp), "%s", + att->body->description ? att->body->description : ""); + snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%s [Error: %s] %c%s%c%s%s", + NEWLINE, err, + att->body->description ? '\"' : ' ', + att->body->description ? buftmp : "", + att->body->description ? '\"' : ' ', + NEWLINE, NEWLINE); + if(!gf_puts(tmp_20k_buf, pc)) + goto write_error; + } + } + + if(att->body->subtype + && (!strucmp(att->body->subtype, "richtext") + || !strucmp(att->body->subtype, "enriched")) + && !(flags & FM_DISPLAY)){ + if(!gf_puts(NEWLINE, pc) || !gf_puts(NEWLINE, pc)) + goto write_error; + } + + return(error_found); + + write_error: + if(style == QStatus) + q_status_message1(SM_ORDER, 3, 4, "Error writing message: %.200s", + error_description(errno)); + + return(1); +} + + + +/* + * Format editorial comment about charset mismatch + */ +int +charset_editorial(char *charset, long int msgno, HANDLE_S **handlesp, int flags, + int quality, int width, gf_io_t pc) +{ + char *p, color[64], buf[2048]; + int i, n; + HANDLE_S *h = NULL; + + snprintf(buf, sizeof(buf), CHARSET_DISCLAIMER_1, charset ? charset : "US-ASCII"); + p = &buf[strlen(buf)]; + + if(!(ps_global->display_charmap + && strucmp(ps_global->display_charmap, "US-ASCII")) + && handlesp + && (flags & FM_DISPLAY) == FM_DISPLAY){ + h = new_handle(handlesp); + h->type = URL; + h->h.url.path = cpystr("x-alpine-help:h_config_char_set"); + + /* + * Because this filter comes after the delete_quotes filter, we need + * to tell delete_quotes that this handle is used, so it won't + * delete it. This is a dangerous thing. + */ + h->is_used = 1; + + if(!(flags & FM_NOCOLOR) + && scroll_handle_start_color(color, sizeof(color), &n)){ + for(i = 0; i < n; i++) + if(p-buf < sizeof(buf)) + *p++ = color[i]; + } + else if(F_OFF(F_SLCTBL_ITEM_NOBOLD, ps_global)){ + if(p-buf+1 < sizeof(buf)){ + *p++ = TAG_EMBED; + *p++ = TAG_BOLDON; + } + } + + if(p-buf+1 < sizeof(buf)){ + *p++ = TAG_EMBED; + *p++ = TAG_HANDLE; + } + + snprintf(p + 1, sizeof(buf)-(p+1-buf), "%d", h->key); + if(p-buf < sizeof(buf)) + *p = strlen(p + 1); + + p += (*p + 1); + } + + sstrncpy(&p, CHARSET_DISCLAIMER_2, sizeof(buf)-(p-buf)); + + if(h){ + /* in case it was the current selection */ + if(p-buf+1 < sizeof(buf)){ + *p++ = TAG_EMBED; + *p++ = TAG_INVOFF; + } + + if(scroll_handle_end_color(color, sizeof(color), &n, 0)){ + for(i = 0; i < n; i++) + if(p-buf < sizeof(buf)) + *p++ = color[i]; + } + else{ + if(p-buf+1 < sizeof(buf)){ + *p++ = TAG_EMBED; + *p++ = TAG_BOLDOFF; + } + } + + if(p-buf < sizeof(buf)) + *p = '\0'; + } + + snprintf(p, sizeof(buf)-(p-buf), CHARSET_DISCLAIMER_3, + ps_global->display_charmap ? ps_global->display_charmap : "US-ASCII", + (quality == CV_LOSES_SPECIAL_CHARS) ? "special " : ""); + + buf[sizeof(buf)-1] = '\0'; + + return(format_editorial(buf, width, flags, handlesp, pc) == NULL + && gf_puts(NEWLINE, pc) && gf_puts(NEWLINE, pc)); +} + + + + +/* + * This one is a little more complicated because it comes late in the + * filtering sequence after colors have been added and url's hilited and + * so on. So if it wants to look at the beginning of the line it has to + * wade through some stuff done by the other filters first. This one is + * skipping the leading indent added by the viewer-margin stuff and leading + * tags. + */ +int +delete_quotes(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + DELQ_S *dq; + char *lp; + int i, lines, not_a_quote = 0; + size_t len; + + dq = (DELQ_S *) local; + if(!dq) + return(0); + + if(dq->lines > 0 || dq->lines == Q_DEL_ALL){ + + lines = (dq->lines == Q_DEL_ALL) ? 0 : dq->lines; + + /* + * First determine if this line is part of a quote. + */ + + /* skip over indent added by viewer-margin-left */ + lp = line; + for(i = dq->indent_length; i > 0 && !not_a_quote && *lp; i--) + if(*lp++ != SPACE) + not_a_quote++; + + /* skip over leading tags */ + while(!not_a_quote + && ((unsigned char) (*lp) == (unsigned char) TAG_EMBED)){ + switch(*++lp){ + case '\0': + not_a_quote++; + break; + + default: + ++lp; + break; + + case TAG_FGCOLOR: + case TAG_BGCOLOR: + case TAG_HANDLE: + switch(*lp){ + case TAG_FGCOLOR: + case TAG_BGCOLOR: + len = RGBLEN + 1; + break; + + case TAG_HANDLE: + len = *++lp + 1; + break; + } + + if(strlen(lp) < len) + not_a_quote++; + else + lp += len; + + break; + + case TAG_EMBED: + break; + } + } + + /* skip over whitespace */ + if(!dq->is_flowed) + while(isspace((unsigned char) *lp)) + lp++; + + /* check first character to see if it is a quote */ + if(!not_a_quote && *lp != '>') + not_a_quote++; + + if(not_a_quote){ + if(dq->in_quote > lines+1 && !dq->delete_all){ + char tmp[500]; + COLOR_PAIR *col = NULL; + char cestart[2 * RGBLEN + 5]; + char ceend[2 * RGBLEN + 5]; + + /* + * Display how many lines were hidden. + */ + + cestart[0] = ceend[0] = '\0'; + if(dq->do_color + && ps_global->VAR_METAMSG_FORE_COLOR + && ps_global->VAR_METAMSG_BACK_COLOR + && (col = new_color_pair(ps_global->VAR_METAMSG_FORE_COLOR, + ps_global->VAR_METAMSG_BACK_COLOR))) + if(!pico_is_good_colorpair(col)) + free_color_pair(&col); + + if(col){ + strncpy(cestart, color_embed(col->fg, col->bg), sizeof(cestart)); + cestart[sizeof(cestart)-1] = '\0'; + strncpy(ceend, color_embed(ps_global->VAR_NORM_FORE_COLOR, + ps_global->VAR_NORM_BACK_COLOR), sizeof(ceend)); + ceend[sizeof(ceend)-1] = '\0'; + free_color_pair(&col); + } + + snprintf(tmp, sizeof(tmp), + "%s[ %s%d lines of quoted text hidden from view%s ]\r\n", + repeat_char(dq->indent_length, SPACE), + cestart, dq->in_quote - lines, ceend); + if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){ + + snprintf(tmp, sizeof(tmp), "%s[ %s%d lines of quoted text hidden%s ]\r\n", + repeat_char(dq->indent_length, SPACE), + cestart, dq->in_quote - lines, ceend); + + if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){ + snprintf(tmp, sizeof(tmp), "%s[ %s%d lines hidden%s ]\r\n", + repeat_char(dq->indent_length, SPACE), + cestart, dq->in_quote - lines, ceend); + + if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){ + snprintf(tmp, sizeof(tmp), "%s[ %s%d hidden%s ]\r\n", + repeat_char(dq->indent_length, SPACE), + cestart, dq->in_quote - lines, ceend); + + if(strlen(tmp)-strlen(cestart)-strlen(ceend)-2 > ps_global->ttyo->screen_cols){ + snprintf(tmp, sizeof(tmp), "%s[...]\r\n", + repeat_char(dq->indent_length, SPACE)); + + if(strlen(tmp)-2 > ps_global->ttyo->screen_cols){ + snprintf(tmp, sizeof(tmp), "%s...\r\n", + repeat_char(dq->indent_length, SPACE)); + + if(strlen(tmp)-2 > ps_global->ttyo->screen_cols){ + snprintf(tmp, sizeof(tmp), "%s\r\n", + repeat_char(MIN(ps_global->ttyo->screen_cols,3), + '.')); + } + } + } + } + } + } + + ins = gf_line_test_new_ins(ins, line, tmp, strlen(tmp)); + mark_handles_in_line(line, dq->handlesp, 1); + ps_global->some_quoting_was_suppressed = 1; + } + else if(dq->in_quote == lines+1 + && dq->saved_line && *dq->saved_line){ + /* + * We would have only had to delete a single line. Instead + * of deleting it, just show it. + */ + ins = gf_line_test_new_ins(ins, line, + *dq->saved_line, + strlen(*dq->saved_line)); + mark_handles_in_line(*dq->saved_line, dq->handlesp, 1); + } + else + mark_handles_in_line(line, dq->handlesp, 1); + + if(dq->saved_line && *dq->saved_line) + fs_give((void **) dq->saved_line); + + dq->in_quote = 0; + } + else{ + dq->in_quote++; /* count it */ + if(dq->in_quote > lines){ + /* + * If the hidden part is a single line we'll show it instead. + */ + if(dq->in_quote == lines+1 && !dq->delete_all){ + if(dq->saved_line && *dq->saved_line) + fs_give((void **) dq->saved_line); + + *dq->saved_line = fs_get(strlen(line) + 3); + snprintf(*dq->saved_line, strlen(line)+3, "%s\r\n", line); + } + + mark_handles_in_line(line, dq->handlesp, 0); + /* skip it, at least for now */ + return(2); + } + + mark_handles_in_line(line, dq->handlesp, 1); + } + } + + return(0); +} + + +/* + * This is a line-at-a-time filter that replaces incoming UTF-8 text with + * ISO-2022-JP text. + */ +int +translate_utf8_to_2022_jp(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + LOC_2022_JP *lpj; + int ret = 0; + + lpj = (LOC_2022_JP *) local; + + if(line && line[0]){ + char *converted; + size_t len; + + converted = utf8_to_charset(line, "ISO-2022-JP", lpj ? lpj->report_err : 0); + + if(!converted) + ret = -1; + + if(converted && converted != line){ + /* delete the old line and replace it with the translation */ + len = strlen(line); + ins = gf_line_test_new_ins(ins, line, "", -((int) len)); + ins = gf_line_test_new_ins(ins, line+len, converted, strlen(converted)); + fs_give((void **) &converted); + } + } + + return ret; +} + + +/* + * Replace quotes of nonflowed messages. This needs to happen + * towards the end of filtering because a lot of prior filters + * depend on "> ", such as quote coloring and suppression. + * Quotes are already colored here, so we have to account for + * that formatting. + */ +int +replace_quotes(long int linenum, char *line, LT_INS_S **ins, void *local) +{ + char *lp = line, *prefix = NULL, *last_prefix = NULL; + int no_more_quotes = 0, len, saw_quote = 0; + + if(ps_global->VAR_QUOTE_REPLACE_STRING){ + get_pair(ps_global->VAR_QUOTE_REPLACE_STRING, &prefix, &last_prefix, 0, 0); + if(!prefix && last_prefix){ + prefix = last_prefix; + last_prefix = NULL; + } + } + else + return(0); + + while(isspace((unsigned char)(*lp))) + lp++; + while(*lp && !no_more_quotes){ + switch(*lp++){ + case '>': + if(*lp == ' '){ + ins = gf_line_test_new_ins(ins, lp - 1, "", -2); + lp++; + } + else + ins = gf_line_test_new_ins(ins, lp - 1, "", -1); + if(strlen(prefix)) + ins = gf_line_test_new_ins(ins, lp, prefix, strlen(prefix)); + saw_quote = 1; + break; + case TAG_EMBED: + switch(*lp++){ + case TAG_FGCOLOR: + case TAG_BGCOLOR: + len = RGBLEN; + break; + case TAG_HANDLE: + len = *lp++ + 1; + break; + } + if(strlen(lp) < len) + no_more_quotes = 1; + else + lp += len; + break; + case ' ': + case '\t': + break; + default: + if(saw_quote && last_prefix) + ins = gf_line_test_new_ins(ins, lp - 1, last_prefix, strlen(last_prefix)); + no_more_quotes = 1; + break; + } + } + if(prefix) + fs_give((void **)&prefix); + if(last_prefix) + fs_give((void **)&last_prefix); + return(0); +} + + +/* + * We need to delete handles that we are removing from the displayed + * text. But there is a complication. Some handles appear at more than + * one location in the text. So we keep track of which handles we're + * actually using as we go, then at the end we delete the ones we + * didn't use. + * + * Args line -- the text line, which includes tags + * handlesp -- handles pointer + * used -- If 1, set handles in this line to used + * if 0, leave handles in this line set as they already are + */ +void +mark_handles_in_line(char *line, HANDLE_S **handlesp, int used) +{ + unsigned char c; + size_t length; + int key, n; + HANDLE_S *h; + + if(!(line && handlesp && *handlesp)) + return; + + length = strlen(line); + + /* mimic code in PutLine0n8b */ + while(length--){ + + c = (unsigned char) *line++; + + if(c == (unsigned char) TAG_EMBED && length){ + + length--; + + switch(*line++){ + case TAG_HANDLE : + length -= *line + 1; /* key length plus length tag */ + + for(key = 0, n = *line++; n; n--) + key = (key * 10) + (*line++ - '0'); + + h = get_handle(*handlesp, key); + if(h){ + h->using_is_used = 1; + /* + * If it's already marked is_used, leave it marked + * that way. We only call this function with used = 0 + * in order to set using_is_used. + */ + h->is_used = (h->is_used || used) ? 1 : 0; + } + + break; + + case TAG_FGCOLOR : + if(length < RGBLEN){ + length = 0; + break; + } + + length -= RGBLEN; + line += RGBLEN; + break; + + case TAG_BGCOLOR : + if(length < RGBLEN){ + length = 0; + break; + } + + length -= RGBLEN; + line += RGBLEN; + break; + + default: + break; + } + } + } +} + + +/* + * parsed html risk state functions + */ +void +clear_html_risk(void) +{ + _html_risk = 0; +} + +void +set_html_risk(void) +{ + _html_risk = 1; +} + +int +get_html_risk(void) +{ + return(_html_risk); +} |