https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r60603 | russell | 2007-04-06 15:58:43 -0500 (Fri, 06 Apr 2007) | 13 lines To be able to achieve the things that we would like to achieve with the Asterisk GUI project, we need a fully functional HTTP interface with access to the Asterisk manager interface. One of the things that was intended to be a part of this system, but was never actually implemented, was the ability for the GUI to be able to upload files to Asterisk. So, this commit adds this in the most minimally invasive way that we could come up with. A lot of work on minimime was done by Steve Murphy. He fixed a lot of bugs in the parser, and updated it to be thread-safe. The ability to check permissions of active manager sessions was added by Dwayne Hubbard. Then, hacking this all together and do doing the modifications necessary to the HTTP interface was done by me. ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@60604 65c4cc65-6c06-0410-ace0-fbb531ad65f3
605 lines
14 KiB
C
605 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.
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/** @} */
|