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 *, ...@@ -53,6 +53,10 @@ typedef int (*mu_assoc_comparator_t) (const char *, const void *,
53 53
54 int mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data); 54 int mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data);
55 55
56 int mu_assoc_mark (mu_assoc_t asc, int (*cond) (char const *, void *, void *),
57 void *data);
58 int mu_assoc_sweep (mu_assoc_t asc);
59
56 60
57 #ifdef __cplusplus 61 #ifdef __cplusplus
58 } 62 }
......
...@@ -24,18 +24,19 @@ ...@@ -24,18 +24,19 @@
24 extern "C" { 24 extern "C" {
25 #endif 25 #endif
26 26
27 #define MU_CTYPE_ALPHA 0x001 27 #define MU_CTYPE_ALPHA 0x0001
28 #define MU_CTYPE_DIGIT 0x002 28 #define MU_CTYPE_DIGIT 0x0002
29 #define MU_CTYPE_BLANK 0x004 29 #define MU_CTYPE_BLANK 0x0004
30 #define MU_CTYPE_CNTRL 0x008 30 #define MU_CTYPE_CNTRL 0x0008
31 #define MU_CTYPE_GRAPH 0x010 31 #define MU_CTYPE_GRAPH 0x0010
32 #define MU_CTYPE_LOWER 0x020 32 #define MU_CTYPE_LOWER 0x0020
33 #define MU_CTYPE_UPPER 0x040 33 #define MU_CTYPE_UPPER 0x0040
34 #define MU_CTYPE_PRINT 0x080 34 #define MU_CTYPE_PRINT 0x0080
35 #define MU_CTYPE_PUNCT 0x100 35 #define MU_CTYPE_PUNCT 0x0100
36 #define MU_CTYPE_SPACE 0x200 36 #define MU_CTYPE_SPACE 0x0200
37 #define MU_CTYPE_XLETR 0x400 37 #define MU_CTYPE_XLETR 0x0400
38 #define MU_CTYPE_ENDLN 0x800 38 #define MU_CTYPE_ENDLN 0x0800
39 #define MU_CTYPE_TSPEC 0x1000 /* tspecials: RFC 2045, section 5.1. */
39 40
40 #define MU_C_TAB_MAX 128 41 #define MU_C_TAB_MAX 128
41 42
...@@ -58,6 +59,7 @@ extern int mu_c_tab[MU_C_TAB_MAX]; ...@@ -58,6 +59,7 @@ extern int mu_c_tab[MU_C_TAB_MAX];
58 #define mu_isascii(c) (((unsigned)c) < MU_C_TAB_MAX) 59 #define mu_isascii(c) (((unsigned)c) < MU_C_TAB_MAX)
59 #define mu_isblank(c) mu_c_is_class (c, MU_CTYPE_BLANK) 60 #define mu_isblank(c) mu_c_is_class (c, MU_CTYPE_BLANK)
60 #define mu_isendln(c) mu_c_is_class (c, MU_CTYPE_ENDLN) 61 #define mu_isendln(c) mu_c_is_class (c, MU_CTYPE_ENDLN)
62 #define mu_istspec(c) mu_c_is_class (c, MU_CTYPE_TSPEC)
61 63
62 #define mu_tolower(c) \ 64 #define mu_tolower(c) \
63 ({ int __c = (c); \ 65 ({ int __c = (c); \
......
...@@ -126,6 +126,8 @@ extern mu_filter_record_t mu_iconv_filter; ...@@ -126,6 +126,8 @@ extern mu_filter_record_t mu_iconv_filter;
126 extern mu_filter_record_t mu_c_escape_filter; 126 extern mu_filter_record_t mu_c_escape_filter;
127 extern mu_filter_record_t mu_htmlent_filter; 127 extern mu_filter_record_t mu_htmlent_filter;
128 extern mu_filter_record_t mu_xml_filter; 128 extern mu_filter_record_t mu_xml_filter;
129 extern mu_filter_record_t mu_percent_filter;
130 extern mu_filter_record_t mu_dq_filter;
129 131
130 enum mu_iconv_fallback_mode 132 enum mu_iconv_fallback_mode
131 { 133 {
......
...@@ -76,6 +76,13 @@ int mu_mime_header_parse_subset (const char *text, const char *charset, ...@@ -76,6 +76,13 @@ int mu_mime_header_parse_subset (const char *text, const char *charset,
76 char **pvalue, 76 char **pvalue,
77 mu_assoc_t assoc); 77 mu_assoc_t assoc);
78 78
79 int mu_mime_header_set_w (mu_header_t hdr, const char *name,
80 const char *value, mu_assoc_t params,
81 size_t line_width);
82 int mu_mime_header_set (mu_header_t hdr, const char *name,
83 const char *value, mu_assoc_t params);
84
85
79 #ifdef __cplusplus 86 #ifdef __cplusplus
80 } 87 }
81 #endif 88 #endif
......
...@@ -195,6 +195,12 @@ enum mu_buffer_type ...@@ -195,6 +195,12 @@ enum mu_buffer_type
195 #define MU_IOCTL_FILTER_GET_DISABLED 0 195 #define MU_IOCTL_FILTER_GET_DISABLED 0
196 #define MU_IOCTL_FILTER_SET_DISABLED 1 196 #define MU_IOCTL_FILTER_SET_DISABLED 1
197 197
198 /* Set transcoder output buffer size.
199 Arg: size_t*
200 Has effect only if the stream is unbuffered
201 */
202 #define MU_IOCTL_FILTER_SET_OUTBUF_SIZE 2
203
198 /* TLS transport streams */ 204 /* TLS transport streams */
199 /* Get cipher info. 205 /* Get cipher info.
200 Arg: mu_property_t * 206 Arg: mu_property_t *
......
...@@ -36,16 +36,14 @@ struct _mu_filter_buffer ...@@ -36,16 +36,14 @@ struct _mu_filter_buffer
36 size_t pos; 36 size_t pos;
37 }; 37 };
38 38
39 #define _MU_FILTER_DISABLED 0x01
40 #define _MU_FILTER_EOF 0x02
41
42 struct _mu_filter_stream 39 struct _mu_filter_stream
43 { 40 {
44 struct _mu_stream stream; 41 struct _mu_stream stream;
45 mu_stream_t transport; 42 mu_stream_t transport;
46 int mode; 43 int mode;
47 int fltflag; 44 unsigned flag_disabled:1;
48 45 unsigned flag_eof:1;
46 size_t outbuf_size;
49 struct _mu_filter_buffer inbuf, outbuf; 47 struct _mu_filter_buffer inbuf, outbuf;
50 mu_filter_xcode_t xcode; 48 mu_filter_xcode_t xcode;
51 void *xdata; 49 void *xdata;
......
...@@ -47,6 +47,7 @@ struct _mu_assoc_elem ...@@ -47,6 +47,7 @@ struct _mu_assoc_elem
47 { 47 {
48 char *name; 48 char *name;
49 struct _mu_assoc_elem *next, *prev; 49 struct _mu_assoc_elem *next, *prev;
50 int mark:1;
50 char *data; 51 char *data;
51 }; 52 };
52 53
...@@ -794,3 +795,33 @@ mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data) ...@@ -794,3 +795,33 @@ mu_assoc_sort_r (mu_assoc_t assoc, mu_assoc_comparator_t cmp, void *data)
794 return 0; 795 return 0;
795 } 796 }
796 797
798 int
799 mu_assoc_mark (mu_assoc_t asc, int (*cond) (char const *, void *, void *),
800 void *data)
801 {
802 struct _mu_assoc_elem *elt;
803
804 if (!asc)
805 return EINVAL;
806 for (elt = asc->head; elt; elt = elt->next)
807 elt->mark = !!cond (elt->name, elt->data, data);
808 return 0;
809 }
810
811 int
812 mu_assoc_sweep (mu_assoc_t asc)
813 {
814 unsigned i;
815
816 if (!asc)
817 return EINVAL;
818
819 for (i = hash_size[asc->hash_num]; i > 0; i--)
820 {
821 if (asc->tab[i-1] && asc->tab[i-1]->mark)
822 assoc_remove (asc, i-1);
823 }
824
825 return 0;
826 }
827
......
...@@ -25,6 +25,7 @@ libfilter_la_SOURCES =\ ...@@ -25,6 +25,7 @@ libfilter_la_SOURCES =\
25 crlfflt.c\ 25 crlfflt.c\
26 decode.c\ 26 decode.c\
27 dot.c\ 27 dot.c\
28 dq.c\
28 filter.c\ 29 filter.c\
29 fltchain.c\ 30 fltchain.c\
30 fromflt.c\ 31 fromflt.c\
...@@ -34,6 +35,7 @@ libfilter_la_SOURCES =\ ...@@ -34,6 +35,7 @@ libfilter_la_SOURCES =\
34 inline-comment.c\ 35 inline-comment.c\
35 linecon.c\ 36 linecon.c\
36 linelenflt.c\ 37 linelenflt.c\
38 percent.c\
37 qpflt.c\ 39 qpflt.c\
38 xml.c 40 xml.c
39 41
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011-2012, 2014-2017 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 /* Doble-quote escaping filter.
19 In encode mode, escapes each occurrence of double-quote and backslash
20 by prefixing it with backslash.
21 In decode mode, removes backslash prefixes.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include <config.h>
26 #endif
27 #include <stdlib.h>
28 #include <string.h>
29 #include <mailutils/errno.h>
30 #include <mailutils/filter.h>
31 #include <mailutils/wordsplit.h>
32 #include <mailutils/cctype.h>
33
34 /* Move min(isize,osize) bytes from iptr to optr, replacing each
35 escapable control character with its escape sequence. */
36 static enum mu_filter_result
37 _dq_encoder (void *xd MU_ARG_UNUSED,
38 enum mu_filter_command cmd,
39 struct mu_filter_io *iobuf)
40 {
41 size_t i, j;
42 const unsigned char *iptr;
43 size_t isize;
44 char *optr;
45 size_t osize;
46
47 switch (cmd)
48 {
49 case mu_filter_init:
50 case mu_filter_done:
51 return mu_filter_ok;
52 default:
53 break;
54 }
55
56 iptr = (const unsigned char *) iobuf->input;
57 isize = iobuf->isize;
58 optr = iobuf->output;
59 osize = iobuf->osize;
60
61 for (i = j = 0; i < isize && j < osize; i++)
62 {
63 unsigned char c = *iptr++;
64
65 if (strchr ("\\\"", c))
66 {
67 if (j + 1 == osize)
68 {
69 if (i == 0)
70 {
71 iobuf->osize = 2;
72 return mu_filter_moreoutput;
73 }
74 break;
75 }
76 else
77 {
78 optr[j++] = '\\';
79 optr[j++] = c;
80 }
81 }
82 else
83 optr[j++] = c;
84 }
85 iobuf->isize = i;
86 iobuf->osize = j;
87 return mu_filter_ok;
88 }
89
90 /* Move min(isize,osize) bytes from iptr to optr, replacing each escape
91 sequence with its ASCII code. */
92 static enum mu_filter_result
93 _dq_decoder (void *xd MU_ARG_UNUSED,
94 enum mu_filter_command cmd,
95 struct mu_filter_io *iobuf)
96 {
97 size_t i, j;
98 const unsigned char *iptr;
99 size_t isize;
100 char *optr;
101 size_t osize;
102
103 switch (cmd)
104 {
105 case mu_filter_init:
106 case mu_filter_done:
107 return mu_filter_ok;
108 default:
109 break;
110 }
111
112 iptr = (const unsigned char *) iobuf->input;
113 isize = iobuf->isize;
114 optr = iobuf->output;
115 osize = iobuf->osize;
116
117 for (i = j = 0; i < isize && j < osize; i++)
118 {
119 unsigned char c = *iptr++;
120 if (c == '\\')
121 {
122 if (i + 1 == isize)
123 break;
124 optr[j++] = *iptr++;
125 }
126 else
127 optr[j++] = c;
128 }
129
130 iobuf->isize = i;
131 iobuf->osize = j;
132 return mu_filter_ok;
133 }
134
135 static struct _mu_filter_record _dq_filter = {
136 "dq",
137 NULL,
138 _dq_encoder,
139 _dq_decoder,
140 };
141
142 mu_filter_record_t mu_dq_filter = &_dq_filter;
143
...@@ -86,6 +86,8 @@ mu_filter_get_list (mu_list_t *plist) ...@@ -86,6 +86,8 @@ mu_filter_get_list (mu_list_t *plist)
86 mu_list_append (filter_list, mu_c_escape_filter); 86 mu_list_append (filter_list, mu_c_escape_filter);
87 mu_list_append (filter_list, mu_htmlent_filter); 87 mu_list_append (filter_list, mu_htmlent_filter);
88 mu_list_append (filter_list, mu_xml_filter); 88 mu_list_append (filter_list, mu_xml_filter);
89 mu_list_append (filter_list, mu_percent_filter);
90 mu_list_append (filter_list, mu_dq_filter);
89 /* FIXME: add the default encodings? */ 91 /* FIXME: add the default encodings? */
90 } 92 }
91 *plist = filter_list; 93 *plist = filter_list;
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2011-2012, 2014-2017 Free Software Foundation, Inc.
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Lesser General Public
6 License as published by the Free Software Foundation; either
7 version 3 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Lesser General Public License for more details.
13
14 You should have received a copy of the GNU Lesser General
15 Public License along with this library. If not, see
16 <http://www.gnu.org/licenses/>. */
17
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21 #include <stdlib.h>
22 #include <string.h>
23 #include <limits.h>
24 #include <mailutils/errno.h>
25 #include <mailutils/filter.h>
26 #include <mailutils/cctype.h>
27
28 static char xchar[] = "0123456789ABCDEF";
29
30 static enum mu_filter_result
31 percent_encoder (void *xd,
32 enum mu_filter_command cmd,
33 struct mu_filter_io *iobuf)
34 {
35 size_t i, j;
36 const unsigned char *iptr;
37 size_t isize;
38 char *optr;
39 size_t osize;
40 char *escape_chars = xd;
41
42 switch (cmd)
43 {
44 case mu_filter_init:
45 case mu_filter_done:
46 return mu_filter_ok;
47 default:
48 break;
49 }
50
51 iptr = (const unsigned char *) iobuf->input;
52 isize = iobuf->isize;
53 optr = iobuf->output;
54 osize = iobuf->osize;
55
56 for (i = j = 0; i < isize && j < osize; i++)
57 {
58 unsigned char c = iptr[i];
59
60 if (c == 0 || strchr (escape_chars, c))
61 {
62 if (j + 3 >= osize)
63 {
64 if (i == 0)
65 {
66 iobuf->osize = 3;
67 return mu_filter_moreoutput;
68 }
69 break;
70 }
71 optr[j++] = '%';
72 optr[j++] = xchar[((c >> 4) & 0xf)];
73 optr[j++] = xchar[c & 0xf];
74 }
75 else
76 optr[j++] = c;
77 }
78 iobuf->isize = i;
79 iobuf->osize = j;
80 return mu_filter_ok;
81 }
82
83 static enum mu_filter_result
84 percent_decoder (void *xd MU_ARG_UNUSED,
85 enum mu_filter_command cmd,
86 struct mu_filter_io *iobuf)
87 {
88 size_t i, j;
89 const unsigned char *iptr;
90 size_t isize;
91 char *optr;
92 size_t osize;
93
94 switch (cmd)
95 {
96 case mu_filter_init:
97 case mu_filter_done:
98 return mu_filter_ok;
99 default:
100 break;
101 }
102
103 iptr = (const unsigned char *) iobuf->input;
104 isize = iobuf->isize;
105 optr = iobuf->output;
106 osize = iobuf->osize;
107
108 for (i = j = 0; i < isize && j < osize; j++)
109 {
110 unsigned char c = iptr[i++];
111 if (c == '%')
112 {
113 char *phi, *plo;
114
115 if (i + 2 >= isize)
116 break;
117 phi = strchr (xchar, mu_toupper (iptr[i]));
118 plo = strchr (xchar, mu_toupper (iptr[i+1]));
119 if (phi && plo)
120 {
121 optr[j] = ((phi - xchar) << 4) + (plo - xchar);
122 i += 2;
123 }
124 else
125 optr[j] = c;
126 }
127 else
128 optr[j] = c;
129 }
130 iobuf->isize = i;
131 iobuf->osize = j;
132 return mu_filter_ok;
133 }
134
135 static int
136 percent_alloc (void **pret, int mode, int argc, const char **argv)
137 {
138 if (mode == MU_FILTER_ENCODE)
139 {
140 char *s;
141 if (argc > 1)
142 {
143 int i;
144 size_t len = 0;
145
146 for (i = 1; i < argc; i++)
147 len += strlen (argv[i]);
148 s = malloc (len + 1);
149 if (!s)
150 return ENOMEM;
151 *pret = s;
152 *s = 0;
153 for (i = 1; i < argc; i++)
154 strcat (s, argv[i]);
155 }
156 else
157 {
158 int i;
159
160 s = malloc (UCHAR_MAX);
161 if (!s)
162 return ENOMEM;
163 *pret = s;
164 for (i = 1; i <= UCHAR_MAX; i++)
165 {
166 if (i == '%' || i == '"' || !mu_isgraph (i))
167 *s++ = i;
168 }
169 *s = 0;
170 }
171 }
172 else
173 *pret = NULL;
174 return 0;
175 }
176
177 static struct _mu_filter_record _percent_filter = {
178 "percent",
179 percent_alloc,
180 percent_encoder,
181 percent_decoder
182 };
183
184 mu_filter_record_t mu_percent_filter = &_percent_filter;
185
186
...@@ -20,6 +20,7 @@ noinst_LTLIBRARIES = libmime.la ...@@ -20,6 +20,7 @@ noinst_LTLIBRARIES = libmime.la
20 libmime_la_SOURCES = \ 20 libmime_la_SOURCES = \
21 attachment.c\ 21 attachment.c\
22 mime.c\ 22 mime.c\
23 mimehdr.c 23 mimehdr.c\
24 mimehdrset.c
24 25
25 AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils 26 AM_CPPFLAGS = @MU_LIB_COMMON_INCLUDES@ -I/libmailutils
......
...@@ -38,6 +38,8 @@ ...@@ -38,6 +38,8 @@
38 #include <mailutils/header.h> 38 #include <mailutils/header.h>
39 #include <mailutils/errno.h> 39 #include <mailutils/errno.h>
40 #include <mailutils/util.h> 40 #include <mailutils/util.h>
41 #include <mailutils/assoc.h>
42 #include <mailutils/io.h>
41 43
42 #include <mailutils/sys/mime.h> 44 #include <mailutils/sys/mime.h>
43 #include <mailutils/sys/message.h> 45 #include <mailutils/sys/message.h>
...@@ -445,6 +447,13 @@ _mimepart_body_lines (mu_body_t body, size_t *plines) ...@@ -445,6 +447,13 @@ _mimepart_body_lines (mu_body_t body, size_t *plines)
445 447
446 /*------ Mime message/header functions for CREATING multipart message -----*/ 448 /*------ Mime message/header functions for CREATING multipart message -----*/
447 static int 449 static int
450 retain_charset (char const *name, void *value MU_ARG_UNUSED,
451 void *data MU_ARG_UNUSED)
452 {
453 return strcmp (name, "charset") != 0;
454 }
455
456 static int
448 _mime_set_content_type (mu_mime_t mime) 457 _mime_set_content_type (mu_mime_t mime)
449 { 458 {
450 const char *content_type; 459 const char *content_type;
...@@ -473,9 +482,36 @@ _mime_set_content_type (mu_mime_t mime) ...@@ -473,9 +482,36 @@ _mime_set_content_type (mu_mime_t mime)
473 for (i = 0; i < mime->nmtp_parts; i++) 482 for (i = 0; i < mime->nmtp_parts; i++)
474 { 483 {
475 mu_header_t hdr; 484 mu_header_t hdr;
485 char *val;
486 int rc;
476 487
477 mu_message_get_header (mime->mtp_parts[i]->msg, &hdr); 488 mu_message_get_header (mime->mtp_parts[i]->msg, &hdr);
478 mu_header_remove (hdr, MU_HEADER_CONTENT_DISPOSITION, 1); 489 mu_header_remove (hdr, MU_HEADER_CONTENT_DISPOSITION, 1);
490
491 rc = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE,
492 &val);
493 if (rc == 0)
494 {
495 mu_content_type_t ct;
496 rc = mu_content_type_parse (val, NULL, &ct);
497 if (rc == 0)
498 {
499 char *type;
500
501 mu_assoc_mark (ct->param, retain_charset, NULL);
502 mu_assoc_sweep (ct->param);
503
504 rc = mu_asprintf (&type, "%s/%s", ct->type, ct->subtype);
505 if (rc == 0)
506 {
507 mu_mime_header_set (hdr,
508 MU_HEADER_CONTENT_TYPE, type,
509 ct->param);
510 free (type);
511 }
512 mu_content_type_destroy (&ct);
513 }
514 }
479 } 515 }
480 516
481 content_type = "multipart/alternative; boundary="; 517 content_type = "multipart/alternative; boundary=";
......
1 /* Functions for formatting RFC-2231-compliant mail headers fields.
2 GNU Mailutils -- a suite of utilities for electronic mail
3 Copyright (C) 1999-2001, 2004-2005, 2007, 2009-2012, 2014-2017 Free
4 Software Foundation, Inc.
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 3 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General
17 Public License along with this library. If not,
18 see <http://www.gnu.org/licenses/>. */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdlib.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <mailutils/mime.h>
27 #include <mailutils/cctype.h>
28 #include <mailutils/cstr.h>
29 #include <mailutils/header.h>
30 #include <mailutils/stream.h>
31 #include <mailutils/filter.h>
32 #include <mailutils/assoc.h>
33 #include <mailutils/errno.h>
34
35 struct header_buffer
36 {
37 mu_stream_t str; /* Output stream */
38 size_t line_len; /* Length of current line */
39 size_t line_max; /* Max. line length */
40 };
41
42 static int
43 mime_store_parameter (char const *name, void *value, void *data)
44 {
45 struct mu_mime_param *p = value;
46 struct header_buffer *hbuf = data;
47 size_t nlen; /* Length of parameter name
48 (eq. sign, eventual seqence no. and language info mark
49 included) */
50 size_t vlen; /* Length of lang'charset' part */
51 int langinfo; /* True if language info is available */
52 int quote = 0; /* 2 if the value should be quoted, 0 otherwise */
53 int segment = -1; /* Segment sequence number */
54 mu_stream_t valstr; /* Value stream (properly encoded) */
55 mu_off_t valsize; /* Number of octets left in valstr */
56 char const *filter_name = NULL; /* Name of the filter for the value */
57 int rc;
58
59 rc = mu_static_memory_stream_create (&valstr, p->value, strlen (p->value));
60 if (rc)
61 return rc;
62
63 nlen = strlen (name);
64 if (p->lang || p->cset)
65 {
66 vlen = 2;
67 if (p->lang)
68 vlen += strlen (p->lang);
69 if (p->cset)
70 vlen += strlen (p->cset);
71 langinfo = 1;
72 filter_name = "percent";
73 }
74 else
75 {
76 if (*mu_str_skip_class_comp (p->value, MU_CTYPE_TSPEC|MU_CTYPE_BLANK))
77 {
78 /* Must be in quoted-string, to use within parameter values */
79 quote = 2;
80 filter_name = "dq";
81 }
82 else
83 quote = 0;
84 vlen = 0;
85 langinfo = 0;
86 }
87
88 if (filter_name)
89 {
90 mu_stream_t tmp;
91
92 rc = mu_filter_create (&tmp, valstr, filter_name, MU_FILTER_ENCODE,
93 MU_STREAM_READ | MU_STREAM_SEEK);
94 if (rc)
95 goto err;
96 mu_stream_unref (valstr);
97 valstr = tmp;
98 rc = mu_memory_stream_create (&tmp, MU_STREAM_RDWR);
99 if (rc == 0)
100 {
101 rc = mu_stream_copy (tmp, valstr, 0, &valsize);
102 mu_stream_destroy (&tmp);
103 }
104 }
105 else
106 rc = mu_stream_size (valstr, &valsize);
107
108 if (rc)
109 goto err;
110
111 nlen += langinfo;
112
113 rc = mu_stream_seek (valstr, 0, MU_SEEK_SET, NULL);
114
115 if (hbuf->line_max == 0)
116 {
117 /* No line wrapping requested. Store the value as it is */
118 mu_stream_printf (hbuf->str, "%s", name);
119 if (langinfo)
120 mu_stream_write (hbuf->str, "*", 1, NULL);
121 mu_stream_write (hbuf->str, "=", 1, NULL);
122 if (vlen)
123 {
124 mu_stream_printf (hbuf->str, "%s'%s'",
125 p->lang ? p->lang : "",
126 p->cset ? p->cset : "");
127 vlen = 0;
128 }
129 else if (quote)
130 mu_stream_write (hbuf->str, "\"", 1, NULL);
131 mu_stream_copy (hbuf->str, valstr, 0, NULL);
132 if (quote)
133 mu_stream_write (hbuf->str, "\"", 1, NULL);
134 if (mu_stream_err (hbuf->str))
135 rc = mu_stream_last_error (hbuf->str);
136 }
137 else
138 {
139 /* Split the value into sequentially indexed segments, each one no
140 wider than the requested line width.
141
142 Without special precautions, an encoded character occurring at
143 the end of a segment can be split between this and the following
144 segment to satisfy line width requirements. To avoid this, the
145 following approach is used:
146
147 1. The value stream is put to unbuffered mode.
148 2. Before each write, the size of the transcoder output buffer
149 in valstr is set to the number of bytes left in the current
150 line.
151
152 This way the transcoder will write as many bytes as possible
153 without breaking the encoded constructs while the unbuffered mode
154 will ensure that it will not be called again to fill up the stream
155 buffer.
156
157 If the line width is insufficient, MU_ERR_BUFSPACE will be returned.
158 */
159 char *iobuf;
160
161 iobuf = malloc (hbuf->line_max + 1);
162 if (!iobuf)
163 {
164 rc = errno;
165 goto err;
166 }
167
168 mu_stream_set_buffer (valstr, mu_buffer_none, 0);
169
170 while (rc == 0 && valsize)
171 {
172 mu_off_t start, nr; /* Start and end positions in stream */
173 size_t sz, n;
174
175 mu_stream_write (hbuf->str, ";", 1, NULL);
176 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &start);
177
178 if (segment >= 0)
179 {
180 mu_stream_write (hbuf->str, "\n", 1, NULL);
181 hbuf->line_len = 0;
182 segment++;
183 }
184 else if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
185 hbuf->line_max)
186 {
187 mu_stream_write (hbuf->str, "\n", 1, NULL);
188 hbuf->line_len = 0;
189 if (hbuf->line_len + valsize + quote + vlen + nlen + 1 >
190 hbuf->line_max)
191 segment++;
192 }
193
194 mu_stream_write (hbuf->str, " ", 1, NULL);
195
196 if (segment >= 0)
197 mu_stream_printf (hbuf->str, "%s*%d", name, segment);
198 else
199 mu_stream_printf (hbuf->str, "%s", name);
200 if (langinfo)
201 mu_stream_write (hbuf->str, "*", 1, NULL);
202 mu_stream_write (hbuf->str, "=", 1, NULL);
203 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
204 nlen = nr - start;
205 hbuf->line_len += nlen;
206 start = nr;
207
208 /* Compute the number of octets to put into the current line.
209 If the requested line width is not enough to accomodate
210 the line, signal the error */
211 if (hbuf->line_max <= (hbuf->line_len + quote + vlen))
212 {
213 rc = MU_ERR_BUFSPACE;
214 break;
215 }
216
217 sz = hbuf->line_max - (hbuf->line_len + quote + vlen);
218 mu_stream_ioctl (valstr, MU_IOCTL_FILTER,
219 MU_IOCTL_FILTER_SET_OUTBUF_SIZE, &sz);
220
221 rc = mu_stream_read (valstr, iobuf, sz, &n);
222 if (rc || n == 0)
223 break;
224
225 if (vlen)
226 {
227 mu_stream_printf (hbuf->str, "%s'%s'",
228 p->lang ? p->lang : "",
229 p->cset ? p->cset : "");
230 vlen = 0;
231 }
232 else if (quote)
233 mu_stream_write (hbuf->str, "\"", 1, NULL);
234
235 mu_stream_write (hbuf->str, iobuf, n, NULL);
236
237 if (quote)
238 mu_stream_write (hbuf->str, "\"", 1, NULL);
239 mu_stream_seek (hbuf->str, 0, MU_SEEK_CUR, &nr);
240 nr -= start;
241 hbuf->line_len += nr;
242 valsize -= n;
243
244 if (mu_stream_err (hbuf->str))
245 rc = mu_stream_last_error (hbuf->str);
246 }
247 free (iobuf);
248 }
249 err:
250 mu_stream_destroy (&valstr);
251
252 return rc;
253 }
254
255 static int
256 mime_header_format (const char *value, mu_assoc_t params,
257 struct header_buffer *hbuf)
258 {
259 size_t l = strlen (value);
260
261 mu_stream_write (hbuf->str, value, l, NULL);
262 hbuf->line_len += l;
263 return mu_assoc_foreach (params, mime_store_parameter, hbuf);
264 }
265
266 /* Store a header in accordance with RFC 2231, Section 3,
267 "Parameter Value Continuations"
268
269 HDR - Message header object
270 NAME - Header name
271 VALUE - Header value part
272 PARAMS - Named parameters (assoc of struct mu_mime_param *)
273 LINE_WIDTH - Maximum line width.
274 */
275
276 int
277 mu_mime_header_set_w (mu_header_t hdr, const char *name,
278 const char *value, mu_assoc_t params, size_t line_width)
279 {
280 struct header_buffer hbuf;
281 int rc;
282
283 rc = mu_memory_stream_create (&hbuf.str, MU_STREAM_RDWR);
284 if (rc)
285 return rc;
286 hbuf.line_len = strlen (name) + 2;
287 hbuf.line_max = line_width;
288 rc = mime_header_format (value, params, &hbuf);
289 if (rc == 0)
290 {
291 mu_off_t pos;
292 char *fmtval;
293
294 mu_stream_seek (hbuf.str, 0, MU_SEEK_CUR, &pos);
295 fmtval = malloc (pos + 1);
296 mu_stream_seek (hbuf.str, 0, MU_SEEK_SET, NULL);
297 mu_stream_read (hbuf.str, fmtval, pos, NULL);
298 fmtval[pos] = 0;
299 rc = mu_header_set_value (hdr, name, fmtval, 1);
300 free (fmtval);
301 }
302 mu_stream_destroy (&hbuf.str);
303 return rc;
304 }
305
306 int
307 mu_mime_header_set (mu_header_t hdr, const char *name,
308 const char *value, mu_assoc_t params)
309 {
310 return mu_mime_header_set_w (hdr, name, value, params, 76);
311 }
312
313
314
...@@ -55,20 +55,20 @@ int mu_c_tab[MU_C_TAB_MAX] = { ...@@ -55,20 +55,20 @@ int mu_c_tab[MU_C_TAB_MAX] = {
55 MU_CTYPE_CNTRL, 55 MU_CTYPE_CNTRL,
56 MU_CTYPE_PRINT|MU_CTYPE_SPACE|MU_CTYPE_BLANK, 56 MU_CTYPE_PRINT|MU_CTYPE_SPACE|MU_CTYPE_BLANK,
57 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 57 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
58 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
58 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 59 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
59 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 60 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
60 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 61 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
61 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 62 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
62 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 63 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
64 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
65 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
63 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 66 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
64 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 67 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
68 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
65 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 69 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
66 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 70 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
67 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 71 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
68 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
69 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
70 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
71 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
72 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 72 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
73 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 73 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
74 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 74 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
...@@ -79,13 +79,13 @@ int mu_c_tab[MU_C_TAB_MAX] = { ...@@ -79,13 +79,13 @@ int mu_c_tab[MU_C_TAB_MAX] = {
79 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 79 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
80 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 80 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
81 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT, 81 MU_CTYPE_DIGIT|MU_CTYPE_GRAPH|MU_CTYPE_PRINT,
82 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 82 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
83 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 83 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
84 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 84 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
85 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 85 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
86 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 86 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
87 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 87 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
88 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 88 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
89 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR, 89 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR,
90 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR, 90 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR,
91 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER|MU_CTYPE_XLETR, 91 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] = { ...@@ -112,9 +112,9 @@ int mu_c_tab[MU_C_TAB_MAX] = {
112 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER, 112 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
113 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER, 113 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
114 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER, 114 MU_CTYPE_ALPHA|MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_UPPER,
115 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 115 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
116 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 116 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
117 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 117 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT|MU_CTYPE_TSPEC,
118 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 118 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
119 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 119 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
120 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT, 120 MU_CTYPE_GRAPH|MU_CTYPE_PRINT|MU_CTYPE_PUNCT,
......
...@@ -119,13 +119,128 @@ filename(lang:el_GR/UTF-8)=αρχείο για την δοκιμασία ...@@ -119,13 +119,128 @@ filename(lang:el_GR/UTF-8)=αρχείο για την δοκιμασία
119 119
120 MIMEHDR([combined charset, lang and cset],[mimehdr08 mimehdr-comb mimehdr-charset-rfc2231], 120 MIMEHDR([combined charset, lang and cset],[mimehdr08 mimehdr-comb mimehdr-charset-rfc2231],
121 [], 121 [],
122 [application/x-stuff 122 [application/x-stuff;
123 title*0*=us-ascii'en'This%20is%20even%20more%20 123 title*0*=us-ascii'en'This%20is%20even%20more%20;
124 title*1*=%2A%2A%2Afun%2A%2A%2A%20 124 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
125 title*2="isn't it!" 125 title*2="isn't it!"
126 ], 126 ],
127 [application/x-stuff 127 [application/x-stuff
128 title(lang:en/us-ascii)=This is even more ***fun*** isn't it! 128 title(lang:en/us-ascii)=This is even more ***fun*** isn't it!
129 ]) 129 ])
130 130
131 MIMEHDR([format: simple],[mimehdr09],
132 [-header=X-MIME-Test-Header -width=26],
133 [application/x-stuff; charset=ascii; title=foobar],
134 [X-MIME-Test-Header: application/x-stuff;
135 charset=ascii;
136 title=foobar
137
138 ])
139
140 MIMEHDR([format: split],[mimehdr10],
141 [-header=X-MIME-Test-Header -width=76],
142 [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"],
143 [X-MIME-Test-Header: attachment;
144 title*0="unusually long filename created by gray expressly for testing pu";
145 title*1="rposes and not meant for any other purpose but testing the forma";
146 title*2="tting of very long MIME headers"
147
148 ])
149
150 MIMEHDR([format: split 2],[mimehdr11],
151 [-header=X-MIME-Test-Header -width=26],
152 [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"],
153 [X-MIME-Test-Header: attachment;
154 title*0="unusually long";
155 title*1=" filename crea";
156 title*2="ted by gray ex";
157 title*3="pressly for te";
158 title*4="sting purposes";
159 title*5=" and not meant";
160 title*6=" for any other";
161 title*7=" purpose but t";
162 title*8="esting the for";
163 title*9="matting of ver";
164 title*10="y long MIME h";
165 title*11="eaders"
166
167 ])
168
169 # This test is split into 5 subtests to make sure encoded character
170 # representation is not split between subsequent numbered parameters.
171 MIMEHDR([format: language info 1],[mimehdr12 mimehdr12a],
172 [-header=X-MIME-Test-Header -width=27],
173 [application/x-stuff;
174 title*0*=us-ascii'en'This%20is%20even%20more%20;
175 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
176 title*2="isn't it!"
177 ],
178 [X-MIME-Test-Header: application/x-stuff;
179 title*0*=en'us-ascii'This;
180 title*1*=%20is%20even%20m;
181 title*2*=ore%20***fun***;
182 title*3*=%20isn't%20it!
183
184 ])
185
186 MIMEHDR([format: language info 2],[mimehdr12 mimehdr12b],
187 [-header=X-MIME-Test-Header -width=28],
188 [application/x-stuff;
189 title*0*=us-ascii'en'This%20is%20even%20more%20;
190 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
191 title*2="isn't it!"
192 ],
193 [X-MIME-Test-Header: application/x-stuff;
194 title*0*=en'us-ascii'This;
195 title*1*=%20is%20even%20mo;
196 title*2*=re%20***fun***;
197 title*3*=%20isn't%20it!
198
199 ])
200
201 MIMEHDR([format: language info 3],[mimehdr12 mimehdr12c],
202 [-header=X-MIME-Test-Header -width=29],
203 [application/x-stuff;
204 title*0*=us-ascii'en'This%20is%20even%20more%20;
205 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
206 title*2="isn't it!"
207 ],
208 [X-MIME-Test-Header: application/x-stuff;
209 title*0*=en'us-ascii'This;
210 title*1*=%20is%20even%20mor;
211 title*2*=e%20***fun***%20is;
212 title*3*=n't%20it!
213
214 ])
215
216 MIMEHDR([format: language info 4],[mimehdr12 mimehdr12d],
217 [-header=X-MIME-Test-Header -width=30],
218 [application/x-stuff;
219 title*0*=us-ascii'en'This%20is%20even%20more%20;
220 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
221 title*2="isn't it!"
222 ],
223 [X-MIME-Test-Header: application/x-stuff;
224 title*0*=en'us-ascii'This;
225 title*1*=%20is%20even%20more;
226 title*2*=%20***fun***%20isn';
227 title*3*=t%20it!
228
229 ])
230
231 MIMEHDR([format: language info 5],[mimehdr12 mimehdr12e],
232 [-header=X-MIME-Test-Header -width=31],
233 [application/x-stuff;
234 title*0*=us-ascii'en'This%20is%20even%20more%20;
235 title*1*=%2A%2A%2Afun%2A%2A%2A%20;
236 title*2="isn't it!"
237 ],
238 [X-MIME-Test-Header: application/x-stuff;
239 title*0*=en'us-ascii'This%20i;
240 title*1*=s%20even%20more%20**;
241 title*2*=*fun***%20isn't%20it;
242 title*3*=!
243
244 ])
245
131 m4_popdef([MIMEHDR]) 246 m4_popdef([MIMEHDR])
......
...@@ -61,6 +61,8 @@ main (int argc, char **argv) ...@@ -61,6 +61,8 @@ main (int argc, char **argv)
61 char *value; 61 char *value;
62 mu_assoc_t assoc; 62 mu_assoc_t assoc;
63 char *charset = NULL; 63 char *charset = NULL;
64 char *header_name = NULL;
65 unsigned long width = 76;
64 66
65 mu_set_program_name (argv[0]); 67 mu_set_program_name (argv[0]);
66 for (i = 1; i < argc; i++) 68 for (i = 1; i < argc; i++)
...@@ -73,9 +75,13 @@ main (int argc, char **argv) ...@@ -73,9 +75,13 @@ main (int argc, char **argv)
73 charset = opt + 9; 75 charset = opt + 9;
74 else if (strcmp (opt, "-h") == 0 || strcmp (opt, "-help") == 0) 76 else if (strcmp (opt, "-h") == 0 || strcmp (opt, "-help") == 0)
75 { 77 {
76 mu_printf ("usage: %s [-charset=cs] [-debug=SPEC]", mu_program_name); 78 mu_printf ("usage: %s [-charset=cs] [-debug=SPEC] [-header=NAME] [-width=N]", mu_program_name);
77 return 0; 79 return 0;
78 } 80 }
81 else if (strncmp (opt, "-header=", 8) == 0)
82 header_name = opt + 8;
83 else if (strncmp (opt, "-width=", 7) == 0)
84 width = strtoul (opt + 7, NULL, 10);
79 else 85 else
80 { 86 {
81 mu_error ("unknown option %s", opt); 87 mu_error ("unknown option %s", opt);
...@@ -97,9 +103,22 @@ main (int argc, char **argv) ...@@ -97,9 +103,22 @@ main (int argc, char **argv)
97 103
98 MU_ASSERT (mu_mime_header_parse ((char*)trans[0], charset, &value, &assoc)); 104 MU_ASSERT (mu_mime_header_parse ((char*)trans[0], charset, &value, &assoc));
99 105
106 if (header_name)
107 {
108 mu_header_t hdr;
109 mu_stream_t hstr;
110
111 MU_ASSERT (mu_header_create (&hdr, NULL, 0));
112 MU_ASSERT (mu_mime_header_set_w (hdr, header_name, value, assoc, width));
113 MU_ASSERT (mu_header_get_streamref (hdr, &hstr));
114 MU_ASSERT (mu_stream_copy (mu_strout, hstr, 0, NULL));
115 }
116 else
117 {
100 mu_printf ("%s\n", value); 118 mu_printf ("%s\n", value);
101 mu_assoc_sort_r (assoc, sort_names, NULL); 119 mu_assoc_sort_r (assoc, sort_names, NULL);
102 mu_assoc_foreach (assoc, print_param, NULL); 120 mu_assoc_foreach (assoc, print_param, NULL);
121 }
103 122
104 return 0; 123 return 0;
105 } 124 }
......
...@@ -464,8 +464,8 @@ saveatt (void *item, void *data) ...@@ -464,8 +464,8 @@ saveatt (void *item, void *data)
464 char *p; 464 char *p;
465 465
466 rc = mu_attachment_create (&part, aptr->content_type, aptr->encoding, 466 rc = mu_attachment_create (&part, aptr->content_type, aptr->encoding,
467 env->alt ? NULL : aptr->name, 467 aptr->name,
468 env->alt ? NULL : aptr->filename); 468 aptr->filename);
469 if (rc) 469 if (rc)
470 { 470 {
471 mu_error (_("can't create attachment %s: %s"), 471 mu_error (_("can't create attachment %s: %s"),
......