diff options
Diffstat (limited to 'main/minimime/mm_context.c')
-rw-r--r-- | main/minimime/mm_context.c | 604 |
1 files changed, 604 insertions, 0 deletions
diff --git a/main/minimime/mm_context.c b/main/minimime/mm_context.c new file mode 100644 index 000000000..e655b925a --- /dev/null +++ b/main/minimime/mm_context.c @@ -0,0 +1,604 @@ +/* + * $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 <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> + +#include "mm_internal.h" + +/** @file mm_context.c + * + * Modules for manipulating MiniMIME contexts + */ + +/** @defgroup context Accessing and manipulating MIME contexts + * + * Each message in MiniMIME is represented by a so called ``context''. A + * context holds all necessary information given about a MIME message, such + * as the envelope, all MIME parts etc. + */ + +/** @{ + * @name Manipulating MiniMIME contexts + */ + +/** + * Creates a new MiniMIME context object. + * + * @return a new MiniMIME context object + * @see mm_context_free + * + * This function creates a new MiniMIME context, which will hold a message. + * The memory needed is allocated dynamically and should later be free'd + * using mm_context_free(). + * + * Before a context can be created, the MiniMIME library needs to be + * initialized properly using mm_library_init(). + * + */ +MM_CTX * +mm_context_new(void) +{ + MM_CTX *ctx; + + MM_ISINIT(); + + ctx = (MM_CTX *)xmalloc(sizeof(MM_CTX)); + ctx->messagetype = MM_MSGTYPE_FLAT; /* This is the default */ + ctx->boundary = NULL; + ctx->preamble = xstrdup("This is a message in MIME format, generated " + "by MiniMIME 0.1"); + + TAILQ_INIT(&ctx->parts); + SLIST_INIT(&ctx->warnings); + + return ctx; +} + +/** + * Releases a MiniMIME context object + * + * @param ctx A valid MiniMIME context + * @see mm_context_new + * + * This function releases all memory associated with MiniMIME context object + * that was created using mm_context_new(). It will also release all memory + * used for the MIME parts attached, and their specific properties (such as + * Content-Type information, headers, and the body data). + */ +void +mm_context_free(MM_CTX *ctx) +{ + struct mm_mimepart *part; + struct mm_warning *warning, *nxt; + + assert(ctx != NULL); + + TAILQ_FOREACH(part, &ctx->parts, next) { + TAILQ_REMOVE(&ctx->parts, part, next); + mm_mimepart_free(part); + } + + if (ctx->boundary != NULL) { + xfree(ctx->boundary); + ctx->boundary = NULL; + } + + if (ctx->preamble != NULL) { + xfree(ctx->preamble); + ctx->preamble = NULL; + } + + for (warning = SLIST_FIRST(&ctx->warnings); + warning != SLIST_END(&ctx->warnings); + warning = nxt) { + nxt = SLIST_NEXT(warning, next); + SLIST_REMOVE(&ctx->warnings, warning, mm_warning, next); + xfree(warning); + warning = NULL; + } + + xfree(ctx); + ctx = NULL; +} + +/** + * Attaches a MIME part object to a MiniMIME context. + * + * @param ctx the MiniMIME context + * @param part the MIME part object to attach + * @return 0 on success or -1 on failure. Sets mm_errno on failure. + * + * This function attaches a MIME part to a context, appending it to the end + * of the message. + * + * The MIME part should be initialized before attaching it using + * mm_mimepart_new(). + */ +int +mm_context_attachpart(MM_CTX *ctx, struct mm_mimepart *part) +{ + assert(ctx != NULL); + assert(part != NULL); + + if (TAILQ_EMPTY(&ctx->parts)) { + TAILQ_INSERT_HEAD(&ctx->parts, part, next); + } else { + TAILQ_INSERT_TAIL(&ctx->parts, part, next); + } + + return 0; +} + +/** + * Attaches a MIME part object to a MiniMIME context at a given position + * + * @param ctx A valid MiniMIME context + * @param part The MIME part object to attach + * @param pos After which part to attach the object + * @return 0 on success or -1 if the given position is invalid + * @see mm_context_attachpart + * + * This function attaches a MIME part object after a given position in the + * specified context. If the position is invalid (out of range), the part + * will not get attached to the message and the function returns -1. If + * the index was in range, the MIME part will get attached after the MIME + * part at the given position, moving any possible following MIME parts one + * down the hierarchy. + */ +int +mm_context_attachpart_after(MM_CTX *ctx, struct mm_mimepart *part, int pos) +{ + struct mm_mimepart *p; + int where; + + where = 0; + p = NULL; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (where == pos) { + p = part; + } + } + + if (p == NULL) { + return(-1); + } + + TAILQ_INSERT_AFTER(&ctx->parts, p, part, next); + + return(0); +} + +/** + * Deletes a MIME part object from a MiniMIME context + * + * @param ctx A valid MiniMIME context object + * @param which The number of the MIME part object to delete + * @param freemem Whether to free the memory associated with the MIME part + * object + * @return 0 on success or -1 on failure. Sets mm_errno on failure. + * + * This function deletes a MIME part from a given context. The MIME part to + * delete is specified as numerical index by the parameter ``which''. If the + * parameter ``freemem'' is set to anything greater than 0, the memory that + * is associated will be free'd by using mm_mimepart_free(), otherwise the + * memory is left untouched (if you still have a pointer to the MIME part + * around). + */ +int +mm_context_deletepart(MM_CTX *ctx, int which, int freemem) +{ + struct mm_mimepart *part; + int cur; + + assert(ctx != NULL); + assert(which >= 0); + + cur = 0; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (cur == which) { + TAILQ_REMOVE(&ctx->parts, part, next); + if (freemem) + mm_mimepart_free(part); + return 0; + } + cur++; + } + + return -1; +} + +/** + * Counts the number of attached MIME part objects in a given MiniMIME context + * + * @param ctx The MiniMIME context + * @returns The number of attached MIME part objects + */ +int +mm_context_countparts(MM_CTX *ctx) +{ + int count; + struct mm_mimepart *part; + + assert(ctx != NULL); + + count = 0; + + if (TAILQ_EMPTY(&ctx->parts)) { + return 0; + } else { + TAILQ_FOREACH(part, &ctx->parts, next) { + count++; + } + } + + assert(count > -1); + + return count; +} + +/** + * Gets a specified MIME part object from a MimeMIME context + * + * @param ctx The MiniMIME context + * @param which The number of the MIME part object to retrieve + * @returns The requested MIME part object on success or a NULL pointer if + * there is no such part. + */ +struct mm_mimepart * +mm_context_getpart(MM_CTX *ctx, int which) +{ + struct mm_mimepart *part; + int cur; + + assert(ctx != NULL); + + cur = 0; + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (cur == which) { + return part; + } + cur++; + } + + return NULL; +} + +/** + * Checks whether a given context represents a composite (multipart) message + * + * @param ctx A valid MiniMIME context object + * @return 1 if the context is a composite message or 0 if it's flat + * + */ +int +mm_context_iscomposite(MM_CTX *ctx) +{ + if (ctx->messagetype == MM_MSGTYPE_MULTIPART) { + return 1; + } else { + return 0; + } +} + +/** + * Checks whether there are any warnings associated with a given context + * + * @param ctx A valid MiniMIME context + * @return 1 if there are warnings associated with the context, otherwise 0 + */ +int +mm_context_haswarnings(MM_CTX *ctx) +{ + if (SLIST_EMPTY(&ctx->warnings)) { + return 0; + } else { + return 1; + } +} + +/** + * Generates a generic boundary string for a given context + * + * @param ctx A valid MiniMIME context + * @return 0 on success or -1 on failure + * + * This function generates a default boundary string for the given context. + * If there is already a boundary for the context, the memory will be free()'d. + */ +int +mm_context_generateboundary(MM_CTX *ctx) +{ + char *boundary; + struct mm_mimepart *part; + struct mm_param *param; + + if (mm_mimeutil_genboundary("++MiniMIME++", 20, &boundary) == -1) { + return(-1); + } + + if (ctx->boundary != NULL) { + xfree(ctx->boundary); + ctx->boundary = NULL; + } + + /* If we already have an envelope, make sure that we also justify the + * "boundary" parameter of the envelope. + */ + part = mm_context_getpart(ctx, 0); + if (part == NULL) { + return(0); + } + if (part->type != NULL) { + param = mm_content_gettypeparamobjbyname(part->type, "boundary"); + if (param == NULL) { + param = mm_param_new(); + param->name = xstrdup("boundary"); + param->value = xstrdup(boundary); + mm_content_attachtypeparam(part->type, param); + } else { + if (param->value != NULL) { + xfree(param->value); + param->value = NULL; + } + param->value = xstrdup(boundary); + } + } + + ctx->boundary = boundary; + return(0); +} + +/** + * Sets a preamble for the given MiniMIME context + * + * @param ctx A valid MiniMIME context + * @param preamble The preamble to set + * @return 0 on success or -1 on failure + * + * This function sets the MIME preamble (the text between the end of envelope + * headers and the beginning of the first MIME part) for a given context + * object. If preamble is a NULL-pointer then the preamble will be deleted, + * and the currently associated memory will be free automagically. + */ +int +mm_context_setpreamble(MM_CTX *ctx, char *preamble) +{ + if (ctx == NULL) + return(-1); + + if (preamble == NULL) { + if (ctx->preamble != NULL) { + xfree(ctx->preamble); + } + ctx->preamble = NULL; + } else { + ctx->preamble = xstrdup(preamble); + } + return(0); +} + +char * +mm_context_getpreamble(MM_CTX *ctx) +{ + if (ctx == NULL) + return(NULL); + + return(ctx->preamble); +} + +/** + * Creates an ASCII message of the specified context + * + * @param ctx A valid MiniMIME context object + * @param flat Where to store the message + * @param flags Flags that affect the flattening process + * + * This function ``flattens'' a MiniMIME context, that is, it creates an ASCII + * represantation of the message the context contains. The flags can be a + * bitwise combination of the following constants: + * + * - MM_FLATTEN_OPAQUE : use opaque MIME parts when flattening + * - MM_FLATTEN_SKIPENVELOPE : do not flatten the envelope part + * + * Great care is taken to not produce invalid MIME output. + */ +int +mm_context_flatten(MM_CTX *ctx, char **flat, size_t *length, int flags) +{ + struct mm_mimepart *part; + char *message; + char *flatpart; + char *buf; + char *envelope_headers; + size_t message_size; + size_t tmp_size; + char envelope; + + mm_errno = MM_ERROR_NONE; + envelope = 1; + + message = NULL; + message_size = 0; + + if (ctx->boundary == NULL) { + if (mm_context_iscomposite(ctx)) { + mm_context_generateboundary(ctx); + } + } + + TAILQ_FOREACH(part, &ctx->parts, next) { + if (envelope) { + if (flags & MM_FLATTEN_SKIPENVELOPE) { + envelope = 0; + if ((message = (char *) malloc(1)) == NULL) { + mm_errno = MM_ERROR_ERRNO; + goto cleanup; + } + *message = '\0'; + continue; + } + + if (part->type == NULL && mm_context_countparts(ctx) > 1) { + if (mm_mimepart_setdefaultcontenttype(part, 1) + == -1) { + goto cleanup; + } + if (mm_context_generateboundary(ctx) == -1) { + goto cleanup; + } + ctx->messagetype = MM_MSGTYPE_MULTIPART; + } + + if (mm_envelope_getheaders(ctx, &envelope_headers, + &tmp_size) == -1) { + return -1; + } + + message = envelope_headers; + message_size = tmp_size; + envelope = 0; + + if (ctx->preamble != NULL + && mm_context_iscomposite(ctx) + && !(flags & MM_FLATTEN_NOPREAMBLE)) { + tmp_size += strlen(ctx->preamble) + + (strlen("\r\n") * 2); + buf = (char *)xrealloc(message, tmp_size); + if (buf == NULL) { + goto cleanup; + } + message_size += tmp_size; + message = buf; + strlcat(message, "\r\n", message_size); + strlcat(message, ctx->preamble, message_size); + strlcat(message, "\r\n", message_size); + } + } else { + /* Enforce Content-Type if none exist */ + if (part->type == NULL) { + if (mm_mimepart_setdefaultcontenttype(part, 0) + == -1) { + goto cleanup; + } + } + + /* Append a boundary if necessary */ + if (ctx->boundary != NULL) { + tmp_size = strlen(ctx->boundary) + + (strlen("\r\n") * 2) + strlen("--"); + + if (tmp_size < 1) { + return(-1); + } + if (message_size + tmp_size < 1) { + return(-1); + } + + buf = (char *)xrealloc(message, message_size + + tmp_size); + if (buf == NULL) { + goto cleanup; + } + message_size += tmp_size; + message = buf; + strlcat(message, "\r\n", message_size); + strlcat(message, "--", message_size); + strlcat(message, ctx->boundary, message_size); + strlcat(message, "\r\n", message_size); + } + + if (mm_mimepart_flatten(part, &flatpart, &tmp_size, + (flags & MM_FLATTEN_OPAQUE)) == -1) { + goto cleanup; + } + + if (tmp_size < 1) { + goto cleanup; + } + + buf = (char *) xrealloc(message, message_size + + tmp_size); + if (buf == NULL) { + goto cleanup; + } + + message_size += tmp_size; + message = buf; + + strlcat(message, flatpart, message_size); + xfree(flatpart); + flatpart = NULL; + } + } + + /* Append end boundary */ + if (ctx->boundary != NULL && mm_context_iscomposite(ctx)) { + tmp_size = strlen(ctx->boundary) + (strlen("\r\n") * 2) + + (strlen("--") * 2); + buf = (char *)xrealloc(message, message_size + tmp_size); + if (buf == NULL) { + goto cleanup; + } + + message_size += tmp_size; + message = buf; + if (message[strlen(message)-1] != 13) + strlcat(message, "\r", message_size); + strlcat(message, "\n", message_size); + strlcat(message, "--", message_size); + strlcat(message, ctx->boundary, message_size); + strlcat(message, "--", message_size); + strlcat(message, "\r\n", message_size); + } + + *flat = message; + *length = message_size; + + return 0; + +cleanup: + if (message != NULL) { + xfree(message); + message = NULL; + } + return -1; +} + +/** @} */ |