sl@0: /* pk7_mime.c */ sl@0: /* Written by Dr Stephen N Henson (shenson@bigfoot.com) for the OpenSSL sl@0: * project. sl@0: */ sl@0: /* ==================================================================== sl@0: * Copyright (c) 1999-2005 The OpenSSL Project. All rights reserved. sl@0: * sl@0: * Redistribution and use in source and binary forms, with or without sl@0: * modification, are permitted provided that the following conditions sl@0: * are met: sl@0: * sl@0: * 1. Redistributions of source code must retain the above copyright sl@0: * notice, this list of conditions and the following disclaimer. sl@0: * sl@0: * 2. Redistributions in binary form must reproduce the above copyright sl@0: * notice, this list of conditions and the following disclaimer in sl@0: * the documentation and/or other materials provided with the sl@0: * distribution. sl@0: * sl@0: * 3. All advertising materials mentioning features or use of this sl@0: * software must display the following acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" sl@0: * sl@0: * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to sl@0: * endorse or promote products derived from this software without sl@0: * prior written permission. For written permission, please contact sl@0: * licensing@OpenSSL.org. sl@0: * sl@0: * 5. Products derived from this software may not be called "OpenSSL" sl@0: * nor may "OpenSSL" appear in their names without prior written sl@0: * permission of the OpenSSL Project. sl@0: * sl@0: * 6. Redistributions of any form whatsoever must retain the following sl@0: * acknowledgment: sl@0: * "This product includes software developed by the OpenSSL Project sl@0: * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" sl@0: * sl@0: * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY sl@0: * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE sl@0: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR sl@0: * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR sl@0: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, sl@0: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT sl@0: * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; sl@0: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) sl@0: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, sl@0: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) sl@0: * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED sl@0: * OF THE POSSIBILITY OF SUCH DAMAGE. sl@0: * ==================================================================== sl@0: * sl@0: * This product includes cryptographic software written by Eric Young sl@0: * (eay@cryptsoft.com). This product includes software written by Tim sl@0: * Hudson (tjh@cryptsoft.com). sl@0: * sl@0: */ sl@0: /* sl@0: © Portions copyright (c) 2006 Nokia Corporation. All rights reserved. sl@0: */ sl@0: #include sl@0: #include sl@0: #include "cryptlib.h" sl@0: #include sl@0: #include sl@0: sl@0: /* MIME and related routines */ sl@0: sl@0: /* MIME format structures sl@0: * Note that all are translated to lower case apart from sl@0: * parameter values. Quotes are stripped off sl@0: */ sl@0: sl@0: typedef struct { sl@0: char *param_name; /* Param name e.g. "micalg" */ sl@0: char *param_value; /* Param value e.g. "sha1" */ sl@0: } MIME_PARAM; sl@0: sl@0: DECLARE_STACK_OF(MIME_PARAM) sl@0: IMPLEMENT_STACK_OF(MIME_PARAM) sl@0: sl@0: typedef struct { sl@0: char *name; /* Name of line e.g. "content-type" */ sl@0: char *value; /* Value of line e.g. "text/plain" */ sl@0: STACK_OF(MIME_PARAM) *params; /* Zero or more parameters */ sl@0: } MIME_HEADER; sl@0: sl@0: DECLARE_STACK_OF(MIME_HEADER) sl@0: IMPLEMENT_STACK_OF(MIME_HEADER) sl@0: sl@0: static int pkcs7_output_data(BIO *bio, BIO *data, PKCS7 *p7, int flags); sl@0: static int B64_write_PKCS7(BIO *bio, PKCS7 *p7); sl@0: static PKCS7 *B64_read_PKCS7(BIO *bio); sl@0: static char * strip_ends(char *name); sl@0: static char * strip_start(char *name); sl@0: static char * strip_end(char *name); sl@0: static MIME_HEADER *mime_hdr_new(char *name, char *value); sl@0: static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value); sl@0: static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio); sl@0: static int mime_hdr_cmp(const MIME_HEADER * const *a, sl@0: const MIME_HEADER * const *b); sl@0: static int mime_param_cmp(const MIME_PARAM * const *a, sl@0: const MIME_PARAM * const *b); sl@0: static void mime_param_free(MIME_PARAM *param); sl@0: static int mime_bound_check(char *line, int linelen, char *bound, int blen); sl@0: static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret); sl@0: static int strip_eol(char *linebuf, int *plen); sl@0: static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name); sl@0: static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name); sl@0: static void mime_hdr_free(MIME_HEADER *hdr); sl@0: sl@0: #define MAX_SMLEN 1024 sl@0: #define mime_debug(x) /* x */ sl@0: sl@0: /* Base 64 read and write of PKCS#7 structure */ sl@0: sl@0: static int B64_write_PKCS7(BIO *bio, PKCS7 *p7) sl@0: { sl@0: BIO *b64; sl@0: if(!(b64 = BIO_new(BIO_f_base64()))) { sl@0: PKCS7err(PKCS7_F_B64_WRITE_PKCS7,ERR_R_MALLOC_FAILURE); sl@0: return 0; sl@0: } sl@0: bio = BIO_push(b64, bio); sl@0: i2d_PKCS7_bio(bio, p7); sl@0: (void)BIO_flush(bio); sl@0: bio = BIO_pop(bio); sl@0: BIO_free(b64); sl@0: return 1; sl@0: } sl@0: sl@0: static PKCS7 *B64_read_PKCS7(BIO *bio) sl@0: { sl@0: BIO *b64; sl@0: PKCS7 *p7; sl@0: if(!(b64 = BIO_new(BIO_f_base64()))) { sl@0: PKCS7err(PKCS7_F_B64_READ_PKCS7,ERR_R_MALLOC_FAILURE); sl@0: return 0; sl@0: } sl@0: bio = BIO_push(b64, bio); sl@0: if(!(p7 = d2i_PKCS7_bio(bio, NULL))) sl@0: PKCS7err(PKCS7_F_B64_READ_PKCS7,PKCS7_R_DECODE_ERROR); sl@0: (void)BIO_flush(bio); sl@0: bio = BIO_pop(bio); sl@0: BIO_free(b64); sl@0: return p7; sl@0: } sl@0: sl@0: /* SMIME sender */ sl@0: sl@0: EXPORT_C int SMIME_write_PKCS7(BIO *bio, PKCS7 *p7, BIO *data, int flags) sl@0: { sl@0: char bound[33], c; sl@0: int i; sl@0: char *mime_prefix, *mime_eol, *msg_type=NULL; sl@0: if (flags & PKCS7_NOOLDMIMETYPE) sl@0: mime_prefix = "application/pkcs7-"; sl@0: else sl@0: mime_prefix = "application/x-pkcs7-"; sl@0: sl@0: if (flags & PKCS7_CRLFEOL) sl@0: mime_eol = "\r\n"; sl@0: else sl@0: mime_eol = "\n"; sl@0: if((flags & PKCS7_DETACHED) && data) { sl@0: /* We want multipart/signed */ sl@0: /* Generate a random boundary */ sl@0: RAND_pseudo_bytes((unsigned char *)bound, 32); sl@0: for(i = 0; i < 32; i++) { sl@0: c = bound[i] & 0xf; sl@0: if(c < 10) c += '0'; sl@0: else c += 'A' - 10; sl@0: bound[i] = c; sl@0: } sl@0: bound[32] = 0; sl@0: BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); sl@0: BIO_printf(bio, "Content-Type: multipart/signed;"); sl@0: BIO_printf(bio, " protocol=\"%ssignature\";", mime_prefix); sl@0: BIO_printf(bio, " micalg=sha1; boundary=\"----%s\"%s%s", sl@0: bound, mime_eol, mime_eol); sl@0: BIO_printf(bio, "This is an S/MIME signed message%s%s", sl@0: mime_eol, mime_eol); sl@0: /* Now write out the first part */ sl@0: BIO_printf(bio, "------%s%s", bound, mime_eol); sl@0: pkcs7_output_data(bio, data, p7, flags); sl@0: BIO_printf(bio, "%s------%s%s", mime_eol, bound, mime_eol); sl@0: sl@0: /* Headers for signature */ sl@0: sl@0: BIO_printf(bio, "Content-Type: %ssignature;", mime_prefix); sl@0: BIO_printf(bio, " name=\"smime.p7s\"%s", mime_eol); sl@0: BIO_printf(bio, "Content-Transfer-Encoding: base64%s", sl@0: mime_eol); sl@0: BIO_printf(bio, "Content-Disposition: attachment;"); sl@0: BIO_printf(bio, " filename=\"smime.p7s\"%s%s", sl@0: mime_eol, mime_eol); sl@0: B64_write_PKCS7(bio, p7); sl@0: BIO_printf(bio,"%s------%s--%s%s", mime_eol, bound, sl@0: mime_eol, mime_eol); sl@0: return 1; sl@0: } sl@0: sl@0: /* Determine smime-type header */ sl@0: sl@0: if (PKCS7_type_is_enveloped(p7)) sl@0: msg_type = "enveloped-data"; sl@0: else if (PKCS7_type_is_signed(p7)) sl@0: { sl@0: /* If we have any signers it is signed-data othewise sl@0: * certs-only. sl@0: */ sl@0: STACK_OF(PKCS7_SIGNER_INFO) *sinfos; sl@0: sinfos = PKCS7_get_signer_info(p7); sl@0: if (sk_PKCS7_SIGNER_INFO_num(sinfos) > 0) sl@0: msg_type = "signed-data"; sl@0: else sl@0: msg_type = "certs-only"; sl@0: } sl@0: /* MIME headers */ sl@0: BIO_printf(bio, "MIME-Version: 1.0%s", mime_eol); sl@0: BIO_printf(bio, "Content-Disposition: attachment;"); sl@0: BIO_printf(bio, " filename=\"smime.p7m\"%s", mime_eol); sl@0: BIO_printf(bio, "Content-Type: %smime;", mime_prefix); sl@0: if (msg_type) sl@0: BIO_printf(bio, " smime-type=%s;", msg_type); sl@0: BIO_printf(bio, " name=\"smime.p7m\"%s", mime_eol); sl@0: BIO_printf(bio, "Content-Transfer-Encoding: base64%s%s", sl@0: mime_eol, mime_eol); sl@0: B64_write_PKCS7(bio, p7); sl@0: BIO_printf(bio, "%s", mime_eol); sl@0: return 1; sl@0: } sl@0: sl@0: /* Handle output of PKCS#7 data */ sl@0: sl@0: sl@0: static int pkcs7_output_data(BIO *out, BIO *data, PKCS7 *p7, int flags) sl@0: { sl@0: BIO *tmpbio, *p7bio; sl@0: sl@0: if (!(flags & PKCS7_STREAM)) sl@0: { sl@0: SMIME_crlf_copy(data, out, flags); sl@0: return 1; sl@0: } sl@0: sl@0: /* Partial sign operation */ sl@0: sl@0: /* Initialize sign operation */ sl@0: p7bio = PKCS7_dataInit(p7, out); sl@0: sl@0: /* Copy data across, computing digests etc */ sl@0: SMIME_crlf_copy(data, p7bio, flags); sl@0: sl@0: /* Must be detached */ sl@0: PKCS7_set_detached(p7, 1); sl@0: sl@0: /* Finalize signatures */ sl@0: PKCS7_dataFinal(p7, p7bio); sl@0: sl@0: /* Now remove any digests prepended to the BIO */ sl@0: sl@0: while (p7bio != out) sl@0: { sl@0: tmpbio = BIO_pop(p7bio); sl@0: BIO_free(p7bio); sl@0: p7bio = tmpbio; sl@0: } sl@0: sl@0: return 1; sl@0: sl@0: } sl@0: sl@0: /* SMIME reader: handle multipart/signed and opaque signing. sl@0: * in multipart case the content is placed in a memory BIO sl@0: * pointed to by "bcont". In opaque this is set to NULL sl@0: */ sl@0: sl@0: EXPORT_C PKCS7 *SMIME_read_PKCS7(BIO *bio, BIO **bcont) sl@0: { sl@0: BIO *p7in; sl@0: STACK_OF(MIME_HEADER) *headers = NULL; sl@0: STACK_OF(BIO) *parts = NULL; sl@0: MIME_HEADER *hdr; sl@0: MIME_PARAM *prm; sl@0: PKCS7 *p7; sl@0: int ret; sl@0: sl@0: if(bcont) *bcont = NULL; sl@0: sl@0: if (!(headers = mime_parse_hdr(bio))) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_PARSE_ERROR); sl@0: return NULL; sl@0: } sl@0: sl@0: if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_CONTENT_TYPE); sl@0: return NULL; sl@0: } sl@0: sl@0: /* Handle multipart/signed */ sl@0: sl@0: if(!strcmp(hdr->value, "multipart/signed")) { sl@0: /* Split into two parts */ sl@0: prm = mime_param_find(hdr, "boundary"); sl@0: if(!prm || !prm->param_value) { sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BOUNDARY); sl@0: return NULL; sl@0: } sl@0: ret = multi_split(bio, prm->param_value, &parts); sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: if(!ret || (sk_BIO_num(parts) != 2) ) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_MULTIPART_BODY_FAILURE); sl@0: sk_BIO_pop_free(parts, BIO_vfree); sl@0: return NULL; sl@0: } sl@0: sl@0: /* Parse the signature piece */ sl@0: p7in = sk_BIO_value(parts, 1); sl@0: sl@0: if (!(headers = mime_parse_hdr(p7in))) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_MIME_SIG_PARSE_ERROR); sl@0: sk_BIO_pop_free(parts, BIO_vfree); sl@0: return NULL; sl@0: } sl@0: sl@0: /* Get content type */ sl@0: sl@0: if(!(hdr = mime_hdr_find(headers, "content-type")) || sl@0: !hdr->value) { sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_NO_SIG_CONTENT_TYPE); sl@0: return NULL; sl@0: } sl@0: sl@0: if(strcmp(hdr->value, "application/x-pkcs7-signature") && sl@0: strcmp(hdr->value, "application/pkcs7-signature")) { sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_SIG_INVALID_MIME_TYPE); sl@0: ERR_add_error_data(2, "type: ", hdr->value); sl@0: sk_BIO_pop_free(parts, BIO_vfree); sl@0: return NULL; sl@0: } sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: /* Read in PKCS#7 */ sl@0: if(!(p7 = B64_read_PKCS7(p7in))) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_PKCS7_SIG_PARSE_ERROR); sl@0: sk_BIO_pop_free(parts, BIO_vfree); sl@0: return NULL; sl@0: } sl@0: sl@0: if(bcont) { sl@0: *bcont = sk_BIO_value(parts, 0); sl@0: BIO_free(p7in); sl@0: sk_BIO_free(parts); sl@0: } else sk_BIO_pop_free(parts, BIO_vfree); sl@0: return p7; sl@0: } sl@0: sl@0: /* OK, if not multipart/signed try opaque signature */ sl@0: sl@0: if (strcmp (hdr->value, "application/x-pkcs7-mime") && sl@0: strcmp (hdr->value, "application/pkcs7-mime")) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7,PKCS7_R_INVALID_MIME_TYPE); sl@0: ERR_add_error_data(2, "type: ", hdr->value); sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: return NULL; sl@0: } sl@0: sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: sl@0: if(!(p7 = B64_read_PKCS7(bio))) { sl@0: PKCS7err(PKCS7_F_SMIME_READ_PKCS7, PKCS7_R_PKCS7_PARSE_ERROR); sl@0: return NULL; sl@0: } sl@0: return p7; sl@0: sl@0: } sl@0: sl@0: /* Copy text from one BIO to another making the output CRLF at EOL */ sl@0: EXPORT_C int SMIME_crlf_copy(BIO *in, BIO *out, int flags) sl@0: { sl@0: char eol; sl@0: int len; sl@0: char linebuf[MAX_SMLEN]; sl@0: if(flags & PKCS7_BINARY) { sl@0: while((len = BIO_read(in, linebuf, MAX_SMLEN)) > 0) sl@0: BIO_write(out, linebuf, len); sl@0: return 1; sl@0: } sl@0: if(flags & PKCS7_TEXT) sl@0: BIO_printf(out, "Content-Type: text/plain\r\n\r\n"); sl@0: while ((len = BIO_gets(in, linebuf, MAX_SMLEN)) > 0) { sl@0: eol = strip_eol(linebuf, &len); sl@0: if (len) sl@0: BIO_write(out, linebuf, len); sl@0: if(eol) BIO_write(out, "\r\n", 2); sl@0: } sl@0: return 1; sl@0: } sl@0: sl@0: /* Strip off headers if they are text/plain */ sl@0: EXPORT_C int SMIME_text(BIO *in, BIO *out) sl@0: { sl@0: #ifndef SYMBIAN sl@0: char iobuf[4096]; sl@0: #else sl@0: char iobuf[512]; sl@0: #endif sl@0: int len; sl@0: STACK_OF(MIME_HEADER) *headers; sl@0: MIME_HEADER *hdr; sl@0: sl@0: if (!(headers = mime_parse_hdr(in))) { sl@0: PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_PARSE_ERROR); sl@0: return 0; sl@0: } sl@0: if(!(hdr = mime_hdr_find(headers, "content-type")) || !hdr->value) { sl@0: PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_MIME_NO_CONTENT_TYPE); sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: return 0; sl@0: } sl@0: if (strcmp (hdr->value, "text/plain")) { sl@0: PKCS7err(PKCS7_F_SMIME_TEXT,PKCS7_R_INVALID_MIME_TYPE); sl@0: ERR_add_error_data(2, "type: ", hdr->value); sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: return 0; sl@0: } sl@0: sk_MIME_HEADER_pop_free(headers, mime_hdr_free); sl@0: while ((len = BIO_read(in, iobuf, sizeof(iobuf))) > 0) sl@0: BIO_write(out, iobuf, len); sl@0: return 1; sl@0: } sl@0: sl@0: /* Split a multipart/XXX message body into component parts: result is sl@0: * canonical parts in a STACK of bios sl@0: */ sl@0: sl@0: static int multi_split(BIO *bio, char *bound, STACK_OF(BIO) **ret) sl@0: { sl@0: char linebuf[MAX_SMLEN]; sl@0: int len, blen; sl@0: int eol = 0, next_eol = 0; sl@0: BIO *bpart = NULL; sl@0: STACK_OF(BIO) *parts; sl@0: char state, part, first; sl@0: sl@0: blen = strlen(bound); sl@0: part = 0; sl@0: state = 0; sl@0: first = 1; sl@0: parts = sk_BIO_new_null(); sl@0: *ret = parts; sl@0: while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { sl@0: state = mime_bound_check(linebuf, len, bound, blen); sl@0: if(state == 1) { sl@0: first = 1; sl@0: part++; sl@0: } else if(state == 2) { sl@0: sk_BIO_push(parts, bpart); sl@0: return 1; sl@0: } else if(part) { sl@0: /* Strip CR+LF from linebuf */ sl@0: next_eol = strip_eol(linebuf, &len); sl@0: if(first) { sl@0: first = 0; sl@0: if(bpart) sk_BIO_push(parts, bpart); sl@0: bpart = BIO_new(BIO_s_mem()); sl@0: BIO_set_mem_eof_return(bpart, 0); sl@0: } else if (eol) sl@0: BIO_write(bpart, "\r\n", 2); sl@0: eol = next_eol; sl@0: if (len) sl@0: BIO_write(bpart, linebuf, len); sl@0: } sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: /* This is the big one: parse MIME header lines up to message body */ sl@0: sl@0: #define MIME_INVALID 0 sl@0: #define MIME_START 1 sl@0: #define MIME_TYPE 2 sl@0: #define MIME_NAME 3 sl@0: #define MIME_VALUE 4 sl@0: #define MIME_QUOTE 5 sl@0: #define MIME_COMMENT 6 sl@0: sl@0: sl@0: static STACK_OF(MIME_HEADER) *mime_parse_hdr(BIO *bio) sl@0: { sl@0: char *p, *q, c; sl@0: char *ntmp; sl@0: char linebuf[MAX_SMLEN]; sl@0: MIME_HEADER *mhdr = NULL; sl@0: STACK_OF(MIME_HEADER) *headers; sl@0: int len, state, save_state = 0; sl@0: sl@0: headers = sk_MIME_HEADER_new(mime_hdr_cmp); sl@0: while ((len = BIO_gets(bio, linebuf, MAX_SMLEN)) > 0) { sl@0: /* If whitespace at line start then continuation line */ sl@0: if(mhdr && isspace((unsigned char)linebuf[0])) state = MIME_NAME; sl@0: else state = MIME_START; sl@0: ntmp = NULL; sl@0: /* Go through all characters */ sl@0: for(p = linebuf, q = linebuf; (c = *p) && (c!='\r') && (c!='\n'); p++) { sl@0: sl@0: /* State machine to handle MIME headers sl@0: * if this looks horrible that's because it *is* sl@0: */ sl@0: sl@0: switch(state) { sl@0: case MIME_START: sl@0: if(c == ':') { sl@0: state = MIME_TYPE; sl@0: *p = 0; sl@0: ntmp = strip_ends(q); sl@0: q = p + 1; sl@0: } sl@0: break; sl@0: sl@0: case MIME_TYPE: sl@0: if(c == ';') { sl@0: mime_debug("Found End Value\n"); sl@0: *p = 0; sl@0: mhdr = mime_hdr_new(ntmp, strip_ends(q)); sl@0: sk_MIME_HEADER_push(headers, mhdr); sl@0: ntmp = NULL; sl@0: q = p + 1; sl@0: state = MIME_NAME; sl@0: } else if(c == '(') { sl@0: save_state = state; sl@0: state = MIME_COMMENT; sl@0: } sl@0: break; sl@0: sl@0: case MIME_COMMENT: sl@0: if(c == ')') { sl@0: state = save_state; sl@0: } sl@0: break; sl@0: sl@0: case MIME_NAME: sl@0: if(c == '=') { sl@0: state = MIME_VALUE; sl@0: *p = 0; sl@0: ntmp = strip_ends(q); sl@0: q = p + 1; sl@0: } sl@0: break ; sl@0: sl@0: case MIME_VALUE: sl@0: if(c == ';') { sl@0: state = MIME_NAME; sl@0: *p = 0; sl@0: mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); sl@0: ntmp = NULL; sl@0: q = p + 1; sl@0: } else if (c == '"') { sl@0: mime_debug("Found Quote\n"); sl@0: state = MIME_QUOTE; sl@0: } else if(c == '(') { sl@0: save_state = state; sl@0: state = MIME_COMMENT; sl@0: } sl@0: break; sl@0: sl@0: case MIME_QUOTE: sl@0: if(c == '"') { sl@0: mime_debug("Found Match Quote\n"); sl@0: state = MIME_VALUE; sl@0: } sl@0: break; sl@0: } sl@0: } sl@0: sl@0: if(state == MIME_TYPE) { sl@0: mhdr = mime_hdr_new(ntmp, strip_ends(q)); sl@0: sk_MIME_HEADER_push(headers, mhdr); sl@0: } else if(state == MIME_VALUE) sl@0: mime_hdr_addparam(mhdr, ntmp, strip_ends(q)); sl@0: if(p == linebuf) break; /* Blank line means end of headers */ sl@0: } sl@0: sl@0: return headers; sl@0: sl@0: } sl@0: sl@0: static char *strip_ends(char *name) sl@0: { sl@0: return strip_end(strip_start(name)); sl@0: } sl@0: sl@0: /* Strip a parameter of whitespace from start of param */ sl@0: static char *strip_start(char *name) sl@0: { sl@0: char *p, c; sl@0: /* Look for first non white space or quote */ sl@0: for(p = name; (c = *p) ;p++) { sl@0: if(c == '"') { sl@0: /* Next char is start of string if non null */ sl@0: if(p[1]) return p + 1; sl@0: /* Else null string */ sl@0: return NULL; sl@0: } sl@0: if(!isspace((unsigned char)c)) return p; sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: /* As above but strip from end of string : maybe should handle brackets? */ sl@0: static char *strip_end(char *name) sl@0: { sl@0: char *p, c; sl@0: if(!name) return NULL; sl@0: /* Look for first non white space or quote */ sl@0: for(p = name + strlen(name) - 1; p >= name ;p--) { sl@0: c = *p; sl@0: if(c == '"') { sl@0: if(p - 1 == name) return NULL; sl@0: *p = 0; sl@0: return name; sl@0: } sl@0: if(isspace((unsigned char)c)) *p = 0; sl@0: else return name; sl@0: } sl@0: return NULL; sl@0: } sl@0: sl@0: static MIME_HEADER *mime_hdr_new(char *name, char *value) sl@0: { sl@0: MIME_HEADER *mhdr; sl@0: char *tmpname, *tmpval, *p; sl@0: int c; sl@0: if(name) { sl@0: if(!(tmpname = BUF_strdup(name))) return NULL; sl@0: for(p = tmpname ; *p; p++) { sl@0: c = *p; sl@0: if(isupper(c)) { sl@0: c = tolower(c); sl@0: *p = c; sl@0: } sl@0: } sl@0: } else tmpname = NULL; sl@0: if(value) { sl@0: if(!(tmpval = BUF_strdup(value))) return NULL; sl@0: for(p = tmpval ; *p; p++) { sl@0: c = *p; sl@0: if(isupper(c)) { sl@0: c = tolower(c); sl@0: *p = c; sl@0: } sl@0: } sl@0: } else tmpval = NULL; sl@0: mhdr = (MIME_HEADER *) OPENSSL_malloc(sizeof(MIME_HEADER)); sl@0: if(!mhdr) return NULL; sl@0: mhdr->name = tmpname; sl@0: mhdr->value = tmpval; sl@0: if(!(mhdr->params = sk_MIME_PARAM_new(mime_param_cmp))) return NULL; sl@0: return mhdr; sl@0: } sl@0: sl@0: static int mime_hdr_addparam(MIME_HEADER *mhdr, char *name, char *value) sl@0: { sl@0: char *tmpname, *tmpval, *p; sl@0: int c; sl@0: MIME_PARAM *mparam; sl@0: if(name) { sl@0: tmpname = BUF_strdup(name); sl@0: if(!tmpname) return 0; sl@0: for(p = tmpname ; *p; p++) { sl@0: c = *p; sl@0: if(isupper(c)) { sl@0: c = tolower(c); sl@0: *p = c; sl@0: } sl@0: } sl@0: } else tmpname = NULL; sl@0: if(value) { sl@0: tmpval = BUF_strdup(value); sl@0: if(!tmpval) return 0; sl@0: } else tmpval = NULL; sl@0: /* Parameter values are case sensitive so leave as is */ sl@0: mparam = (MIME_PARAM *) OPENSSL_malloc(sizeof(MIME_PARAM)); sl@0: if(!mparam) return 0; sl@0: mparam->param_name = tmpname; sl@0: mparam->param_value = tmpval; sl@0: sk_MIME_PARAM_push(mhdr->params, mparam); sl@0: return 1; sl@0: } sl@0: sl@0: static int mime_hdr_cmp(const MIME_HEADER * const *a, sl@0: const MIME_HEADER * const *b) sl@0: { sl@0: return(strcmp((*a)->name, (*b)->name)); sl@0: } sl@0: sl@0: static int mime_param_cmp(const MIME_PARAM * const *a, sl@0: const MIME_PARAM * const *b) sl@0: { sl@0: return(strcmp((*a)->param_name, (*b)->param_name)); sl@0: } sl@0: sl@0: /* Find a header with a given name (if possible) */ sl@0: sl@0: static MIME_HEADER *mime_hdr_find(STACK_OF(MIME_HEADER) *hdrs, char *name) sl@0: { sl@0: MIME_HEADER htmp; sl@0: int idx; sl@0: htmp.name = name; sl@0: idx = sk_MIME_HEADER_find(hdrs, &htmp); sl@0: if(idx < 0) return NULL; sl@0: return sk_MIME_HEADER_value(hdrs, idx); sl@0: } sl@0: sl@0: static MIME_PARAM *mime_param_find(MIME_HEADER *hdr, char *name) sl@0: { sl@0: MIME_PARAM param; sl@0: int idx; sl@0: param.param_name = name; sl@0: idx = sk_MIME_PARAM_find(hdr->params, ¶m); sl@0: if(idx < 0) return NULL; sl@0: return sk_MIME_PARAM_value(hdr->params, idx); sl@0: } sl@0: sl@0: static void mime_hdr_free(MIME_HEADER *hdr) sl@0: { sl@0: if(hdr->name) OPENSSL_free(hdr->name); sl@0: if(hdr->value) OPENSSL_free(hdr->value); sl@0: if(hdr->params) sk_MIME_PARAM_pop_free(hdr->params, mime_param_free); sl@0: OPENSSL_free(hdr); sl@0: } sl@0: sl@0: static void mime_param_free(MIME_PARAM *param) sl@0: { sl@0: if(param->param_name) OPENSSL_free(param->param_name); sl@0: if(param->param_value) OPENSSL_free(param->param_value); sl@0: OPENSSL_free(param); sl@0: } sl@0: sl@0: /* Check for a multipart boundary. Returns: sl@0: * 0 : no boundary sl@0: * 1 : part boundary sl@0: * 2 : final boundary sl@0: */ sl@0: static int mime_bound_check(char *line, int linelen, char *bound, int blen) sl@0: { sl@0: if(linelen == -1) linelen = strlen(line); sl@0: if(blen == -1) blen = strlen(bound); sl@0: /* Quickly eliminate if line length too short */ sl@0: if(blen + 2 > linelen) return 0; sl@0: /* Check for part boundary */ sl@0: if(!strncmp(line, "--", 2) && !strncmp(line + 2, bound, blen)) { sl@0: if(!strncmp(line + blen + 2, "--", 2)) return 2; sl@0: else return 1; sl@0: } sl@0: return 0; sl@0: } sl@0: sl@0: static int strip_eol(char *linebuf, int *plen) sl@0: { sl@0: int len = *plen; sl@0: char *p, c; sl@0: int is_eol = 0; sl@0: p = linebuf + len - 1; sl@0: for (p = linebuf + len - 1; len > 0; len--, p--) sl@0: { sl@0: c = *p; sl@0: if (c == '\n') sl@0: is_eol = 1; sl@0: else if (c != '\r') sl@0: break; sl@0: } sl@0: *plen = len; sl@0: return is_eol; sl@0: }