summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2017-04-22 12:50:41 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2017-04-22 20:14:02 +0300
commiteaf6c0f065f0b1b09203dcd543acafcbcf414a61 (patch)
treeada72b08fe1bee1957b0c427f42a69bf774234a5
parent5aea5b2d9b0c87fe81ee566dba830a568398679c (diff)
downloadmailutils-eaf6c0f065f0b1b09203dcd543acafcbcf414a61.tar.gz
mailutils-eaf6c0f065f0b1b09203dcd543acafcbcf414a61.tar.bz2
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.
-rw-r--r--include/mailutils/assoc.h4
-rw-r--r--include/mailutils/cctype.h28
-rw-r--r--include/mailutils/filter.h4
-rw-r--r--include/mailutils/mime.h7
-rw-r--r--include/mailutils/stream.h6
-rw-r--r--include/mailutils/sys/filter.h8
-rw-r--r--libmailutils/base/assoc.c31
-rw-r--r--libmailutils/filter/Makefile.am2
-rw-r--r--libmailutils/filter/dq.c143
-rw-r--r--libmailutils/filter/filter.c2
-rw-r--r--libmailutils/filter/percent.c186
-rw-r--r--libmailutils/mime/Makefile.am3
-rw-r--r--libmailutils/mime/mime.c36
-rw-r--r--libmailutils/mime/mimehdrset.c314
-rw-r--r--libmailutils/stream/fltstream.c242
-rw-r--r--libmailutils/string/muctype.c30
-rw-r--r--libmailutils/tests/mimehdr.at121
-rw-r--r--libmailutils/tests/mimehdr.c27
-rw-r--r--mail/send.c4
19 files changed, 1069 insertions, 129 deletions
diff --git a/include/mailutils/assoc.h b/include/mailutils/assoc.h
index 23ae7cda4..a8f9845e1 100644
--- a/include/mailutils/assoc.h
+++ b/include/mailutils/assoc.h
@@ -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
}
diff --git a/include/mailutils/cctype.h b/include/mailutils/cctype.h
index ecd9c4007..bed749b96 100644
--- a/include/mailutils/cctype.h
+++ b/include/mailutils/cctype.h
@@ -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); \
diff --git a/include/mailutils/filter.h b/include/mailutils/filter.h
index 8bd107100..c8bddd40b 100644
--- a/include/mailutils/filter.h
+++ b/include/mailutils/filter.h
@@ -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,
diff --git a/include/mailutils/mime.h b/include/mailutils/mime.h
index 786f96f2b..503ebe399 100644
--- a/include/mailutils/mime.h
+++ b/include/mailutils/mime.h
@@ -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
}
diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h
index 2e7b86cb5..f7f58ba8a 100644
--- a/include/mailutils/stream.h
+++ b/include/mailutils/stream.h
@@ -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 *
diff --git a/include/mailutils/sys/filter.h b/include/mailutils/sys/filter.h
index 1ee0b1942..23b5affcd 100644
--- a/include/mailutils/sys/filter.h
+++ b/include/mailutils/sys/filter.h
@@ -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;
diff --git a/libmailutils/base/assoc.c b/libmailutils/base/assoc.c
index 4e31b499a..b0e353d32 100644
--- a/libmailutils/base/assoc.c
+++ b/libmailutils/base/assoc.c
@@ -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;
+}
+
diff --git a/libmailutils/filter/Makefile.am b/libmailutils/filter/Makefile.am
index b735bf772..5daedacae 100644
--- a/libmailutils/filter/Makefile.am
+++ b/libmailutils/filter/Makefile.am
@@ -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
diff --git a/libmailutils/filter/dq.c b/libmailutils/filter/dq.c
new file mode 100644
index 000000000..d3bb18523
--- /dev/null
+++ b/libmailutils/filter/dq.c
@@ -0,0 +1,143 @@
+/* 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;
+
diff --git a/libmailutils/filter/filter.c b/libmailutils/filter/filter.c
index 0717425fc..00bc50fa2 100644
--- a/libmailutils/filter/filter.c
+++ b/libmailutils/filter/filter.c
@@ -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;
diff --git a/libmailutils/filter/percent.c b/libmailutils/filter/percent.c
new file mode 100644
index 000000000..62d248d88
--- /dev/null
+++ b/libmailutils/filter/percent.c
@@ -0,0 +1,186 @@
+/* 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;
+
+
diff --git a/libmailutils/mime/Makefile.am b/libmailutils/mime/Makefile.am
index 98895cfd7..3edb9fdfa 100644
--- a/libmailutils/mime/Makefile.am
+++ b/libmailutils/mime/Makefile.am
@@ -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
diff --git a/libmailutils/mime/mime.c b/libmailutils/mime/mime.c
index 9c3879769..e4463d307 100644
--- a/libmailutils/mime/mime.c
+++ b/libmailutils/mime/mime.c
@@ -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=";
diff --git a/libmailutils/mime/mimehdrset.c b/libmailutils/mime/mimehdrset.c
new file mode 100644
index 000000000..7c127caee
--- /dev/null
+++ b/libmailutils/mime/mimehdrset.c
@@ -0,0 +1,314 @@
+/* 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;