X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/disorder/blobdiff_plain/39d4aa6b1c612305bf06760529e2a1532b9818a5..5891b0a8916232a54a4856e186b1d21a44b38a48:/lib/mime.c diff --git a/lib/mime.c b/lib/mime.c index c8ffe31..a97c4d1 100644 --- a/lib/mime.c +++ b/lib/mime.c @@ -27,11 +27,14 @@ #include #include +#include + #include "mem.h" #include "mime.h" #include "vector.h" #include "hex.h" #include "log.h" +#include "base64.h" /** @brief Match whitespace characters */ static int whitespace(int c) { @@ -70,7 +73,7 @@ static int tspecial(int c) { } } -/** @brief Mathc RFC2616 seprator characters */ +/** @brief Match RFC2616 seprator characters */ static int http_separator(int c) { switch(c) { case '(': @@ -206,6 +209,8 @@ static const char *parsetoken(const char *s, char **valuep, * @param parameternamep Where to store parameter name * @param parameternvaluep Wher to store parameter value * @return 0 on success, non-0 on error + * + * See RFC 2045 s5. */ int mime_content_type(const char *s, char **typep, @@ -251,7 +256,12 @@ int mime_content_type(const char *s, * @param s Start of message * @param callback Called for each header field * @param u Passed to callback - * @return Pointer to decoded body (might be in original string) + * @return Pointer to decoded body (might be in original string), or NULL on error + * + * This does an RFC 822-style parse and honors Content-Transfer-Encoding as + * described in RFC 2045 + * s6. @p callback is called for each header field encountered, in order, + * with ASCII characters in the header name forced to lower case. */ const char *mime_parse(const char *s, int (*callback)(const char *name, const char *value, @@ -282,12 +292,13 @@ const char *mime_parse(const char *s, } if(*s) s += 2; if(cte) { - if(!strcmp(cte, "base64")) return mime_base64(s); + if(!strcmp(cte, "base64")) return mime_base64(s, 0); if(!strcmp(cte, "quoted-printable")) return mime_qp(s); } return s; } +/** @brief Match the boundary string */ static int isboundary(const char *ptr, const char *boundary, size_t bl) { return (ptr[0] == '-' && ptr[1] == '-' @@ -295,24 +306,29 @@ static int isboundary(const char *ptr, const char *boundary, size_t bl) { && (iscrlf(ptr + bl + 2) || (ptr[bl + 2] == '-' && ptr[bl + 3] == '-' - && iscrlf(ptr + bl + 4)))); + && (iscrlf(ptr + bl + 4) || *(ptr + bl + 4) == 0)))); } +/** @brief Match the final boundary string */ static int isfinal(const char *ptr, const char *boundary, size_t bl) { return (ptr[0] == '-' && ptr[1] == '-' && !strncmp(ptr + 2, boundary, bl) && ptr[bl + 2] == '-' && ptr[bl + 3] == '-' - && iscrlf(ptr + bl + 4)); + && (iscrlf(ptr + bl + 4) || *(ptr + bl + 4) == 0)); } /** @brief Parse a multipart MIME body * @param s Start of message - * @param callback CAllback for each part + * @param callback Callback for each part * @param boundary Boundary string * @param u Passed to callback * @return 0 on success, non-0 on error + * + * See RFC 2046 + * s5.1. @p callback is called for each part (not yet decoded in any way) + * in succession; you should probably call mime_parse() for each part. */ int mime_multipart(const char *s, int (*callback)(const char *s, void *u), @@ -322,12 +338,16 @@ int mime_multipart(const char *s, const char *start, *e; int ret; - if(!isboundary(s, boundary, bl)) return -1; + /* We must start with a boundary string */ + if(!isboundary(s, boundary, bl)) + return -1; + /* Keep going until we hit a final boundary */ while(!isfinal(s, boundary, bl)) { s = strstr(s, "\r\n") + 2; start = s; while(!isboundary(s, boundary, bl)) { - if(!(e = strstr(s, "\r\n"))) return -1; + if(!(e = strstr(s, "\r\n"))) + return -1; s = e + 2; } if((ret = callback(xstrndup(start, @@ -344,6 +364,9 @@ int mime_multipart(const char *s, * @param parameternamep Where to store parameter name * @param parameternvaluep Wher to store parameter value * @return 0 on success, non-0 on error + * + * See RFC 2388 s3 + * and RFC 2183. */ int mime_rfc2388_content_disposition(const char *s, char **dispositionp, @@ -382,6 +405,9 @@ int mime_rfc2388_content_disposition(const char *s, /** @brief Convert MIME quoted-printable * @param s Quoted-printable data * @return Decoded data + * + * See RFC 2045 + * s6.7. */ char *mime_qp(const char *s) { struct dynstr d; @@ -425,41 +451,6 @@ char *mime_qp(const char *s) { return d.vec; } -/** @brief Convert MIME base64 - * @param s base64 data - * @return Decoded data - */ -char *mime_base64(const char *s) { - struct dynstr d; - const char *t; - int b[4], n, c; - static const char table[] = - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - - dynstr_init(&d); - n = 0; - while((c = (unsigned char)*s++)) { - if((t = strchr(table, c))) { - b[n++] = t - table; - if(n == 4) { - dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); - dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); - dynstr_append(&d, (b[2] << 6) + b[3]); - n = 0; - } - } else if(c == '=') { - if(n >= 2) { - dynstr_append(&d, (b[0] << 2) + (b[1] >> 4)); - if(n == 3) - dynstr_append(&d, (b[1] << 4) + (b[2] >> 2)); - } - break; - } - } - dynstr_terminate(&d); - return d.vec; -} - /** @brief Parse a RFC2109 Cookie: header * @param s Header field value * @param cd Where to store result