asterisk/main/minimime/mm_context.c

615 lines
14 KiB
C

/*
* $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.
*/
#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
/** @} */