Re-implement parser for RFC-2231 header fields.
The new implementation is able to return all parameters at once, in an associative array. A subset of parameters can be requested. The data are automatically converted to the output charset. In addition, RFC-2047 parser is extended to support language specifications (RFC-2231, chapter 5). * include/mailutils/message.h (MU_MIMEHDR_CSINFO) (MU_MIMEHDR_MULTILINE): Remove, not public anymore. (mu_mimehdr_get_param,mu_mimehdr_aget_param) (mu_message_aget_attachment_name) (mu_message_get_attachment_name): Remove pflags agrument. * include/mailutils/mime.h (mu_mime_param): New struct. (mu_rfc2047_decode_param) (mu_mime_header_parse,mu_mime_header_parse_subset): New proto. * libmailutils/base/rfc2047.c (_rfc2047_decode_param): New auxiliary function. Use memory stream to collect data. (mu_rfc2047_decode): Rewrite as a wrapper around the above. (mu_rfc2047_decode_param): New function. * libmailutils/filter/decode.c (mu_decode_filter_args): Pass actual (instead of maximal) number of arguments to mu_filter_chain_create. * libmailutils/mime/mimehdr.c: Rewrite from scratch. * libmailutils/tests/.gitignore: Add mimehdr. * libmailutils/tests/Makefile.am (noinst_PROGRAMS): Add mimehdr. (TESTSUITE_AT): Add mimehdr.at. * libmailutils/tests/mimehdr.at: New test. * libmailutils/tests/mimehdr.c: New test program. * libmailutils/tests/testsuite.at: Include.
Showing
10 changed files
with
1165 additions
and
589 deletions
... | @@ -162,31 +162,20 @@ extern int mu_mime_io_buffer_aget_charset (mu_mime_io_buffer_t info, | ... | @@ -162,31 +162,20 @@ extern int mu_mime_io_buffer_aget_charset (mu_mime_io_buffer_t info, |
162 | const char **charset); | 162 | const char **charset); |
163 | 163 | ||
164 | 164 | ||
165 | /* Bit values for *pflags in functions below */ | ||
166 | #define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */ | ||
167 | #define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language | ||
168 | info */ | ||
169 | |||
170 | extern int mu_mimehdr_get_disp (const char *str, char *buf, size_t bufsz, | 165 | extern int mu_mimehdr_get_disp (const char *str, char *buf, size_t bufsz, |
171 | size_t *retsz); | 166 | size_t *retsz); |
172 | extern int mu_mimehdr_aget_disp (const char *str, char **pvalue); | 167 | extern int mu_mimehdr_aget_disp (const char *str, char **pvalue); |
173 | extern int mu_mimehdr_get_param (const char *str, const char *param, | 168 | extern int mu_mimehdr_get_param (const char *str, const char *param, |
174 | char *buf, size_t bufsz, size_t *retsz, | 169 | char *buf, size_t bufsz, size_t *retsz); |
175 | int *pflags); | ||
176 | extern int mu_mimehdr_aget_param (const char *str, const char *param, | 170 | extern int mu_mimehdr_aget_param (const char *str, const char *param, |
177 | char **pval, int *pflags); | 171 | char **pval); |
178 | extern int mu_mimehdr_decode_param (const char *value, int csinfo, | ||
179 | const char *charset, | ||
180 | char **pval, char **plang); | ||
181 | extern int mu_mimehdr_aget_decoded_param (const char *str, const char *param, | 172 | extern int mu_mimehdr_aget_decoded_param (const char *str, const char *param, |
182 | const char *charset, | 173 | const char *charset, |
183 | char **pval, char **plang); | 174 | char **pval, char **plang); |
184 | 175 | ||
185 | extern int mu_message_get_attachment_name (mu_message_t, char *name, | 176 | extern int mu_message_get_attachment_name (mu_message_t, char *name, |
186 | size_t bufsz, size_t* sz, | 177 | size_t bufsz, size_t* sz); |
187 | int *pflags); | 178 | extern int mu_message_aget_attachment_name (mu_message_t, char **name); |
188 | extern int mu_message_aget_attachment_name (mu_message_t, char **name, | ||
189 | int *pflags); | ||
190 | extern int mu_message_aget_decoded_attachment_name (mu_message_t msg, | 179 | extern int mu_message_aget_decoded_attachment_name (mu_message_t msg, |
191 | const char *charset, | 180 | const char *charset, |
192 | char **name, | 181 | char **name, | ... | ... |
... | @@ -29,6 +29,13 @@ | ... | @@ -29,6 +29,13 @@ |
29 | extern "C" { | 29 | extern "C" { |
30 | #endif | 30 | #endif |
31 | 31 | ||
32 | struct mu_mime_param | ||
33 | { | ||
34 | char *lang; | ||
35 | char *cset; | ||
36 | char *value; | ||
37 | }; | ||
38 | |||
32 | int mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags); | 39 | int mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags); |
33 | void mu_mime_destroy (mu_mime_t *pmime); | 40 | void mu_mime_destroy (mu_mime_t *pmime); |
34 | void mu_mime_ref (mu_mime_t mime); | 41 | void mu_mime_ref (mu_mime_t mime); |
... | @@ -49,13 +56,22 @@ int mu_rfc2047_decode (const char *tocode, const char *fromstr, | ... | @@ -49,13 +56,22 @@ int mu_rfc2047_decode (const char *tocode, const char *fromstr, |
49 | 56 | ||
50 | int mu_rfc2047_encode (const char *charset, const char *encoding, | 57 | int mu_rfc2047_encode (const char *charset, const char *encoding, |
51 | const char *text, char **result); | 58 | const char *text, char **result); |
59 | int mu_rfc2047_decode_param (const char *tocode, const char *input, | ||
60 | struct mu_mime_param *param); | ||
52 | 61 | ||
53 | int mu_base64_encode (const unsigned char *input, size_t input_len, | 62 | int mu_base64_encode (const unsigned char *input, size_t input_len, |
54 | unsigned char **output, size_t * output_len); | 63 | unsigned char **output, size_t * output_len); |
55 | 64 | ||
56 | int mu_base64_decode (const unsigned char *input, size_t input_len, | 65 | int mu_base64_decode (const unsigned char *input, size_t input_len, |
57 | unsigned char **output, size_t * output_len); | 66 | unsigned char **output, size_t * output_len); |
58 | 67 | ||
68 | |||
69 | int mu_mime_header_parse (const char *text, char *charset, char **pvalue, | ||
70 | mu_assoc_t *paramtab); | ||
71 | int mu_mime_header_parse_subset (const char *text, const char *charset, | ||
72 | char **pvalue, | ||
73 | mu_assoc_t assoc); | ||
74 | |||
59 | #ifdef __cplusplus | 75 | #ifdef __cplusplus |
60 | } | 76 | } |
61 | #endif | 77 | #endif | ... | ... |
... | @@ -28,20 +28,9 @@ | ... | @@ -28,20 +28,9 @@ |
28 | #include <mailutils/stream.h> | 28 | #include <mailutils/stream.h> |
29 | #include <mailutils/filter.h> | 29 | #include <mailutils/filter.h> |
30 | #include <mailutils/errno.h> | 30 | #include <mailutils/errno.h> |
31 | #include <mailutils/mime.h> | ||
31 | #include <mailutils/util.h> | 32 | #include <mailutils/util.h> |
32 | 33 | ||
33 | static int | ||
34 | realloc_buffer (char **bufp, size_t *bufsizep, size_t incr) | ||
35 | { | ||
36 | size_t newsize = *bufsizep + incr; | ||
37 | char *newp = realloc (*bufp, newsize); | ||
38 | if (newp == NULL) | ||
39 | return 1; | ||
40 | *bufp = newp; | ||
41 | *bufsizep = newsize; | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | int | 34 | int |
46 | getword (char **pret, const char **pstr, int delim) | 35 | getword (char **pret, const char **pstr, int delim) |
47 | { | 36 | { |
... | @@ -65,52 +54,32 @@ getword (char **pret, const char **pstr, int delim) | ... | @@ -65,52 +54,32 @@ getword (char **pret, const char **pstr, int delim) |
65 | return 0; | 54 | return 0; |
66 | } | 55 | } |
67 | 56 | ||
68 | int | 57 | static int |
69 | mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) | 58 | _rfc2047_decode_param (const char *tocode, const char *input, |
59 | struct mu_mime_param *param) | ||
70 | { | 60 | { |
71 | int status = 0; | 61 | int status = 0; |
72 | const char *fromstr; | 62 | const char *fromstr; |
73 | char *buffer; | ||
74 | size_t bufsize; | ||
75 | size_t bufpos; | ||
76 | size_t run_count = 0; | 63 | size_t run_count = 0; |
77 | char *fromcode = NULL; | 64 | char *fromcode = NULL; |
78 | char *encoding_type = NULL; | 65 | char *encoding_type = NULL; |
79 | char *encoded_text = NULL; | 66 | char *encoded_text = NULL; |
67 | char *tocodetmp = NULL; | ||
68 | mu_stream_t str; | ||
80 | 69 | ||
81 | #define BUFINC 128 | 70 | memset (param, 0, sizeof (*param)); |
82 | #define CHKBUF(count) do { \ | ||
83 | if (bufpos+count >= bufsize) \ | ||
84 | { \ | ||
85 | size_t s = bufpos + count - bufsize; \ | ||
86 | if (s < BUFINC) \ | ||
87 | s = BUFINC; \ | ||
88 | if (realloc_buffer (&buffer, &bufsize, s)) \ | ||
89 | { \ | ||
90 | free (buffer); \ | ||
91 | free (fromcode); \ | ||
92 | free (encoding_type); \ | ||
93 | free (encoded_text); \ | ||
94 | return ENOMEM; \ | ||
95 | } \ | ||
96 | } \ | ||
97 | } while (0) | ||
98 | |||
99 | if (!input) | ||
100 | return EINVAL; | ||
101 | if (!ptostr) | ||
102 | return MU_ERR_OUT_PTR_NULL; | ||
103 | 71 | ||
104 | fromstr = input; | 72 | status = mu_memory_stream_create (&str, MU_STREAM_RDWR); |
73 | if (status) | ||
74 | return status; | ||
105 | 75 | ||
106 | /* Allocate the buffer. It is assumed that encoded string is always | 76 | if (tocode && (param->cset = strdup (tocode)) == NULL) |
107 | longer than it's decoded variant, so it's safe to use its length | 77 | { |
108 | as the first estimate */ | 78 | mu_stream_destroy (&str); |
109 | bufsize = strlen (fromstr) + 1; | 79 | return ENOMEM; |
110 | buffer = malloc (bufsize); | 80 | } |
111 | if (buffer == NULL) | 81 | |
112 | return ENOMEM; | 82 | fromstr = input; |
113 | bufpos = 0; | ||
114 | 83 | ||
115 | while (*fromstr) | 84 | while (*fromstr) |
116 | { | 85 | { |
... | @@ -119,13 +88,39 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) | ... | @@ -119,13 +88,39 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) |
119 | mu_stream_t filter = NULL; | 88 | mu_stream_t filter = NULL; |
120 | mu_stream_t in_stream = NULL; | 89 | mu_stream_t in_stream = NULL; |
121 | const char *filter_type = NULL; | 90 | const char *filter_type = NULL; |
122 | size_t nbytes = 0, size; | 91 | size_t size; |
123 | const char *sp = fromstr + 2; | 92 | const char *sp = fromstr + 2; |
124 | char tmp[128]; | 93 | char *lang; |
125 | 94 | ||
126 | status = getword (&fromcode, &sp, '?'); | 95 | status = getword (&fromcode, &sp, '?'); |
127 | if (status) | 96 | if (status) |
128 | break; | 97 | break; |
98 | lang = strchr (fromcode, '*'); | ||
99 | if (lang) | ||
100 | *lang++ = 0; | ||
101 | if (!param->cset) | ||
102 | { | ||
103 | param->cset = strdup (fromcode); | ||
104 | if (!param->cset) | ||
105 | { | ||
106 | status = ENOMEM; | ||
107 | break; | ||
108 | } | ||
109 | } | ||
110 | if (lang && !param->lang && (param->lang = strdup (lang)) == NULL) | ||
111 | { | ||
112 | status = ENOMEM; | ||
113 | break; | ||
114 | } | ||
115 | if (!tocode) | ||
116 | { | ||
117 | if ((tocodetmp = strdup (fromcode)) == NULL) | ||
118 | { | ||
119 | status = ENOMEM; | ||
120 | break; | ||
121 | } | ||
122 | tocode = tocodetmp; | ||
123 | } | ||
129 | status = getword (&encoding_type, &sp, '?'); | 124 | status = getword (&encoding_type, &sp, '?'); |
130 | if (status) | 125 | if (status) |
131 | break; | 126 | break; |
... | @@ -162,22 +157,12 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) | ... | @@ -162,22 +157,12 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) |
162 | 157 | ||
163 | mu_static_memory_stream_create (&in_stream, encoded_text, size); | 158 | mu_static_memory_stream_create (&in_stream, encoded_text, size); |
164 | mu_stream_seek (in_stream, 0, MU_SEEK_SET, NULL); | 159 | mu_stream_seek (in_stream, 0, MU_SEEK_SET, NULL); |
165 | status = mu_decode_filter (&filter, in_stream, filter_type, fromcode, | 160 | status = mu_decode_filter (&filter, in_stream, filter_type, |
166 | tocode); | 161 | fromcode, tocode); |
167 | mu_stream_unref (in_stream); | 162 | mu_stream_unref (in_stream); |
168 | if (status != 0) | 163 | if (status != 0) |
169 | break; | 164 | break; |
170 | 165 | status = mu_stream_copy (str, filter, 0, NULL); | |
171 | while ((status = | ||
172 | mu_stream_read (filter, tmp, sizeof (tmp), &nbytes)) == 0 | ||
173 | && nbytes) | ||
174 | { | ||
175 | CHKBUF (nbytes); | ||
176 | memcpy (buffer + bufpos, tmp, nbytes); | ||
177 | bufpos += nbytes; | ||
178 | } | ||
179 | |||
180 | mu_stream_close (filter); | ||
181 | mu_stream_destroy (&filter); | 166 | mu_stream_destroy (&filter); |
182 | 167 | ||
183 | if (status) | 168 | if (status) |
... | @@ -198,44 +183,89 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) | ... | @@ -198,44 +183,89 @@ mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) |
198 | { | 183 | { |
199 | if (--run_count) | 184 | if (--run_count) |
200 | { | 185 | { |
201 | CHKBUF (run_count); | 186 | status = mu_stream_write (str, fromstr - run_count, |
202 | memcpy (buffer + bufpos, fromstr - run_count, run_count); | 187 | run_count, NULL); |
203 | bufpos += run_count; | 188 | if (status) |
189 | break; | ||
204 | run_count = 0; | 190 | run_count = 0; |
205 | } | 191 | } |
206 | CHKBUF (1); | 192 | status = mu_stream_write (str, fromstr, 1, NULL); |
207 | buffer[bufpos++] = *fromstr++; | 193 | if (status) |
194 | break; | ||
195 | fromstr++; | ||
208 | } | 196 | } |
209 | } | 197 | } |
210 | else | 198 | else |
211 | { | 199 | { |
212 | CHKBUF (1); | 200 | status = mu_stream_write (str, fromstr, 1, NULL); |
213 | buffer[bufpos++] = *fromstr++; | 201 | if (status) |
202 | break; | ||
203 | fromstr++; | ||
214 | } | 204 | } |
215 | } | 205 | } |
216 | 206 | ||
217 | if (*fromstr) | 207 | if (status == 0 && *fromstr) |
218 | { | 208 | status = mu_stream_write (str, fromstr, strlen (fromstr), NULL); |
219 | size_t len = strlen (fromstr); | ||
220 | CHKBUF (len); | ||
221 | memcpy (buffer + bufpos, fromstr, len); | ||
222 | bufpos += len; | ||
223 | } | ||
224 | 209 | ||
225 | CHKBUF (1); | ||
226 | buffer[bufpos++] = 0; | ||
227 | |||
228 | free (fromcode); | 210 | free (fromcode); |
229 | free (encoding_type); | 211 | free (encoding_type); |
230 | free (encoded_text); | 212 | free (encoded_text); |
213 | free (tocodetmp); | ||
214 | |||
215 | if (status == 0) | ||
216 | { | ||
217 | mu_off_t size; | ||
231 | 218 | ||
232 | if (status) | 219 | mu_stream_size (str, &size); |
233 | free (buffer); | 220 | param->value = malloc (size + 1); |
234 | else | 221 | if (!param->value) |
235 | *ptostr = realloc (buffer, bufpos); | 222 | status = ENOMEM; |
223 | else | ||
224 | { | ||
225 | mu_stream_seek (str, 0, MU_SEEK_SET, NULL); | ||
226 | status = mu_stream_read (str, param->value, size, NULL); | ||
227 | param->value[size] = 0; | ||
228 | } | ||
229 | } | ||
230 | |||
231 | mu_stream_destroy (&str); | ||
236 | return status; | 232 | return status; |
237 | } | 233 | } |
238 | 234 | ||
235 | int | ||
236 | mu_rfc2047_decode_param (const char *tocode, const char *input, | ||
237 | struct mu_mime_param *param) | ||
238 | { | ||
239 | int rc; | ||
240 | struct mu_mime_param tmp; | ||
241 | |||
242 | if (!input) | ||
243 | return EINVAL; | ||
244 | if (!param) | ||
245 | return MU_ERR_OUT_PTR_NULL; | ||
246 | rc = _rfc2047_decode_param (tocode, input, &tmp); | ||
247 | if (rc == 0) | ||
248 | *param = tmp; | ||
249 | return rc; | ||
250 | } | ||
251 | |||
252 | int | ||
253 | mu_rfc2047_decode (const char *tocode, const char *input, char **ptostr) | ||
254 | { | ||
255 | int rc; | ||
256 | struct mu_mime_param param; | ||
257 | |||
258 | if (!input) | ||
259 | return EINVAL; | ||
260 | if (!ptostr) | ||
261 | return MU_ERR_OUT_PTR_NULL; | ||
262 | rc = _rfc2047_decode_param (tocode, input, ¶m); | ||
263 | free (param.cset); | ||
264 | free (param.lang); | ||
265 | if (rc == 0) | ||
266 | *ptostr = param.value; | ||
267 | return rc; | ||
268 | } | ||
239 | 269 | ||
240 | /** | 270 | /** |
241 | Encode a header according to RFC 2047 | 271 | Encode a header according to RFC 2047 | ... | ... |
... | @@ -73,7 +73,7 @@ mu_decode_filter_args (mu_stream_t *pfilter, mu_stream_t input, | ... | @@ -73,7 +73,7 @@ mu_decode_filter_args (mu_stream_t *pfilter, mu_stream_t input, |
73 | 73 | ||
74 | rc = mu_filter_chain_create (pfilter, input, | 74 | rc = mu_filter_chain_create (pfilter, input, |
75 | MU_FILTER_DECODE, MU_STREAM_READ, | 75 | MU_FILTER_DECODE, MU_STREAM_READ, |
76 | xargc, xargv); | 76 | i, xargv); |
77 | free (xargv); | 77 | free (xargv); |
78 | return rc; | 78 | return rc; |
79 | } | 79 | } | ... | ... |
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | 1 | /* Operations on RFC-2231-compliant mail headers fields. |
2 | GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010, 2011 | 3 | Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010, 2011 |
3 | Free Software Foundation, Inc. | 4 | Free Software Foundation, Inc. |
4 | 5 | ||
... | @@ -15,6 +16,7 @@ | ... | @@ -15,6 +16,7 @@ |
15 | You should have received a copy of the GNU Lesser General | 16 | You should have received a copy of the GNU Lesser General |
16 | Public License along with this library. If not, | 17 | Public License along with this library. If not, |
17 | see <http://www.gnu.org/licenses/>. */ | 18 | see <http://www.gnu.org/licenses/>. */ |
19 | |||
18 | #ifdef HAVE_CONFIG_H | 20 | #ifdef HAVE_CONFIG_H |
19 | #include <config.h> | 21 | #include <config.h> |
20 | #endif | 22 | #endif |
... | @@ -32,386 +34,693 @@ | ... | @@ -32,386 +34,693 @@ |
32 | #include <mailutils/mime.h> | 34 | #include <mailutils/mime.h> |
33 | #include <mailutils/filter.h> | 35 | #include <mailutils/filter.h> |
34 | #include <mailutils/util.h> | 36 | #include <mailutils/util.h> |
37 | #include <mailutils/wordsplit.h> | ||
38 | #include <mailutils/assoc.h> | ||
39 | #include <mailutils/iterator.h> | ||
40 | #include <mailutils/diag.h> | ||
41 | #include <mailutils/nls.h> | ||
42 | |||
43 | #define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */ | ||
44 | #define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language | ||
45 | info */ | ||
46 | |||
47 | /* Free members of struct mu_mime_param, but do not free it itself. */ | ||
48 | static void | ||
49 | _mu_mime_param_free (struct mu_mime_param *p) | ||
50 | { | ||
51 | free (p->lang); | ||
52 | free (p->cset); | ||
53 | free (p->value); | ||
54 | } | ||
35 | 55 | ||
36 | /* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ | 56 | /* Treat ITEM as a pointer to struct mu_mime_param and reclaim all |
37 | #define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) | 57 | memory associated with it. |
38 | 58 | ||
39 | /* _header_get_param - an auxiliary function to extract values from | 59 | This is intended for use as a destroy_item method of assoc tables and |
40 | Content-Type, Content-Disposition and similar headers. | 60 | lists. */ |
61 | static void | ||
62 | _mu_mime_param_free_item (void *item) | ||
63 | { | ||
64 | _mu_mime_param_free (item); | ||
65 | free (item); | ||
66 | } | ||
41 | 67 | ||
42 | Arguments: | 68 | /* Recode a string between two charsets. |
43 | 69 | ||
44 | FIELD_BODY Header value, complying to RFCs 2045, 2183, 2231.3; | 70 | Input: |
45 | DISP Disposition. Unless it is NULL, the disposition part | 71 | TEXT - A string. |
46 | of FIELD_BODY is compared with it. If they differ, | 72 | ICS - Charset of TEXT. |
47 | the function returns MU_ERR_NOENT. | 73 | OCS - Charset to convert TEXT to. |
48 | PARAM Name of the parameter to extract from FIELD_BODY; | 74 | Output: |
49 | BUF Where to extract the value to; | 75 | PRESULT - On success, the pointer to the resulting string is stored here. |
50 | BUFSZ Size of BUF; | ||
51 | PRET Pointer to the memory location for the return buffer (see | ||
52 | below). | ||
53 | PLEN Pointer to the return size. | ||
54 | PFLAGS On return, flags describing the parameter are stored there. | ||
55 | The MU_MIMEHDR_MULTILINE bit is set if the parameter value | ||
56 | was multiline (RFC 2231.3). The MU_MIMEHDR_CSINFO bit is set | ||
57 | if the parameter value includes charset/language | ||
58 | information (RFC 2231.4). | ||
59 | |||
60 | The function parses FIELD_BODY and extracts the value of the parameter | ||
61 | PARAM. | ||
62 | |||
63 | If BUF is not NULL and BUFSZ is not 0, the extracted value is stored into | ||
64 | BUF. At most BUFSZ-1 bytes are copied. | ||
65 | |||
66 | Otherwise, if PRET is not NULL, the function allocates enough memory to | ||
67 | hold the extracted value, copies there the result, and stores the | ||
68 | pointer to the allocated memory into the location pointed to by PRET. | ||
69 | |||
70 | If PLEN is not NULL, the size of the extracted value (without terminating | ||
71 | NUL character) is stored there. | ||
72 | |||
73 | If BUF==NULL *and* PRET==NULL, no memory is allocated, but PLEN is | ||
74 | honored anyway, i.e. unless it is NULL it receives size of the result. | ||
75 | This can be used to estimate the needed buffer size. | ||
76 | |||
77 | Return values: | ||
78 | 0 on success. | ||
79 | MU_ERR_NOENT, requested parameter not found, or disposition does | ||
80 | not match DISP. | ||
81 | MU_ERR_PARSE, if FIELD_BODY does not comply to any of the abovemntioned | ||
82 | RFCs. | ||
83 | ENOMEM , if unable to allocate memory. | ||
84 | */ | 76 | */ |
77 | static int | ||
78 | _recode_string (char *text, const char *ics, const char *ocs, char **presult) | ||
79 | { | ||
80 | mu_stream_t istr, ostr, cvt; | ||
81 | mu_off_t size; | ||
82 | char *decoded; | ||
83 | int rc; | ||
84 | |||
85 | rc = mu_static_memory_stream_create (&istr, text, strlen (text)); | ||
86 | if (rc) | ||
87 | return rc; | ||
88 | rc = mu_memory_stream_create (&ostr, 0); | ||
89 | if (rc) | ||
90 | return rc; | ||
91 | rc = mu_decode_filter (&cvt, istr, NULL, ics, ocs); | ||
92 | mu_stream_unref (istr); | ||
93 | if (rc) | ||
94 | { | ||
95 | mu_stream_unref (ostr); | ||
96 | return rc; | ||
97 | } | ||
98 | rc = mu_stream_copy (ostr, cvt, 0, &size); | ||
99 | mu_stream_unref (cvt); | ||
100 | if (rc) | ||
101 | { | ||
102 | mu_stream_unref (ostr); | ||
103 | return rc; | ||
104 | } | ||
85 | 105 | ||
86 | /* Internal flag used by _header_get_param to delay increasing | 106 | decoded = malloc (size + 1); |
87 | estimated continuation index. */ | 107 | if (!decoded) |
88 | #define _MU_MIMEHDR_INCR_CIND 0x8000 | 108 | { |
109 | mu_stream_unref (ostr); | ||
110 | return ENOMEM; | ||
111 | } | ||
89 | 112 | ||
90 | int | 113 | mu_stream_seek (ostr, 0, MU_SEEK_SET, NULL); |
91 | _header_get_param (const char *field_body, | 114 | rc = mu_stream_read (ostr, decoded, size, NULL); |
92 | const char *disp, | 115 | mu_stream_unref (ostr); |
93 | const char *param, | 116 | if (rc) |
94 | char *buf, size_t bufsz, | 117 | free (decoded); |
95 | char **pret, size_t *plen, | 118 | else |
96 | int *pflags) | 119 | { |
120 | decoded[size] = 0; | ||
121 | *presult = decoded; | ||
122 | } | ||
123 | return rc; | ||
124 | } | ||
125 | |||
126 | /* Structure for composing continued parameters. | ||
127 | See RFC 2231, Section 3, "Parameter Value Continuations" */ | ||
128 | struct param_continuation | ||
97 | { | 129 | { |
98 | int res = MU_ERR_NOENT; /* Return value, pessimistic default */ | 130 | char *param_name; /* Parameter name */ |
99 | size_t param_len = strlen (param); | 131 | size_t param_length; /* Length of param_name */ |
100 | char *p; | 132 | mu_stream_t param_value; /* Its value (memory stream) */ |
101 | size_t size; | 133 | int param_cind; /* Expected continued parameter index. */ |
102 | char *mem = NULL; /* Allocated memory storage */ | 134 | /* Language/character set information */ |
103 | size_t retlen = 0; /* Total number of bytes copied */ | 135 | const char *param_lang; |
104 | unsigned long cind = 0; /* Expected continued parameter index. | 136 | const char *param_cset; |
105 | See RFC 2231, Section 3, | 137 | }; |
106 | "Parameter Value Continuations" */ | ||
107 | int flags = 0; | ||
108 | |||
109 | if (field_body == NULL) | ||
110 | return EINVAL; | ||
111 | 138 | ||
112 | if (bufsz == 0) /* Make sure buf value is meaningful */ | 139 | static int |
113 | buf = NULL; | 140 | free_param_continuation (struct param_continuation *p) |
141 | { | ||
142 | free (p->param_name); | ||
143 | mu_stream_destroy (&p->param_value); | ||
144 | /* param_lang and param_cset are handled separately */ | ||
145 | memset (p, 0, sizeof (*p)); | ||
146 | } | ||
147 | |||
148 | /* Auxiliary function to store the data collected in CONT into ASSOC. | ||
149 | If SUBSET is True, ASSOC is populated with empty mu_mime_param | ||
150 | structures. In this case data will be stored only if CONT->param_name | ||
151 | is already in ASSOC. If OUTCHARSET is not NULL, the value from | ||
152 | CONT->param_value will be recoded to that charset before storing it. */ | ||
153 | static int | ||
154 | flush_param (struct param_continuation *cont, mu_assoc_t assoc, int subset, | ||
155 | const char *outcharset) | ||
156 | { | ||
157 | int rc; | ||
158 | struct mu_mime_param param, *param_slot = NULL; | ||
159 | mu_off_t size; | ||
114 | 160 | ||
115 | p = strchr (field_body, ';'); | 161 | if (subset) |
116 | if (!p) | ||
117 | return MU_ERR_NOENT; | ||
118 | /* Allow for possible whitespace before the semicolon */ | ||
119 | for (size = p - field_body; | ||
120 | size > 0 && mu_isblank (field_body[size-1]); size--) | ||
121 | ; | ||
122 | /* Remove surrounding quotes. | ||
123 | FIXME: unescape the quoted contents. */ | ||
124 | if (field_body[0] == '"' && field_body[size-1] == '"') | ||
125 | { | 162 | { |
126 | field_body++; | 163 | param_slot = mu_assoc_ref (assoc, cont->param_name); |
127 | size -= 2; | 164 | if (!param_slot) |
165 | return 0; | ||
128 | } | 166 | } |
129 | if (disp && mu_c_strncasecmp (field_body, disp, size)) | 167 | |
130 | return MU_ERR_NOENT; | 168 | if (cont->param_lang) |
131 | |||
132 | while (p && *p) | ||
133 | { | 169 | { |
134 | char *v, *e, *ep, *cp; | 170 | param.lang = strdup (cont->param_lang); |
135 | size_t len, escaped_chars = 0; | 171 | if (!param.lang) |
136 | 172 | return ENOMEM; | |
137 | if (*p != ';') | 173 | } |
174 | else | ||
175 | param.lang = NULL; | ||
176 | |||
177 | if (outcharset || cont->param_cset) | ||
178 | { | ||
179 | param.cset = strdup (outcharset ? outcharset : cont->param_cset); | ||
180 | if (!param.cset) | ||
138 | { | 181 | { |
139 | res = MU_ERR_PARSE; | 182 | free (param.lang); |
140 | break; | 183 | return ENOMEM; |
141 | } | 184 | } |
142 | 185 | } | |
143 | /* walk upto start of param */ | 186 | |
144 | p = mu_str_skip_class (p + 1, MU_CTYPE_SPACE); | 187 | rc = mu_stream_size (cont->param_value, &size); |
188 | if (rc == 0) | ||
189 | { | ||
190 | param.value = malloc (size + 1); | ||
191 | if (!param.value) | ||
192 | rc = ENOMEM; | ||
193 | } | ||
145 | 194 | ||
146 | /* Reportedly, some MUAs insert several semicolons */ | 195 | if (rc == 0) |
147 | if (*p == ';') | 196 | { |
148 | continue; | 197 | rc = mu_stream_seek (cont->param_value, 0, MU_SEEK_SET, NULL); |
198 | if (rc == 0) | ||
199 | rc = mu_stream_read (cont->param_value, param.value, size, NULL); | ||
200 | param.value[size] = 0; | ||
201 | } | ||
202 | |||
203 | if (rc) | ||
204 | { | ||
205 | free (param.lang); | ||
206 | free (param.cset); | ||
207 | return rc; | ||
208 | } | ||
149 | 209 | ||
150 | /* Ignore stray characters */ | 210 | if (cont->param_cset && outcharset && |
151 | if (_ISSPECIAL (*p)) | 211 | mu_c_strcasecmp (cont->param_cset, outcharset)) |
212 | { | ||
213 | char *tmp; | ||
214 | rc = _recode_string (param.value, cont->param_cset, outcharset, &tmp); | ||
215 | free (param.value); | ||
216 | if (rc) | ||
152 | { | 217 | { |
153 | p = strchr (p, ';'); | 218 | free (param.lang); |
154 | continue; | 219 | free (param.cset); |
220 | return rc; | ||
155 | } | 221 | } |
222 | param.value = tmp; | ||
223 | } | ||
156 | 224 | ||
157 | if ((ep = strchr (p, '=')) == NULL) | 225 | if (param_slot) |
158 | break; | 226 | { |
159 | /* Allow for optional whitespace after '=' */ | 227 | *param_slot = param; |
160 | v = mu_str_skip_class (ep + 1, MU_CTYPE_SPACE); | 228 | } |
161 | /* Find end of the parameter */ | 229 | else |
162 | if (*v == '"') | 230 | { |
231 | rc = mu_assoc_install (assoc, cont->param_name, ¶m); | ||
232 | if (rc) | ||
233 | _mu_mime_param_free (¶m); | ||
234 | } | ||
235 | |||
236 | return rc; | ||
237 | } | ||
238 | |||
239 | /* Create and initialize an empty associative array for parameters. */ | ||
240 | int | ||
241 | mu_mime_param_assoc_create (mu_assoc_t *paramtab) | ||
242 | { | ||
243 | mu_assoc_t assoc; | ||
244 | int rc = mu_assoc_create (&assoc, sizeof (struct mu_mime_param), | ||
245 | MU_ASSOC_ICASE); | ||
246 | if (rc == 0) | ||
247 | mu_assoc_set_free (assoc, _mu_mime_param_free_item); | ||
248 | *paramtab = assoc; | ||
249 | return rc; | ||
250 | } | ||
251 | |||
252 | /* Add an empty structure for the slot NAME in ASSOC. */ | ||
253 | int | ||
254 | mu_mime_param_assoc_add (mu_assoc_t assoc, const char *name) | ||
255 | { | ||
256 | struct mu_mime_param param; | ||
257 | |||
258 | memset (¶m, 0, sizeof param); | ||
259 | return mu_assoc_install (assoc, name, ¶m); | ||
260 | } | ||
261 | |||
262 | /* See FIXME near the end of _mime_header_parse, below. */ | ||
263 | static int | ||
264 | _remove_entry (void *item, void *data) | ||
265 | { | ||
266 | struct mu_mime_param *p = item; | ||
267 | mu_assoc_t assoc = data; | ||
268 | mu_assoc_remove_ref (assoc, p); | ||
269 | return 0; | ||
270 | } | ||
271 | |||
272 | /* A working horse of this module. Parses input string, which should | ||
273 | be a header field value complying to RFCs 2045, 2183, 2231.3. | ||
274 | |||
275 | Input: | ||
276 | TEXT - The string. | ||
277 | ASSOC - Associative array of parameters indexed by their names. | ||
278 | SUBSET - If true, store only those parameters that are already | ||
279 | in ASSOC. | ||
280 | Output: | ||
281 | PVALUE - Unless NULL, a pointer to the field value is stored here on | ||
282 | success. | ||
283 | ASSOC - Unless NULL, parameters are stored here. | ||
284 | |||
285 | Either PVALUE or ASSOC (but not both) can be NULL, meaning that the | ||
286 | corresponding data are of no interest to the caller. | ||
287 | */ | ||
288 | static int | ||
289 | _mime_header_parse (const char *text, char **pvalue, | ||
290 | mu_assoc_t assoc, const char *outcharset, int subset) | ||
291 | { | ||
292 | int rc; | ||
293 | struct mu_wordsplit ws; | ||
294 | struct param_continuation cont; | ||
295 | size_t i; | ||
296 | |||
297 | ws.ws_delim = " \t\r\n;"; | ||
298 | ws.ws_escape = "\\\""; | ||
299 | if (mu_wordsplit (text, &ws, | ||
300 | MU_WRDSF_DELIM | MU_WRDSF_ESCAPE | | ||
301 | MU_WRDSF_NOVAR | MU_WRDSF_NOCMD | | ||
302 | MU_WRDSF_DQUOTE | MU_WRDSF_SQUEEZE_DELIMS | | ||
303 | MU_WRDSF_RETURN_DELIMS | MU_WRDSF_WS)) | ||
304 | { | ||
305 | mu_debug (MU_DEBCAT_MIME, MU_DEBUG_ERROR, | ||
306 | (_("wordsplit: %s"), mu_wordsplit_strerror (&ws))); | ||
307 | return MU_ERR_PARSE; | ||
308 | } | ||
309 | |||
310 | if (!assoc) | ||
311 | { | ||
312 | if (!pvalue) | ||
313 | return MU_ERR_OUT_PTR_NULL; | ||
314 | *pvalue = strdup (ws.ws_wordv[i]); | ||
315 | mu_wordsplit_free (&ws); | ||
316 | if (!*pvalue) | ||
317 | return ENOMEM; | ||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | memset (&cont, 0, sizeof (cont)); | ||
322 | for (i = 1; i < ws.ws_wordc; i++) | ||
323 | { | ||
324 | size_t klen; | ||
325 | char *key; | ||
326 | char *val; | ||
327 | const char *lang = NULL; | ||
328 | const char *cset = NULL; | ||
329 | char *langp = NULL; | ||
330 | char *csetp = NULL; | ||
331 | char *p; | ||
332 | char *decoded; | ||
333 | int flags = 0; | ||
334 | struct mu_mime_param param; | ||
335 | |||
336 | key = ws.ws_wordv[i]; | ||
337 | if (key[0] == ';') | ||
338 | /* Reportedly, some MUAs insert several semicolons */ | ||
339 | continue; | ||
340 | p = strchr (key, '='); | ||
341 | if (!p) | ||
342 | val = ""; | ||
343 | else | ||
163 | { | 344 | { |
164 | /* Quoted string */ | 345 | *p++ = 0; |
165 | for (e = ++v; *e != '"'; e++) | 346 | val = p; |
347 | } | ||
348 | |||
349 | klen = strlen (key); | ||
350 | if (klen == 0) | ||
351 | continue; | ||
352 | |||
353 | p = strchr (key, '*'); | ||
354 | if (p) | ||
355 | { | ||
356 | /* It is a parameter value continuation (RFC 2231, Section 3) | ||
357 | or parameter value character set and language information | ||
358 | (ibid., Section 4). */ | ||
359 | klen = p - key; | ||
360 | if (p[1]) | ||
166 | { | 361 | { |
167 | if (*e == 0) /* Malformed header */ | 362 | if (mu_isdigit (p[1])) |
168 | { | 363 | { |
169 | res = MU_ERR_PARSE; | 364 | char *q; |
170 | break; | 365 | unsigned long n = strtoul (p + 1, &q, 10); |
171 | } | 366 | |
172 | if (*e == '\\') | 367 | if (*q && *q != '*') |
173 | { | ||
174 | if (*++e == 0) | ||
175 | { | 368 | { |
176 | res = MU_ERR_PARSE; | 369 | mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0, |
177 | break; | 370 | (_("malformed parameter name %s: skipping"), |
371 | key)); | ||
372 | continue; | ||
178 | } | 373 | } |
179 | escaped_chars++; | 374 | |
375 | if (n != cont.param_cind) | ||
376 | { | ||
377 | mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0, | ||
378 | (_("continuation index out of sequence in %s: " | ||
379 | "skipping"), | ||
380 | key)); | ||
381 | /* Ignore this parameter. Another possibility would be | ||
382 | to drop the continuation assembled so far. That makes | ||
383 | little difference, because the string is malformed | ||
384 | anyway. | ||
385 | |||
386 | We try to continue just to gather as many information | ||
387 | as possible from this mess. | ||
388 | */ | ||
389 | continue; | ||
390 | } | ||
391 | |||
392 | if (n == 0) | ||
393 | { | ||
394 | cont.param_name = malloc (klen + 1); | ||
395 | if (!cont.param_name) | ||
396 | { | ||
397 | rc = ENOMEM; | ||
398 | break; | ||
399 | } | ||
400 | cont.param_length = klen; | ||
401 | memcpy (cont.param_name, key, klen); | ||
402 | cont.param_name[klen] = 0; | ||
403 | |||
404 | rc = mu_memory_stream_create (&cont.param_value, | ||
405 | MU_STREAM_RDWR); | ||
406 | if (rc) | ||
407 | break; | ||
408 | } | ||
409 | else if (cont.param_length != klen || | ||
410 | memcmp (cont.param_name, key, klen)) | ||
411 | { | ||
412 | mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0, | ||
413 | (_("continuation name mismatch: %s: " | ||
414 | "skipping"), | ||
415 | key)); | ||
416 | continue; | ||
417 | } | ||
418 | |||
419 | if (*q == '*') | ||
420 | flags |= MU_MIMEHDR_CSINFO; | ||
421 | |||
422 | cont.param_cind++; | ||
423 | flags |= MU_MIMEHDR_MULTILINE; | ||
180 | } | 424 | } |
181 | } | 425 | } |
182 | if (res == MU_ERR_PARSE) | 426 | else |
183 | break; | 427 | { |
184 | len = e - v; | 428 | flags |= MU_MIMEHDR_CSINFO; |
185 | e++; | 429 | *p = 0; |
430 | } | ||
186 | } | 431 | } |
187 | else | 432 | else if (cont.param_name) |
188 | { | 433 | { |
189 | for (e = v + 1; *e && !(*e == ';' || mu_isspace (*e)); e++) | 434 | rc = flush_param (&cont, assoc, subset, outcharset); |
190 | ; | 435 | free_param_continuation (&cont); |
191 | len = e - v; | 436 | if (rc) |
437 | break; | ||
192 | } | 438 | } |
439 | |||
440 | if (flags & MU_MIMEHDR_CSINFO) | ||
441 | { | ||
442 | p = strchr (val, '\''); | ||
443 | if (p) | ||
444 | { | ||
445 | char *q = strchr (p + 1, '\''); | ||
446 | if (q) | ||
447 | { | ||
448 | cset = val; | ||
449 | *p++ = 0; | ||
450 | lang = p; | ||
451 | *q++ = 0; | ||
452 | val = q; | ||
453 | } | ||
454 | } | ||
193 | 455 | ||
194 | /* Is it our parameter? */ | 456 | if ((flags & MU_MIMEHDR_MULTILINE) && cont.param_cind == 1) |
195 | if (mu_c_strncasecmp (p, param, param_len)) | 457 | { |
196 | { /* nope, jump to next */ | 458 | cont.param_lang = lang; |
197 | p = strchr (e, ';'); | 459 | cont.param_cset = cset; |
198 | continue; | 460 | } |
199 | } | 461 | } |
200 | 462 | ||
201 | cp = p + param_len; | 463 | if (flags & MU_MIMEHDR_CSINFO) |
202 | |||
203 | if (*cp == '*') | ||
204 | { | 464 | { |
205 | cp++; | 465 | char *tmp; |
206 | /* It is a parameter value continuation (RFC 2231, Section 3) | ||
207 | or parameter value character set and language information | ||
208 | (ibid., Section 4). */ | ||
209 | if (mu_isdigit (*cp)) | ||
210 | { | ||
211 | /* See if the index is OK */ | ||
212 | 466 | ||
213 | char *end; | 467 | rc = mu_str_url_decode (&tmp, val); |
214 | unsigned long n = strtoul (cp, &end, 10); | 468 | if (rc) |
215 | 469 | break; | |
216 | if (*end == '*') | 470 | if (!(flags & MU_MIMEHDR_MULTILINE)) |
217 | { | 471 | { |
218 | flags |= MU_MIMEHDR_CSINFO; | 472 | if (!outcharset || mu_c_strcasecmp (cset, outcharset) == 0) |
219 | end++; | 473 | decoded = tmp; |
220 | } | 474 | else |
221 | if (n != cind) | ||
222 | { | 475 | { |
223 | res = MU_ERR_PARSE; | 476 | rc = _recode_string (tmp, cset, outcharset, &decoded); |
224 | break; | 477 | free (tmp); |
225 | } | 478 | } |
226 | /* Everything OK, mark this as a multiline (continued) | 479 | if (rc) |
227 | parameter. We also need to increment the estimation, | 480 | break; |
228 | but it cannot be done right now because its value is | ||
229 | used below to decide whether to do flag cleanup | ||
230 | on error. So we set _MU_MIMEHDR_INCR_CIND flag instead | ||
231 | and increment cind later. */ | ||
232 | flags |= (MU_MIMEHDR_MULTILINE|_MU_MIMEHDR_INCR_CIND); | ||
233 | /* And point cp to the last character: there are more | ||
234 | checks ahead. */ | ||
235 | cp = end; | ||
236 | } | 481 | } |
237 | else | 482 | else |
238 | flags |= MU_MIMEHDR_CSINFO; | 483 | decoded = tmp; |
239 | } | 484 | } |
240 | /* Allow for optional whitespace before '=' */ | 485 | else |
241 | cp = mu_str_skip_class (cp, MU_CTYPE_SPACE); | ||
242 | /* cp must now point to the equals sign */ | ||
243 | if (cp != ep) | ||
244 | { | 486 | { |
245 | /* Clean up everything, unless we're in the middle of a | 487 | rc = mu_rfc2047_decode_param (outcharset, val, ¶m); |
246 | parameter continuation. */ | 488 | if (rc) |
247 | if (cind == 0) | 489 | return rc; |
248 | flags = 0; | 490 | cset = csetp = param.cset; |
249 | 491 | lang = langp = param.lang; | |
250 | /* Try next parameter */ | 492 | decoded = param.value; |
251 | p = strchr (e, ';'); | ||
252 | continue; | ||
253 | } | 493 | } |
254 | 494 | val = decoded; | |
255 | if (flags & _MU_MIMEHDR_INCR_CIND) | 495 | |
496 | if (flags & MU_MIMEHDR_MULTILINE) | ||
256 | { | 497 | { |
257 | /* Increase the estimation. */ | 498 | rc = mu_stream_write (cont.param_value, val, strlen (val), |
258 | flags &= ~_MU_MIMEHDR_INCR_CIND; | 499 | NULL); |
259 | cind++; | 500 | free (decoded); |
501 | free (csetp); | ||
502 | free (langp); | ||
503 | if (rc) | ||
504 | break; | ||
505 | continue; | ||
260 | } | 506 | } |
261 | |||
262 | res = 0; /* Indicate success */ | ||
263 | |||
264 | /* Prepare P for the next iteration */ | ||
265 | p = e; | ||
266 | 507 | ||
267 | /* Escape characters that appear in quoted-pairs are | 508 | memset (¶m, 0, sizeof (param)); |
268 | semantically "invisible" (RFC 2822, Section 3.2.2, | 509 | if (lang) |
269 | "Quoted characters") */ | ||
270 | len -= escaped_chars; | ||
271 | |||
272 | /* Adjust len if nearing end of the buffer */ | ||
273 | if (bufsz && len >= bufsz) | ||
274 | len = bufsz - 1; | ||
275 | |||
276 | if (pret) | ||
277 | { | 510 | { |
278 | /* The caller wants us to allocate the memory */ | 511 | param.lang = strdup (lang); |
279 | if (!buf && !mem) | 512 | if (!param.lang) |
280 | { | 513 | rc = ENOMEM; |
281 | mem = malloc (len + 1); | 514 | else |
282 | if (!mem) | ||
283 | { | ||
284 | res = ENOMEM; | ||
285 | break; | ||
286 | } | ||
287 | buf = mem; | ||
288 | } | ||
289 | else if (mem) | ||
290 | { | 515 | { |
291 | /* If we got here, it means we are iterating over | 516 | param.cset = strdup (cset); |
292 | a parameter value continuation, and cind=0 has | 517 | if (!param.cset) |
293 | already been passed. Reallocate the memory to | ||
294 | accomodate next chunk of data. */ | ||
295 | char *newmem = realloc (mem, retlen + len + 1); | ||
296 | if (!newmem) | ||
297 | { | 518 | { |
298 | res = ENOMEM; | 519 | free (param.lang); |
299 | break; | 520 | rc = ENOMEM; |
300 | } | 521 | } |
301 | buf = mem = newmem; | ||
302 | } | 522 | } |
303 | } | 523 | } |
304 | 524 | ||
305 | if (buf) | 525 | free (csetp); |
526 | free (langp); | ||
527 | |||
528 | if (rc) | ||
529 | { | ||
530 | free (decoded); | ||
531 | break; | ||
532 | } | ||
533 | |||
534 | param.value = strdup (val); | ||
535 | free (decoded); | ||
536 | if (!param.value) | ||
306 | { | 537 | { |
307 | /* Actually copy the data. Buf is not NULL either because | 538 | _mu_mime_param_free (¶m); |
308 | the user passed it as an argument, or because we allocated | 539 | rc = ENOMEM; |
309 | memory for it. */ | 540 | break; |
310 | if (escaped_chars) | 541 | } |
542 | |||
543 | if (subset) | ||
544 | { | ||
545 | struct mu_mime_param *p = mu_assoc_ref (assoc, key); | ||
546 | if (p) | ||
547 | *p = param; | ||
548 | } | ||
549 | else | ||
550 | { | ||
551 | rc = mu_assoc_install (assoc, key, ¶m); | ||
552 | if (rc) | ||
311 | { | 553 | { |
312 | int i; | 554 | _mu_mime_param_free (¶m); |
313 | for (i = 0; i < len; i++) | 555 | break; |
314 | { | ||
315 | if (*v == '\\') | ||
316 | ++v; | ||
317 | buf[retlen + i] = *v++; | ||
318 | } | ||
319 | } | 556 | } |
320 | else | ||
321 | memcpy (buf + retlen, v, len); | ||
322 | } | 557 | } |
323 | /* Adjust total result size ... */ | 558 | } |
324 | retlen += len; | 559 | |
325 | /* ... and remaining buffer size, if necessary */ | 560 | if (rc == 0 && cont.param_name) |
326 | if (bufsz) | 561 | rc = flush_param (&cont, assoc, subset, outcharset); |
562 | free_param_continuation (&cont); | ||
563 | if (rc == 0) | ||
564 | { | ||
565 | if (pvalue) | ||
327 | { | 566 | { |
328 | bufsz -= len; | 567 | *pvalue = strdup (ws.ws_wordv[0]); |
329 | if (bufsz == 0) | 568 | if (!*pvalue) |
330 | break; | 569 | rc = ENOMEM; |
331 | } | 570 | } |
332 | } | 571 | } |
333 | 572 | ||
334 | if (res == 0) | 573 | mu_wordsplit_free (&ws); |
574 | |||
575 | if (subset) | ||
335 | { | 576 | { |
336 | /* Everything OK, prepare the returned data. */ | 577 | /* Eliminate empty elements. |
337 | if (buf) | 578 | |
338 | buf[retlen] = 0; | 579 | FIXME: What I wanted to do initially is commented out, because |
339 | if (plen) | 580 | unfortunately iterator_ctl is not defined for assoc tables... |
340 | *plen = retlen; | 581 | */ |
341 | if (pret) | 582 | #if 0 |
342 | *pret = mem; | 583 | mu_iterator_t itr; |
343 | if (pflags) | 584 | |
344 | *pflags = flags; | 585 | rc = mu_assoc_get_iterator (assoc, &itr); |
586 | if (rc == 0) | ||
587 | { | ||
588 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | ||
589 | mu_iterator_next (itr)) | ||
590 | { | ||
591 | const char *name; | ||
592 | struct mu_mime_param *p; | ||
593 | |||
594 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&p); | ||
595 | if (!p->value) | ||
596 | mu_iterator_ctl (itr, mu_itrctl_delete, NULL); | ||
597 | } | ||
598 | mu_iterator_destroy (&itr); | ||
599 | } | ||
600 | #else | ||
601 | /* ... Instead, the following kludgy approach is taken: */ | ||
602 | mu_iterator_t itr; | ||
603 | mu_list_t elist; | ||
604 | |||
605 | rc = mu_list_create (&elist); | ||
606 | if (rc) | ||
607 | return rc; | ||
608 | rc = mu_assoc_get_iterator (assoc, &itr); | ||
609 | if (rc == 0) | ||
610 | { | ||
611 | for (mu_iterator_first (itr); rc == 0 && !mu_iterator_is_done (itr); | ||
612 | mu_iterator_next (itr)) | ||
613 | { | ||
614 | const char *name; | ||
615 | struct mu_mime_param *p; | ||
616 | |||
617 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&p); | ||
618 | if (!p->value) | ||
619 | rc = mu_list_append (elist, p); | ||
620 | } | ||
621 | mu_iterator_destroy (&itr); | ||
622 | } | ||
623 | if (rc == 0) | ||
624 | mu_list_foreach (elist, _remove_entry, assoc); | ||
625 | mu_list_destroy (&elist); | ||
626 | #endif | ||
345 | } | 627 | } |
346 | else if (mem) | 628 | |
347 | free (mem); | 629 | return rc; |
348 | return res; | ||
349 | } | 630 | } |
350 | 631 | ||
351 | static size_t | 632 | /* Parse header value from TEXT and return its value and a subset of |
352 | disp_segment_len (const char *str) | 633 | parameters. |
634 | |||
635 | Input: | ||
636 | TEXT - Header value. | ||
637 | CSET - Output charset. Can be NULL, in which case no conversions | ||
638 | take place. | ||
639 | ASSOC - Parameter array initialized with empty slots for those | ||
640 | parameters, which are wanted on output. It should be | ||
641 | created using mu_mime_param_assoc_create and populated | ||
642 | using mu_mime_param_assoc_add. | ||
643 | Output: | ||
644 | PVALUE - A pointer to the field value is stored here on success. | ||
645 | ASSOC - Receives available parameters matching the input subset. | ||
646 | |||
647 | Either PVALUE or ASSOC (but not both) can be NULL, meaning that the | ||
648 | corresponding data are of no interest to the caller. | ||
649 | */ | ||
650 | int | ||
651 | mu_mime_header_parse_subset (const char *text, const char *cset, | ||
652 | char **pvalue, mu_assoc_t assoc) | ||
353 | { | 653 | { |
354 | char *p = strchr (str, ';'); | 654 | return _mime_header_parse (text, pvalue, assoc, cset, 1); |
355 | size_t size; | ||
356 | |||
357 | if (!p) | ||
358 | size = strlen (str); | ||
359 | else | ||
360 | size = p - str; | ||
361 | while (size > 0 && mu_isblank (str[size-1])) | ||
362 | size--; | ||
363 | return size; | ||
364 | } | 655 | } |
365 | 656 | ||
366 | /* STR is a value of a structured MIME header, e.g. Content-Type. | 657 | /* Parse header value from TEXT and return its value and parameters. |
658 | |||
659 | Input: | ||
660 | TEXT - Header value. | ||
661 | CSET - Output charset. Can be NULL, in which case no conversions | ||
662 | take place. | ||
663 | Output: | ||
664 | PVALUE - A pointer to the field value is stored here on success. | ||
665 | PASSOC - Receives an associative array of parameters. | ||
666 | |||
667 | Either PVALUE or PASSOC (but not both) can be NULL, meaning that the | ||
668 | corresponding data are of no interest to the caller. | ||
669 | */ | ||
670 | int | ||
671 | mu_mime_header_parse (const char *text, char *cset, char **pvalue, | ||
672 | mu_assoc_t *passoc) | ||
673 | { | ||
674 | int rc; | ||
675 | mu_assoc_t assoc; | ||
676 | |||
677 | rc = mu_mime_param_assoc_create (&assoc); | ||
678 | if (rc == 0) | ||
679 | { | ||
680 | rc = _mime_header_parse (text, pvalue, assoc, cset, 0); | ||
681 | if (rc || !passoc) | ||
682 | mu_assoc_destroy (&assoc); | ||
683 | else | ||
684 | *passoc = assoc; | ||
685 | } | ||
686 | |||
687 | return rc; | ||
688 | } | ||
689 | |||
690 | /* TEXT is a value of a structured MIME header, e.g. Content-Type. | ||
367 | This function returns the `disposition part' of it. In other | 691 | This function returns the `disposition part' of it. In other |
368 | words, it returns disposition, if STR is a Content-Disposition | 692 | words, it returns disposition, if TEXT is a Content-Disposition |
369 | value, and `type/subtype' part, if it is a Content-Type value. | 693 | value, and `type/subtype' part, if it is a Content-Type value. |
370 | */ | 694 | */ |
371 | int | 695 | int |
372 | mu_mimehdr_get_disp (const char *str, char *buf, size_t bufsz, size_t *retsz) | 696 | mu_mimehdr_get_disp (const char *text, char *buf, size_t bufsz, size_t *retsz) |
373 | { | 697 | { |
374 | size_t size; | 698 | int rc; |
375 | 699 | char *value; | |
376 | str = mu_str_skip_class (str, MU_CTYPE_BLANK); | 700 | |
377 | size = disp_segment_len (str); | 701 | rc = mu_mime_header_parse (text, NULL, &value, NULL); |
378 | if (size > 2 && str[0] == '"' && str[size-1] == '"') | 702 | if (rc == 0) |
379 | { | 703 | { |
380 | str++; | 704 | size_t size = strlen (value); |
381 | size -= 2; | 705 | if (size > bufsz) |
706 | size = bufsz; | ||
707 | if (buf) | ||
708 | size = mu_cpystr (buf, value, size); | ||
709 | if (retsz) | ||
710 | *retsz = size; | ||
382 | } | 711 | } |
383 | if (buf) | 712 | free (value); |
384 | size = mu_cpystr (buf, str, size); | ||
385 | if (retsz) | ||
386 | *retsz = size; | ||
387 | return 0; | 713 | return 0; |
388 | } | 714 | } |
389 | 715 | ||
390 | /* Same as mu_mimehdr_get_disp, but allocates memory */ | 716 | /* Same as mu_mimehdr_get_disp, but allocates memory */ |
391 | int | 717 | int |
392 | mu_mimehdr_aget_disp (const char *str, char **pvalue) | 718 | mu_mimehdr_aget_disp (const char *text, char **pvalue) |
393 | { | 719 | { |
394 | char *p; | 720 | return mu_mime_header_parse (text, NULL, pvalue, NULL); |
395 | size_t size; | ||
396 | |||
397 | str = mu_str_skip_class (str, MU_CTYPE_BLANK); | ||
398 | size = disp_segment_len (str); | ||
399 | if (size > 2 && str[0] == '"' && str[size-1] == '"') | ||
400 | { | ||
401 | str++; | ||
402 | size -= 2; | ||
403 | } | ||
404 | |||
405 | p = malloc (size + 1); | ||
406 | if (!p) | ||
407 | return ENOMEM; | ||
408 | memcpy (p, str, size); | ||
409 | p[size] = 0; | ||
410 | *pvalue = p; | ||
411 | return 0; | ||
412 | } | 721 | } |
413 | 722 | ||
414 | /* Get the value of the parameter PARAM from STR, which must be | 723 | /* Get the value of parameter NAME from STR, which must be |
415 | a value of a structured MIME header. | 724 | a value of a structured MIME header. |
416 | At most BUFSZ-1 of data are stored in BUF. A terminating NUL | 725 | At most BUFSZ-1 of data are stored in BUF. A terminating NUL |
417 | character is appended to it. | 726 | character is appended to it. |
... | @@ -419,202 +728,100 @@ mu_mimehdr_aget_disp (const char *str, char **pvalue) | ... | @@ -419,202 +728,100 @@ mu_mimehdr_aget_disp (const char *str, char **pvalue) |
419 | Unless NULL, RETSZ is filled with the actual length of the | 728 | Unless NULL, RETSZ is filled with the actual length of the |
420 | returned data (not including the NUL terminator). | 729 | returned data (not including the NUL terminator). |
421 | 730 | ||
422 | Unless PFLAGS is null it will contain, on return, the flags describing | ||
423 | the parameter. The MU_MIMEHDR_MULTILINE bit is set if the parameter value | ||
424 | was multiline (RFC 2231.3). The MU_MIMEHDR_CSINFO bit is set if the | ||
425 | parameter value includes charset/language information (RFC 2231.4). | ||
426 | |||
427 | BUF may be NULL, in which case the function will only fill | 731 | BUF may be NULL, in which case the function will only fill |
428 | RETSZ and PFLAGS, as described above. */ | 732 | RETSZ, as described above. */ |
429 | int | 733 | int |
430 | mu_mimehdr_get_param (const char *str, const char *param, | 734 | mu_mimehdr_get_param (const char *str, const char *name, |
431 | char *buf, size_t bufsz, size_t *retsz, | 735 | char *buf, size_t bufsz, size_t *retsz) |
432 | int *pflags) | ||
433 | { | 736 | { |
434 | return _header_get_param (str, NULL, param, buf, bufsz, NULL, retsz, | 737 | int rc; |
435 | pflags); | 738 | char *value; |
739 | |||
740 | rc = mu_mimehdr_aget_param (str, name, &value); | ||
741 | if (rc == 0) | ||
742 | { | ||
743 | size_t size = strlen (value); | ||
744 | if (size > bufsz) | ||
745 | size = bufsz; | ||
746 | if (buf) | ||
747 | size = mu_cpystr (buf, value, size); | ||
748 | if (retsz) | ||
749 | *retsz = size; | ||
750 | } | ||
751 | free (value); | ||
752 | return rc; | ||
436 | } | 753 | } |
437 | 754 | ||
438 | /* Same as mu_mimehdr_get_param, but allocates memory. */ | 755 | /* Same as mu_mimehdr_get_param, but allocates memory. */ |
439 | int | 756 | int |
440 | mu_mimehdr_aget_param (const char *str, const char *param, | 757 | mu_mimehdr_aget_param (const char *str, const char *name, char **pval) |
441 | char **pval, int *pflags) | ||
442 | { | 758 | { |
443 | return _header_get_param (str, NULL, param, NULL, 0, pval, NULL, pflags); | 759 | return mu_mimehdr_aget_decoded_param (str, name, NULL, pval, NULL); |
444 | } | 760 | } |
445 | 761 | ||
446 | /* Decode a parameter value. Arguments: | 762 | |
447 | 763 | /* Similar to mu_mimehdr_aget_param, but the returned value is decoded | |
448 | Input: | 764 | according to the CHARSET. Unless PLANG is NULL, it receives malloc'ed |
449 | VALUE Parameter value. | 765 | language name from STR. If there was no language name, *PLANG is set |
450 | FLAGS Flags obtained from a previous call to one of the functions | 766 | to NULL. |
451 | above. | 767 | */ |
452 | CHARSET Output charset. | ||
453 | |||
454 | Output: | ||
455 | PVAL A pointer to the decoded value is stored there. | ||
456 | The memory is allocated using malloc. | ||
457 | PLANG If language information was present in VALUE, its | ||
458 | malloc'ed copy is stored in the memory location pointed | ||
459 | to by this variable. If there was no language information, | ||
460 | *PLANG is set to NULL. | ||
461 | |||
462 | Both PVAL and PLANG may be NULL if that particular piece of information | ||
463 | is not needed. */ | ||
464 | int | 768 | int |
465 | mu_mimehdr_decode_param (const char *value, int flags, | 769 | mu_mimehdr_aget_decoded_param (const char *str, const char *name, |
466 | const char *charset, char **pval, char **plang) | 770 | const char *charset, |
771 | char **pval, char **plang) | ||
467 | { | 772 | { |
468 | char *decoded; | 773 | mu_assoc_t assoc; |
469 | int rc; | 774 | int rc; |
470 | char *lang = NULL; | ||
471 | char *data; | ||
472 | 775 | ||
473 | if (flags == 0) | 776 | rc = mu_mime_param_assoc_create (&assoc); |
474 | { | 777 | if (rc == 0) |
475 | rc = mu_rfc2047_decode (charset, value, &decoded); | ||
476 | if (rc) | ||
477 | return rc; | ||
478 | } | ||
479 | else | ||
480 | { | 778 | { |
481 | rc = mu_str_url_decode (&decoded, value); | 779 | rc = mu_mime_param_assoc_add (assoc, name); |
482 | if (rc) | 780 | if (rc == 0) |
483 | return rc; | ||
484 | |||
485 | if ((flags & MU_MIMEHDR_CSINFO) | ||
486 | && (lang = strchr (decoded, '\'')) | ||
487 | && (data = strchr (lang + 1, '\''))) | ||
488 | { | 781 | { |
489 | char *source_cs = decoded; | 782 | rc = mu_mime_header_parse_subset (str, charset, NULL, assoc); |
490 | 783 | if (rc == 0) | |
491 | *lang++ = 0; | ||
492 | *data++ = 0; | ||
493 | |||
494 | lang = lang[0] ? strdup (lang) : NULL; | ||
495 | |||
496 | if (source_cs[0] && charset && mu_c_strcasecmp (source_cs, charset)) | ||
497 | { | 784 | { |
498 | char *outval = NULL; | 785 | struct mu_mime_param *param = mu_assoc_ref (assoc, name); |
499 | mu_stream_t instr = NULL; | 786 | if (!param) |
500 | mu_stream_t outstr = NULL; | 787 | rc = MU_ERR_NOENT; |
501 | mu_stream_t cvt = NULL; | 788 | else |
502 | char iobuf[512]; | ||
503 | |||
504 | do | ||
505 | { | 789 | { |
506 | size_t total = 0, pos; | 790 | *pval = param->value; |
507 | size_t nbytes; | 791 | if (plang) |
508 | |||
509 | rc = mu_static_memory_stream_create (&instr, data, | ||
510 | strlen (data)); | ||
511 | if (rc) | ||
512 | break; | ||
513 | |||
514 | rc = mu_memory_stream_create (&outstr, 0); | ||
515 | if (rc) | ||
516 | break; | ||
517 | |||
518 | rc = mu_decode_filter (&cvt, instr, NULL, | ||
519 | source_cs, charset); | ||
520 | if (rc) | ||
521 | break; | ||
522 | |||
523 | while (mu_stream_read (cvt, iobuf, sizeof (iobuf), | ||
524 | &nbytes) == 0 | ||
525 | && nbytes) | ||
526 | { | ||
527 | rc = mu_stream_write (outstr, iobuf, nbytes, NULL); | ||
528 | if (rc) | ||
529 | break; | ||
530 | total += nbytes; | ||
531 | } | ||
532 | |||
533 | if (rc) | ||
534 | break; | ||
535 | |||
536 | outval = malloc (total + 1); | ||
537 | if (!outval) | ||
538 | { | 792 | { |
539 | rc = ENOMEM; | 793 | *plang = param->lang; |
540 | break; | 794 | param->lang = NULL; |
541 | } | 795 | } |
542 | 796 | param->value = NULL; | |
543 | mu_stream_seek (outstr, 0, MU_SEEK_SET, NULL); | ||
544 | pos = 0; | ||
545 | while (mu_stream_read (outstr, outval + pos, | ||
546 | total - pos, &nbytes) == 0 | ||
547 | && nbytes) | ||
548 | pos += nbytes; | ||
549 | outval[pos] = 0; | ||
550 | } | 797 | } |
551 | while (0); | ||
552 | |||
553 | mu_stream_close (cvt); | ||
554 | mu_stream_destroy (&cvt); | ||
555 | mu_stream_close (instr); | ||
556 | mu_stream_destroy (&instr); | ||
557 | mu_stream_close (outstr); | ||
558 | mu_stream_destroy (&outstr); | ||
559 | |||
560 | free (decoded); | ||
561 | |||
562 | if (rc) | ||
563 | { | ||
564 | /* Cleanup after an error. */ | ||
565 | free (lang); | ||
566 | free (outval); | ||
567 | return rc; | ||
568 | } | ||
569 | decoded = outval; | ||
570 | } | 798 | } |
571 | else | ||
572 | memmove (decoded, data, strlen (data) + 1); | ||
573 | } | 799 | } |
574 | } | 800 | mu_assoc_destroy (&assoc); |
575 | |||
576 | if (pval) | ||
577 | *pval = decoded; | ||
578 | else | ||
579 | free (decoded); | ||
580 | |||
581 | if (plang) | ||
582 | *plang = lang; | ||
583 | return 0; | ||
584 | } | ||
585 | |||
586 | /* Similar to mu_mimehdr_aget_param, but the returned value is decoded | ||
587 | according to the CHARSET. Unless PLANG is NULL, it receives malloc'ed | ||
588 | language name from STR. If there was no language name, *PLANG is set | ||
589 | to NULL. | ||
590 | */ | ||
591 | int | ||
592 | mu_mimehdr_aget_decoded_param (const char *str, const char *param, | ||
593 | const char *charset, | ||
594 | char **pval, char **plang) | ||
595 | { | ||
596 | char *value; | ||
597 | int rc; | ||
598 | int flags; | ||
599 | |||
600 | rc = mu_mimehdr_aget_param (str, param, &value, &flags); | ||
601 | if (rc == 0) | ||
602 | { | ||
603 | rc = mu_mimehdr_decode_param (value, flags, charset, pval, plang); | ||
604 | free (value); | ||
605 | } | 801 | } |
606 | return rc; | 802 | return rc; |
607 | } | 803 | } |
608 | 804 | ||
609 | /* Get the attachment name from MSG. See _header_get_param, for a | 805 | /* Get the attachment name from a message. |
610 | description of the rest of arguments. */ | 806 | |
807 | Input: | ||
808 | MSG - The input message. | ||
809 | CHARSET - Character set to recode output values to. Can be NULL. | ||
810 | Output: | ||
811 | PBUF - Output value. | ||
812 | PSZ - Its size in bytes, not counting the terminating zero. | ||
813 | PLANG - Language the name is written in, if provided in the header. | ||
814 | |||
815 | Either PSZ or PLAN (or both) can be NULL. | ||
816 | */ | ||
611 | static int | 817 | static int |
612 | _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | 818 | _get_attachment_name (mu_message_t msg, const char *charset, |
613 | char **pbuf, size_t *sz, int *pflags) | 819 | char **pbuf, size_t *psz, char **plang) |
614 | { | 820 | { |
615 | int ret = EINVAL; | 821 | int ret = EINVAL; |
616 | mu_header_t hdr; | 822 | mu_header_t hdr; |
617 | char *value = NULL; | 823 | char *value = NULL; |
824 | mu_assoc_t assoc; | ||
618 | 825 | ||
619 | if (!msg) | 826 | if (!msg) |
620 | return ret; | 827 | return ret; |
... | @@ -622,7 +829,8 @@ _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ... | @@ -622,7 +829,8 @@ _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, |
622 | if ((ret = mu_message_get_header (msg, &hdr)) != 0) | 829 | if ((ret = mu_message_get_header (msg, &hdr)) != 0) |
623 | return ret; | 830 | return ret; |
624 | 831 | ||
625 | ret = mu_header_aget_value_unfold (hdr, "Content-Disposition", &value); | 832 | ret = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_DISPOSITION, |
833 | &value); | ||
626 | 834 | ||
627 | /* If the header wasn't there, we'll fall back to Content-Type, but | 835 | /* If the header wasn't there, we'll fall back to Content-Type, but |
628 | other errors are fatal. */ | 836 | other errors are fatal. */ |
... | @@ -631,33 +839,88 @@ _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ... | @@ -631,33 +839,88 @@ _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, |
631 | 839 | ||
632 | if (ret == 0 && value != NULL) | 840 | if (ret == 0 && value != NULL) |
633 | { | 841 | { |
634 | ret = _header_get_param (value, "attachment", | 842 | ret = mu_mime_param_assoc_create (&assoc); |
635 | "filename", buf, bufsz, pbuf, sz, pflags); | 843 | if (ret) |
636 | free (value); | ||
637 | value = NULL; | ||
638 | if (ret == 0 || ret != MU_ERR_NOENT) | ||
639 | return ret; | 844 | return ret; |
845 | ret = mu_mime_param_assoc_add (assoc, "filename"); | ||
846 | if (ret == 0) | ||
847 | { | ||
848 | char *disp; | ||
849 | |||
850 | ret = mu_mime_header_parse_subset (value, charset, &disp, assoc); | ||
851 | if (ret == 0) | ||
852 | { | ||
853 | struct mu_mime_param *param; | ||
854 | if (mu_c_strcasecmp (disp, "attachment") == 0 && | ||
855 | (param = mu_assoc_ref (assoc, "filename"))) | ||
856 | { | ||
857 | *pbuf = param->value; | ||
858 | if (psz) | ||
859 | *psz = strlen (*pbuf); | ||
860 | param->value = NULL; | ||
861 | if (plang) | ||
862 | { | ||
863 | *plang = param->lang; | ||
864 | param->lang = NULL; | ||
865 | } | ||
866 | } | ||
867 | else | ||
868 | ret = MU_ERR_NOENT; | ||
869 | free (disp); | ||
870 | mu_assoc_destroy (&assoc); | ||
871 | } | ||
872 | } | ||
640 | } | 873 | } |
641 | 874 | ||
875 | free (value); | ||
876 | |||
877 | if (ret == 0) | ||
878 | return ret; | ||
879 | |||
642 | /* If we didn't get the name, we fall back on the Content-Type name | 880 | /* If we didn't get the name, we fall back on the Content-Type name |
643 | parameter. */ | 881 | parameter. */ |
644 | 882 | ||
645 | free (value); | 883 | ret = mu_header_aget_value_unfold (hdr, MU_HEADER_CONTENT_TYPE, &value); |
646 | ret = mu_header_aget_value_unfold (hdr, "Content-Type", &value); | ||
647 | if (ret == 0) | 884 | if (ret == 0) |
648 | ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz, | 885 | { |
649 | pflags); | 886 | ret = mu_mime_param_assoc_create (&assoc); |
650 | free (value); | 887 | if (ret) |
888 | return ret; | ||
889 | ret = mu_mime_param_assoc_add (assoc, "name"); | ||
890 | if (ret == 0) | ||
891 | { | ||
892 | ret = mu_mime_header_parse_subset (value, charset, NULL, assoc); | ||
893 | if (ret == 0) | ||
894 | { | ||
895 | struct mu_mime_param *param; | ||
896 | if ((param = mu_assoc_ref (assoc, "name"))) | ||
897 | { | ||
898 | *pbuf = param->value; | ||
899 | if (psz) | ||
900 | *psz = strlen (*pbuf); | ||
901 | param->value = NULL; | ||
902 | if (plang) | ||
903 | { | ||
904 | *plang = param->lang; | ||
905 | param->lang = NULL; | ||
906 | } | ||
907 | } | ||
908 | else | ||
909 | ret = MU_ERR_NOENT; | ||
910 | } | ||
911 | } | ||
912 | free (value); | ||
913 | } | ||
651 | 914 | ||
652 | return ret; | 915 | return ret; |
653 | } | 916 | } |
654 | 917 | ||
655 | int | 918 | int |
656 | mu_message_aget_attachment_name (mu_message_t msg, char **name, int *pflags) | 919 | mu_message_aget_attachment_name (mu_message_t msg, char **name) |
657 | { | 920 | { |
658 | if (name == NULL) | 921 | if (name == NULL) |
659 | return MU_ERR_OUT_PTR_NULL; | 922 | return MU_ERR_OUT_PTR_NULL; |
660 | return _get_attachment_name (msg, NULL, 0, name, NULL, pflags); | 923 | return _get_attachment_name (msg, NULL, name, NULL, NULL); |
661 | } | 924 | } |
662 | 925 | ||
663 | int | 926 | int |
... | @@ -666,21 +929,28 @@ mu_message_aget_decoded_attachment_name (mu_message_t msg, | ... | @@ -666,21 +929,28 @@ mu_message_aget_decoded_attachment_name (mu_message_t msg, |
666 | char **pval, | 929 | char **pval, |
667 | char **plang) | 930 | char **plang) |
668 | { | 931 | { |
669 | char *value; | 932 | if (pval == NULL) |
670 | int flags; | 933 | return MU_ERR_OUT_PTR_NULL; |
671 | int rc = mu_message_aget_attachment_name (msg, &value, &flags); | 934 | return _get_attachment_name (msg, charset, pval, NULL, plang); |
672 | if (rc == 0) | ||
673 | { | ||
674 | rc = mu_mimehdr_decode_param (value, flags, charset, pval, plang); | ||
675 | free (value); | ||
676 | } | ||
677 | return rc; | ||
678 | } | 935 | } |
679 | 936 | ||
680 | int | 937 | int |
681 | mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | 938 | mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, |
682 | size_t *sz, int *pflags) | 939 | size_t *sz) |
683 | { | 940 | { |
684 | return _get_attachment_name (msg, buf, bufsz, NULL, sz, pflags); | 941 | char *tmp; |
942 | size_t size; | ||
943 | int rc = _get_attachment_name (msg, NULL, &tmp, &size, NULL); | ||
944 | if (rc == 0) | ||
945 | { | ||
946 | if (size > bufsz) | ||
947 | size = bufsz; | ||
948 | if (buf) | ||
949 | size = mu_cpystr (buf, tmp, size); | ||
950 | if (sz) | ||
951 | *sz = size; | ||
952 | } | ||
953 | free (tmp); | ||
954 | return rc; | ||
685 | } | 955 | } |
686 | 956 | ... | ... |
... | @@ -51,6 +51,7 @@ noinst_PROGRAMS = \ | ... | @@ -51,6 +51,7 @@ noinst_PROGRAMS = \ |
51 | imapio\ | 51 | imapio\ |
52 | listop\ | 52 | listop\ |
53 | mailcap\ | 53 | mailcap\ |
54 | mimehdr\ | ||
54 | prop\ | 55 | prop\ |
55 | scantime\ | 56 | scantime\ |
56 | strftime\ | 57 | strftime\ |
... | @@ -88,6 +89,7 @@ TESTSUITE_AT = \ | ... | @@ -88,6 +89,7 @@ TESTSUITE_AT = \ |
88 | linecon.at\ | 89 | linecon.at\ |
89 | list.at\ | 90 | list.at\ |
90 | mailcap.at\ | 91 | mailcap.at\ |
92 | mimehdr.at\ | ||
91 | prop.at\ | 93 | prop.at\ |
92 | scantime.at\ | 94 | scantime.at\ |
93 | strftime.at\ | 95 | strftime.at\ | ... | ... |
libmailutils/tests/mimehdr.at
0 → 100644
1 | # This file is part of GNU Mailutils. -*- Autotest -*- | ||
2 | # Copyright (C) 2011 Free Software Foundation, Inc. | ||
3 | # | ||
4 | # GNU Mailutils is free software; you can redistribute it and/or | ||
5 | # modify it under the terms of the GNU General Public License as | ||
6 | # published by the Free Software Foundation; either version 3, or (at | ||
7 | # your option) any later version. | ||
8 | # | ||
9 | # GNU Mailutils is distributed in the hope that it will be useful, but | ||
10 | # WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | # General Public License for more details. | ||
13 | # | ||
14 | # You should have received a copy of the GNU General Public License | ||
15 | # along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. | ||
16 | |||
17 | # Warning: This text contains 8-bit UTF-8 | ||
18 | |||
19 | AT_BANNER(RFC 2231 header fields) | ||
20 | |||
21 | dnl --------------------------------------------------------------------- | ||
22 | dnl MIMEHDR([NAME], [KW], [OPT], [INPUT], [STDOUT = `'], [STDERR = `']) | ||
23 | dnl | ||
24 | m4_pushdef([MIMEHDR],[ | ||
25 | m4_pushdef([MU_TEST_GROUP],[mimehdr]) | ||
26 | m4_pushdef([MU_TEST_KEYWORDS],[mimehdr rfc2231]) | ||
27 | m4_pushdef([MU_TEST_COMMAND],[mimehdr $3]) | ||
28 | MU_GENERIC_TEST([$1],[$2],[$4],[],[$5],[$6]) | ||
29 | m4_popdef([MU_TEST_COMMAND]) | ||
30 | m4_popdef([MU_TEST_KEYWORDS]) | ||
31 | m4_popdef([MU_TEST_GROUP]) | ||
32 | ]) | ||
33 | dnl --------------------------------------------------------------------- | ||
34 | |||
35 | MIMEHDR([simple],[mimehdr00 mimehdr-simple], | ||
36 | [], | ||
37 | [message/external-body; access-type=URL; | ||
38 | URL="ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" | ||
39 | ], | ||
40 | [message/external-body | ||
41 | access-type=URL | ||
42 | URL=ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar | ||
43 | ]) | ||
44 | |||
45 | MIMEHDR([continuation],[mimehdr01 mimehdr-cont mimehdr-cont-00], | ||
46 | [], | ||
47 | [message/external-body; access-type=URL; | ||
48 | URL*0="ftp://"; | ||
49 | URL*1="cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar" | ||
50 | ], | ||
51 | [message/external-body | ||
52 | access-type=URL | ||
53 | URL=ftp://cs.utk.edu/pub/moore/bulk-mailer/bulk-mailer.tar | ||
54 | ]) | ||
55 | |||
56 | MIMEHDR([charset (2047)],[mimehdr02 mimehdr-charset-rfc2047 mimehdr-charset-00], | ||
57 | [], | ||
58 | [attachment; charset=utf-8; | ||
59 | filename==?UTF-8?B?zrHPgc+HzrXOr86/IM6zzrnOsSDPhM63zr0gzrTOv866zrnOvM6xz4POr86x==?= | ||
60 | ], | ||
61 | [attachment | ||
62 | charset=utf-8 | ||
63 | filename=αρχείο για την δοκιμασία | ||
64 | ]) | ||
65 | |||
66 | MIMEHDR([charset with language (2047)],[mimehdr03 mimehdr-charset-rfc2047 mimehdr-charset-01], | ||
67 | [], | ||
68 | [attachment; charset=utf-8; | ||
69 | filename==?UTF-8*el?B?zrHPgc+HzrXOr86/IM6zzrnOsSDPhM63zr0gzrTOv866zrnOvM6xz4POr86x==?= | ||
70 | ], | ||
71 | [attachment | ||
72 | charset=utf-8 | ||
73 | filename(lang:el/UTF-8)=αρχείο για την δοκιμασία | ||
74 | ]) | ||
75 | |||
76 | MIMEHDR([no charset (2231)],[mimehdr04 mimehdr-no-charset-rfc2231 mimehdr-nocharset-00], | ||
77 | [], | ||
78 | [attachment; charset=utf-8; | ||
79 | filename*=%CE%B1%CF%81%CF%87%CE%B5%CE%AF%CE%BF%20%CE%B3%CE%B9%CE%B1%20%CF%84%CE%B7%CE%BD%20%CE%B4%CE%BF%CE%BA%CE%B9%CE%BC%CE%B1%CF%83%CE%AF%CE%B1 | ||
80 | ], | ||
81 | [attachment | ||
82 | charset=utf-8 | ||
83 | filename=αρχείο για την δοκιμασία | ||
84 | ]) | ||
85 | |||
86 | MIMEHDR([charset (2231)],[mimehdr05 mimehdr-charset-rfc2231 mimehdr-charset-rfc2231-00 mimehdr-charset-03], | ||
87 | [], | ||
88 | [attachment; charset=utf-8; | ||
89 | filename*=UTF-8''%CE%B1%CF%81%CF%87%CE%B5%CE%AF%CE%BF%20%CE%B3%CE%B9%CE%B1%20%CF%84%CE%B7%CE%BD%20%CE%B4%CE%BF%CE%BA%CE%B9%CE%BC%CE%B1%CF%83%CE%AF%CE%B1 | ||
90 | ], | ||
91 | [attachment | ||
92 | charset=utf-8 | ||
93 | filename(lang:/UTF-8)=αρχείο για την δοκιμασία | ||
94 | ]) | ||
95 | |||
96 | MIMEHDR([charset with language (2231)],[mimehdr06 mimehdr-charset-rfc2231 mimehdr-charset-rfc2231-01 mimehdr-charset-04], | ||
97 | [], | ||
98 | [attachment; charset=utf-8; | ||
99 | filename*=UTF-8'el_GR'%CE%B1%CF%81%CF%87%CE%B5%CE%AF%CE%BF%20%CE%B3%CE%B9%CE%B1%20%CF%84%CE%B7%CE%BD%20%CE%B4%CE%BF%CE%BA%CE%B9%CE%BC%CE%B1%CF%83%CE%AF%CE%B1 | ||
100 | ], | ||
101 | [attachment | ||
102 | charset=utf-8 | ||
103 | filename(lang:el_GR/UTF-8)=αρχείο για την δοκιμασία | ||
104 | ]) | ||
105 | |||
106 | MIMEHDR([charset with language and continuation (2231)],[mimehdr07 mimehdr-charset-rfc2231 mimehdr-charset-rfc2231-02 mimehdr-charset-05], | ||
107 | [], | ||
108 | [attachment; charset=utf-8; | ||
109 | filename*00*=UTF-8'el_GR'%CE%B1%CF%81%CF%87%CE%B5; | ||
110 | filename*01*=%CE%AF%CE%BF%20%CE%B3%CE; | ||
111 | filename*02*=%B9%CE%B1%20%CF%84%CE%B7; | ||
112 | filename*03*=%CE%BD%20%CE%B4%CE%BF%CE; | ||
113 | filename*04*=%BA%CE%B9%CE%BC%CE%B1%CF%83%CE%AF%CE%B1 | ||
114 | ], | ||
115 | [attachment | ||
116 | charset=utf-8 | ||
117 | filename(lang:el_GR/UTF-8)=αρχείο για την δοκιμασία | ||
118 | ]) | ||
119 | |||
120 | MIMEHDR([combined charset, lang and cset],[mimehdr08 mimehdr-comb mimehdr-charset-rfc2231], | ||
121 | [], | ||
122 | [application/x-stuff | ||
123 | title*0*=us-ascii'en'This%20is%20even%20more%20 | ||
124 | title*1*=%2A%2A%2Afun%2A%2A%2A%20 | ||
125 | title*2="isn't it!" | ||
126 | ], | ||
127 | [application/x-stuff | ||
128 | title(lang:en/us-ascii)=This is even more ***fun*** isn't it! | ||
129 | ]) | ||
130 | |||
131 | m4_popdef([MIMEHDR]) |
libmailutils/tests/mimehdr.c
0 → 100644
1 | /* GNU Mailutils -- a suite of utilities for electronic mail | ||
2 | Copyright (C) 2005, 2007, 2009, 2010, 2011 Free Software Foundation, | ||
3 | Inc. | ||
4 | |||
5 | GNU Mailutils is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 3, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | GNU Mailutils is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNU Mailutils. If not, see <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 <mailutils/assoc.h> | ||
24 | #include <mailutils/header.h> | ||
25 | #include <mailutils/message.h> | ||
26 | #include <mailutils/mime.h> | ||
27 | #include <mailutils/iterator.h> | ||
28 | #include <mailutils/stream.h> | ||
29 | #include <mailutils/stdstream.h> | ||
30 | #include <mailutils/util.h> | ||
31 | #include <mailutils/cstr.h> | ||
32 | #include <mailutils/cctype.h> | ||
33 | #include <mailutils/error.h> | ||
34 | #include <mailutils/errno.h> | ||
35 | |||
36 | struct named_param | ||
37 | { | ||
38 | const char *name; | ||
39 | struct mu_mime_param const *param; | ||
40 | }; | ||
41 | |||
42 | static int | ||
43 | sort_names (void const *a, void const *b) | ||
44 | { | ||
45 | struct named_param const *pa = a; | ||
46 | struct named_param const *pb = b; | ||
47 | return mu_c_strcasecmp (pa->name, pb->name); | ||
48 | } | ||
49 | |||
50 | static int | ||
51 | print_named_param (void *item, void *data) | ||
52 | { | ||
53 | struct named_param const *p = item; | ||
54 | struct mu_mime_param const *param = p->param; | ||
55 | |||
56 | mu_printf ("%s", p->name); | ||
57 | if (param->lang) | ||
58 | mu_printf ("(lang:%s/%s)", param->lang, param->cset); | ||
59 | mu_printf ("=%s\n", param->value); | ||
60 | return 0; | ||
61 | } | ||
62 | |||
63 | int | ||
64 | main (int argc, char **argv) | ||
65 | { | ||
66 | int i; | ||
67 | mu_stream_t tmp; | ||
68 | mu_transport_t trans[2]; | ||
69 | char *value; | ||
70 | mu_assoc_t assoc; | ||
71 | mu_iterator_t itr; | ||
72 | mu_list_t list; | ||
73 | char *charset = NULL; | ||
74 | |||
75 | mu_set_program_name (argv[0]); | ||
76 | for (i = 1; i < argc; i++) | ||
77 | { | ||
78 | char *opt = argv[i]; | ||
79 | |||
80 | if (strncmp (opt, "-debug=", 7) == 0) | ||
81 | mu_debug_parse_spec (opt + 7); | ||
82 | else if (strncmp (opt, "-charset=", 9) == 0) | ||
83 | charset = opt + 9; | ||
84 | else if (strcmp (opt, "-h") == 0 || strcmp (opt, "-help") == 0) | ||
85 | { | ||
86 | mu_printf ("usage: %s [-charset=cs] [-debug=SPEC]", mu_program_name); | ||
87 | return 0; | ||
88 | } | ||
89 | else | ||
90 | { | ||
91 | mu_error ("unknown option %s", opt); | ||
92 | return 1; | ||
93 | } | ||
94 | } | ||
95 | |||
96 | if (i != argc) | ||
97 | { | ||
98 | mu_error ("too many arguments"); | ||
99 | return 1; | ||
100 | } | ||
101 | |||
102 | MU_ASSERT (mu_memory_stream_create (&tmp, MU_STREAM_RDWR)); | ||
103 | MU_ASSERT (mu_stream_copy (tmp, mu_strin, 0, NULL)); | ||
104 | MU_ASSERT (mu_stream_write (tmp, "", 1, NULL)); | ||
105 | MU_ASSERT (mu_stream_ioctl (tmp, MU_IOCTL_TRANSPORT, MU_IOCTL_OP_GET, | ||
106 | trans)); | ||
107 | |||
108 | MU_ASSERT (mu_mime_header_parse ((char*)trans[0], charset, &value, &assoc)); | ||
109 | |||
110 | mu_printf ("%s\n", value); | ||
111 | MU_ASSERT (mu_list_create (&list)); | ||
112 | MU_ASSERT (mu_assoc_get_iterator (assoc, &itr)); | ||
113 | for (mu_iterator_first (itr); !mu_iterator_is_done (itr); | ||
114 | mu_iterator_next (itr)) | ||
115 | { | ||
116 | const char *name; | ||
117 | struct mu_mime_param *p; | ||
118 | struct named_param *np; | ||
119 | |||
120 | mu_iterator_current_kv (itr, (const void **)&name, (void**)&p); | ||
121 | np = malloc (sizeof (*np)); | ||
122 | if (!np) | ||
123 | abort (); | ||
124 | np->name = name; | ||
125 | np->param = p; | ||
126 | MU_ASSERT (mu_list_append (list, np)); | ||
127 | } | ||
128 | mu_iterator_destroy (&itr); | ||
129 | |||
130 | mu_list_sort (list, sort_names); | ||
131 | mu_list_foreach (list, print_named_param, NULL); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 |
... | @@ -96,4 +96,6 @@ m4_include([imapio.at]) | ... | @@ -96,4 +96,6 @@ m4_include([imapio.at]) |
96 | m4_include([scantime.at]) | 96 | m4_include([scantime.at]) |
97 | m4_include([strftime.at]) | 97 | m4_include([strftime.at]) |
98 | 98 | ||
99 | m4_include([fsaf.at]) | ||
... | \ No newline at end of file | ... | \ No newline at end of file |
99 | m4_include([fsaf.at]) | ||
100 | |||
101 | m4_include([mimehdr.at]) | ... | ... |
-
Please register or sign in to post a comment