diff options
Diffstat (limited to 'trunk/main/minimime/mm_mimepart.c')
-rw-r--r-- | trunk/main/minimime/mm_mimepart.c | 659 |
1 files changed, 659 insertions, 0 deletions
diff --git a/trunk/main/minimime/mm_mimepart.c b/trunk/main/minimime/mm_mimepart.c new file mode 100644 index 000000000..631debae0 --- /dev/null +++ b/trunk/main/minimime/mm_mimepart.c @@ -0,0 +1,659 @@ +/* + * $Id$ + * + * MiniMIME - a library for handling MIME messages + * + * Copyright (C) 2003 Jann Fischer <rezine@mistrust.net> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the author nor the names of the contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JANN FISCHER AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JANN FISCHER OR THE VOICES IN HIS HEAD + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <assert.h> + +#include "mm_internal.h" + +/** @file mm_mimepart.c + * + * This module contains functions for manipulating MIME header objects. + */ + +/** @defgroup mimepart Accessing and manipulating MIME parts + * + * MIME parts, also called entities, represent the structure of a MIME + * message. ``Normal'' internet messages have only a single part, and + * are called ``flat'' messages. Multipart messages have more then one + * part, and each MIME part can have it's own subset of headers. + * + * Provided here are functions to easily access all informations from + * a MIME part, including their specific headers and bodies. + */ + +/** @{ + * @name Creating and destroying MIME parts + */ + +/** + * Allocates memory for a new mm_mimepart structure and initializes it. + * + * @return A pointer to a struct of type mm_mimeheader or NULL on failure + * @see mm_mimepart_free + * @note The memory must be freed by using mm_mimepart_free() later on. + */ +struct mm_mimepart * +mm_mimepart_new(void) +{ + struct mm_mimepart *part; + + part = (struct mm_mimepart *)xmalloc(sizeof(struct mm_mimepart)); + + TAILQ_INIT(&part->headers); + + part->opaque_length = 0; + part->opaque_body = NULL; + + part->length = 0; + part->body = NULL; + + part->type = NULL; + + return part; +} + +/** + * Creates a MIME part from a file + * + * @param filename The name of the file to create the MIME part from + * @return A pointer to a new MIME part object + * + * This function creates a new MIME part object from a file. The object should + * be freed using mm_mimepart_free() later on. This function does NOT set the + * Content-Type and neither does any encoding work. + */ +struct mm_mimepart * +mm_mimepart_fromfile(const char *filename) +{ + int fd; + char *data; + size_t r; + struct stat st; + struct mm_mimepart *part; + + mm_errno = MM_ERROR_NONE; + + if ((fd = open(filename, O_RDONLY)) == -1) { + mm_errno = MM_ERROR_ERRNO; + return NULL; + } + + if ((stat(filename, &st)) == -1) { + mm_errno = MM_ERROR_ERRNO; + close(fd); + return NULL; + } + + data = xmalloc(st.st_size); + r = read(fd, data, st.st_size); + if (r != st.st_size) { + mm_errno = MM_ERROR_ERRNO; + close(fd); + return(NULL); + } + + data[r] = '\0'; + close(fd); + + part = mm_mimepart_new(); + part->length = r; + part->body = data; + + return part; +} + + +/** + * Frees all memory allocated by a mm_mimepart object. + * + * @param part A pointer to an allocated mm_mimepart object + * @see mm_mimepart_new + */ +void +mm_mimepart_free(struct mm_mimepart *part) +{ + struct mm_mimeheader *header; + + assert(part != NULL); + + TAILQ_FOREACH(header, &part->headers, next) { + mm_mimeheader_free(header); + TAILQ_REMOVE(&part->headers, header, next); + } + + if (part->opaque_body != NULL) { + xfree(part->opaque_body); + part->opaque_body = NULL; + part->body = NULL; + } else if (part->body != NULL) { + xfree(part->body); + part->body = NULL; + } + + if (part->type != NULL) { + mm_content_free(part->type); + part->type = NULL; + } + + xfree(part); + part = NULL; +} + +/** @} */ + +/** @{ + * @name Accessing the MIME part's mail header + */ + +/** + * Attaches a mm_mimeheader object to a MIME part + * + * @param part A valid MIME part object + * @param header A valid MIME header object + * @return 0 if successfull or -1 if the header could not be attached + */ +int +mm_mimepart_attachheader(struct mm_mimepart *part, struct mm_mimeheader *header) +{ + assert(part != NULL); + assert(header != NULL); + + if (TAILQ_EMPTY(&part->headers)) { + TAILQ_INSERT_HEAD(&part->headers, header, next); + } else { + TAILQ_INSERT_TAIL(&part->headers, header, next); + } + + return(0); +} + +/** + * Retrieves the number of MIME headers available in a MIME part + * + * @param part A valid MIME part object + * @return The number of MIME headers within the MIME part + */ +int +mm_mimepart_countheaders(struct mm_mimepart *part) +{ + int found; + struct mm_mimeheader *header; + + assert(part != NULL); + + found = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + found++; + } + + return found; +} + +/** + * Retrieves the number of MIME headers with a given name in a MIME part + * + * @param part A valid MIME part object + * @param name The name of the MIME header which to count for + * @return The number of MIME headers within the MIME part + */ +int +mm_mimepart_countheaderbyname(struct mm_mimepart *part, const char *name) +{ + int found; + struct mm_mimeheader *header; + + assert(part != NULL); + + found = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + if (strcasecmp(header->name, name) == 0) { + found++; + } + } + + return found; +} + +/** + * Get a MIME header object from a MIME part + * + * @param part A valid MIME part object + * @param name The name of the MIME header which to retrieve + * @param idx Which header field to get (in case of multiple headers of the + * same name). + * @return A pointer to the requested MIME header on success, or NULL if there + * either isn't a header with the requested name or idx is out of + * range. + */ +struct mm_mimeheader * +mm_mimepart_getheaderbyname(struct mm_mimepart *part, const char *name, int idx) +{ + struct mm_mimeheader *header; + int curidx; + + curidx = 0; + + TAILQ_FOREACH(header, &part->headers, next) { + if (!strcasecmp(header->name, name)) { + if (curidx == idx) + return header; + else + curidx++; + } + } + + /* Not found */ + return NULL; +} + +/** + * Gets the value of a MIME header object + * + * @param part A valid MIME part object + * @param name The name of the header field to get the value from + * @param idx The index of the header field to get, in case there are multiple + * headers with the same name. + * @return A pointer to the requested value on success, or NULL if there either + * isn't a header with the requested name or idx is out of range. + * + */ +const char * +mm_mimepart_getheadervalue(struct mm_mimepart *part, const char *name, int idx) +{ + struct mm_mimeheader *header; + + header = mm_mimepart_getheaderbyname(part, name, idx); + if (header == NULL) + return NULL; + else + return header->value; +} + +/** + * Initializes a header loop for a given MIME part + * + * @param part A valid MIME part object + * @param id The address of a MIME header object (to allow reentrance) + * @return 0 on success or -1 on failure + * @see mm_mimepart_headers_next + * + * Looping through headers can be done in the following way: + * + * @code + * struct mm_mimeheader *header, *lheader; + * + * mm_mimepart_headers_start(part, &lheader); + * + * while ((header = mm_mimepart_headers_next(part, &lheader)) != NULL) { + * printf("%s: %s\n", header->name, header->value); + * } + * + * @endcode + * + * For convienience, the macro mm_mimepart_headers_foreach() can be used to + * loop through headers in a one-shot manner. + */ +int +mm_mimepart_headers_start(struct mm_mimepart *part, struct mm_mimeheader **id) +{ + assert(part != NULL); + + if (TAILQ_EMPTY(&part->headers)) { + return -1; + } + *id = NULL; + return 0; +} + +/** + * Returns the next MIME header of a given MIME part object + * + * @param part A valid MIME part object + * @param id A previously initialized MIME header object + * @return A pointer to the MIME header object or NULL if end of headers was + * reached. + * @see mm_mimepart_headers_start + */ +struct mm_mimeheader * +mm_mimepart_headers_next(struct mm_mimepart *part, struct mm_mimeheader **id) +{ + struct mm_mimeheader *header; + + assert(part != NULL); + + if (*id == NULL) { + header = TAILQ_FIRST(&part->headers); + } else { + header = TAILQ_NEXT(*id, next); + } + *id = header; + + return header; +} + +/** @} */ + +/** @{ + * @name Accessing and manipulating the MIME part's body + */ + +/** + * Gets the pointer to the MIME part's body data + * + * @param part A valid MIME part object + * @param opaque Whether to get the opaque part or not + * @return A pointer to the MIME part's body + * @see mm_mimepart_setbody + * + */ +char * +mm_mimepart_getbody(struct mm_mimepart *part, int opaque) +{ + assert(part != NULL); + + if (opaque) + return part->opaque_body; + else + return part->body; +} + +/** + * Sets the MIME part's body data + * + * @param part A valid MIME part object + * @param data A pointer to the data which to set + * @see mm_mimepart_getbody + * + * This functions sets the body data for a given MIME part. The string pointed + * to by data must be NUL-terminated. The data is copied into the MIME part's + * body, and thus, the memory pointed to by data can be freed after the + * operation. + */ +#if 0 +void +mm_mimepart_setbody(struct mm_mimepart *part, const char *data, int opaque) +{ + assert(part != NULL); + assert(data != NULL); + + if (opaque) { + part->opaque_body = xstrdup(data); + part->body = part->opaque_body; + } else { + part->body = xstrdup(data); + } + part->length = strlen(data); +} +#endif + +/** + * Gets the length of a given MIME part object + * + * @param part A valid MIME part object + * @returns The size of the part's body in byte. + * + * This function returns the total length of the given MIME part's body. The + * length does not include the headers of the MIME parts. If the function + * returns 0, no body part is set currently. + */ +size_t +mm_mimepart_getlength(struct mm_mimepart *part) +{ + assert(part != NULL); + + return part->length; +} + + +/** + * Decodes a MIME part according to it's encoding using MiniMIME codecs + * + * @param A valid MIME part object + * @return 0 if the MIME part could be successfully decoded or -1 if not + * @note Sets mm_errno on error + * + * This function decodes the body of a MIME part with a registered decoder + * according to it's Content-Transfer-Encoding header field. + */ +char * +mm_mimepart_decode(struct mm_mimepart *part) +{ + extern struct mm_codecs codecs; + struct mm_codec *codec; + void *decoded; + + assert(part != NULL); + assert(part->type != NULL); + + decoded = NULL; + + /* No encoding associated */ + if (part->type->encstring == NULL) + return NULL; + + /* Loop through codecs and find a suitable one */ + SLIST_FOREACH(codec, &codecs, next) { + if (!strcasecmp(part->type->encstring, codec->encoding)) { + decoded = codec->decoder((char *)part->body); + break; + } + } + + return decoded; +} + +/** + * Creates an ASCII representation of the given MIME part + * + * @param part A valid MIME part object + * @param result Where to store the result + * @param length Where to store the length of the result + * @param opaque Whether to use the opaque MIME part + * @returtn 0 on success or -1 on error. + * @see mm_context_flatten + * + * This function creates an ASCII representation of a given MIME part. It will + * dynamically allocate the memory needed and stores the result in the memory + * region pointed to by result. The length of the result will be stored in + * length. If opaque is set to 1, mm_mimepart_flatten will store an opaque + * version of the MIME part in result, which means no headers will be created + * or sanitized. This is particulary useful if the part is digitally signed by + * e.g. PGP, and the signature spans the header fields of the part in question. + * + */ +int +mm_mimepart_flatten(struct mm_mimepart *part, char **result, size_t *length, + int opaque) +{ + size_t part_length; + char *buf; + char *ct_hdr; + + *result = NULL; + *length = 0; + buf = NULL; + ct_hdr = NULL; + part_length = 0; + + if (opaque && part->opaque_body != NULL) { + part_length = strlen(part->opaque_body); + *result = xstrdup(part->opaque_body); + *length = part_length; + return(0); + } else { + if (part->type == NULL) { + return(-1); + } + + ct_hdr = mm_content_tostring(part->type); + if (ct_hdr == NULL) { + return(-1); + } + + part_length += strlen(ct_hdr) + 2; + part_length += strlen("\r\n") * 2; + part_length += strlen(part->body); + + if (part_length < 0) { + goto cleanup; + } + + buf = (char *) xmalloc(part_length); + if (buf == NULL) { + goto cleanup; + } + + snprintf(buf, part_length, + "%s\r\n\r\n%s\r\n", + ct_hdr, + part->body); + + xfree(ct_hdr); + ct_hdr = NULL; + + *result = buf; + *length = part_length; + } + + return(0); + +cleanup: + if (ct_hdr != NULL) { + xfree(ct_hdr); + ct_hdr = NULL; + } + if (buf != NULL) { + xfree(buf); + buf = NULL; + } + + *result = NULL; + *length = 0; + + return -1; +} + +/** + * Sets the default Content-Type for a given MIME part + * + * @param part A valid MIME part object + * @param part Whether the Content-Type should be for composite or not + * @return 0 on success or -1 on failure + * + * This function sets a default Content-Type according to RFC 2045 with a value + * of "text/plain; charset="us-ascii"". This function should only be used if + * the MIME part in question does not have a valid Content-Type specification. + */ +int +mm_mimepart_setdefaultcontenttype(struct mm_mimepart *part, int composite) +{ + struct mm_content *type; + struct mm_param *param; + + if (part == NULL) { + return(-1); + } + + if (part->type != NULL) { + mm_content_free(part->type); + part->type = NULL; + } + + type = mm_content_new(); + if (composite) { + type->maintype = xstrdup("multipart"); + type->subtype = xstrdup("mixed"); + } else { + type->maintype = xstrdup("text"); + type->subtype = xstrdup("plain"); + param = mm_param_new(); + param->name = xstrdup("charset"); + param->value = xstrdup("us-ascii"); + mm_content_attachtypeparam(type, param); + } + + mm_mimepart_attachcontenttype(part, type); + + return (0); +} + +/** @{ + * @name Accessing the MIME part's Content-Type information + */ + +/** + * Attaches a context type object to a MIME part + * + * @param part A valid MIME part object + * @param ct The content type object to attach + * @return Nothing + * + * This function attaches a Content-Type object to a MIME part. It does not + * care whether the Content-Type suites the actual content in the MIME part, + * so the programmer should take care of that. + */ +void +mm_mimepart_attachcontenttype(struct mm_mimepart *part, struct mm_content *ct) +{ + part->type = ct; +} + +/** + * Gets the Content-Type of a given MIME part object + * + * @param part A valid MIME part object + * @return The Content-Type object of the specified MIME part + * + * This function returns a pointer to the Content-Type object of the given + * MIME part. This pointer might be set to NULL, indicating that there is + * no Content-Type object for the given MIME part currently. + */ +struct mm_content * +mm_mimepart_getcontent(struct mm_mimepart *part) +{ + assert(part != NULL); + + return part->type; +} + +/** @} */ |