Commit eaf6c0f0 eaf6c0f065f0b1b09203dcd543acafcbcf414a61 by Sergey Poznyakoff

Functions for formatting RFC-2231-compliant mail headers fields.

* include/mailutils/cctype.h (MU_CTYPE_TSPEC): New class.  Represents
tspecials as per RFC 2045, section 5.1.
(mu_istspec): New define.
* libmailutils/string/muctype.c (mu_c_tab): Mark tspecials

* include/mailutils/assoc.h (mu_assoc_mark)
(mu_assoc_sweep): New protos.
* libmailutils/base/assoc.c (_mu_assoc_elem): New field: mark
(mu_assoc_mark,mu_assoc_sweep): New functions.

* libmailutils/filter/Makefile.am (libfilter_la_SOURCES): Add dq.c
and percent.c.
* libmailutils/filter/dq.c: New file.
* libmailutils/filter/percent.c: New file.
* include/mailutils/filter.h (mu_percent_filter)
(mu_dq_filter): New externs.
* libmailutils/filter/filter.c (mu_filter_get_list): Register
mu_percent_filter and mu_dq_filter.

* include/mailutils/mime.h (mu_mime_header_set)
(mu_mime_header_set_w): New protos.
* libmailutils/mime/Makefile.am (libmime_la_SOURCES): Add mimehdrset.c
* libmailutils/mime/mimehdrset.c: New file.
* libmailutils/mime/mime.c (_mime_set_content_type): For multipart/alternative,
remove also all parameters except charset from the Content-Type header.
* mail/send.c (saveatt): Remove the now unneeded conditionals.
* libmailutils/tests/mimehdr.at: Test formatting functions.

* include/mailutils/stream.h (MU_IOCTL_FILTER_SET_OUTBUF_SIZE): New ioctl.
* include/mailutils/sys/filter.h (_MU_FILTER_DISABLED)
(_MU_FILTER_EOF): Remove.  Use bitfields instead.
(_mu_filter_stream): Remove fltflag.  New fields: flag_disabled,
flag_eof, outbuf_size.
* libmailutils/stream/fltstream.c (MFB_BASE)
(MFB_CURPTR, MFB_ENDPTR, MFB_SIZE, MFB_LEVEL)
(MFB_POS, MFB_RDBYTES, MFB_FREESIZE)
(MBF_CLEAR, MBF_FREE): Replace with inline functions.
(init_iobuf): Use the outbuf_size field (unless 0) to
set the output buffer size.
(filter_read): Stop if on success if outbuf_size is set,
without trying to fill the entire buffer.
(filter_ctl): Handle MU_IOCTL_FILTER_SET_OUTBUF_SIZE.

