Improve compatibility with RFC 2231 and RFC 2047.
* mailbox/mimehdr.c: New file. * mailbox/Makefile.am (libmailutils_la_SOURCES): Add mailbox/mimehdr.c. * mailbox/attachment.c (_header_get_param) (_get_attachment_name, mu_message_aget_attachment_name) (mu_message_get_attachment_name: Move to mailbox/mimehdr.c (with edits). (mu_message_save_attachment): Add a FIXME comment. * include/mailutils/message.h (MU_MIMEHDR_MULTILINE) (MU_MIMEHDR_CSINFO): New defines. (mu_mimehdr_get_disp,mu_mimehdr_aget_disp) (mu_mimehdr_get_param,mu_mimehdr_aget_param) (mu_mimehdr_decode_param) (mu_mimehdr_aget_decoded_param): New prototypes. (mu_message_aget_attachment_name): Change signature. (mu_message_aget_decoded_attachment_name): New prototype. * mailbox/mutil.c (mu_hex2ul): Fix a silly bug (have anybody ever tried to use that function?!?) * mailbox/testsuite/Urls: Update. * examples/mimetest.c (message_display_parts): Add a FIXME comment. * libmu_cpp/message.cc (Message::get_attachment_name): Likewise. * mh/mhn.c (store_handler): Likewise. * python/libmu_py/message.c (api_message_get_attachment_name): Likewise.
Showing
10 changed files
with
44 additions
and
301 deletions
... | @@ -242,7 +242,8 @@ message_display_parts (mu_message_t msg, int indent) | ... | @@ -242,7 +242,8 @@ message_display_parts (mu_message_t msg, int indent) |
242 | { | 242 | { |
243 | /* Save the attachements. */ | 243 | /* Save the attachements. */ |
244 | char *fname = NULL; | 244 | char *fname = NULL; |
245 | mu_message_aget_attachment_name (part, &fname); | 245 | /* FIXME: CS/Lang info is ignored */ |
246 | mu_message_aget_attachment_name (part, &fname, NULL); | ||
246 | if (fname == NULL) | 247 | if (fname == NULL) |
247 | fname = mu_tempname (NULL); | 248 | fname = mu_tempname (NULL); |
248 | 249 | ... | ... |
... | @@ -119,9 +119,36 @@ extern int mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg, | ... | @@ -119,9 +119,36 @@ extern int mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg, |
119 | extern int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg, | 119 | extern int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg, |
120 | void **data); | 120 | void **data); |
121 | 121 | ||
122 | /* Bit values for *pflags in functions below */ | ||
123 | #define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */ | ||
124 | #define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language | ||
125 | info */ | ||
126 | |||
127 | extern int mu_mimehdr_get_disp (const char *str, const char *param, | ||
128 | char *buf, size_t bufsz, size_t *retsz); | ||
129 | extern int mu_mimehdr_aget_disp (const char *str, const char *param, | ||
130 | char **pvalue); | ||
131 | extern int mu_mimehdr_get_param (const char *str, const char *param, | ||
132 | char *buf, size_t bufsz, size_t *retsz, | ||
133 | int *pflags); | ||
134 | extern int mu_mimehdr_aget_param (const char *str, const char *param, | ||
135 | char **pval, int *pflags); | ||
136 | extern int mu_mimehdr_decode_param (const char *value, int csinfo, | ||
137 | const char *charset, | ||
138 | char **pval, char **plang); | ||
139 | extern int mu_mimehdr_aget_decoded_param (const char *str, const char *param, | ||
140 | const char *charset, | ||
141 | char **pval, char **plang); | ||
142 | |||
122 | extern int mu_message_get_attachment_name (mu_message_t, char *name, | 143 | extern int mu_message_get_attachment_name (mu_message_t, char *name, |
123 | size_t bufsz, size_t* sz); | 144 | size_t bufsz, size_t* sz, |
124 | extern int mu_message_aget_attachment_name (mu_message_t, char **name); | 145 | int *pflags); |
146 | extern int mu_message_aget_attachment_name (mu_message_t, char **name, | ||
147 | int *pflags); | ||
148 | extern int mu_message_aget_decoded_attachment_name (mu_message_t msg, | ||
149 | const char *charset, | ||
150 | char **name, | ||
151 | char **plang); | ||
125 | 152 | ||
126 | extern int mu_message_save_to_mailbox (mu_message_t msg, | 153 | extern int mu_message_save_to_mailbox (mu_message_t msg, |
127 | mu_debug_t debug, const char *toname, | 154 | mu_debug_t debug, const char *toname, | ... | ... |
... | @@ -224,7 +224,8 @@ Message :: get_attachment_name () | ... | @@ -224,7 +224,8 @@ Message :: get_attachment_name () |
224 | char *c_name; | 224 | char *c_name; |
225 | std::string name; | 225 | std::string name; |
226 | 226 | ||
227 | int status = mu_message_aget_attachment_name (msg, &c_name); | 227 | /* FIXME: CS/Lang info is ignored */ |
228 | int status = mu_message_aget_attachment_name (msg, &c_name, NULL); | ||
228 | if (status) | 229 | if (status) |
229 | throw Exception ("Message::get_attachment_name", status); | 230 | throw Exception ("Message::get_attachment_name", status); |
230 | if (c_name) { | 231 | if (c_name) { | ... | ... |
... | @@ -101,6 +101,7 @@ libmailutils_la_SOURCES = \ | ... | @@ -101,6 +101,7 @@ libmailutils_la_SOURCES = \ |
101 | memory_stream.c\ | 101 | memory_stream.c\ |
102 | message_stream.c\ | 102 | message_stream.c\ |
103 | mime.c\ | 103 | mime.c\ |
104 | mimehdr.c\ | ||
104 | mkfilename.c\ | 105 | mkfilename.c\ |
105 | monitor.c\ | 106 | monitor.c\ |
106 | msrv.c\ | 107 | msrv.c\ | ... | ... |
... | @@ -185,297 +185,6 @@ _attachment_free (struct _msg_info *info, int free_message) | ... | @@ -185,297 +185,6 @@ _attachment_free (struct _msg_info *info, int free_message) |
185 | free (info); | 185 | free (info); |
186 | } | 186 | } |
187 | 187 | ||
188 | /* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ | ||
189 | #define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) | ||
190 | |||
191 | /* _header_get_param - an auxiliary function to extract values from | ||
192 | Content-Type, Content-Disposition and similar headers. | ||
193 | |||
194 | Arguments: | ||
195 | |||
196 | FIELD_BODY Header value, complying to RFCs 2045, 2183, 2231.3; | ||
197 | DISP Disposition. Unless it is NULL, the disposition part | ||
198 | of FIELD_BODY is compared with it. If they differ, | ||
199 | the function returns MU_ERR_NOENT. | ||
200 | PARAM Name of the parameter to extract from FIELD_BODY; | ||
201 | BUF Where to extract the value to; | ||
202 | BUFSZ Size of BUF; | ||
203 | PRET Pointer to the memory location for the return buffer (see | ||
204 | below). | ||
205 | PLEN Pointer to the return size. | ||
206 | |||
207 | The function parses FIELD_BODY and extracts the value of the parameter | ||
208 | PARAM. | ||
209 | |||
210 | If BUF is not NULL and BUFSZ is not 0, the extracted value is stored into | ||
211 | BUF. At most BUFSZ-1 bytes are copied. | ||
212 | |||
213 | Otherwise, if PRET is not NULL, the function allocates enough memory to | ||
214 | hold the extracted value, copies there the result, and stores the | ||
215 | pointer to the allocated memory into the location pointed to by PRET. | ||
216 | |||
217 | If PLEN is not NULL, the size of the extracted value (without terminating | ||
218 | NUL character) is stored there. | ||
219 | |||
220 | If BUF==NULL *and* PRET==NULL, no memory is allocated, but PLEN is | ||
221 | honored anyway, i.e. unless it is NULL it receives size of the result. | ||
222 | This can be used to estimate the needed buffer size. | ||
223 | |||
224 | Return values: | ||
225 | 0 on success. | ||
226 | MU_ERR_NOENT, requested parameter not found, or disposition does | ||
227 | not match DISP. | ||
228 | MU_ERR_PARSE, if FIELD_BODY does not comply to any of the abovemntioned | ||
229 | RFCs. | ||
230 | ENOMEM , if unable to allocate memory. | ||
231 | */ | ||
232 | |||
233 | int | ||
234 | _header_get_param (char *field_body, | ||
235 | const char *disp, | ||
236 | const char *param, | ||
237 | char *buf, size_t bufsz, | ||
238 | char **pret, size_t *plen) | ||
239 | { | ||
240 | int res = MU_ERR_NOENT; /* Return value, pessimistic default */ | ||
241 | size_t param_len = strlen (param); | ||
242 | char *p; | ||
243 | char *mem = NULL; /* Allocated memory storage */ | ||
244 | size_t retlen = 0; /* Total number of bytes copied */ | ||
245 | unsigned long cind = 0; /* Expected continued parameter index. | ||
246 | See RFC 2231, Section 3, | ||
247 | "Parameter Value Continuations" */ | ||
248 | |||
249 | if (field_body == NULL) | ||
250 | return EINVAL; | ||
251 | |||
252 | if (bufsz == 0) /* Make sure buf value is meaningful */ | ||
253 | buf = NULL; | ||
254 | |||
255 | p = strchr (field_body, ';'); | ||
256 | if (!p) | ||
257 | return MU_ERR_NOENT; | ||
258 | if (disp && mu_c_strncasecmp (field_body, disp, p - field_body)) | ||
259 | return MU_ERR_NOENT; | ||
260 | |||
261 | while (p && *p) | ||
262 | { | ||
263 | char *v, *e; | ||
264 | size_t len, escaped_chars = 0; | ||
265 | |||
266 | if (*p != ';') | ||
267 | { | ||
268 | res = MU_ERR_PARSE; | ||
269 | break; | ||
270 | } | ||
271 | |||
272 | /* walk upto start of param */ | ||
273 | p = mu_str_skip_class (p + 1, MU_CTYPE_SPACE); | ||
274 | if ((v = strchr (p, '=')) == NULL) | ||
275 | break; | ||
276 | v++; | ||
277 | /* Find end of the parameter */ | ||
278 | if (*v == '"') | ||
279 | { | ||
280 | /* Quoted string */ | ||
281 | for (e = ++v; *e != '"'; e++) | ||
282 | { | ||
283 | if (*e == 0) /* Malformed header */ | ||
284 | { | ||
285 | res = MU_ERR_PARSE; | ||
286 | break; | ||
287 | } | ||
288 | if (*e == '\\') | ||
289 | { | ||
290 | if (*++e == 0) | ||
291 | { | ||
292 | res = MU_ERR_PARSE; | ||
293 | break; | ||
294 | } | ||
295 | escaped_chars++; | ||
296 | } | ||
297 | } | ||
298 | if (res == MU_ERR_PARSE) | ||
299 | break; | ||
300 | len = e - v; | ||
301 | e++; | ||
302 | } | ||
303 | else | ||
304 | { | ||
305 | for (e = v + 1; !(_ISSPECIAL (*e) || mu_isspace (*e)); e++) | ||
306 | ; | ||
307 | len = e - v; | ||
308 | } | ||
309 | |||
310 | /* Is it our parameter? */ | ||
311 | if (mu_c_strncasecmp (p, param, param_len)) | ||
312 | { /* nope, jump to next */ | ||
313 | p = strchr (e, ';'); | ||
314 | continue; | ||
315 | } | ||
316 | |||
317 | res = 0; /* Indicate success */ | ||
318 | |||
319 | if (p[param_len] == '*') | ||
320 | { | ||
321 | /* Parameter value continuation (RFC 2231, Section 3). | ||
322 | See if the index is OK */ | ||
323 | char *end; | ||
324 | unsigned long n = strtoul (p + param_len + 1, &end, 10); | ||
325 | if (*end != '=' || n != cind) | ||
326 | { | ||
327 | res = MU_ERR_PARSE; | ||
328 | break; | ||
329 | } | ||
330 | /* Everything OK, increase the estimation */ | ||
331 | cind++; | ||
332 | } | ||
333 | |||
334 | /* Prepare P for the next iteration */ | ||
335 | p = e; | ||
336 | |||
337 | /* Escape characters that appear in quoted-pairs are | ||
338 | semantically "invisible" (RFC 2822, Section 3.2.2, | ||
339 | "Quoted characters") */ | ||
340 | len -= escaped_chars; | ||
341 | |||
342 | /* Adjust len if nearing end of the buffer */ | ||
343 | if (bufsz && len >= bufsz) | ||
344 | len = bufsz - 1; | ||
345 | |||
346 | if (pret) | ||
347 | { | ||
348 | /* The caller wants us to allocate the memory */ | ||
349 | if (!buf && !mem) | ||
350 | { | ||
351 | mem = malloc (len + 1); | ||
352 | if (!mem) | ||
353 | { | ||
354 | res = ENOMEM; | ||
355 | break; | ||
356 | } | ||
357 | buf = mem; | ||
358 | } | ||
359 | else if (mem) | ||
360 | { | ||
361 | /* If we got here, it means we are iterating over | ||
362 | a parameter value continuation, and cind=0 has | ||
363 | already been passed. Reallocate the memory to | ||
364 | accomodate next chunk of data. */ | ||
365 | char *newmem = realloc (mem, retlen + len + 1); | ||
366 | if (!newmem) | ||
367 | { | ||
368 | res = ENOMEM; | ||
369 | break; | ||
370 | } | ||
371 | mem = newmem; | ||
372 | } | ||
373 | } | ||
374 | |||
375 | if (buf) | ||
376 | { | ||
377 | /* Actually copy the data. Buf is not NULL either because | ||
378 | the user passed it as an argument, or because we allocated | ||
379 | memory for it. */ | ||
380 | if (escaped_chars) | ||
381 | { | ||
382 | int i; | ||
383 | for (i = 0; i < len; i++) | ||
384 | { | ||
385 | if (*v == '\\') | ||
386 | ++v; | ||
387 | buf[retlen + i] = *v++; | ||
388 | } | ||
389 | } | ||
390 | else | ||
391 | memcpy (buf + retlen, v, len); | ||
392 | } | ||
393 | /* Adjust total result size ... */ | ||
394 | retlen += len; | ||
395 | /* ... and remaining buffer size, if necessary */ | ||
396 | if (bufsz) | ||
397 | { | ||
398 | bufsz -= len; | ||
399 | if (bufsz == 0) | ||
400 | break; | ||
401 | } | ||
402 | } | ||
403 | |||
404 | if (res == 0) | ||
405 | { | ||
406 | /* Everything OK, prepare the returned data. */ | ||
407 | if (buf) | ||
408 | buf[retlen] = 0; | ||
409 | if (plen) | ||
410 | *plen = retlen; | ||
411 | if (pret) | ||
412 | *pret = mem; | ||
413 | } | ||
414 | else if (mem) | ||
415 | free (mem); | ||
416 | return res; | ||
417 | } | ||
418 | |||
419 | /* Get the attachment name from MSG. See _header_get_param, for a | ||
420 | description of the rest of arguments. */ | ||
421 | static int | ||
422 | _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ||
423 | char **pbuf, size_t *sz) | ||
424 | { | ||
425 | int ret = EINVAL; | ||
426 | mu_header_t hdr; | ||
427 | char *value = NULL; | ||
428 | |||
429 | if (!msg) | ||
430 | return ret; | ||
431 | |||
432 | if ((ret = mu_message_get_header (msg, &hdr)) != 0) | ||
433 | return ret; | ||
434 | |||
435 | ret = mu_header_aget_value (hdr, "Content-Disposition", &value); | ||
436 | |||
437 | /* If the header wasn't there, we'll fall back to Content-Type, but | ||
438 | other errors are fatal. */ | ||
439 | if (ret != 0 && ret != MU_ERR_NOENT) | ||
440 | return ret; | ||
441 | |||
442 | if (ret == 0 && value != NULL) | ||
443 | { | ||
444 | ret = _header_get_param (value, "attachment", | ||
445 | "filename", buf, bufsz, pbuf, sz); | ||
446 | free (value); | ||
447 | value = NULL; | ||
448 | if (ret == 0 || ret != MU_ERR_NOENT) | ||
449 | return ret; | ||
450 | } | ||
451 | |||
452 | /* If we didn't get the name, we fall back on the Content-Type name | ||
453 | parameter. */ | ||
454 | |||
455 | free (value); | ||
456 | ret = mu_header_aget_value (hdr, "Content-Type", &value); | ||
457 | if (ret == 0) | ||
458 | ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz); | ||
459 | free (value); | ||
460 | |||
461 | return ret; | ||
462 | } | ||
463 | |||
464 | int | ||
465 | mu_message_aget_attachment_name (mu_message_t msg, char **name) | ||
466 | { | ||
467 | if (name == NULL) | ||
468 | return MU_ERR_OUT_PTR_NULL; | ||
469 | return _get_attachment_name (msg, NULL, 0, name, NULL); | ||
470 | } | ||
471 | |||
472 | int | ||
473 | mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ||
474 | size_t *sz) | ||
475 | { | ||
476 | return _get_attachment_name (msg, buf, bufsz, NULL, sz); | ||
477 | } | ||
478 | |||
479 | int | 188 | int |
480 | mu_message_save_attachment (mu_message_t msg, const char *filename, | 189 | mu_message_save_attachment (mu_message_t msg, const char *filename, |
481 | void **data) | 190 | void **data) |
... | @@ -499,7 +208,8 @@ mu_message_save_attachment (mu_message_t msg, const char *filename, | ... | @@ -499,7 +208,8 @@ mu_message_save_attachment (mu_message_t msg, const char *filename, |
499 | { | 208 | { |
500 | if (filename == NULL) | 209 | if (filename == NULL) |
501 | { | 210 | { |
502 | ret = mu_message_aget_attachment_name (msg, &partname); | 211 | /* FIXME: Charset info is ignored */ |
212 | ret = mu_message_aget_attachment_name (msg, &partname, NULL); | ||
503 | if (partname) | 213 | if (partname) |
504 | fname = partname; | 214 | fname = partname; |
505 | } | 215 | } | ... | ... |
mailbox/mimehdr.c
0 → 100644
This diff is collapsed.
Click to expand it.
... | @@ -75,10 +75,10 @@ mu_hex2ul (char hex) | ... | @@ -75,10 +75,10 @@ mu_hex2ul (char hex) |
75 | return hex - '0'; | 75 | return hex - '0'; |
76 | 76 | ||
77 | if (hex >= 'a' && hex <= 'z') | 77 | if (hex >= 'a' && hex <= 'z') |
78 | return hex - 'a'; | 78 | return hex - 'a' + 10; |
79 | 79 | ||
80 | if (hex >= 'A' && hex <= 'Z') | 80 | if (hex >= 'A' && hex <= 'Z') |
81 | return hex - 'A'; | 81 | return hex - 'A' + 10; |
82 | 82 | ||
83 | return -1; | 83 | return -1; |
84 | } | 84 | } | ... | ... |
... | @@ -58,7 +58,7 @@ scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74 => SUCCESS | ... | @@ -58,7 +58,7 @@ scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74 => SUCCESS |
58 | user <user> | 58 | user <user> |
59 | passwd <pass> | 59 | passwd <pass> |
60 | auth <> | 60 | auth <> |
61 | host <hest> | 61 | host <host> |
62 | port 0 | 62 | port 0 |
63 | path <> | 63 | path <> |
64 | 64 | ... | ... |
... | @@ -1590,7 +1590,9 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, | ... | @@ -1590,7 +1590,9 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, |
1590 | { | 1590 | { |
1591 | char *val; | 1591 | char *val; |
1592 | 1592 | ||
1593 | if (mu_message_aget_attachment_name (msg, &val) == 0) | 1593 | /* FIXME: Take into account CS/Lang info and recode the value |
1594 | if necessary */ | ||
1595 | if (mu_message_aget_attachment_name (msg, &val, NULL) == 0) | ||
1594 | { | 1596 | { |
1595 | name = normalize_path (dir, val); | 1597 | name = normalize_path (dir, val); |
1596 | free (val); | 1598 | free (val); | ... | ... |
... | @@ -287,7 +287,8 @@ api_message_get_attachment_name (PyObject *self, PyObject *args) | ... | @@ -287,7 +287,8 @@ api_message_get_attachment_name (PyObject *self, PyObject *args) |
287 | if (!PyArg_ParseTuple (args, "O!", &PyMessageType, &py_msg)) | 287 | if (!PyArg_ParseTuple (args, "O!", &PyMessageType, &py_msg)) |
288 | return NULL; | 288 | return NULL; |
289 | 289 | ||
290 | status = mu_message_aget_attachment_name (py_msg->msg, &name); | 290 | /* FIXME: CS/Lang info is ignored */ |
291 | status = mu_message_aget_attachment_name (py_msg->msg, &name, NULL); | ||
291 | return status_object (status, PyString_FromString (name ? name : "")); | 292 | return status_object (status, PyString_FromString (name ? name : "")); |
292 | } | 293 | } |
293 | 294 | ... | ... |
-
Please register or sign in to post a comment