/* GNU Mailutils -- a suite of utilities for electronic mail Copyright (C) 1999-2021 Free Software Foundation, Inc. GNU Mailutils is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GNU Mailutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GNU Mailutils. If not, see . */ #include #include #include static mu_list_t text_mime_list; static char const *default_text_types[] = { "text/*", "application/*shell", "application/shellscript", "*/x-csrc", "*/x-csource", "*/x-diff", "*/x-patch", "*/x-perl", "*/x-php", "*/x-python", "*/x-sh", NULL }; static void text_mime_add (char const *s); struct mime_pattern { char *pat_type; char *pat_subtype; }; static int text_mime_cmp (const void *item, const void *ptr) { const struct mime_pattern *pat = item; mu_content_type_t ct = (mu_content_type_t) ptr; if (mu_imap_wildmatch_ci (pat->pat_type, ct->type, 0) == 0) { if (ct->subtype == NULL) return pat->pat_subtype != NULL; if (pat->pat_subtype == NULL || mu_imap_wildmatch (pat->pat_subtype, ct->subtype, '/') == 0) return 0; } return 1; } static void text_mime_init (void) { if (!text_mime_list) { int i; int rc = mu_list_create (&text_mime_list); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_list_create", NULL, rc); mu_alloc_die (); } mu_list_set_destroy_item (text_mime_list, mu_list_free_item); mu_list_set_comparator (text_mime_list, text_mime_cmp); for (i = 0; default_text_types[i]; i++) text_mime_add (default_text_types[i]); } } static void text_mime_add (char const *s) { int rc; struct mime_pattern *mp; char *p; text_mime_init (); mp = mu_alloc (sizeof *mp + strlen (s) + 1); mp->pat_type = (char*)(mp + 1); strcpy (mp->pat_type, s); p = strchr (mp->pat_type, '/'); if (p) *p++ = 0; mp->pat_subtype = p; rc = mu_list_append (text_mime_list, mp); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_list_append", NULL, rc); mu_alloc_die (); } } static int cb_text_type (void *data, mu_config_value_t *val) { size_t i; switch (val->type) { case MU_CFG_STRING: text_mime_add (val->v.string); break; case MU_CFG_LIST: for (i = 0; i < val->v.arg.c; i++) { if (mu_cfg_assert_value_type (&val->v.arg.v[i], MU_CFG_STRING)) return 1; text_mime_add (val->v.arg.v[i].v.string); } break; default: mu_error ("%s", _("expected string or list")); } return 0; } static struct mu_cfg_param mime_param[] = { { "text-type", mu_cfg_callback, NULL, 0, cb_text_type, N_("Define textual mime types."), N_("arg: pattern or list of patterns") }, { NULL } }; struct mu_cli_capa mu_cli_capa_mime = { .name = "mime", .cfg = mime_param }; static int is_text_part (mu_content_type_t ct) { text_mime_init (); return mu_list_locate (text_mime_list, ct, NULL) == 0; } static int charset_setup (mu_stream_t *pstr, mu_content_type_t ct, char const *charset) { struct mu_mime_param *param; if (charset && mu_assoc_lookup (ct->param, "charset", ¶m) == 0 && mu_c_strcasecmp (param->value, charset)) { mu_stream_t input = *pstr; char const *argv[] = { "iconv", NULL, NULL, NULL }; mu_stream_t flt; int rc; argv[1] = param->value; argv[2] = charset; rc = mu_filter_chain_create (&flt, input, MU_FILTER_ENCODE, MU_STREAM_READ, MU_ARRAY_SIZE (argv) - 1, (char**) argv); if (rc) { mu_error (_("can't convert from charset %s to %s: %s"), param->value, charset, mu_strerror (rc)); return rc; } mu_stream_unref (input); *pstr = flt; } return 0; } int message_body_stream (mu_message_t msg, int unix_header, char const *charset, mu_stream_t *pstr) { int rc; mu_header_t hdr; mu_body_t body; mu_stream_t d_stream; mu_stream_t stream = NULL; char *encoding = NULL; char *buf; mu_content_type_t ct; /* Get the headers. */ rc = mu_message_get_header (msg, &hdr); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_header", NULL, rc); return rc; } /* Read and parse the Content-Type header. */ rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE, &buf); if (rc == MU_ERR_NOENT) { buf = strdup ("text/plain"); if (!buf) return errno; } else if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_header_aget_value_unfold", NULL, rc); return rc; } rc = mu_content_type_parse_ext (buf, NULL, MU_CONTENT_TYPE_RELAXED | MU_CONTENT_TYPE_PARAM, &ct); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_content_type_parse_ext", buf, rc); free (buf); return rc; } free (buf); buf = NULL; if (is_text_part (ct)) /* Process only textual parts */ { /* Get the body stream. */ rc = mu_message_get_body (msg, &body); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_body", NULL, rc); goto err; } rc = mu_body_get_streamref (body, &stream); if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_body_get_streamref", NULL, rc); goto err; } /* Filter it through the appropriate decoder. */ rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TRANSFER_ENCODING, &encoding); if (rc == 0) mu_rtrim_class (encoding, MU_CTYPE_SPACE); else if (rc == MU_ERR_NOENT) encoding = NULL; else if (rc) { mu_diag_funcall (MU_DIAG_ERROR, "mu_header_aget_value_unfold", NULL, rc); goto err; } if (encoding == NULL || *encoding == '\0') /* No need to filter */; else if ((rc = mu_filter_create (&d_stream, stream, encoding, MU_FILTER_DECODE, MU_STREAM_READ)) == 0) { mu_stream_unref (stream); stream = d_stream; } else if (rc == MU_ERR_NOENT) { mu_error ("unknown encoding: %s", encoding); } else { mu_diag_funcall (MU_DIAG_ERROR, "mu_filter_create", encoding, rc); /* FIXME: continue anyway? */ } /* Convert the content to the requested charset. */ rc = charset_setup (&stream, ct, charset); if (rc) goto err; if (unix_header) { rc = mu_filter_create (&d_stream, stream, "FROM", MU_FILTER_ENCODE, MU_STREAM_READ); if (rc == 0) { mu_stream_unref (stream); stream = d_stream; } else { mu_diag_funcall (MU_DIAG_ERROR, "mu_filter_create", "FROM", rc); /* continue anyway */ } } } else rc = MU_ERR_USER0; err: free (buf); free (encoding); mu_content_type_destroy (&ct); if (rc == 0) *pstr = stream; else mu_stream_destroy (&stream); return rc; }