Make mu_message_(a?)get_attachment_name RFC-compatible.
* mailbox/attachment.c (_header_get_param): Rewrite. (_get_attachment_name): New auxiliary function. (mu_message_aget_attachment_name) (mu_message_get_attachment_name): Rewrite using _get_attachment_name. * mh/mhn.c (store_handler): Use mu_message_aget_attachment_name.
Showing
2 changed files
with
243 additions
and
127 deletions
... | @@ -188,78 +188,243 @@ _attachment_free (struct _msg_info *info, int free_message) | ... | @@ -188,78 +188,243 @@ _attachment_free (struct _msg_info *info, int free_message) |
188 | /* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ | 188 | /* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ |
189 | #define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) | 189 | #define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) |
190 | 190 | ||
191 | static char * | 191 | /* _header_get_param - an auxiliary function to extract values from |
192 | _header_get_param (char *field_body, const char *param, size_t *len) | 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) | ||
193 | { | 239 | { |
194 | char *str, *p, *v, *e; | 240 | int res = MU_ERR_NOENT; /* Return value, pessimistic default */ |
195 | int quoted = 0, was_quoted = 0; | 241 | size_t param_len = strlen (param); |
196 | 242 | char *p; | |
197 | if (len == NULL || (str = field_body) == NULL) | 243 | char *mem = NULL; /* Allocated memory storage */ |
198 | return NULL; | 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; | ||
199 | 251 | ||
200 | p = strchr (str, ';'); | 252 | if (bufsz == 0) /* Make sure buf value is meaningful */ |
201 | while (p) | 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) | ||
202 | { | 262 | { |
203 | p++; | 263 | char *v, *e; |
204 | while (mu_isspace (*p)) /* walk upto start of param */ | 264 | size_t len, escaped_chars = 0; |
205 | p++; | 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); | ||
206 | if ((v = strchr (p, '=')) == NULL) | 274 | if ((v = strchr (p, '=')) == NULL) |
207 | break; | 275 | break; |
208 | *len = 0; | 276 | v++; |
209 | v = e = v + 1; | 277 | /* Find end of the parameter */ |
210 | while (*e && (quoted || (!_ISSPECIAL (*e) && !mu_isspace (*e)))) | 278 | if (*v == '"') |
211 | { /* skip pass value and calc len */ | 279 | { |
212 | if (*e == '\"') | 280 | /* Quoted string */ |
213 | quoted = ~quoted, was_quoted = 1; | 281 | for (e = ++v; *e != '"'; e++) |
214 | else | 282 | { |
215 | (*len)++; | 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; | ||
216 | e++; | 301 | e++; |
217 | } | 302 | } |
218 | if (mu_c_strncasecmp (p, param, strlen (param))) | 303 | else |
219 | { /* no match jump to next */ | 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 */ | ||
220 | p = strchr (e, ';'); | 313 | p = strchr (e, ';'); |
221 | continue; | 314 | continue; |
222 | } | 315 | } |
223 | else | 316 | |
224 | return was_quoted ? v + 1 : v; /* return unquoted value */ | 317 | res = 0; /* Indicate success */ |
225 | } | 318 | |
226 | return NULL; | 319 | if (p[param_len] == '*') |
227 | } | 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; | ||
228 | 336 | ||
229 | int | 337 | /* Escape characters that appear in quoted-pairs are |
230 | mu_message_aget_attachment_name (mu_message_t msg, char **name) | 338 | semantically "invisible" (RFC 2822, Section 3.2.2, |
231 | { | 339 | "Quoted characters") */ |
232 | size_t sz = 0; | 340 | len -= escaped_chars; |
233 | int ret = 0; | ||
234 | 341 | ||
235 | if (name == NULL) | 342 | /* Adjust len if nearing end of the buffer */ |
236 | return MU_ERR_OUT_PTR_NULL; | 343 | if (bufsz && len >= bufsz) |
344 | len = bufsz - 1; | ||
237 | 345 | ||
238 | if ((ret = mu_message_get_attachment_name (msg, NULL, 0, &sz)) != 0) | 346 | if (pret) |
239 | return ret; | 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 | } | ||
240 | 374 | ||
241 | *name = malloc (sz + 1); | 375 | if (buf) |
242 | if (!*name) | 376 | { |
243 | return ENOMEM; | 377 | /* Actually copy the data. Buf is not NULL either because |
244 | 378 | the user passed it as an argument, or because we allocated | |
245 | if ((ret = mu_message_get_attachment_name (msg, *name, sz + 1, NULL)) != 0) | 379 | memory for it. */ |
246 | { | 380 | if (escaped_chars) |
247 | free (*name); | 381 | { |
248 | *name = NULL; | 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 | } | ||
249 | } | 402 | } |
250 | 403 | ||
251 | return ret; | 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; | ||
252 | } | 417 | } |
253 | 418 | ||
254 | int | 419 | /* Get the attachment name from MSG. See _header_get_param, for a |
255 | mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | 420 | description of the rest of arguments. */ |
256 | size_t *sz) | 421 | static int |
422 | _get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ||
423 | char **pbuf, size_t *sz) | ||
257 | { | 424 | { |
258 | int ret = EINVAL; | 425 | int ret = EINVAL; |
259 | mu_header_t hdr; | 426 | mu_header_t hdr; |
260 | char *value = NULL; | 427 | char *value = NULL; |
261 | char *name = NULL; | ||
262 | size_t namesz = 0; | ||
263 | 428 | ||
264 | if (!msg) | 429 | if (!msg) |
265 | return ret; | 430 | return ret; |
... | @@ -276,41 +441,39 @@ mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, | ... | @@ -276,41 +441,39 @@ mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, |
276 | 441 | ||
277 | if (ret == 0 && value != NULL) | 442 | if (ret == 0 && value != NULL) |
278 | { | 443 | { |
279 | /* FIXME: this is cheezy, it should check the value of the | 444 | ret = _header_get_param (value, "attachment", |
280 | Content-Disposition field, not strstr it. */ | 445 | "filename", buf, bufsz, pbuf, sz); |
281 | 446 | free (value); | |
282 | if (strstr (value, "attachment") != NULL) | 447 | value = NULL; |
283 | name = _header_get_param (value, "filename", &namesz); | 448 | if (ret == 0 || ret != MU_ERR_NOENT) |
449 | return ret; | ||
284 | } | 450 | } |
285 | 451 | ||
286 | /* If we didn't get the name, we fall back on the Content-Type name | 452 | /* If we didn't get the name, we fall back on the Content-Type name |
287 | parameter. */ | 453 | parameter. */ |
288 | 454 | ||
289 | if (name == NULL) | 455 | free (value); |
290 | { | 456 | ret = mu_header_aget_value (hdr, "Content-Type", &value); |
291 | if (value) | 457 | if (ret == 0) |
292 | free (value); | 458 | ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz); |
293 | 459 | free (value); | |
294 | ret = mu_header_aget_value (hdr, "Content-Type", &value); | ||
295 | name = _header_get_param (value, "name", &namesz); | ||
296 | } | ||
297 | |||
298 | if (name) | ||
299 | { | ||
300 | ret = 0; | ||
301 | |||
302 | name[namesz] = '\0'; | ||
303 | 460 | ||
304 | if (sz) | 461 | return ret; |
305 | *sz = namesz; | 462 | } |
306 | 463 | ||
307 | if (buf) | 464 | int |
308 | strncpy (buf, name, bufsz); | 465 | mu_message_aget_attachment_name (mu_message_t msg, char **name) |
309 | } | 466 | { |
310 | else | 467 | if (name == NULL) |
311 | ret = MU_ERR_NOENT; | 468 | return MU_ERR_OUT_PTR_NULL; |
469 | return _get_attachment_name (msg, NULL, 0, name, NULL); | ||
470 | } | ||
312 | 471 | ||
313 | return ret; | 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); | ||
314 | } | 477 | } |
315 | 478 | ||
316 | int | 479 | int | ... | ... |
... | @@ -1588,59 +1588,12 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, | ... | @@ -1588,59 +1588,12 @@ store_handler (mu_message_t msg, msg_part_t part, char *type, char *encoding, |
1588 | 1588 | ||
1589 | if (mode_options & OPT_AUTO) | 1589 | if (mode_options & OPT_AUTO) |
1590 | { | 1590 | { |
1591 | mu_header_t hdr; | ||
1592 | char *val; | 1591 | char *val; |
1593 | 1592 | ||
1594 | if (mu_message_get_header (msg, &hdr) == 0) | 1593 | if (mu_message_aget_attachment_name (msg, &val) == 0) |
1595 | { | 1594 | { |
1596 | int argc; | 1595 | name = normalize_path (dir, val); |
1597 | char **argv; | 1596 | free (val); |
1598 | |||
1599 | if (mu_header_aget_value (hdr, MU_HEADER_CONTENT_DISPOSITION, &val) == 0) | ||
1600 | { | ||
1601 | if (mu_argcv_get (val, "=", NULL, &argc, &argv) == 0) | ||
1602 | { | ||
1603 | int i; | ||
1604 | |||
1605 | for (i = 0; i < argc; i++) | ||
1606 | { | ||
1607 | if (strcmp (argv[i], "filename") == 0 | ||
1608 | && ++i < argc | ||
1609 | && argv[i][0] == '=' | ||
1610 | && ++i < argc) | ||
1611 | { | ||
1612 | name = normalize_path (dir, argv[i]); | ||
1613 | break; | ||
1614 | } | ||
1615 | } | ||
1616 | mu_argcv_free (argc, argv); | ||
1617 | } | ||
1618 | free (val); | ||
1619 | } | ||
1620 | |||
1621 | if (!name | ||
1622 | && mu_header_aget_value (hdr, MU_HEADER_CONTENT_TYPE, &val) == 0) | ||
1623 | { | ||
1624 | if (mu_argcv_get (val, "=", NULL, &argc, &argv) == 0) | ||
1625 | { | ||
1626 | int i; | ||
1627 | |||
1628 | for (i = 0; i < argc; i++) | ||
1629 | { | ||
1630 | if ((strcmp (argv[i], "filename") == 0 | ||
1631 | || strcmp (argv[i], "name") == 0) | ||
1632 | && ++i < argc | ||
1633 | && argv[i][0] == '=' | ||
1634 | && ++i < argc) | ||
1635 | { | ||
1636 | name = normalize_path (dir, argv[i]); | ||
1637 | break; | ||
1638 | } | ||
1639 | } | ||
1640 | mu_argcv_free (argc, argv); | ||
1641 | } | ||
1642 | free (val); | ||
1643 | } | ||
1644 | } | 1597 | } |
1645 | } | 1598 | } |
1646 | 1599 | ... | ... |
-
Please register or sign in to post a comment