Commit 3c5aed39 3c5aed3965c70e6e1a894005b1adc7a407d0aae9 by Sergey Poznyakoff

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.
1 parent c22d07a3
...@@ -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, &param);
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, &param);
232 if (rc)
233 _mu_mime_param_free (&param);
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 (&param, 0, sizeof param);
259 return mu_assoc_install (assoc, name, &param);
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, &param);
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 (&param, 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 (&param);
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, &param);
552 if (rc)
311 { 553 {
312 int i; 554 _mu_mime_param_free (&param);
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
......
...@@ -16,6 +16,7 @@ fsfolder ...@@ -16,6 +16,7 @@ fsfolder
16 imapio 16 imapio
17 listop 17 listop
18 mailcap 18 mailcap
19 mimehdr
19 prop 20 prop
20 scantime 21 scantime
21 strftime 22 strftime
......
...@@ -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\
......
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])
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])
......