asterisk/main/minimime/mm_mimepart.c

660 lines
16 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 <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <ctype.h>
#include <assert.h>
#include "mm_internal.h"
/** @file mm_mimepart.c
*
* This module contains functions for manipulating MIME header objects.
*/
/** @defgroup mimepart Accessing and manipulating MIME parts
*
* MIME parts, also called entities, represent the structure of a MIME
* message. ``Normal'' internet messages have only a single part, and
* are called ``flat'' messages. Multipart messages have more then one
* part, and each MIME part can have it's own subset of headers.
*
* Provided here are functions to easily access all informations from
* a MIME part, including their specific headers and bodies.
*/
/** @{
* @name Creating and destroying MIME parts
*/
/**
* Allocates memory for a new mm_mimepart structure and initializes it.
*
* @return A pointer to a struct of type mm_mimeheader or NULL on failure
* @see mm_mimepart_free
* @note The memory must be freed by using mm_mimepart_free() later on.
*/
struct mm_mimepart *
mm_mimepart_new(void)
{
struct mm_mimepart *part;
part = (struct mm_mimepart *)xmalloc(sizeof(struct mm_mimepart));
TAILQ_INIT(&part->headers);
part->opaque_length = 0;
part->opaque_body = NULL;
part->length = 0;
part->body = NULL;
part->type = NULL;
return part;
}
/**
* Creates a MIME part from a file
*
* @param filename The name of the file to create the MIME part from
* @return A pointer to a new MIME part object
*
* This function creates a new MIME part object from a file. The object should
* be freed using mm_mimepart_free() later on. This function does NOT set the
* Content-Type and neither does any encoding work.
*/
struct mm_mimepart *
mm_mimepart_fromfile(const char *filename)
{
int fd;
char *data;
size_t r;
struct stat st;
struct mm_mimepart *part;
mm_errno = MM_ERROR_NONE;
if ((fd = open(filename, O_RDONLY)) == -1) {
mm_errno = MM_ERROR_ERRNO;
return NULL;
}
if ((stat(filename, &st)) == -1) {
mm_errno = MM_ERROR_ERRNO;
close(fd);
return NULL;
}
data = xmalloc(st.st_size);
r = read(fd, data, st.st_size);
if (r != st.st_size) {
mm_errno = MM_ERROR_ERRNO;
close(fd);
return(NULL);
}
data[r] = '\0';
close(fd);
part = mm_mimepart_new();
part->length = r;
part->body = data;
return part;
}
/**
* Frees all memory allocated by a mm_mimepart object.
*
* @param part A pointer to an allocated mm_mimepart object
* @see mm_mimepart_new
*/
void
mm_mimepart_free(struct mm_mimepart *part)
{
struct mm_mimeheader *header;
assert(part != NULL);
TAILQ_FOREACH(header, &part->headers, next) {
mm_mimeheader_free(header);
TAILQ_REMOVE(&part->headers, header, next);
}
if (part->opaque_body != NULL) {
xfree(part->opaque_body);
part->opaque_body = NULL;
part->body = NULL;
} else if (part->body != NULL) {
xfree(part->body);
part->body = NULL;
}
if (part->type != NULL) {
mm_content_free(part->type);
part->type = NULL;
}
xfree(part);
part = NULL;
}
/** @} */
/** @{
* @name Accessing the MIME part's mail header
*/
/**
* Attaches a mm_mimeheader object to a MIME part
*
* @param part A valid MIME part object
* @param header A valid MIME header object
* @return 0 if successfull or -1 if the header could not be attached
*/
int
mm_mimepart_attachheader(struct mm_mimepart *part, struct mm_mimeheader *header)
{
assert(part != NULL);
assert(header != NULL);
if (TAILQ_EMPTY(&part->headers)) {
TAILQ_INSERT_HEAD(&part->headers, header, next);
} else {
TAILQ_INSERT_TAIL(&part->headers, header, next);
}
return(0);
}
/**
* Retrieves the number of MIME headers available in a MIME part
*
* @param part A valid MIME part object
* @return The number of MIME headers within the MIME part
*/
int
mm_mimepart_countheaders(struct mm_mimepart *part)
{
int found;
struct mm_mimeheader *header;
assert(part != NULL);
found = 0;
TAILQ_FOREACH(header, &part->headers, next) {
found++;
}
return found;
}
/**
* Retrieves the number of MIME headers with a given name in a MIME part
*
* @param part A valid MIME part object
* @param name The name of the MIME header which to count for
* @return The number of MIME headers within the MIME part
*/
int
mm_mimepart_countheaderbyname(struct mm_mimepart *part, const char *name)
{
int found;
struct mm_mimeheader *header;
assert(part != NULL);
found = 0;
TAILQ_FOREACH(header, &part->headers, next) {
if (strcasecmp(header->name, name) == 0) {
found++;
}
}
return found;
}
/**
* Get a MIME header object from a MIME part
*
* @param part A valid MIME part object
* @param name The name of the MIME header which to retrieve
* @param idx Which header field to get (in case of multiple headers of the
* same name).
* @return A pointer to the requested MIME header on success, or NULL if there
* either isn't a header with the requested name or idx is out of
* range.
*/
struct mm_mimeheader *
mm_mimepart_getheaderbyname(struct mm_mimepart *part, const char *name, int idx)
{
struct mm_mimeheader *header;
int curidx;
curidx = 0;
TAILQ_FOREACH(header, &part->headers, next) {
if (!strcasecmp(header->name, name)) {
if (curidx == idx)
return header;
else
curidx++;
}
}
/* Not found */
return NULL;
}
/**
* Gets the value of a MIME header object
*
* @param part A valid MIME part object
* @param name The name of the header field to get the value from
* @param idx The index of the header field to get, in case there are multiple
* headers with the same name.
* @return A pointer to the requested value on success, or NULL if there either
* isn't a header with the requested name or idx is out of range.
*
*/
const char *
mm_mimepart_getheadervalue(struct mm_mimepart *part, const char *name, int idx)
{
struct mm_mimeheader *header;
header = mm_mimepart_getheaderbyname(part, name, idx);
if (header == NULL)
return NULL;
else
return header->value;
}
/**
* Initializes a header loop for a given MIME part
*
* @param part A valid MIME part object
* @param id The address of a MIME header object (to allow reentrance)
* @return 0 on success or -1 on failure
* @see mm_mimepart_headers_next
*
* Looping through headers can be done in the following way:
*
* @code
* struct mm_mimeheader *header, *lheader;
*
* mm_mimepart_headers_start(part, &lheader);
*
* while ((header = mm_mimepart_headers_next(part, &lheader)) != NULL) {
* printf("%s: %s\n", header->name, header->value);
* }
*
* @endcode
*
* For convienience, the macro mm_mimepart_headers_foreach() can be used to
* loop through headers in a one-shot manner.
*/
int
mm_mimepart_headers_start(struct mm_mimepart *part, struct mm_mimeheader **id)
{
assert(part != NULL);
if (TAILQ_EMPTY(&part->headers)) {
return -1;
}
*id = NULL;
return 0;
}
/**
* Returns the next MIME header of a given MIME part object
*
* @param part A valid MIME part object
* @param id A previously initialized MIME header object
* @return A pointer to the MIME header object or NULL if end of headers was
* reached.
* @see mm_mimepart_headers_start
*/
struct mm_mimeheader *
mm_mimepart_headers_next(struct mm_mimepart *part, struct mm_mimeheader **id)
{
struct mm_mimeheader *header;
assert(part != NULL);
if (*id == NULL) {
header = TAILQ_FIRST(&part->headers);
} else {
header = TAILQ_NEXT(*id, next);
}
*id = header;
return header;
}
/** @} */
/** @{
* @name Accessing and manipulating the MIME part's body
*/
/**
* Gets the pointer to the MIME part's body data
*
* @param part A valid MIME part object
* @param opaque Whether to get the opaque part or not
* @return A pointer to the MIME part's body
* @see mm_mimepart_setbody
*
*/
char *
mm_mimepart_getbody(struct mm_mimepart *part, int opaque)
{
assert(part != NULL);
if (opaque)
return part->opaque_body;
else
return part->body;
}
/**
* Sets the MIME part's body data
*
* @param part A valid MIME part object
* @param data A pointer to the data which to set
* @see mm_mimepart_getbody
*
* This functions sets the body data for a given MIME part. The string pointed
* to by data must be NUL-terminated. The data is copied into the MIME part's
* body, and thus, the memory pointed to by data can be freed after the
* operation.
*/
#if 0
void
mm_mimepart_setbody(struct mm_mimepart *part, const char *data, int opaque)
{
assert(part != NULL);
assert(data != NULL);
if (opaque) {
part->opaque_body = xstrdup(data);
part->body = part->opaque_body;
} else {
part->body = xstrdup(data);
}
part->length = strlen(data);
}
#endif
/**
* Gets the length of a given MIME part object
*
* @param part A valid MIME part object
* @returns The size of the part's body in byte.
*
* This function returns the total length of the given MIME part's body. The
* length does not include the headers of the MIME parts. If the function
* returns 0, no body part is set currently.
*/
size_t
mm_mimepart_getlength(struct mm_mimepart *part)
{
assert(part != NULL);
return part->length;
}
/**
* Decodes a MIME part according to it's encoding using MiniMIME codecs
*
* @param A valid MIME part object
* @return 0 if the MIME part could be successfully decoded or -1 if not
* @note Sets mm_errno on error
*
* This function decodes the body of a MIME part with a registered decoder
* according to it's Content-Transfer-Encoding header field.
*/
char *
mm_mimepart_decode(struct mm_mimepart *part)
{
extern struct mm_codecs codecs;
struct mm_codec *codec;
void *decoded;
assert(part != NULL);
assert(part->type != NULL);
decoded = NULL;
/* No encoding associated */
if (part->type->encstring == NULL)
return NULL;
/* Loop through codecs and find a suitable one */
SLIST_FOREACH(codec, &codecs, next) {
if (!strcasecmp(part->type->encstring, codec->encoding)) {
decoded = codec->decoder((char *)part->body);
break;
}
}
return decoded;
}
/**
* Creates an ASCII representation of the given MIME part
*
* @param part A valid MIME part object
* @param result Where to store the result
* @param length Where to store the length of the result
* @param opaque Whether to use the opaque MIME part
* @returtn 0 on success or -1 on error.
* @see mm_context_flatten
*
* This function creates an ASCII representation of a given MIME part. It will
* dynamically allocate the memory needed and stores the result in the memory
* region pointed to by result. The length of the result will be stored in
* length. If opaque is set to 1, mm_mimepart_flatten will store an opaque
* version of the MIME part in result, which means no headers will be created
* or sanitized. This is particulary useful if the part is digitally signed by
* e.g. PGP, and the signature spans the header fields of the part in question.
*
*/
int
mm_mimepart_flatten(struct mm_mimepart *part, char **result, size_t *length,
int opaque)
{
size_t part_length;
char *buf;
char *ct_hdr;
*result = NULL;
*length = 0;
buf = NULL;
ct_hdr = NULL;
part_length = 0;
if (opaque && part->opaque_body != NULL) {
part_length = strlen(part->opaque_body);
*result = xstrdup(part->opaque_body);
*length = part_length;
return(0);
} else {
if (part->type == NULL) {
return(-1);
}
ct_hdr = mm_content_tostring(part->type);
if (ct_hdr == NULL) {
return(-1);
}
part_length += strlen(ct_hdr) + 2;
part_length += strlen("\r\n") * 2;
part_length += strlen(part->body);
if (part_length < 0) {
goto cleanup;
}
buf = (char *) xmalloc(part_length);
if (buf == NULL) {
goto cleanup;
}
snprintf(buf, part_length,
"%s\r\n\r\n%s\r\n",
ct_hdr,
part->body);
xfree(ct_hdr);
ct_hdr = NULL;
*result = buf;
*length = part_length;
}
return(0);
cleanup:
if (ct_hdr != NULL) {
xfree(ct_hdr);
ct_hdr = NULL;
}
if (buf != NULL) {
xfree(buf);
buf = NULL;
}
*result = NULL;
*length = 0;
return -1;
}
/**
* Sets the default Content-Type for a given MIME part
*
* @param part A valid MIME part object
* @param part Whether the Content-Type should be for composite or not
* @return 0 on success or -1 on failure
*
* This function sets a default Content-Type according to RFC 2045 with a value
* of "text/plain; charset="us-ascii"". This function should only be used if
* the MIME part in question does not have a valid Content-Type specification.
*/
int
mm_mimepart_setdefaultcontenttype(struct mm_mimepart *part, int composite)
{
struct mm_content *type;
struct mm_param *param;
if (part == NULL) {
return(-1);
}
if (part->type != NULL) {
mm_content_free(part->type);
part->type = NULL;
}
type = mm_content_new();
if (composite) {
type->maintype = xstrdup("multipart");
type->subtype = xstrdup("mixed");
} else {
type->maintype = xstrdup("text");
type->subtype = xstrdup("plain");
param = mm_param_new();
param->name = xstrdup("charset");
param->value = xstrdup("us-ascii");
mm_content_attachtypeparam(type, param);
}
mm_mimepart_attachcontenttype(part, type);
return (0);
}
/** @{
* @name Accessing the MIME part's Content-Type information
*/
/**
* Attaches a context type object to a MIME part
*
* @param part A valid MIME part object
* @param ct The content type object to attach
* @return Nothing
*
* This function attaches a Content-Type object to a MIME part. It does not
* care whether the Content-Type suites the actual content in the MIME part,
* so the programmer should take care of that.
*/
void
mm_mimepart_attachcontenttype(struct mm_mimepart *part, struct mm_content *ct)
{
part->type = ct;
}
/**
* Gets the Content-Type of a given MIME part object
*
* @param part A valid MIME part object
* @return The Content-Type object of the specified MIME part
*
* This function returns a pointer to the Content-Type object of the given
* MIME part. This pointer might be set to NULL, indicating that there is
* no Content-Type object for the given MIME part currently.
*/
struct mm_content *
mm_mimepart_getcontent(struct mm_mimepart *part)
{
assert(part != NULL);
return part->type;
}
/** @} */