diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-04-06 21:55:15 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2010-04-06 22:28:34 +0300 |
commit | b2c1b1ff400a604fa41c168351630bac3238177e (patch) | |
tree | df6ac2049b2fccc2f7526009c2edd3c7298fecf6 | |
parent | d5ae81e503facb6b39cebd5dbf6697dfa429ef07 (diff) | |
download | mailutils-b2c1b1ff400a604fa41c168351630bac3238177e.tar.gz mailutils-b2c1b1ff400a604fa41c168351630bac3238177e.tar.bz2 |
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.
-rw-r--r-- | examples/mimetest.c | 3 | ||||
-rw-r--r-- | include/mailutils/message.h | 31 | ||||
-rw-r--r-- | libmu_cpp/message.cc | 3 | ||||
-rw-r--r-- | mailbox/Makefile.am | 1 | ||||
-rw-r--r-- | mailbox/attachment.c | 294 | ||||
-rw-r--r-- | mailbox/mimehdr.c | 614 | ||||
-rw-r--r-- | mailbox/mutil.c | 4 | ||||
-rw-r--r-- | mailbox/testsuite/Urls | 2 | ||||
-rw-r--r-- | mh/mhn.c | 4 | ||||
-rw-r--r-- | python/libmu_py/message.c | 3 |
10 files changed, 658 insertions, 301 deletions
diff --git a/examples/mimetest.c b/examples/mimetest.c index 90a00a02e..c42dc4449 100644 --- a/examples/mimetest.c +++ b/examples/mimetest.c @@ -242,7 +242,8 @@ message_display_parts (mu_message_t msg, int indent) { /* Save the attachements. */ char *fname = NULL; - mu_message_aget_attachment_name (part, &fname); + /* FIXME: CS/Lang info is ignored */ + mu_message_aget_attachment_name (part, &fname, NULL); if (fname == NULL) fname = mu_tempname (NULL); diff --git a/include/mailutils/message.h b/include/mailutils/message.h index 724766ae0..3ff25882f 100644 --- a/include/mailutils/message.h +++ b/include/mailutils/message.h @@ -119,9 +119,36 @@ extern int mu_message_encapsulate (mu_message_t msg, mu_message_t *newmsg, extern int mu_message_unencapsulate (mu_message_t msg, mu_message_t *newmsg, void **data); +/* Bit values for *pflags in functions below */ +#define MU_MIMEHDR_MULTILINE 0x01 /* Parameter was multiline */ +#define MU_MIMEHDR_CSINFO 0x02 /* Parameter contains charset/language + info */ + +extern int mu_mimehdr_get_disp (const char *str, const char *param, + char *buf, size_t bufsz, size_t *retsz); +extern int mu_mimehdr_aget_disp (const char *str, const char *param, + char **pvalue); +extern int mu_mimehdr_get_param (const char *str, const char *param, + char *buf, size_t bufsz, size_t *retsz, + int *pflags); +extern int mu_mimehdr_aget_param (const char *str, const char *param, + char **pval, int *pflags); +extern int mu_mimehdr_decode_param (const char *value, int csinfo, + const char *charset, + char **pval, char **plang); +extern int mu_mimehdr_aget_decoded_param (const char *str, const char *param, + const char *charset, + char **pval, char **plang); + extern int mu_message_get_attachment_name (mu_message_t, char *name, - size_t bufsz, size_t* sz); -extern int mu_message_aget_attachment_name (mu_message_t, char **name); + size_t bufsz, size_t* sz, + int *pflags); +extern int mu_message_aget_attachment_name (mu_message_t, char **name, + int *pflags); +extern int mu_message_aget_decoded_attachment_name (mu_message_t msg, + const char *charset, + char **name, + char **plang); extern int mu_message_save_to_mailbox (mu_message_t msg, mu_debug_t debug, const char *toname, diff --git a/libmu_cpp/message.cc b/libmu_cpp/message.cc index 55fdd380e..21f3ec6d1 100644 --- a/libmu_cpp/message.cc +++ b/libmu_cpp/message.cc @@ -224,7 +224,8 @@ Message :: get_attachment_name () char *c_name; std::string name; - int status = mu_message_aget_attachment_name (msg, &c_name); + /* FIXME: CS/Lang info is ignored */ + int status = mu_message_aget_attachment_name (msg, &c_name, NULL); if (status) throw Exception ("Message::get_attachment_name", status); if (c_name) { diff --git a/mailbox/Makefile.am b/mailbox/Makefile.am index 5146528ae..96c203a69 100644 --- a/mailbox/Makefile.am +++ b/mailbox/Makefile.am @@ -101,6 +101,7 @@ libmailutils_la_SOURCES = \ memory_stream.c\ message_stream.c\ mime.c\ + mimehdr.c\ mkfilename.c\ monitor.c\ msrv.c\ diff --git a/mailbox/attachment.c b/mailbox/attachment.c index 39165e6f7..9ef1455eb 100644 --- a/mailbox/attachment.c +++ b/mailbox/attachment.c @@ -185,297 +185,6 @@ _attachment_free (struct _msg_info *info, int free_message) free (info); } -/* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ -#define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) - -/* _header_get_param - an auxiliary function to extract values from - Content-Type, Content-Disposition and similar headers. - - Arguments: - - FIELD_BODY Header value, complying to RFCs 2045, 2183, 2231.3; - DISP Disposition. Unless it is NULL, the disposition part - of FIELD_BODY is compared with it. If they differ, - the function returns MU_ERR_NOENT. - PARAM Name of the parameter to extract from FIELD_BODY; - BUF Where to extract the value to; - BUFSZ Size of BUF; - PRET Pointer to the memory location for the return buffer (see - below). - PLEN Pointer to the return size. - - The function parses FIELD_BODY and extracts the value of the parameter - PARAM. - - If BUF is not NULL and BUFSZ is not 0, the extracted value is stored into - BUF. At most BUFSZ-1 bytes are copied. - - Otherwise, if PRET is not NULL, the function allocates enough memory to - hold the extracted value, copies there the result, and stores the - pointer to the allocated memory into the location pointed to by PRET. - - If PLEN is not NULL, the size of the extracted value (without terminating - NUL character) is stored there. - - If BUF==NULL *and* PRET==NULL, no memory is allocated, but PLEN is - honored anyway, i.e. unless it is NULL it receives size of the result. - This can be used to estimate the needed buffer size. - - Return values: - 0 on success. - MU_ERR_NOENT, requested parameter not found, or disposition does - not match DISP. - MU_ERR_PARSE, if FIELD_BODY does not comply to any of the abovemntioned - RFCs. - ENOMEM , if unable to allocate memory. -*/ - -int -_header_get_param (char *field_body, - const char *disp, - const char *param, - char *buf, size_t bufsz, - char **pret, size_t *plen) -{ - int res = MU_ERR_NOENT; /* Return value, pessimistic default */ - size_t param_len = strlen (param); - char *p; - char *mem = NULL; /* Allocated memory storage */ - size_t retlen = 0; /* Total number of bytes copied */ - unsigned long cind = 0; /* Expected continued parameter index. - See RFC 2231, Section 3, - "Parameter Value Continuations" */ - - if (field_body == NULL) - return EINVAL; - - if (bufsz == 0) /* Make sure buf value is meaningful */ - buf = NULL; - - p = strchr (field_body, ';'); - if (!p) - return MU_ERR_NOENT; - if (disp && mu_c_strncasecmp (field_body, disp, p - field_body)) - return MU_ERR_NOENT; - - while (p && *p) - { - char *v, *e; - size_t len, escaped_chars = 0; - - if (*p != ';') - { - res = MU_ERR_PARSE; - break; - } - - /* walk upto start of param */ - p = mu_str_skip_class (p + 1, MU_CTYPE_SPACE); - if ((v = strchr (p, '=')) == NULL) - break; - v++; - /* Find end of the parameter */ - if (*v == '"') - { - /* Quoted string */ - for (e = ++v; *e != '"'; e++) - { - if (*e == 0) /* Malformed header */ - { - res = MU_ERR_PARSE; - break; - } - if (*e == '\\') - { - if (*++e == 0) - { - res = MU_ERR_PARSE; - break; - } - escaped_chars++; - } - } - if (res == MU_ERR_PARSE) - break; - len = e - v; - e++; - } - else - { - for (e = v + 1; !(_ISSPECIAL (*e) || mu_isspace (*e)); e++) - ; - len = e - v; - } - - /* Is it our parameter? */ - if (mu_c_strncasecmp (p, param, param_len)) - { /* nope, jump to next */ - p = strchr (e, ';'); - continue; - } - - res = 0; /* Indicate success */ - - if (p[param_len] == '*') - { - /* Parameter value continuation (RFC 2231, Section 3). - See if the index is OK */ - char *end; - unsigned long n = strtoul (p + param_len + 1, &end, 10); - if (*end != '=' || n != cind) - { - res = MU_ERR_PARSE; - break; - } - /* Everything OK, increase the estimation */ - cind++; - } - - /* Prepare P for the next iteration */ - p = e; - - /* Escape characters that appear in quoted-pairs are - semantically "invisible" (RFC 2822, Section 3.2.2, - "Quoted characters") */ - len -= escaped_chars; - - /* Adjust len if nearing end of the buffer */ - if (bufsz && len >= bufsz) - len = bufsz - 1; - - if (pret) - { - /* The caller wants us to allocate the memory */ - if (!buf && !mem) - { - mem = malloc (len + 1); - if (!mem) - { - res = ENOMEM; - break; - } - buf = mem; - } - else if (mem) - { - /* If we got here, it means we are iterating over - a parameter value continuation, and cind=0 has - already been passed. Reallocate the memory to - accomodate next chunk of data. */ - char *newmem = realloc (mem, retlen + len + 1); - if (!newmem) - { - res = ENOMEM; - break; - } - mem = newmem; - } - } - - if (buf) - { - /* Actually copy the data. Buf is not NULL either because - the user passed it as an argument, or because we allocated - memory for it. */ - if (escaped_chars) - { - int i; - for (i = 0; i < len; i++) - { - if (*v == '\\') - ++v; - buf[retlen + i] = *v++; - } - } - else - memcpy (buf + retlen, v, len); - } - /* Adjust total result size ... */ - retlen += len; - /* ... and remaining buffer size, if necessary */ - if (bufsz) - { - bufsz -= len; - if (bufsz == 0) - break; - } - } - - if (res == 0) - { - /* Everything OK, prepare the returned data. */ - if (buf) - buf[retlen] = 0; - if (plen) - *plen = retlen; - if (pret) - *pret = mem; - } - else if (mem) - free (mem); - return res; -} - -/* Get the attachment name from MSG. See _header_get_param, for a - description of the rest of arguments. */ -static int -_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, - char **pbuf, size_t *sz) -{ - int ret = EINVAL; - mu_header_t hdr; - char *value = NULL; - - if (!msg) - return ret; - - if ((ret = mu_message_get_header (msg, &hdr)) != 0) - return ret; - - ret = mu_header_aget_value (hdr, "Content-Disposition", &value); - - /* If the header wasn't there, we'll fall back to Content-Type, but - other errors are fatal. */ - if (ret != 0 && ret != MU_ERR_NOENT) - return ret; - - if (ret == 0 && value != NULL) - { - ret = _header_get_param (value, "attachment", - "filename", buf, bufsz, pbuf, sz); - free (value); - value = NULL; - if (ret == 0 || ret != MU_ERR_NOENT) - return ret; - } - - /* If we didn't get the name, we fall back on the Content-Type name - parameter. */ - - free (value); - ret = mu_header_aget_value (hdr, "Content-Type", &value); - if (ret == 0) - ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz); - free (value); - - return ret; -} - -int -mu_message_aget_attachment_name (mu_message_t msg, char **name) -{ - if (name == NULL) - return MU_ERR_OUT_PTR_NULL; - return _get_attachment_name (msg, NULL, 0, name, NULL); -} - -int -mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, - size_t *sz) -{ - return _get_attachment_name (msg, buf, bufsz, NULL, sz); -} - int mu_message_save_attachment (mu_message_t msg, const char *filename, void **data) @@ -499,7 +208,8 @@ mu_message_save_attachment (mu_message_t msg, const char *filename, { if (filename == NULL) { - ret = mu_message_aget_attachment_name (msg, &partname); + /* FIXME: Charset info is ignored */ + ret = mu_message_aget_attachment_name (msg, &partname, NULL); if (partname) fname = partname; } diff --git a/mailbox/mimehdr.c b/mailbox/mimehdr.c new file mode 100644 index 000000000..848842b26 --- /dev/null +++ b/mailbox/mimehdr.c @@ -0,0 +1,614 @@ +/* GNU Mailutils -- a suite of utilities for electronic mail + Copyright (C) 1999, 2000, 2001, 2004, 2005, 2007, 2009, 2010 Free + Software Foundation, Inc. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 3 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General + Public License along with this library. If not, + see <http://www.gnu.org/licenses/>. */ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> + +#include <mailutils/cctype.h> +#include <mailutils/cstr.h> +#include <mailutils/errno.h> +#include <mailutils/message.h> +#include <mailutils/header.h> +#include <mailutils/stream.h> +#include <mailutils/url.h> /* FIXME: for mu_url_decode, which should + be renamed! */ +#include <mailutils/mime.h> +#include <mailutils/filter.h> +#include <mailutils/mutil.h> + +/* See RFC 2045, 5.1. Syntax of the Content-Type Header Field */ +#define _ISSPECIAL(c) !!strchr ("()<>@,;:\\\"/[]?=", c) + +/* _header_get_param - an auxiliary function to extract values from + Content-Type, Content-Disposition and similar headers. + + Arguments: + + FIELD_BODY Header value, complying to RFCs 2045, 2183, 2231.3; + DISP Disposition. Unless it is NULL, the disposition part + of FIELD_BODY is compared with it. If they differ, + the function returns MU_ERR_NOENT. + PARAM Name of the parameter to extract from FIELD_BODY; + BUF Where to extract the value to; + BUFSZ Size of BUF; + PRET Pointer to the memory location for the return buffer (see + below). + PLEN Pointer to the return size. + PFLAGS On return, flags describing the parameter are stored there. + The MU_MIMEHDR_MULTILINE bit is set if the parameter value + was multiline (RFC 2231.3). The MU_MIMEHDR_CSINFO bit is set + if the parameter value includes charset/language + information (RFC 2231.4). + + The function parses FIELD_BODY and extracts the value of the parameter + PARAM. + + If BUF is not NULL and BUFSZ is not 0, the extracted value is stored into + BUF. At most BUFSZ-1 bytes are copied. + + Otherwise, if PRET is not NULL, the function allocates enough memory to + hold the extracted value, copies there the result, and stores the + pointer to the allocated memory into the location pointed to by PRET. + + If PLEN is not NULL, the size of the extracted value (without terminating + NUL character) is stored there. + + If BUF==NULL *and* PRET==NULL, no memory is allocated, but PLEN is + honored anyway, i.e. unless it is NULL it receives size of the result. + This can be used to estimate the needed buffer size. + + Return values: + 0 on success. + MU_ERR_NOENT, requested parameter not found, or disposition does + not match DISP. + MU_ERR_PARSE, if FIELD_BODY does not comply to any of the abovemntioned + RFCs. + ENOMEM , if unable to allocate memory. +*/ + +int +_header_get_param (const char *field_body, + const char *disp, + const char *param, + char *buf, size_t bufsz, + char **pret, size_t *plen, + int *pflags) +{ + int res = MU_ERR_NOENT; /* Return value, pessimistic default */ + size_t param_len = strlen (param); + char *p; + char *mem = NULL; /* Allocated memory storage */ + size_t retlen = 0; /* Total number of bytes copied */ + unsigned long cind = 0; /* Expected continued parameter index. + See RFC 2231, Section 3, + "Parameter Value Continuations" */ + int flags = 0; + + if (field_body == NULL) + return EINVAL; + + if (bufsz == 0) /* Make sure buf value is meaningful */ + buf = NULL; + + p = strchr (field_body, ';'); + if (!p) + return MU_ERR_NOENT; + if (disp && mu_c_strncasecmp (field_body, disp, p - field_body)) + return MU_ERR_NOENT; + + while (p && *p) + { + char *v, *e; + size_t len, escaped_chars = 0; + + if (*p != ';') + { + res = MU_ERR_PARSE; + break; + } + + /* walk upto start of param */ + p = mu_str_skip_class (p + 1, MU_CTYPE_SPACE); + if ((v = strchr (p, '=')) == NULL) + break; + v++; + /* Find end of the parameter */ + if (*v == '"') + { + /* Quoted string */ + for (e = ++v; *e != '"'; e++) + { + if (*e == 0) /* Malformed header */ + { + res = MU_ERR_PARSE; + break; + } + if (*e == '\\') + { + if (*++e == 0) + { + res = MU_ERR_PARSE; + break; + } + escaped_chars++; + } + } + if (res == MU_ERR_PARSE) + break; + len = e - v; + e++; + } + else + { + for (e = v + 1; !(_ISSPECIAL (*e) || mu_isspace (*e)); e++) + ; + len = e - v; + } + + /* Is it our parameter? */ + if (mu_c_strncasecmp (p, param, param_len)) + { /* nope, jump to next */ + p = strchr (e, ';'); + continue; + } + + res = 0; /* Indicate success */ + + if (p[param_len] == '*') + { + char *cp = p + param_len + 1; + + /* It is a parameter value continuation (RFC 2231, Section 3) + or parameter value character set and language information + (ibid., Section 4). */ + if (*cp == '=') + flags |= MU_MIMEHDR_CSINFO; + else if (mu_isdigit (*cp)) + { + /* See if the index is OK */ + + char *end; + unsigned long n = strtoul (cp, &end, 10); + + if (*end == '*') + { + flags |= MU_MIMEHDR_CSINFO; + end++; + } + if (*end != '=' || n != cind) + { + res = MU_ERR_PARSE; + break; + } + /* Everything OK, increase the estimation */ + flags |= MU_MIMEHDR_MULTILINE; + cind++; + } + } + + /* Prepare P for the next iteration */ + p = e; + + /* Escape characters that appear in quoted-pairs are + semantically "invisible" (RFC 2822, Section 3.2.2, + "Quoted characters") */ + len -= escaped_chars; + + /* Adjust len if nearing end of the buffer */ + if (bufsz && len >= bufsz) + len = bufsz - 1; + + if (pret) + { + /* The caller wants us to allocate the memory */ + if (!buf && !mem) + { + mem = malloc (len + 1); + if (!mem) + { + res = ENOMEM; + break; + } + buf = mem; + } + else if (mem) + { + /* If we got here, it means we are iterating over + a parameter value continuation, and cind=0 has + already been passed. Reallocate the memory to + accomodate next chunk of data. */ + char *newmem = realloc (mem, retlen + len + 1); + if (!newmem) + { + res = ENOMEM; + break; + } + mem = newmem; + } + } + + if (buf) + { + /* Actually copy the data. Buf is not NULL either because + the user passed it as an argument, or because we allocated + memory for it. */ + if (escaped_chars) + { + int i; + for (i = 0; i < len; i++) + { + if (*v == '\\') + ++v; + buf[retlen + i] = *v++; + } + } + else + memcpy (buf + retlen, v, len); + } + /* Adjust total result size ... */ + retlen += len; + /* ... and remaining buffer size, if necessary */ + if (bufsz) + { + bufsz -= len; + if (bufsz == 0) + break; + } + } + + if (res == 0) + { + /* Everything OK, prepare the returned data. */ + if (buf) + buf[retlen] = 0; + if (plen) + *plen = retlen; + if (pret) + *pret = mem; + if (pflags) + *pflags = flags; + } + else if (mem) + free (mem); + return res; +} + +/* STR is a value of a structured MIME header, e.g. Content-Type. + This function returns the `disposition part' of it. In other + words, it returns disposition, if STR is a Content-Disposition + value, and `type/subtype' part, if it is a Content-Type value. +*/ +int +mu_mimehdr_get_disp (const char *str, const char *param, + char *buf, size_t bufsz, size_t *retsz) +{ + char *p = strchr (str, ';'); + size_t size; + + if (!p) + return MU_ERR_NOENT; + size = p - str; + if (buf) + size = mu_cpystr (buf, str, size); + if (retsz) + *retsz = size; + return 0; +} + +/* Same as mu_mimehdr_get_disp, but allocates memory */ +int +mu_mimehdr_aget_disp (const char *str, const char *param, char **pvalue) +{ + char *p = strchr (str, ';'); + size_t size; + + if (!p) + return MU_ERR_NOENT; + size = p - str; + p = malloc (size + 1); + if (!p) + return ENOMEM; + memcpy (p, str, size); + p[size] = 0; + return 0; +} + +/* Get the value of the parameter PARAM from STR, which must be + a value of a structured MIME header. + At most BUFSZ-1 of data are stored in BUF. A terminating NUL + character is appended to it. + + Unless NULL, RETSZ is filled with the actual length of the + returned data (not including the NUL terminator). + + Unless PFLAGS is null it will contain, on return, the flags describing + the parameter. The MU_MIMEHDR_MULTILINE bit is set if the parameter value + was multiline (RFC 2231.3). The MU_MIMEHDR_CSINFO bit is set if the + parameter value includes charset/language information (RFC 2231.4). + + BUF may be NULL, in which case the function will only fill + RETSZ and PFLAGS, as described above. */ +int +mu_mimehdr_get_param (const char *str, const char *param, + char *buf, size_t bufsz, size_t *retsz, + int *pflags) +{ + return _header_get_param (str, NULL, param, buf, bufsz, NULL, retsz, + pflags); +} + +/* Same as mu_mimehdr_get_param, but allocates memory. */ +int +mu_mimehdr_aget_param (const char *str, const char *param, + char **pval, int *pflags) +{ + return _header_get_param (str, NULL, param, NULL, 0, pval, NULL, pflags); +} + +/* Decode a parameter value. Arguments: + + Input: + VALUE Parameter value. + FLAGS Flags obtained from a previous call to one of the functions + above. + CHARSET Output charset. + + Output: + PVAL A pointer to the decoded value is stored there. + The memory is allocated using malloc. + PLANG If language information was present in VALUE, its + malloc'ed copy is stored in the memory location pointed + to by this variable. If there was no language information, + *PLANG is set to NULL. + + Both PVAL and PLANG may be NULL if that particular piece of information + is not needed. */ +int +mu_mimehdr_decode_param (const char *value, int flags, + const char *charset, char **pval, char **plang) +{ + char *decoded; + int rc; + char *lang = NULL; + char *data; + + if (flags == 0) + { + rc = mu_rfc2047_decode (charset, value, &decoded); + if (rc) + return rc; + } + else + { + decoded = mu_url_decode (value); + if (!decoded) + return ENOMEM; + + if ((flags & MU_MIMEHDR_CSINFO) + && (lang = strchr (decoded, '\'')) + && (data = strchr (lang + 1, '\''))) + { + char *source_cs = decoded; + + *lang++ = 0; + *data++ = 0; + + lang = lang[0] ? strdup (lang) : NULL; + + if (source_cs[0] && charset && mu_c_strcasecmp (source_cs, charset)) + { + char *outval = NULL; + mu_stream_t instr = NULL; + mu_stream_t outstr = NULL; + mu_stream_t cvt = NULL; + char iobuf[512]; + + do + { + size_t total = 0, pos; + size_t nbytes; + + rc = mu_memory_stream_create (&instr, 0, 0); + if (rc) + break; + rc = mu_stream_write (instr, data, strlen (data), 0, NULL); + if (rc) + break; + + rc = mu_memory_stream_create (&outstr, 0, 0); + if (rc) + break; + + rc = mu_filter_iconv_create (&cvt, instr, source_cs, charset, + MU_STREAM_NO_CLOSE, + mu_default_fallback_mode); + if (rc) + break; + + rc = mu_stream_open (cvt); + if (rc) + break; + + while (mu_stream_sequential_read (cvt, iobuf, sizeof (iobuf), + &nbytes) == 0 + && nbytes) + { + rc = mu_stream_sequential_write (outstr, iobuf, nbytes); + if (rc) + break; + total += nbytes; + } + + if (rc) + break; + + outval = malloc (total + 1); + if (!outval) + { + rc = ENOMEM; + break; + } + + mu_stream_seek (outstr, 0, SEEK_SET); + pos = 0; + while (mu_stream_sequential_read (outstr, outval + pos, + total - pos, &nbytes) == 0 + && nbytes) + pos += nbytes; + outval[pos] = 0; + } + while (0); + + mu_stream_close (cvt); + mu_stream_destroy (&cvt, mu_stream_get_owner (cvt)); + mu_stream_close (instr); + mu_stream_destroy (&instr, mu_stream_get_owner (instr)); + mu_stream_close (outstr); + mu_stream_destroy (&outstr, mu_stream_get_owner (outstr)); + + free (decoded); + + if (rc) + { + /* Cleanup after an error. */ + free (lang); + free (outval); + return rc; + } + decoded = outval; + } + else + memmove (decoded, data, strlen (data) + 1); + } + } + + if (pval) + *pval = decoded; + else + free (decoded); + + if (plang) + *plang = lang; + return 0; +} + +/* Similar to mu_mimehdr_aget_param, but the returned value is decoded + according to the CHARSET. Unless PLANG is NULL, it receives malloc'ed + language name from STR. If there was no language name, *PLANG is set + to NULL. +*/ +int +mu_mimehdr_aget_decoded_param (const char *str, const char *param, + const char *charset, + char **pval, char **plang) +{ + char *value; + int rc; + int flags; + + rc = mu_mimehdr_aget_param (str, param, &value, &flags); + if (rc == 0) + { + rc = mu_mimehdr_decode_param (value, flags, charset, pval, plang); + free (value); + } + return rc; +} + +/* Get the attachment name from MSG. See _header_get_param, for a + description of the rest of arguments. */ +static int +_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, + char **pbuf, size_t *sz, int *pflags) +{ + int ret = EINVAL; + mu_header_t hdr; + char *value = NULL; + + if (!msg) + return ret; + + if ((ret = mu_message_get_header (msg, &hdr)) != 0) + return ret; + + ret = mu_header_aget_value_unfold (hdr, "Content-Disposition", &value); + + /* If the header wasn't there, we'll fall back to Content-Type, but + other errors are fatal. */ + if (ret != 0 && ret != MU_ERR_NOENT) + return ret; + + if (ret == 0 && value != NULL) + { + ret = _header_get_param (value, "attachment", + "filename", buf, bufsz, pbuf, sz, pflags); + free (value); + value = NULL; + if (ret == 0 || ret != MU_ERR_NOENT) + return ret; + } + + /* If we didn't get the name, we fall back on the Content-Type name + parameter. */ + + free (value); + ret = mu_header_aget_value_unfold (hdr, "Content-Type", &value); + if (ret == 0) + ret = _header_get_param (value, NULL, "name", buf, bufsz, pbuf, sz, + pflags); + free (value); + + return ret; +} + +int +mu_message_aget_attachment_name (mu_message_t msg, char **name, int *pflags) +{ + if (name == NULL) + return MU_ERR_OUT_PTR_NULL; + return _get_attachment_name (msg, NULL, 0, name, NULL, pflags); +} + +int +mu_message_aget_decoded_attachment_name (mu_message_t msg, + const char *charset, + char **pval, + char **plang) +{ + char *value; + int flags; + int rc = mu_message_aget_attachment_name (msg, &value, &flags); + if (rc == 0) + { + rc = mu_mimehdr_decode_param (value, flags, charset, pval, plang); + free (value); + } + return rc; +} + +int +mu_message_get_attachment_name (mu_message_t msg, char *buf, size_t bufsz, + size_t *sz, int *pflags) +{ + return _get_attachment_name (msg, buf, bufsz, NULL, sz, pflags); +} + diff --git a/mailbox/mutil.c b/mailbox/mutil.c index ce53794e5..19dbcc1c8 100644 --- a/mailbox/mutil.c +++ b/mailbox/mutil.c @@ -75,10 +75,10 @@ mu_hex2ul (char hex) return hex - '0'; if (hex >= 'a' && hex <= 'z') - return hex - 'a'; + return hex - 'a' + 10; if (hex >= 'A' && hex <= 'Z') - return hex - 'A'; + return hex - 'A' + 10; return -1; } diff --git a/mailbox/testsuite/Urls b/mailbox/testsuite/Urls index ee7479fc1..a6e565eac 100644 --- a/mailbox/testsuite/Urls +++ b/mailbox/testsuite/Urls @@ -58,7 +58,7 @@ scheme://%75%73%65%72:%70%61%73%73@%68%6f%73%74 => SUCCESS user <user> passwd <pass> auth <> - host <hest> + host <host> port 0 path <> |