asterisk/main/minimime/mm_contenttype.c
Russell Bryant 0a9750ef9f Merged revisions 60603 via svnmerge from
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
2007-04-06 21:16:38 +00:00

758 lines
17 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 <ctype.h>
#include <assert.h>
#include "mm_internal.h"
#include "mm_util.h"
/* This file is documented using Doxygen */
/**
* @file mm_contenttype.c
*
* This module contains functions for manipulating Content-Type objects.
*/
/** @defgroup contenttype Accessing and manipulating Content-Type objects */
struct mm_encoding_mappings {
const char *idstring;
int type;
};
static struct mm_encoding_mappings mm_content_enctypes[] = {
{ "Base64", MM_ENCODING_BASE64 },
{ "Quoted-Printable", MM_ENCODING_QUOTEDPRINTABLE },
{ NULL, - 1},
};
static const char *mm_composite_maintypes[] = {
"multipart",
"message",
NULL,
};
static const char *mm_composite_encodings[] = {
"7bit",
"8bit",
"binary",
NULL,
};
/** @{
* @name Functions for manipulating Content objects
*/
/**
* Creates a new object to hold a Content representation.
* The allocated memory must later be freed using mm_content_free()
*
* @return An object representing a MIME Content-Type
* @see mm_content_free
* @ingroup contenttype
*/
struct mm_content *
mm_content_new(void)
{
struct mm_content *ct;
ct = (struct mm_content *)xmalloc(sizeof(struct mm_content));
ct->maintype = NULL;
ct->subtype = NULL;
TAILQ_INIT(&ct->type_params);
TAILQ_INIT(&ct->disposition_params);
ct->encoding = MM_ENCODING_NONE;
ct->encstring = NULL;
return ct;
}
/**
* Releases all memory associated with an Content object
*
* @param ct A Content-Type object
* @return Nothing
* @ingroup contenttype
*/
void
mm_content_free(struct mm_content *ct)
{
struct mm_param *param;
assert(ct != NULL);
if (ct->maintype != NULL) {
xfree(ct->maintype);
ct->maintype = NULL;
}
if (ct->subtype != NULL) {
xfree(ct->subtype);
ct->subtype = NULL;
}
if (ct->encstring != NULL) {
xfree(ct->encstring);
ct->encstring = NULL;
}
TAILQ_FOREACH(param, &ct->type_params, next) {
TAILQ_REMOVE(&ct->type_params, param, next);
mm_param_free(param);
}
TAILQ_FOREACH(param, &ct->disposition_params, next) {
TAILQ_REMOVE(&ct->disposition_params, param, next);
mm_param_free(param);
}
xfree(ct);
}
/**
* Attaches a content-type parameter to a Content object
*
* @param ct The target Content object
* @param param The Content-Type parameter which to attach
* @return 0 on success and -1 on failure
* @ingroup contenttype
*/
int
mm_content_attachtypeparam(struct mm_content *ct, struct mm_param *param)
{
assert(ct != NULL);
assert(param != NULL);
if (TAILQ_EMPTY(&ct->type_params)) {
TAILQ_INSERT_HEAD(&ct->type_params, param, next);
} else {
TAILQ_INSERT_TAIL(&ct->type_params, param, next);
}
return 0;
}
/**
* Attaches a content-disposition parameter to a Content-Disposition object
*
* @param ct The target Content object
* @param param The Content-Type parameter which to attach
* @return 0 on success and -1 on failure
* @ingroup contenttype
*/
int
mm_content_attachdispositionparam(struct mm_content *ct, struct mm_param *param)
{
assert(ct != NULL);
assert(param != NULL);
if (TAILQ_EMPTY(&ct->disposition_params)) {
TAILQ_INSERT_HEAD(&ct->disposition_params, param, next);
} else {
TAILQ_INSERT_TAIL(&ct->disposition_params, param, next);
}
return 0;
}
/**
* Gets a Content-Type parameter value from a Content object.
*
* @param ct the Content object
* @param name the name of the parameter to retrieve
* @return The value of the parameter on success or a NULL pointer on failure
* @ingroup contenttype
*/
char *
mm_content_gettypeparambyname(struct mm_content *ct, const char *name)
{
struct mm_param *param;
assert(ct != NULL);
TAILQ_FOREACH(param, &ct->type_params, next) {
if (!strcasecmp(param->name, name)) {
return param->value;
}
}
return NULL;
}
/**
* Gets a Content-Disposition parameter value from a Content object.
*
* @param ct the Content object
* @param name the name of the parameter to retrieve
* @return The value of the parameter on success or a NULL pointer on failure
* @ingroup contenttype
*/
char *
mm_content_getdispositionparambyname(struct mm_content *ct, const char *name)
{
struct mm_param *param;
assert(ct != NULL);
TAILQ_FOREACH(param, &ct->disposition_params, next) {
if (!strcasecmp(param->name, name)) {
return param->value;
}
}
return NULL;
}
struct mm_param *
mm_content_gettypeparamobjbyname(struct mm_content *ct, const char *name)
{
struct mm_param *param;
assert(ct != NULL);
TAILQ_FOREACH(param, &ct->type_params, next) {
if (!strcasecmp(param->name, name)) {
return param;
}
}
return NULL;
}
struct mm_param *
mm_content_getdispositionparamobjbyname(struct mm_content *ct, const char *name)
{
struct mm_param *param;
assert(ct != NULL);
TAILQ_FOREACH(param, &ct->disposition_params, next) {
if (!strcasecmp(param->name, name)) {
return param;
}
}
return NULL;
}
/**
* Sets the MIME main Content-Type for a MIME Content object
*
* @param ct The MIME Content object
* @param value The value which to set the main type to
* @param copy Whether to make a copy of the value (original value must be
* freed afterwards to prevent memory leaks).
*/
int
mm_content_setmaintype(struct mm_content *ct, char *value, int copy)
{
assert(ct != NULL);
assert(value != NULL);
if (copy) {
/**
* @bug The xfree() call could lead to undesirable results.
* Do we really need it?
*/
if (ct->maintype != NULL) {
xfree(ct->maintype);
}
ct->maintype = xstrdup(value);
} else {
ct->maintype = value;
}
return 0;
}
/**
* Retrieves the main MIME Content-Type stored in a Content object
*
* @param ct A valid Content object
* @returns A pointer to the string representing the main type
* @ingroup contenttype
*/
char *
mm_content_getmaintype(struct mm_content *ct)
{
assert(ct != NULL);
assert(ct->maintype != NULL);
return ct->maintype;
}
/**
* Sets the MIME Content-Disposition type for a MIME Content object
*
* @param ct The MIME Content object
* @param value The value which to set the main type to
* @param copy Whether to make a copy of the value (original value must be
* freed afterwards to prevent memory leaks).
*/
int
mm_content_setdispositiontype(struct mm_content *ct, char *value, int copy)
{
assert(ct != NULL);
assert(value != NULL);
if (copy) {
/**
* @bug The xfree() call could lead to undesirable results.
* Do we really need it?
*/
if (ct->disposition_type != NULL) {
xfree(ct->disposition_type);
}
ct->disposition_type = xstrdup(value);
} else {
ct->disposition_type = value;
}
return 0;
}
/**
* Retrieves the Content-Disposition MIME type stored in a Content object
*
* @param ct A valid Content-Type object
* @returns A pointer to the string representing the main type
* @ingroup contenttype
*/
char *
mm_content_getdispositiontype(struct mm_content *ct)
{
assert(ct != NULL);
assert(ct->disposition_type != NULL);
return ct->disposition_type;
}
/**
* Retrieves the sub MIME Content-Type stored in a Content object
*
* @param ct A valid Content-Type object
* @return A pointer to the string holding the current sub MIME type
* @ingroup contenttype
*/
char *
mm_content_getsubtype(struct mm_content *ct)
{
assert(ct != NULL);
assert(ct->subtype != NULL);
return ct->subtype;
}
/**
* Sets the MIME sub Content-Type for a MIME Content object
*
* @param ct The MIME Content-Type object
* @param value The value which to set the sub type to
* @param copy Whether to make a copy of the value (original value must be
* freed afterwards to prevent memory leaks).
*/
int
mm_content_setsubtype(struct mm_content *ct, char *value, int copy)
{
assert(ct != NULL);
assert(value != NULL);
if (copy) {
/**
* @bug The xfree() call could lead to undesirable results.
* Do we really need it?
*/
if (ct->subtype != NULL) {
xfree(ct->subtype);
}
ct->subtype = xstrdup(value);
} else {
ct->subtype = value;
}
return 0;
}
int
mm_content_settype(struct mm_content *ct, const char *fmt, ...)
{
char *maint, *subt;
char buf[512], *parse;
va_list ap;
mm_errno = MM_ERROR_NONE;
va_start(ap, fmt);
/* Make sure no truncation occurs */
if (vsnprintf(buf, sizeof buf, fmt, ap) > sizeof buf) {
mm_errno = MM_ERROR_ERRNO;
mm_error_setmsg("Input string too long");
return -1;
}
va_end(ap);
parse = buf;
maint = strsep(&parse, "/");
if (maint == NULL) {
mm_errno = MM_ERROR_PARSE;
mm_error_setmsg("Invalid type specifier: %s", buf);
return -1;
}
ct->maintype = xstrdup(maint);
subt = strsep(&parse, "");
if (subt == NULL) {
mm_errno = MM_ERROR_PARSE;
mm_error_setmsg("Invalid type specifier: %s", buf);
return -1;
}
ct->subtype = xstrdup(subt);
return 0;
}
/**
* Checks whether the Content-Type represents a composite message or not
*
* @param ct A valid Content-Type object
* @returns 1 if the Content-Type object represents a composite message or
* 0 if not.
*/
int
mm_content_iscomposite(struct mm_content *ct)
{
int i;
for (i = 0; mm_composite_maintypes[i] != NULL; i++) {
if (!strcasecmp(ct->maintype, mm_composite_maintypes[i])) {
return 1;
}
}
/* Not found */
return 0;
}
/**
* Verifies whether a string represents a valid encoding or not.
*
* @param encoding The string to verify
* @return 1 if the encoding string is valid or 0 if not
*
*/
int
mm_content_isvalidencoding(const char *encoding)
{
int i;
for (i = 0; mm_composite_encodings[i] != NULL; i++) {
if (!strcasecmp(encoding, mm_composite_encodings[i])) {
return 1;
}
}
/* Not found */
return 0;
}
/**
* Set the encoding of a MIME entitity according to a mapping table
*
* @param ct A valid content type object
* @param encoding A string representing the content encoding
* @return 0 if successfull or -1 if not (i.e. unknown content encoding)
*/
int
mm_content_setencoding(struct mm_content *ct, const char *encoding)
{
int i;
assert(ct != NULL);
assert(encoding != NULL);
for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
ct->encoding = mm_content_enctypes[i].type;
ct->encstring = xstrdup(encoding);
return 0;
}
}
/* If we didn't find a mapping, set the encoding to unknown */
ct->encoding = MM_ENCODING_UNKNOWN;
ct->encstring = NULL;
return 1;
}
/**
* Gets the numerical ID of a content encoding identifier
*
* @param ct A valid Content Type object
* @param encoding A string representing the content encoding identifier
* @return The numerical ID of the content encoding
*/
int
mm_content_getencoding(struct mm_content *ct, const char *encoding)
{
int i;
assert(ct != NULL);
for (i = 0; mm_content_enctypes[i].idstring != NULL; i++) {
if (!strcasecmp(mm_content_enctypes[i].idstring, encoding)) {
return mm_content_enctypes[i].type;
}
}
/* Not found */
return MM_ENCODING_UNKNOWN;
}
/**
* Constructs a MIME conform string of Content-Type parameters.
*
* @param ct A valid Content Type object
* @return A pointer to a string representing the Content-Type parameters
* in MIME terminology, or NULL if either the Content-Type object
* is invalid, has no parameters or no memory could be allocated.
*
* This function constructs a MIME conform string including all the parameters
* associated with the given Content-Type object. It should NOT be used if
* you need an opaque copy of the current MIME part (e.g. for PGP purposes).
*/
char *
mm_content_typeparamstostring(struct mm_content *ct)
{
size_t size, new_size;
struct mm_param *param;
char *param_string, *cur_param;
char *buf;
size = 1;
param_string = NULL;
cur_param = NULL;
param_string = (char *) xmalloc(size);
*param_string = '\0';
/* Concatenate all Content-Type parameters attached to the current
* Content-Type object to a single string.
*/
TAILQ_FOREACH(param, &ct->type_params, next) {
if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
param->value) == -1) {
goto cleanup;
}
new_size = size + strlen(cur_param) + 1;
if (new_size < 0 || new_size > 1000) {
size = 0;
goto cleanup;
}
buf = (char *) xrealloc(param_string, new_size);
if (buf == NULL) {
size = 0;
goto cleanup;
}
param_string = buf;
size = new_size;
strlcat(param_string, cur_param, size);
xfree(cur_param);
cur_param = NULL;
}
return param_string;
cleanup:
if (param_string != NULL) {
xfree(param_string);
param_string = NULL;
}
if (cur_param != NULL) {
xfree(cur_param);
cur_param = NULL;
}
return NULL;
}
/**
* Constructs a MIME conformant string of Content-Disposition parameters.
*
* @param ct A valid Content object
* @return A pointer to a string representing the Content-Disposition parameters
* in MIME terminology, or NULL if either the Content object
* is invalid, has no Disposition parameters or no memory could be allocated.
*
* This function constructs a MIME conforming string including all the parameters
* associated with the given Content-Disposition object. It should NOT be used if
* you need an opaque copy of the current MIME part (e.g. for PGP purposes).
*/
char *
mm_content_dispositionparamstostring(struct mm_content *ct)
{
size_t size, new_size;
struct mm_param *param;
char *param_string, *cur_param;
char *buf;
size = 1;
param_string = NULL;
cur_param = NULL;
param_string = (char *) xmalloc(size);
*param_string = '\0';
/* Concatenate all Content-Disposition parameters attached to the current
* Content object to a single string.
*/
TAILQ_FOREACH(param, &ct->disposition_params, next) {
if (asprintf(&cur_param, "; %s=\"%s\"", param->name,
param->value) == -1) {
goto cleanup;
}
new_size = size + strlen(cur_param) + 1;
if (new_size < 0 || new_size > 1000) {
size = 0;
goto cleanup;
}
buf = (char *) xrealloc(param_string, new_size);
if (buf == NULL) {
size = 0;
goto cleanup;
}
param_string = buf;
size = new_size;
strlcat(param_string, cur_param, size);
xfree(cur_param);
cur_param = NULL;
}
return param_string;
cleanup:
if (param_string != NULL) {
xfree(param_string);
param_string = NULL;
}
if (cur_param != NULL) {
xfree(cur_param);
cur_param = NULL;
}
return NULL;
}
/**
* Creates a Content-Type header according to the object given
*
* @param ct A valid Content-Type object
*
*/
char *
mm_content_tostring(struct mm_content *ct)
{
char *paramstring;
char *buf;
char *headerstring;
size_t size;
paramstring = NULL;
headerstring = NULL;
buf = NULL;
if (ct == NULL) {
return NULL;
}
if (ct->maintype == NULL || ct->subtype == NULL) {
return NULL;
}
size = strlen(ct->maintype) + strlen(ct->subtype) + 2;
headerstring = (char *)xmalloc(size);
snprintf(headerstring, size, "%s/%s", ct->maintype, ct->subtype);
paramstring = mm_content_typeparamstostring(ct);
if (paramstring == NULL) {
goto cleanup;
}
size += strlen(paramstring) + strlen("Content-Type: ") + 1;
buf = (char *)malloc(size);
if (buf == NULL) {
goto cleanup;
}
snprintf(buf, size, "Content-Type: %s%s", headerstring, paramstring);
xfree(headerstring);
xfree(paramstring);
headerstring = NULL;
paramstring = NULL;
return buf;
cleanup:
if (paramstring != NULL) {
xfree(paramstring);
paramstring = NULL;
}
if (headerstring != NULL) {
xfree(headerstring);
headerstring = NULL;
}
if (buf != NULL) {
xfree(buf);
buf = NULL;
}
return NULL;
}
/** @} */