* libmailutils/tests/mimehdr.c: New option -width: format and
print the value assuming given line width.
1 parent 5aea5b2d
......@@ -53,6 +53,10 @@ typedef int (*mu_assoc_comparator_t) (const char *, const void *,
int mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data);
int mu_assoc_mark (mu_assoc_t asc, int (*cond) (char const *, void *, void *),
void *data);
int mu_assoc_sweep (mu_assoc_t asc);
#ifdef __cplusplus
}
......
......@@ -24,18 +24,19 @@
extern "C" {
#endif
#define MU_CTYPE_ALPHA 0x001
#define MU_CTYPE_DIGIT 0x002
#define MU_CTYPE_BLANK 0x004
#define MU_CTYPE_CNTRL 0x008
#define MU_CTYPE_GRAPH 0x010
#define MU_CTYPE_LOWER 0x020
#define MU_CTYPE_UPPER 0x040
#define MU_CTYPE_PRINT 0x080
#define MU_CTYPE_PUNCT 0x100
#define MU_CTYPE_SPACE 0x200
#define MU_CTYPE_XLETR 0x400
#define MU_CTYPE_ENDLN 0x800
#define MU_CTYPE_ALPHA 0x0001
#define MU_CTYPE_DIGIT 0x0002
#define MU_CTYPE_BLANK 0x0004
#define MU_CTYPE_CNTRL 0x0008
#define MU_CTYPE_GRAPH 0x0010
#define MU_CTYPE_LOWER 0x0020
#define MU_CTYPE_UPPER 0x0040
#define MU_CTYPE_PRINT 0x0080
#define MU_CTYPE_PUNCT 0x0100
#define MU_CTYPE_SPACE 0x0200
#define MU_CTYPE_XLETR 0x0400
#define MU_CTYPE_ENDLN 0x0800
#define MU_CTYPE_TSPEC 0x1000 /* tspecials: RFC 2045, section 5.1. */
#define MU_C_TAB_MAX 128
......@@ -58,7 +59,8 @@ extern int mu_c_tab[MU_C_TAB_MAX];
#define mu_isascii(c) (((unsigned)c) < MU_C_TAB_MAX)
#define mu_isblank(c) mu_c_is_class (c, MU_CTYPE_BLANK)
#define mu_isendln(c) mu_c_is_class (c, MU_CTYPE_ENDLN)
#define mu_istspec(c) mu_c_is_class (c, MU_CTYPE_TSPEC)
#define mu_tolower(c) \
({ int __c = (c); \
(__c >= 'A' && __c <= 'Z' ? __c - 'A' + 'a' : __c); \
......
......@@ -126,7 +126,9 @@ extern mu_filter_record_t mu_iconv_filter;
extern mu_filter_record_t mu_c_escape_filter;
extern mu_filter_record_t mu_htmlent_filter;
extern mu_filter_record_t mu_xml_filter;
extern mu_filter_record_t mu_percent_filter;
extern mu_filter_record_t mu_dq_filter;
enum mu_iconv_fallback_mode
{
mu_fallback_none,
......
......@@ -75,6 +75,13 @@ int mu_mime_header_parse (const char *text, const char *charset, char **pvalue,
int mu_mime_header_parse_subset (const char *text, const char *charset,
char **pvalue,
mu_assoc_t assoc);
int mu_mime_header_set_w (mu_header_t hdr, const char *name,
const char *value, mu_assoc_t params,
size_t line_width);
int mu_mime_header_set (mu_header_t hdr, const char *name,
const char *value, mu_assoc_t params);
#ifdef __cplusplus
}
......
......@@ -195,6 +195,12 @@ enum mu_buffer_type
#define MU_IOCTL_FILTER_GET_DISABLED 0
#define MU_IOCTL_FILTER_SET_DISABLED 1
/* Set transcoder output buffer size.
Arg: size_t*
Has effect only if the stream is unbuffered
*/
#define MU_IOCTL_FILTER_SET_OUTBUF_SIZE 2
/* TLS transport streams */
/* Get cipher info.
Arg: mu_property_t *
......
......@@ -36,16 +36,14 @@ struct _mu_filter_buffer
size_t pos;
};
#define _MU_FILTER_DISABLED 0x01
#define _MU_FILTER_EOF 0x02
struct _mu_filter_stream
{
struct _mu_stream stream;
mu_stream_t transport;
int mode;
int fltflag;
unsigned flag_disabled:1;
unsigned flag_eof:1;
size_t outbuf_size;
struct _mu_filter_buffer inbuf, outbuf;
mu_filter_xcode_t xcode;
void *xdata;
......
......@@ -47,6 +47,7 @@ struct _mu_assoc_elem
{
char *name;
struct _mu_assoc_elem *next, *prev;
int mark:1;
char *data;
};
......@@ -793,4 +794,34 @@ mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data)
return 0;
}
int
mu_assoc_mark (mu_assoc_t asc, int (*cond) (char const *, void *, void *),
void *data)
{
struct _mu_assoc_elem *elt;
if (!asc)
return EINVAL;
for (elt = asc->head; elt; elt = elt->next)
elt->mark = !!cond (elt->name, elt->data, data);
return 0;
}
int
mu_assoc_sweep (mu_assoc_t asc)
{
unsigned i;
if (!asc)
return EINVAL;
for (i = hash_size[asc->hash_num]; i > 0; i--)
{
if (asc->tab[i-1] && asc->tab[i-1]->mark)
assoc_remove (asc, i-1);
}
return 0;
}
......
......@@ -25,6 +25,7 @@ libfilter_la_SOURCES =\
crlfflt.c\
decode.c\
dot.c\
dq.c\
filter.c\
fltchain.c\
fromflt.c\
......@@ -34,6 +35,7 @@ libfilter_la_SOURCES =\
inline-comment.c\
linecon.c\
linelenflt.c\
percent.c\
qpflt.c\
xml.c
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011-2012, 2014-2017 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
/* Doble-quote escaping filter.
In encode mode, escapes each occurrence of double-quote and backslash
by prefixing it with backslash.
In decode mode, removes backslash prefixes.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <mailutils/errno.h>
#include <mailutils/filter.h>
#include <mailutils/wordsplit.h>
#include <mailutils/cctype.h>
/* Move min(isize,osize) bytes from iptr to optr, replacing each
escapable control character with its escape sequence. */
static enum mu_filter_result
_dq_encoder (void *xd MU_ARG_UNUSED,
enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
size_t i, j;
const unsigned char *iptr;
size_t isize;
char *optr;
size_t osize;
switch (cmd)
{
case mu_filter_init:
case mu_filter_done:
return mu_filter_ok;
default:
break;
}
iptr = (const unsigned char *) iobuf->input;
isize = iobuf->isize;
optr = iobuf->output;
osize = iobuf->osize;
for (i = j = 0; i < isize && j < osize; i++)
{
unsigned char c = *iptr++;
if (strchr ("\\\"", c))
{
if (j + 1 == osize)
{
if (i == 0)
{
iobuf->osize = 2;
return mu_filter_moreoutput;
}
break;
}
else
{
optr[j++] = '\\';
optr[j++] = c;
}
}
else
optr[j++] = c;
}
iobuf->isize = i;
iobuf->osize = j;
return mu_filter_ok;
}
/* Move min(isize,osize) bytes from iptr to optr, replacing each escape
sequence with its ASCII code. */
static enum mu_filter_result
_dq_decoder (void *xd MU_ARG_UNUSED,
enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
size_t i, j;
const unsigned char *iptr;
size_t isize;
char *optr;
size_t osize;
switch (cmd)
{
case mu_filter_init:
case mu_filter_done:
return mu_filter_ok;
default:
break;
}
iptr = (const unsigned char *) iobuf->input;
isize = iobuf->isize;
optr = iobuf->output;
osize = iobuf->osize;
for (i = j = 0; i < isize && j < osize; i++)
{
unsigned char c = *iptr++;
if (c == '\\')
{
if (i + 1 == isize)
break;
optr[j++] = *iptr++;
}
else
optr[j++] = c;
}
iobuf->isize = i;
iobuf->osize = j;
return mu_filter_ok;
}
static struct _mu_filter_record _dq_filter = {
"dq",
NULL,
_dq_encoder,
_dq_decoder,
};
mu_filter_record_t mu_dq_filter = &_dq_filter;
......@@ -86,6 +86,8 @@ mu_filter_get_list (mu_list_t *plist)
mu_list_append (filter_list, mu_c_escape_filter);
mu_list_append (filter_list, mu_htmlent_filter);
mu_list_append (filter_list, mu_xml_filter);
mu_list_append (filter_list, mu_percent_filter);
mu_list_append (filter_list, mu_dq_filter);
/* FIXME: add the default encodings? */
}
*plist = filter_list;
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2011-2012, 2014-2017 Free Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not, see
<http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <mailutils/errno.h>
#include <mailutils/filter.h>
#include <mailutils/cctype.h>
static char xchar[] = "0123456789ABCDEF";
static enum mu_filter_result
percent_encoder (void *xd,
enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
size_t i, j;
const unsigned char *iptr;
size_t isize;
char *optr;
size_t osize;
char *escape_chars = xd;
switch (cmd)
{
case mu_filter_init:
case mu_filter_done:
return mu_filter_ok;
default:
break;
}
iptr = (const unsigned char *) iobuf->input;
isize = iobuf->isize;
optr = iobuf->output;
osize = iobuf->osize;
for (i = j = 0; i < isize && j < osize; i++)
{
unsigned char c = iptr[i];
if (c == 0 || strchr (escape_chars, c))
{
if (j + 3 >= osize)
{
if (i == 0)
{
iobuf->osize = 3;
return mu_filter_moreoutput;
}
break;
}
optr[j++] = '%';
optr[j++] = xchar[((c >> 4) & 0xf)];
optr[j++] = xchar[c & 0xf];
}
else
optr[j++] = c;
}
iobuf->isize = i;
iobuf->osize = j;
return mu_filter_ok;
}
static enum mu_filter_result
percent_decoder (void *xd MU_ARG_UNUSED,
enum mu_filter_command cmd,
struct mu_filter_io *iobuf)
{
size_t i, j;
const unsigned char *iptr;
size_t isize;
char *optr;
size_t osize;
switch (cmd)
{
case mu_filter_init:
case mu_filter_done:
return mu_filter_ok;
default:
break;
}
iptr = (const unsigned char *) iobuf->input;
isize = iobuf->isize;
optr = iobuf->output;
osize = iobuf->osize;
for (i = j = 0; i < isize && j < osize; j++)
{
unsigned char c = iptr[i++];
if (c == '%')
{
char *phi, *plo;
if (i + 2 >= isize)
break;
phi = strchr (xchar, mu_toupper (iptr[i]));
plo = strchr (xchar, mu_toupper (iptr[i+1]));
if (phi && plo)
{
optr[j] = ((phi - xchar) << 4) + (plo - xchar);
i += 2;
}
else
optr[j] = c;
}
else
optr[j] = c;
}
iobuf->isize = i;
iobuf->osize = j;
return mu_filter_ok;
}
static int
percent_alloc (void **pret, int mode, int argc, const char **argv)
{
if (mode == MU_FILTER_ENCODE)
{
char *s;
if (argc > 1)
{
int i;
size_t len = 0;
for (i = 1; i < argc; i++)
len += strlen (argv[i]);
s = malloc (len + 1);
if (!s)
return ENOMEM;
*pret = s;
*s = 0;
for (i = 1; i < argc; i++)
strcat (s, argv[i]);
}
else
{
int i;
s = malloc (UCHAR_MAX);
if (!s)
return ENOMEM;
*pret = s;
for (i = 1; i <= UCHAR_MAX; i++)
{
if (i == '%' || i == '"' || !mu_isgraph (i))
*s++ = i;
}
*s = 0;
}
}
else
*pret = NULL;
return 0;
}
static struct _mu_filter_record _percent_filter = {
"percent",
percent_alloc,
percent_encoder,
percent_decoder
};
mu_filter_record_t mu_percent_filter = &_percent_filter;
......@@ -20,6 +20,7 @@ noinst_LTLIBRARIES = libmime.la
libmime_la_SOURCES = \
attachment.c\
mime.c\
mimehdr.c
mimehdr.c\
mimehdrset.c
AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
......
......@@ -38,6 +38,8 @@
#include <mailutils/header.h>
#include <mailutils/errno.h>
#include <mailutils/util.h>
#include <mailutils/assoc.h>
#include <mailutils/io.h>
#include <mailutils/sys/mime.h>
#include <mailutils/sys/message.h>
......@@ -445,6 +447,13 @@ _mimepart_body_lines (mu_body_t body, size_t *plines)
/*------ Mime message/header functions for CREATING multipart message -----*/
static int
retain_charset (char const *name, void *value MU_ARG_UNUSED,
void *data MU_ARG_UNUSED)
{
return strcmp (name, "charset") != 0;
}
static int
_mime_set_content_type (mu_mime_t mime)
{
const char *content_type;
......@@ -473,9 +482,36 @@ _mime_set_content_type (mu_mime_t mime)
for (i = 0; i < mime->nmtp_parts; i++)
{
mu_header_t hdr;
char *val;
int rc;
mu_message_get_header (mime->mtp_parts[i]->msg, &hdr);
mu_header_remove (hdr, MU_HEADER_CONTENT_DISPOSITION, 1);
rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE,
&val);
if (rc == 0)
{
mu_content_type_t ct;
rc = mu_content_type_parse (val, NULL, &ct);
if (rc == 0)
{
char *type;
mu_assoc_mark (ct->param, retain_charset, NULL);
mu_assoc_sweep (ct->param);
rc = mu_asprintf (&type, "%s/%s", ct->type, ct->subtype);
if (rc == 0)
{
mu_mime_header_set (hdr,
MU_HEADER_CONTENT_TYPE, type,
ct->param);
free (type);
}
mu_content_type_destroy (&ct);
}
}
}
content_type = "multipart/alternative; boundary=";
......
/* Functions for formatting RFC-2231-compliant mail headers fields.
GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 1999-2001, 2004-2005, 2007, 2009-2012, 2014-2017 Free
Software Foundation, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General
Public License along with this library. If not,
see <http://www.gnu.org/licenses/>. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <mailutils/mime.h>
#include <mailutils/cctype.h>
#include <mailutils/cstr.h>
#include <mailutils/header.h>
#include <mailutils/stream.h>
#include <mailutils/filter.h>
#include <mailutils/assoc.h>
#include <mailutils/errno.h>
struct header_buffer
{
mu_stream_t str; /* Output stream */
size_t line_len; /* Length of current line */
size_t line_max; /* Max. line length */
};
static int
mime_store_parameter (char const *name, void *value, void *data)
{
struct mu_mime_param *p = value;
struct header_buffer *hbuf = data;
size_t nlen; /* Length of parameter name
(eq. sign, eventual seqence no. and language info mark
included) */
size_t vlen; /* Length of lang'charset' part */
int langinfo; /* True if language info is available */
int quote = 0; /* 2 if the value should be quoted, 0 otherwise */
int segment = -1; /* Segment sequence number */
mu_stream_t valstr; /* Value stream (properly encoded) */
mu_off_t valsize; /* Number of octets left in valstr */
char const *filter_name = NULL; /* Name of the filter for the value */
int rc;
rc = mu_static_memory_stream_create (&valstr, p->value, strlen (p->value));
if (rc)
return rc;
nlen = strlen (name);
if (p->lang || p->cset)
{
vlen = 2;
if (p->lang)
vlen += strlen (p->lang);
if (p->cset)
vlen += strlen (p->cset);
langinfo = 1;
filter_name = "percent";
}
else
{
if (*mu_str_skip_class_comp (p->value, MU_CTYPE_TSPEC|MU_CTYPE_BLANK))
{
/* Must be in quoted-string, to use within parameter values */
quote = 2;
filter_name = "dq";
}
else
quote = 0;
vlen = 0;
langinfo = 0;
}
if (filter_name)
{
mu_stream_t tmp;
rc = mu_filter_create (&tmp, valstr, filter_name, MU_FILTER_ENCODE,
MU_STREAM_READ | MU_STREAM_SEEK);
if (rc)
goto err;
mu_stream_unref (valstr);
valstr = tmp;
rc = mu_memory_stream_create (&tmp, MU_STREAM_RDWR);
if (rc == 0)
{
rc = mu_stream_copy (tmp, valstr, 0, &valsize);
mu_stream_destroy (&tmp);
}
}
else
rc = mu_stream_size (valstr, &valsize);
if (rc)
goto err;
nlen += langinfo;
rc = mu_stream_seek (valstr, 0, MU_SEEK_SET, NULL);
if (hbuf->line_max == 0)
{
/* No line wrapping requested. Store the value as it is */
mu_stream_printf (hbuf->str, "%s", name);
if (langinfo)
mu_stream_write (hbuf->str, "*", 1, NULL);
mu_stream_write (hbuf->str, "=", 1, NULL);
if (vlen)
{
mu_stream_printf (hbuf->str, "%s'%s'",
p->lang ? p->lang : "",
p->cset ? p->cset : "");
vlen = 0;
}
else if (quote)
mu_stream_write (hbuf->str, "\"", 1, NULL);
mu_stream_copy (hbuf->str, valstr, 0, NULL);
if (quote)
mu_stream_write (hbuf->str, "\"", 1, NULL);
if (mu_stream_err (hbuf->str))
rc = mu_stream_last_error (hbuf->str);
}
else
{
/* Split the value into sequentially indexed segments, each one no
wider than the requested line width.
Without special precautions, an encoded character occurring at
the end of a segment can be split between this and the following
segment to satisfy line width requirements. To avoid this, the
following approach is used:
1. The value stream is put to unbuffered mode.
2. Before each write, the size of the transcoder output buffer
in valstr is set to the number of bytes left in the current
line.
This way the transcoder will write as many bytes as possible
without breaking the encoded constructs while the unbuffered mode
will ensure that it will not be called again to fill up the stream
buffer.
If the line width is insufficient, MU_ERR_BUFSPACE will be returned.
*/
char *iobuf;
iobuf = malloc (hbuf->line_max + 1);
if (!iobuf)
{
rc = errno;
goto err;
}
mu_stream_set_buffer (valstr, mu_buffer_none, 0);
while (rc == 0 && valsize)
{
mu_off_t start, nr; /* Start and end positions in stream */
size_t sz, n;
mu_stream_write (hbuf->str, ";", 1, NULL);
mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &start);
if (segment >= 0)
{
mu_stream_write (hbuf->str, "\n", 1, NULL);
hbuf->line_len = 0;
segment++;
}
else if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
hbuf->line_max)
{
mu_stream_write (hbuf->str, "\n", 1, NULL);
hbuf->line_len = 0;
if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
hbuf->line_max)
segment++;
}
mu_stream_write (hbuf->str, " ", 1, NULL);
if (segment >= 0)
mu_stream_printf (hbuf->str, "%s*%d", name, segment);
else
mu_stream_printf (hbuf->str, "%s", name);
if (langinfo)
mu_stream_write (hbuf->str, "*", 1, NULL);
mu_stream_write (hbuf->str, "=", 1, NULL);
mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
nlen = nr - start;
hbuf->line_len += nlen;
start = nr;
/* Compute the number of octets to put into the current line.
If the requested line width is not enough to accomodate
the line, signal the error */
if (hbuf->line_max <= (hbuf->line_len + quote + vlen))
{
rc = MU_ERR_BUFSPACE;
break;
}
sz = hbuf->line_max - (hbuf->line_len + quote + vlen);
mu_stream_ioctl (valstr, MU_IOCTL_FILTER,
MU_IOCTL_FILTER_SET_OUTBUF_SIZE, &sz);
rc = mu_stream_read (valstr, iobuf, sz, &n);
if (rc || n == 0)
break;
if (vlen)
{
mu_stream_printf (hbuf->str, "%s'%s'",
p->lang ? p->lang : "",
p->cset ? p->cset : "");
vlen = 0;
}
else if (quote)
mu_stream_write (hbuf->str, "\"", 1, NULL);
mu_stream_write (hbuf->str, iobuf, n, NULL);
if (quote)
mu_stream_write (hbuf->str, "\"", 1, NULL);
mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
nr -= start;
hbuf->line_len += nr;
valsize -= n;
if (mu_stream_err (hbuf->str))
rc = mu_stream_last_error (hbuf->str);
}
free (iobuf);
}
err:
mu_stream_destroy (&valstr);
return rc;
}
static int
mime_header_format (const char *value, mu_assoc_t params,
struct header_buffer *hbuf)
{
size_t l = strlen (value);
mu_stream_write (hbuf->str, value, l, NULL);
hbuf->line_len += l;
return mu_assoc_foreach (params, mime_store_parameter, hbuf);
}
/* Store a header in accordance with RFC 2231, Section 3,
"Parameter Value Continuations"
HDR - Message header object
NAME - Header name
VALUE - Header value part
PARAMS - Named parameters (assoc of struct mu_mime_param *)
LINE_WIDTH - Maximum line width.
*/
int
mu_mime_header_set_w (mu_header_t hdr, const char *name,
const char *value, mu_assoc_t params, size_t line_width)
{
struct header_buffer hbuf;
int rc;
rc = mu_memory_stream_create (&hbuf.str, MU_STREAM_RDWR);
if (rc)
return rc;
hbuf.line_len = strlen (name) + 2;
hbuf.line_max = line_width;
rc = mime_header_format (value, params, &hbuf);
if (rc == 0)
{
mu_off_t pos;
char *fmtval;
mu_stream_seek (hbuf.str, 0, MU_SEEK_CUR, &pos);
fmtval = malloc (pos + 1);
mu_stream_seek (hbuf.str, 0, MU_SEEK_SET, NULL);
mu_stream_read (hbuf.str, fmtval, pos, NULL);
fmtval[pos] = 0;
rc = mu_header_set_value (hdr, name, fmtval, 1);
free (fmtval);
}
mu_stream_destroy (&hbuf.str);
return rc;
}
int
mu_mime_header_set (mu_header_t hdr, const char *name,
const char *value, mu_assoc_t params)
{
return mu_mime_header_set_w (hdr, name, value, params, 76);
}
......@@ -55,20 +55,20 @@ int mu_c_tab[MU_C_TAB_MAX] = {
MU_CTYPE_CNTRL,
MU_CTYPE_PRINT|MU_CTYPE_SPACE|MU_CTYPE_BLANK,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
......@@ -79,13 +79,13 @@ int mu_c_tab[MU_C_TAB_MAX] = {
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR,
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR,
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR,
......@@ -112,9 +112,9 @@ int mu_c_tab[MU_C_TAB_MAX] = {
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
......
......@@ -119,13 +119,128 @@ filename(lang:el_GR/UTF-8)=αρχείο για την δοκιμασία
MIMEHDR([combined charset, lang and cset],[mimehdr08 mimehdr-comb mimehdr-charset-rfc2231],
[],
[application/x-stuff
title*0*=us-ascii'en'This%20is%20even%20more%20
title*1*=%2A%2A%2Afun%2A%2A%2A%20
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[application/x-stuff
title(lang:en/us-ascii)=This is even more ***fun*** isn't it!
])
MIMEHDR([format: simple],[mimehdr09],
[-header=X-MIME-Test-Header -width=26],
[application/x-stuff; charset=ascii; title=foobar],
[X-MIME-Test-Header: application/x-stuff;
charset=ascii;
title=foobar
])
MIMEHDR([format: split],[mimehdr10],
[-header=X-MIME-Test-Header -width=76],
[attachment; title="unusually long filename created by gray expressly for testing purposes and not meant for any other purpose but testing the formatting of very long MIME headers"],
[X-MIME-Test-Header: attachment;
title*0="unusually long filename created by gray expressly for testing pu";
title*1="rposes and not meant for any other purpose but testing the forma";
title*2="tting of very long MIME headers"
])
MIMEHDR([format: split 2],[mimehdr11],
[-header=X-MIME-Test-Header -width=26],
[attachment; title="unusually long filename created by gray expressly for testing purposes and not meant for any other purpose but testing the formatting of very long MIME headers"],
[X-MIME-Test-Header: attachment;
title*0="unusually long";
title*1=" filename crea";
title*2="ted by gray ex";
title*3="pressly for te";
title*4="sting purposes";
title*5=" and not meant";
title*6=" for any other";
title*7=" purpose but t";
title*8="esting the for";
title*9="matting of ver";
title*10="y long MIME h";
title*11="eaders"
])
# This test is split into 5 subtests to make sure encoded character
# representation is not split between subsequent numbered parameters.
MIMEHDR([format: language info 1],[mimehdr12 mimehdr12a],
[-header=X-MIME-Test-Header -width=27],
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[X-MIME-Test-Header: application/x-stuff;
title*0*=en'us-ascii'This;
title*1*=%20is%20even%20m;
title*2*=ore%20***fun***;
title*3*=%20isn't%20it!
])
MIMEHDR([format: language info 2],[mimehdr12 mimehdr12b],
[-header=X-MIME-Test-Header -width=28],
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[X-MIME-Test-Header: application/x-stuff;
title*0*=en'us-ascii'This;
title*1*=%20is%20even%20mo;
title*2*=re%20***fun***;
title*3*=%20isn't%20it!
])
MIMEHDR([format: language info 3],[mimehdr12 mimehdr12c],
[-header=X-MIME-Test-Header -width=29],
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[X-MIME-Test-Header: application/x-stuff;
title*0*=en'us-ascii'This;
title*1*=%20is%20even%20mor;
title*2*=e%20***fun***%20is;
title*3*=n't%20it!
])
MIMEHDR([format: language info 4],[mimehdr12 mimehdr12d],
[-header=X-MIME-Test-Header -width=30],
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[X-MIME-Test-Header: application/x-stuff;
title*0*=en'us-ascii'This;
title*1*=%20is%20even%20more;
title*2*=%20***fun***%20isn';
title*3*=t%20it!
])
MIMEHDR([format: language info 5],[mimehdr12 mimehdr12e],
[-header=X-MIME-Test-Header -width=31],
[application/x-stuff;
title*0*=us-ascii'en'This%20is%20even%20more%20;
title*1*=%2A%2A%2Afun%2A%2A%2A%20;
title*2="isn't it!"
],
[X-MIME-Test-Header: application/x-stuff;
title*0*=en'us-ascii'This%20i;
title*1*=s%20even%20more%20**;
title*2*=*fun***%20isn't%20it;
title*3*=!
])
m4_popdef([MIMEHDR])
......
......@@ -61,6 +61,8 @@ main (int argc, char **argv)
char *value;
mu_assoc_t assoc;
char *charset = NULL;
char *header_name = NULL;
unsigned long width = 76;
mu_set_program_name (argv[0]);
for (i = 1; i < argc; i++)
......@@ -73,9 +75,13 @@ main (int argc, char **argv)
charset = opt + 9;
else if (strcmp (opt, "-h") == 0 || strcmp (opt, "-help") == 0)
{
mu_printf ("usage: %s [-charset=cs] [-debug=SPEC]", mu_program_name);
mu_printf ("usage: %s [-charset=cs] [-debug=SPEC] [-header=NAME] [-width=N]", mu_program_name);
return 0;
}
else if (strncmp (opt, "-header=", 8) == 0)
header_name = opt + 8;
else if (strncmp (opt, "-width=", 7) == 0)
width = strtoul (opt + 7, NULL, 10);
else
{
mu_error ("unknown option %s", opt);
......@@ -97,9 +103,22 @@ main (int argc, char **argv)
MU_ASSERT (mu_mime_header_parse ((char*)trans[0], charset, &value, &assoc));
mu_printf ("%s\n", value);
mu_assoc_sort_r (assoc, sort_names, NULL);
mu_assoc_foreach (assoc, print_param, NULL);
if (header_name)
{
mu_header_t hdr;
mu_stream_t hstr;
MU_ASSERT (mu_header_create (&hdr, NULL, 0));
MU_ASSERT (mu_mime_header_set_w (hdr, header_name, value, assoc, width));
MU_ASSERT (mu_header_get_streamref (hdr, &hstr));
MU_ASSERT (mu_stream_copy (mu_strout, hstr, 0, NULL));
}
else
{
mu_printf ("%s\n", value);
mu_assoc_sort_r (assoc, sort_names, NULL);
mu_assoc_foreach (assoc, print_param, NULL);
}
return 0;
}
......
......@@ -464,8 +464,8 @@ saveatt (void *item, void *data)
char *p;
rc = mu_attachment_create (&part, aptr->content_type, aptr->encoding,
env->alt ? NULL : aptr->name,
env->alt ? NULL : aptr->filename);
aptr->name,
aptr->filename);
if (rc)
{
mu_error (_("can't create attachment %s: %s"),
......