int df_valid_test(struct mail_bodystruct *body, char *test) { int passed = 0; if(!(passed = !test)){ /* NO test always wins */ if(!*test){ passed++; /* NULL test also wins! */ } else if(body && !struncmp(test, "_CHARSET(", 9)){ char *p = strrindex(test, ')'); if(p){ *p = '\0'; /* tie off user charset */ if((p = parameter_val(body->parameter,"charset")) != NULL){ passed = !strucmp(test + 9, p); fs_give((void **) &p); } else passed = !strucmp(test + 9, "us-ascii"); } else dprint((1, "filter trigger: malformed test: %s\n", test ? test : "?")); } } return(passed); }
int rfc2231_list_params(PARMLIST_S *plist) { PARAMETER *pp, **ppp; int i; if(plist->value) fs_give((void **) &plist->value); for(pp = plist->list; pp; pp = pp->next){ /* get a name */ for(i = 0; i < 32; i++) if(!(plist->attrib[i] = pp->attribute[i]) || pp->attribute[i] == '*'){ plist->attrib[i] = '\0'; for(ppp = &plist->seen; *ppp && strucmp((*ppp)->attribute, plist->attrib); ppp = &(*ppp)->next) ; if(!*ppp){ plist->list = pp->next; *ppp = mail_newbody_parameter(); /* add to seen list */ (*ppp)->attribute = cpystr(plist->attrib); plist->value = parameter_val(pp,plist->attrib); return(TRUE); } break; } if(i >= 32) q_status_message1(SM_ORDER | SM_DING, 0, 3, "Overly long attachment parameter ignored: %.25s...", pp->attribute); } return(FALSE); }
/*---------------------------------------------------------------------- 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, add_me = 1, doraw = RAWSTRING; 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)); } if((flags & FM_DISPLAY) && !(flags & FM_NOCOLOR) && pico_usingcolor() && VAR_SPECIAL_TEXT_FORE_COLOR && VAR_SPECIAL_TEXT_BACK_COLOR){ filters[filtcnt].filter = gf_line_test; filters[filtcnt++].data = gf_line_test_opt(color_this_text, NULL); } /* * 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_quote_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){ add_me = 0; filters[filtcnt].filter = gf_quote_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 (add_me){ filters[filtcnt].filter = gf_quote_test; filters[filtcnt++].data = gf_line_test_opt(select_quote, &doraw); } /* * 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_quote_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_quote_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); }
/* * mc_cmd_bldr - construct a command string to execute * * If tmp_file is null, then the contents of the given MIME segment * is not provided. This is useful for building the "test=" string * as it doesn't operate on the segment's data. * * The return value is an alloc'd copy of the command to be executed. */ char * mc_cmd_bldr(char *controlstring, int type, char *subtype, BODY *body, char *tmp_file, char **err) { char *from, *to, *s, *parm; int prefixed = 0, used_tmp_file = 0; dprint((8, "- mc_cmd_bldr -\n")); for(from = controlstring, to = tmp_20k_buf; *from; ++from){ if(prefixed){ /* previous char was % */ prefixed = 0; switch(*from){ case '%': /* turned \% into this earlier */ if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '%'; break; case 's': /* insert tmp_file name in cmd */ if(tmp_file){ used_tmp_file = 1; sstrncpy(&to, tmp_file, SIZEOF_20KBUF-(to-tmp_20k_buf)); } else dprint((1, "mc_cmd_bldr: %%s in cmd but not supplied!\n")); break; case 't': /* insert MIME type/subtype */ /* quote to prevent funny business */ if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '\''; sstrncpy(&to, body_type_names(type), SIZEOF_20KBUF-(to-tmp_20k_buf)); if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '/'; sstrncpy(&to, subtype, SIZEOF_20KBUF-(to-tmp_20k_buf)); if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '\''; break; case '{': /* insert requested MIME param */ if(F_OFF(F_DO_MAILCAP_PARAM_SUBST, ps_global)){ int save; dprint((2, "mc_cmd_bldr: param subs %s\n", from ? from : "?")); if(err){ if((s = strindex(from, '}')) != NULL){ save = *++s; *s = '\0'; } snprintf(tmp_20k_buf, SIZEOF_20KBUF, "Mailcap: see hidden feature %.200s (%%%.200s)", feature_list_name(F_DO_MAILCAP_PARAM_SUBST), from); *err = tmp_20k_buf; if(s) *s = save; } return(NULL); } s = strindex(from, '}'); if(!s){ q_status_message1(SM_ORDER, 0, 4, "Ignoring ill-formed parameter reference in mailcap file: %.200s", from); break; } *s = '\0'; ++from; /* from is the part inside the brackets now */ parm = parameter_val(body ? body->parameter : NULL, from); dprint((9, "mc_cmd_bldr: parameter %s = %s\n", from ? from : "?", parm ? parm : "(not found)")); /* * Quote parameter values for /bin/sh. * Put single quotes around the whole thing but every time * there is an actual single quote put it outside of the * single quotes with a backslash in front of it. So the * parameter value fred's car * turns into 'fred'\''s car' */ if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '\''; /* opening quote */ if(parm){ char *p; /* * Copy value, but quote single quotes for /bin/sh * Backslash quote is ignored inside single quotes so * have to put those outside of the single quotes. * (The parm+1000 nonsense is to protect against * malicious mail trying to overflow our buffer.) * * TCH - Change 2/8/1999 * Also quote the ` to prevent execution of arbitrary code */ for(p = parm; *p && p < parm+1000; p++){ if((*p == '\'') || (*p == '`')){ if(to-tmp_20k_buf+4 < SIZEOF_20KBUF){ *to++ = '\''; /* closing quote */ *to++ = '\\'; *to++ = *p; /* quoted character */ *to++ = '\''; /* opening quote */ } } else if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = *p; } fs_give((void **) &parm); } if(to-tmp_20k_buf < SIZEOF_20KBUF) *to++ = '\''; /* closing quote for /bin/sh */ *s = '}'; /* restore */ from = s; break; /* * %n and %F are used by metamail to support otherwise * unrecognized multipart Content-Types. Pine does * not use these since we're only dealing with the individual * parts at this point. */ case 'n': case 'F': default: dprint((9, "Ignoring %s format code in mailcap file: %%%c\n", (*from == 'n' || *from == 'F') ? "unimplemented" : "unrecognized", *from)); break; } } else if(*from == '%') /* next char is special */ prefixed = 1; else if(to-tmp_20k_buf < SIZEOF_20KBUF) /* regular character, just copy */ *to++ = *from; } if(to-tmp_20k_buf < SIZEOF_20KBUF) *to = '\0'; tmp_20k_buf[SIZEOF_20KBUF-1] = '\0'; /* * file not specified, redirect to stdin */ if(!used_tmp_file && tmp_file) snprintf(to, SIZEOF_20KBUF-(to-tmp_20k_buf), MC_ADD_TMP, tmp_file); return(cpystr(tmp_20k_buf)); }