/* * $Id$ * * MiniMIME - a library for handling MIME messages * * Copyright (C) 2003 Jann Fischer * 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 #include #include #include #include #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. */ #if 0 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); } #endif /** * 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. */ #if 0 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); } #endif /** * 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. */ #if 0 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); } #endif #if 0 char * mm_context_getpreamble(MM_CTX *ctx) { if (ctx == NULL) return(NULL); return(ctx->preamble); } #endif /** * 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. */ #if 0 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; } #endif /** @} */