/* 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;
}