summaryrefslogtreecommitdiff
path: root/libmailutils/mime.c
diff options
context:
space:
mode:
Diffstat (limited to 'libmailutils/mime.c')
-rw-r--r--libmailutils/mime.c1071
1 files changed, 1071 insertions, 0 deletions
diff --git a/libmailutils/mime.c b/libmailutils/mime.c
new file mode 100644
index 000000000..2c923cc74
--- /dev/null
+++ b/libmailutils/mime.c
@@ -0,0 +1,1071 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 1999, 2000, 2001, 2003, 2004, 2007, 2009, 2010 Free
+ Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, write to the
+ Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef HAVE_STRINGS_H
+# include <strings.h>
+#endif
+
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/message.h>
+#include <mailutils/stream.h>
+#include <mailutils/body.h>
+#include <mailutils/header.h>
+#include <mailutils/errno.h>
+#include <mailutils/mutil.h>
+
+#include <mailutils/sys/mime.h>
+#include <mailutils/sys/stream.h>
+
+#ifndef TRUE
+#define TRUE (1)
+#define FALSE (0)
+#endif
+
+#define CT_MULTIPART_DIGEST "multipart/digest"
+#define CT_MULTIPART_DIGEST_LEN (sizeof (CT_MULTIPART_DIGEST) - 1)
+
+/* TODO:
+ * Need to prevent re-entry into mime lib, but allow non-blocking re-entry
+ * into lib.
+ */
+
+static int
+_mime_is_multipart_digest (mu_mime_t mime)
+{
+ if (mime->content_type)
+ return mu_c_strncasecmp (CT_MULTIPART_DIGEST, mime->content_type,
+ CT_MULTIPART_DIGEST_LEN) == 0;
+ return 0;
+}
+
+static int
+_mime_append_part (mu_mime_t mime, mu_message_t msg, int offset, int len, int lines)
+{
+ struct _mime_part *mime_part, **part_arr;
+ int ret;
+ size_t size;
+ mu_header_t hdr;
+
+ if ((mime_part = calloc (1, sizeof (*mime_part))) == NULL)
+ return ENOMEM;
+
+ if (mime->nmtp_parts >= mime->tparts)
+ {
+ if ((part_arr =
+ realloc (mime->mtp_parts,
+ (mime->tparts + 5) * sizeof (mime_part))) == NULL)
+ {
+ free (mime_part);
+ return ENOMEM;
+ }
+ mime->mtp_parts = part_arr;
+ mime->tparts += 5;
+ }
+ mime->mtp_parts[mime->nmtp_parts++] = mime_part;
+ if (msg == NULL)
+ {
+ if ((ret = mu_message_create (&(mime_part->msg), mime_part)) == 0)
+ {
+ if ((ret =
+ mu_header_create (&hdr, mime->header_buf,
+ mime->header_length)) != 0)
+ {
+ mu_message_destroy (&mime_part->msg, mime_part);
+ free (mime_part);
+ return ret;
+ }
+ mu_message_set_header (mime_part->msg, hdr, mime_part);
+ }
+ else
+ {
+ free (mime_part);
+ return ret;
+ }
+ mime->header_length = 0;
+ if ((ret =
+ mu_header_get_value (hdr, MU_HEADER_CONTENT_TYPE, NULL,
+ 0, &size)) != 0 || size == 0)
+ {
+ if (_mime_is_multipart_digest (mime))
+ mu_header_set_value (hdr,
+ MU_HEADER_CONTENT_TYPE, "message/rfc822", 0);
+ else
+ mu_header_set_value (hdr, MU_HEADER_CONTENT_TYPE, "text/plain",
+ 0);
+ }
+ mime_part->len = len;
+ mime_part->lines = lines;
+ mime_part->offset = offset;
+ }
+ else
+ {
+ mu_message_ref (msg);
+ mu_message_size (msg, &mime_part->len);
+ mu_message_lines (msg, &mime_part->lines);
+ if (mime->nmtp_parts > 1)
+ mime_part->offset = mime->mtp_parts[mime->nmtp_parts - 2]->len;
+ mime_part->msg = msg;
+ }
+ mime_part->mime = mime;
+ return 0;
+}
+
+#define _ISSPECIAL(c) ( \
+ ((c) == '(') || ((c) == ')') || ((c) == '<') || ((c) == '>') \
+ || ((c) == '@') || ((c) == ',') || ((c) == ';') || ((c) == ':') \
+ || ((c) == '\\') || ((c) == '.') || ((c) == '[') \
+ || ((c) == ']') )
+
+static void
+_mime_munge_content_header (char *field_body)
+{
+ char *p, *e, *str = field_body;
+ int quoted = 0;
+
+ mu_str_stripws (field_body);
+
+ if ((e = strchr (str, ';')) == NULL)
+ return;
+ while (*e == ';')
+ {
+ p = e;
+ e++;
+ while (*e && mu_isspace (*e)) /* remove space up to param */
+ e++;
+ memmove (p + 1, e, strlen (e) + 1);
+ e = p + 1;
+
+ while (*e && *e != '=') /* find end of value */
+ e++;
+ e = p = e + 1;
+ while (*e
+ && (quoted
+ || (!_ISSPECIAL (*e) && !mu_isspace (*e))))
+ {
+ if (*e == '\\')
+ { /* escaped */
+ memmove (e, e + 1, strlen (e));
+ }
+ else if (*e == '\"')
+ quoted = ~quoted;
+ e++;
+ }
+ }
+}
+
+static char *
+_mime_get_param (char *field_body, const char *param, int *len)
+{
+ char *str, *p, *v, *e;
+ int quoted = 0, was_quoted;
+
+ if (len == NULL || (str = field_body) == NULL)
+ return NULL;
+
+ p = strchr (str, ';');
+ while (p)
+ {
+ p++;
+ if ((v = strchr (p, '=')) == NULL)
+ break;
+ *len = 0;
+ v = e = v + 1;
+ was_quoted = 0;
+ while (*e
+ && (quoted
+ || (!_ISSPECIAL (*e) && !mu_isspace (*e))))
+ { /* skip pass value and calc len */
+ if (*e == '\"')
+ quoted = ~quoted, was_quoted = 1;
+ else
+ (*len)++;
+ e++;
+ }
+ if (mu_c_strncasecmp (p, param, strlen (param)))
+ { /* no match jump to next */
+ p = strchr (e, ';');
+ continue;
+ }
+ else
+ return was_quoted ? v + 1 : v; /* return unquoted value */
+ }
+ return NULL;
+}
+
+static int
+_mime_setup_buffers (mu_mime_t mime)
+{
+ if (mime->cur_buf == NULL
+ && (mime->cur_buf = malloc (mime->buf_size)) == NULL)
+ {
+ return ENOMEM;
+ }
+ if (mime->cur_line == NULL
+ && (mime->cur_line = calloc (mime->line_size, 1)) == NULL)
+ {
+ free (mime->cur_buf);
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static void
+_mime_append_header_line (mu_mime_t mime)
+{
+ if (mime->header_length + mime->line_ndx > mime->header_buf_size)
+ {
+ char *nhb;
+
+ if ((nhb =
+ realloc (mime->header_buf,
+ mime->header_length + mime->line_ndx + 128)) == NULL)
+ return;
+ mime->header_buf = nhb;
+ mime->header_buf_size = mime->header_length + mime->line_ndx + 128;
+ }
+ memcpy (mime->header_buf + mime->header_length, mime->cur_line,
+ mime->line_ndx);
+ mime->header_length += mime->line_ndx;
+}
+
+static int
+_mime_parse_mpart_message (mu_mime_t mime)
+{
+ char *cp, *cp2;
+ int blength, mb_length, mb_offset, mb_lines, ret;
+ size_t nbytes;
+
+ if (!(mime->flags & MIME_PARSER_ACTIVE))
+ {
+ char *boundary;
+ int len;
+
+ if ((ret = _mime_setup_buffers (mime)) != 0)
+ return ret;
+ if ((boundary =
+ _mime_get_param (mime->content_type, "boundary", &len)) == NULL)
+ return EINVAL;
+ if ((mime->boundary = calloc (1, len + 1)) == NULL)
+ return ENOMEM;
+ strncpy (mime->boundary, boundary, len);
+
+ mime->cur_offset = 0;
+ mime->line_ndx = 0;
+ mime->parser_state = MIME_STATE_SCAN_BOUNDARY;
+ mime->flags |= MIME_PARSER_ACTIVE;
+ }
+ mb_length = mime->body_length;
+ mb_offset = mime->body_offset;
+ mb_lines = mime->body_lines;
+ blength = strlen (mime->boundary);
+
+ mu_stream_seek (mime->stream, mime->cur_offset, MU_SEEK_SET, NULL);
+ while ((ret =
+ mu_stream_read (mime->stream, mime->cur_buf, mime->buf_size,
+ &nbytes)) == 0 && nbytes)
+ {
+ cp = mime->cur_buf;
+ while (nbytes)
+ {
+ mime->cur_line[mime->line_ndx] = *cp;
+ if (*cp == '\n')
+ {
+ switch (mime->parser_state)
+ {
+ case MIME_STATE_BEGIN_LINE:
+ mime->cur_line[0] = *cp;
+ mime->line_ndx = 0;
+ mime->parser_state = MIME_STATE_SCAN_BOUNDARY;
+ break;
+
+ case MIME_STATE_SCAN_BOUNDARY:
+ cp2 =
+ mime->cur_line[0] ==
+ '\n' ? mime->cur_line + 1 : mime->cur_line;
+ if (mime->line_ndx >= blength)
+ {
+ if ((!strncmp (cp2, "--", 2)
+ && !mu_c_strncasecmp (cp2 + 2, mime->boundary,
+ blength))
+ || !mu_c_strncasecmp (cp2, mime->boundary, blength))
+ {
+ mime->parser_state = MIME_STATE_HEADERS;
+ mime->flags &= ~MIME_PARSER_HAVE_CR;
+ mb_length = mime->cur_offset
+ - mb_offset
+ - mime->line_ndx;
+ if (mime->header_length)
+ /* this skips the preamble */
+ {
+ /* RFC 1521 [Page 30]:
+ NOTE: The CRLF preceding the encapsulation
+ line is conceptually attached to the boundary
+ so that it is possible to have a part that
+ does not end with a CRLF (line break). Body
+ parts that must be considered to end with line
+ breaks, therefore, must have two CRLFs
+ preceding the encapsulation line, the first
+ of which is part of the preceding body part,
+ and the second of which is part of the
+ encapsulation boundary. */
+
+ if (mb_lines)
+ /* to prevent negative values in case of a
+ malformed message */
+ mb_lines--;
+
+ _mime_append_part (mime, NULL,
+ mb_offset, mb_length,
+ mb_lines);
+ }
+
+ if ((&mime->cur_line[mime->line_ndx] - cp2 - 1 >
+ blength
+ && !strncmp (cp2 + blength + 2, "--", 2))
+ || (&mime->cur_line[mime->line_ndx] - cp2 - 1 ==
+ blength
+ && !strncmp (cp2 + blength, "--", 2)))
+ { /* last boundary */
+ mime->parser_state = MIME_STATE_BEGIN_LINE;
+ mime->header_length = 0;
+ }
+ else
+ mime->line_ndx = -1; /* headers parsing requires
+ empty line */
+ break;
+ }
+ }
+
+ if (mime->header_length)
+ mb_lines++;
+
+ mime->line_ndx = 0;
+ mime->cur_line[0] = *cp; /* stay in this state but
+ leave '\n' at begining */
+ break;
+
+ case MIME_STATE_HEADERS:
+ mime->line_ndx++;
+ _mime_append_header_line (mime);
+ if (mime->line_ndx == 1 || mime->cur_line[0] == '\r')
+ {
+ mime->parser_state = MIME_STATE_SCAN_BOUNDARY;
+ mb_offset = mime->cur_offset + 1;
+ mb_lines = 0;
+ }
+ mime->line_ndx = -1;
+ break;
+ }
+ }
+ mime->line_ndx++;
+ if (mime->line_ndx >= mime->line_size)
+ {
+ size_t newsize = mime->line_size + MIME_MAX_HDR_LEN;
+ char *p = realloc (mime->cur_line, newsize);
+ if (!p)
+ {
+ ret = ENOMEM;
+ break;
+ }
+ mime->cur_line = p;
+ mime->line_size = newsize;
+ }
+ mime->cur_offset++;
+ nbytes--;
+ cp++;
+ }
+ }
+ mime->body_lines = mb_lines;
+ mime->body_length = mb_length;
+ mime->body_offset = mb_offset;
+ if (ret != EAGAIN)
+ { /* finished cleanup */
+ if (mime->header_length) /* this skips the preamble */
+ _mime_append_part (mime, NULL, mb_offset, mb_length, mb_lines);
+ mime->flags &= ~MIME_PARSER_ACTIVE;
+ mime->body_offset = mime->body_length =
+ mime->header_length = mime->body_lines = 0;
+ }
+ return ret;
+}
+
+/*------ Mime message functions for READING a multipart message -----*/
+
+static int
+_mimepart_body_size (mu_body_t body, size_t *psize)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _mime_part *mime_part = mu_message_get_owner (msg);
+
+ if (mime_part == NULL)
+ return EINVAL;
+ if (psize)
+ *psize = mime_part->len;
+ return 0;
+}
+
+static int
+_mimepart_body_lines (mu_body_t body, size_t *plines)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ struct _mime_part *mime_part = mu_message_get_owner (msg);
+
+ if (mime_part == NULL)
+ return EINVAL;
+ if (plines)
+ *plines = mime_part->lines;
+ return 0;
+}
+
+/*------ Mime message/header functions for CREATING multipart message -----*/
+static int
+_mime_set_content_type (mu_mime_t mime)
+{
+ const char *content_type;
+ mu_header_t hdr = NULL;
+ size_t size;
+ int ret;
+
+ /* Delayed the creation of the header 'til they create the final message via
+ mu_mime_get_message() */
+ if (mime->hdrs == NULL)
+ return 0;
+ if (mime->nmtp_parts > 1)
+ {
+ char *cstr;
+
+ if (mime->flags & MIME_ADDED_MULTIPART_CT)
+ return 0;
+ if (mime->flags & MU_MIME_MULTIPART_MIXED)
+ content_type = "multipart/mixed; boundary=";
+ else
+ content_type = "multipart/alternative; boundary=";
+ if (mime->boundary == NULL)
+ {
+ char boundary[128];
+
+ snprintf (boundary, sizeof boundary, "%ld-%ld=:%ld",
+ (long) random (), (long) time (0), (long) getpid ());
+ if ((mime->boundary = strdup (boundary)) == NULL)
+ return ENOMEM;
+ }
+ size = strlen (content_type) + 2 + strlen (mime->boundary) + 1;
+ cstr = malloc (size);
+ if (!cstr)
+ return ENOMEM;
+ strcpy (cstr, content_type);
+ strcat (cstr, "\"");
+ strcat (cstr, mime->boundary);
+ strcat (cstr, "\"");
+ mime->flags |= MIME_ADDED_MULTIPART_CT;
+
+ ret = mu_header_set_value (mime->hdrs, MU_HEADER_CONTENT_TYPE, cstr, 1);
+ free (cstr);
+ }
+ else
+ {
+ if ((mime->flags & (MIME_ADDED_CT | MIME_ADDED_MULTIPART_CT))
+ == MIME_ADDED_CT)
+ return 0;
+ mime->flags &= ~MIME_ADDED_MULTIPART_CT;
+ if (mime->nmtp_parts)
+ mu_message_get_header (mime->mtp_parts[0]->msg, &hdr);
+
+ if (hdr == NULL
+ || mu_header_sget_value (hdr, MU_HEADER_CONTENT_TYPE,
+ &content_type))
+ content_type = "text/plain; charset=us-ascii";
+
+ ret = mu_header_set_value (mime->hdrs, MU_HEADER_CONTENT_TYPE,
+ content_type, 1);
+ if (ret)
+ return ret;
+
+ if (hdr)
+ {
+ const char *content_te;
+
+ /* if the only part contains a transfer-encoding
+ field, set it on the message header too */
+ if (mu_header_sget_value (hdr,
+ MU_HEADER_CONTENT_TRANSFER_ENCODING,
+ &content_te) == 0)
+ ret = mu_header_set_value (mime->hdrs,
+ MU_HEADER_CONTENT_TRANSFER_ENCODING,
+ content_te, 1);
+
+ if (ret == 0
+ && mu_header_sget_value (hdr,
+ MU_HEADER_CONTENT_DESCRIPTION,
+ &content_te) == 0)
+ ret = mu_header_set_value (mime->hdrs,
+ MU_HEADER_CONTENT_DESCRIPTION,
+ content_te, 1);
+
+ }
+ }
+ mime->flags |= MIME_ADDED_CT;
+ return ret;
+}
+
+
+static int
+_mime_part_size (mu_mime_t mime, size_t *psize)
+{
+ int i, ret;
+ size_t size, total = 0;
+
+ if (mime->nmtp_parts == 0)
+ return EINVAL;
+
+ if ((ret = _mime_set_content_type (mime)) != 0)
+ return ret;
+ for (i = 0; i < mime->nmtp_parts; i++)
+ {
+ mu_message_size (mime->mtp_parts[i]->msg, &size);
+ total += size;
+ if (mime->nmtp_parts > 1) /* boundary line */
+ total += strlen (mime->boundary) + 3;
+ }
+ if (mime->nmtp_parts > 1) /* ending boundary line */
+ total += 2;
+ *psize = total;
+ return 0;
+}
+
+
+struct _mime_body_stream
+{
+ struct _mu_stream stream;
+ mu_mime_t mime;
+};
+
+static int
+_mime_body_stream_size (mu_stream_t stream, mu_off_t *psize)
+{
+ struct _mime_body_stream *mstr = (struct _mime_body_stream *)stream;
+ mu_mime_t mime = mstr->mime;
+ size_t sz;
+ int rc = _mime_part_size (mime, &sz);
+ if (rc == 0)
+ *psize = sz;
+ return rc;
+}
+
+static void
+mime_reset_state (mu_mime_t mime)
+{ /* reset message */
+ mime->cur_offset = 0;
+ mime->cur_part = 0;
+ mime->part_offset = 0;
+
+ if (mime->nmtp_parts > 1)
+ mime->flags |= MIME_INSERT_BOUNDARY;
+}
+
+/* FIXME: The seek method is defective */
+static int
+_mime_body_stream_seek (mu_stream_t stream, mu_off_t off, mu_off_t *presult)
+{
+ struct _mime_body_stream *mstr = (struct _mime_body_stream *)stream;
+ mu_mime_t mime = mstr->mime;
+
+ if (off == 0)
+ mime_reset_state (mime);
+
+ if (off != mime->cur_offset)
+ return ESPIPE;
+ *presult = off;
+ return 0;
+}
+
+#define ADD_CHAR(buf, c, offset, buflen, total, nbytes) \
+ do \
+ { \
+ *(buf)++ = c; \
+ (offset)++; \
+ (total)++; \
+ if (--(buflen) == 0) \
+ { \
+ *(nbytes) = total; \
+ return 0; \
+ } \
+ } \
+ while (0)
+
+static int
+_mime_body_stream_read (mu_stream_t stream, char *buf, size_t buflen, size_t *nbytes)
+{
+ struct _mime_body_stream *mstr = (struct _mime_body_stream *)stream;
+ mu_mime_t mime = mstr->mime;
+ int ret = 0;
+ size_t total = 0;
+
+ if (mime->nmtp_parts == 0)
+ return EINVAL;
+
+ if ((ret = _mime_set_content_type (mime)) == 0)
+ {
+ do
+ {
+ size_t part_nbytes = 0;
+
+ if (buflen == 0)
+ break;
+ if (mime->nmtp_parts > 1)
+ {
+ size_t len;
+
+ if (mime->flags & MIME_INSERT_BOUNDARY)
+ {
+ if ((mime->flags & MIME_ADDING_BOUNDARY) == 0)
+ {
+ mime->boundary_len = strlen (mime->boundary);
+ mime->preamble = 2;
+ if (mime->cur_part == mime->nmtp_parts)
+ mime->postamble = 2;
+ mime->flags |= MIME_ADDING_BOUNDARY;
+ }
+ while (mime->preamble)
+ {
+ mime->preamble--;
+ ADD_CHAR (buf, '-', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ len = strlen (mime->boundary) - mime->boundary_len;
+ while (mime->boundary_len)
+ {
+ mime->boundary_len--;
+ ADD_CHAR (buf,
+ mime->boundary[len++],
+ mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ while (mime->postamble)
+ {
+ mime->postamble--;
+ ADD_CHAR (buf, '-', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+ mime->flags &=
+ ~(MIME_INSERT_BOUNDARY | MIME_ADDING_BOUNDARY);
+ mime->part_offset = 0;
+ ADD_CHAR (buf, '\n', mime->cur_offset, buflen,
+ total, nbytes);
+ }
+
+ if (!mime->part_stream)
+ {
+ if (mime->cur_part >= mime->nmtp_parts)
+ {
+ *nbytes = total;
+ return 0;
+ }
+ ret = mu_message_get_streamref (mime->mtp_parts[mime->cur_part]->msg,
+ &mime->part_stream);
+ }
+ }
+ else if (!mime->part_stream)
+ {
+ mu_body_t part_body;
+
+ if (mime->cur_part >= mime->nmtp_parts)
+ {
+ *nbytes = total;
+ return 0;
+ }
+ mu_message_get_body (mime->mtp_parts[mime->cur_part]->msg,
+ &part_body);
+ ret = mu_body_get_streamref (part_body, &mime->part_stream);
+ }
+ if (ret)
+ break;
+ ret = mu_stream_seek (mime->part_stream, mime->part_offset,
+ MU_SEEK_SET, NULL);
+ if (ret)
+ {
+ mu_stream_destroy (&mime->part_stream);
+ break;
+ }
+ while (buflen > 0 &&
+ (ret = mu_stream_read (mime->part_stream, buf, buflen,
+ &part_nbytes)) == 0)
+ {
+ if (part_nbytes)
+ {
+ mime->part_offset += part_nbytes;
+ mime->cur_offset += part_nbytes;
+ total += part_nbytes;
+ buflen -= part_nbytes;
+ buf += part_nbytes;
+ }
+ else
+ {
+ mu_stream_destroy (&mime->part_stream);
+ mime->flags |= MIME_INSERT_BOUNDARY;
+ mime->cur_part++;
+ ADD_CHAR (buf, '\n', mime->cur_offset, buflen,
+ total, nbytes);
+ break;
+ }
+ }
+ }
+ while (ret == 0 && mime->cur_part <= mime->nmtp_parts);
+ }
+ if (ret)
+ mu_stream_destroy (&mime->part_stream);
+
+ *nbytes = total;
+ return ret;
+}
+
+static int
+_mime_body_stream_ioctl (mu_stream_t stream, int code, void *arg)
+{
+ struct _mime_body_stream *mstr = (struct _mime_body_stream *)stream;
+ mu_mime_t mime = mstr->mime;
+ mu_stream_t msg_stream;
+ int rc;
+
+ switch (code)
+ {
+ case MU_IOCTL_GET_TRANSPORT:
+ if (!arg)
+ return EINVAL;
+
+ if (mime->nmtp_parts == 0 || mime->cur_offset == 0)
+ return EINVAL;
+ rc = mu_message_get_streamref (mime->mtp_parts[mime->cur_part]->msg,
+ &msg_stream);
+ if (rc)
+ break;
+ rc = mu_stream_ioctl (msg_stream, code, arg);
+ mu_stream_destroy (&msg_stream);
+ break;
+
+ default:
+ rc = ENOSYS;
+ }
+ return rc;
+}
+
+static int
+create_mime_body_stream (mu_stream_t *pstr, mu_mime_t mime)
+{
+ struct _mime_body_stream *sp =
+ (struct _mime_body_stream *)_mu_stream_create (sizeof (*sp),
+ MU_STREAM_READ | MU_STREAM_SEEK);
+ if (!sp)
+ return ENOMEM;
+ sp->stream.read = _mime_body_stream_read;
+ sp->stream.seek = _mime_body_stream_seek;
+ sp->stream.ctl = _mime_body_stream_ioctl;
+ sp->stream.size = _mime_body_stream_size;
+ sp->mime = mime;
+ mime_reset_state (mime);
+ *pstr = (mu_stream_t) sp;
+ return 0;
+}
+
+
+static int
+_mime_body_size (mu_body_t body, size_t *psize)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ mu_mime_t mime = mu_message_get_owner (msg);
+ return _mime_part_size (mime, psize);
+}
+
+static int
+_mime_body_lines (mu_body_t body, size_t *plines)
+{
+ mu_message_t msg = mu_body_get_owner (body);
+ mu_mime_t mime = mu_message_get_owner (msg);
+ int i, ret;
+ size_t lines;
+
+ if (mime->nmtp_parts == 0)
+ return EINVAL;
+
+ if ((ret = _mime_set_content_type (mime)) != 0)
+ return ret;
+ for (i = 0; i < mime->nmtp_parts; i++)
+ {
+ mu_message_lines (mime->mtp_parts[i]->msg, &lines);
+ plines += lines;
+ if (mime->nmtp_parts > 1) /* boundary line */
+ plines++;
+ }
+ return 0;
+}
+
+int
+mu_mime_create (mu_mime_t *pmime, mu_message_t msg, int flags)
+{
+ mu_mime_t mime = NULL;
+ int ret = 0;
+ size_t size;
+ mu_body_t body;
+
+ if (pmime == NULL)
+ return EINVAL;
+ *pmime = NULL;
+ if ((mime = calloc (1, sizeof (*mime))) == NULL)
+ return ENOMEM;
+ if (msg)
+ {
+ if ((ret = mu_message_get_header (msg, &mime->hdrs)) == 0)
+ {
+ if ((ret =
+ mu_header_get_value (mime->hdrs,
+ MU_HEADER_CONTENT_TYPE,
+ NULL, 0, &size)) == 0 && size)
+ {
+ if ((mime->content_type = malloc (size + 1)) == NULL)
+ ret = ENOMEM;
+ else if ((ret = mu_header_get_value (mime->hdrs,
+ MU_HEADER_CONTENT_TYPE,
+ mime->content_type,
+ size + 1, 0)) == 0)
+ _mime_munge_content_header (mime->content_type);
+ }
+ else
+ {
+ if (ret == MU_ERR_NOENT)
+ {
+ ret = 0;
+ if ((mime->content_type =
+ strdup ("text/plain; charset=us-ascii")) == NULL)
+ /* default as per spec. */
+ ret = ENOMEM;
+ }
+ }
+ if (ret == 0)
+ {
+ mime->msg = msg;
+ mime->buf_size = MIME_DFLT_BUF_SIZE;
+ mime->line_size = MIME_MAX_HDR_LEN;
+ mu_message_get_body (msg, &body);
+ mu_body_get_streamref (body, &mime->stream);
+ }
+ }
+ }
+ else
+ {
+ mime->flags |= MIME_NEW_MESSAGE | MU_MIME_MULTIPART_MIXED;
+ }
+ if (ret != 0)
+ {
+ if (mime->content_type)
+ free (mime->content_type);
+ free (mime);
+ }
+ else
+ {
+ mime->flags |= (flags & MIME_FLAG_MASK);
+ *pmime = mime;
+ }
+ return ret;
+}
+
+void
+mu_mime_destroy (mu_mime_t *pmime)
+{
+ mu_mime_t mime;
+ struct _mime_part *mime_part;
+ int i;
+
+ if (pmime && *pmime)
+ {
+ mime = *pmime;
+ if (mime->mtp_parts != NULL)
+ {
+ for (i = 0; i < mime->nmtp_parts; i++)
+ {
+ mime_part = mime->mtp_parts[i];
+ if (mime_part->msg && mime->flags & MIME_NEW_MESSAGE)
+ mu_message_unref (mime_part->msg);
+ else
+ mu_message_destroy (&mime_part->msg, mime_part);
+ free (mime_part);
+ }
+ free (mime->mtp_parts);
+ }
+ mu_stream_destroy (&mime->stream);
+ mu_stream_destroy (&mime->part_stream);
+ if (mime->msg && mime->flags & MIME_NEW_MESSAGE)
+ mu_message_destroy (&mime->msg, mime);
+ if (mime->content_type)
+ free (mime->content_type);
+ if (mime->cur_buf)
+ free (mime->cur_buf);
+ if (mime->cur_line)
+ free (mime->cur_line);
+ if (mime->boundary)
+ free (mime->boundary);
+ if (mime->header_buf)
+ free (mime->header_buf);
+ free (mime);
+ *pmime = NULL;
+ }
+}
+
+int
+mu_mime_get_part (mu_mime_t mime, size_t part, mu_message_t *msg)
+{
+ size_t nmtp_parts;
+ int ret = 0, flags = 0;
+ mu_stream_t stream;
+ mu_body_t body;
+ struct _mime_part *mime_part;
+
+ if ((ret = mu_mime_get_num_parts (mime, &nmtp_parts)) == 0)
+ {
+ if (part < 1 || part > nmtp_parts)
+ return MU_ERR_NOENT;
+ if (nmtp_parts == 1 && mime->mtp_parts == NULL)
+ *msg = mime->msg;
+ else
+ {
+ mime_part = mime->mtp_parts[part - 1];
+ if (!mime_part->body_created
+ && (ret = mu_body_create (&body, mime_part->msg)) == 0)
+ {
+ mu_body_set_size (body, _mimepart_body_size, mime_part->msg);
+ mu_body_set_lines (body, _mimepart_body_lines, mime_part->msg);
+ mu_stream_get_flags (mime->stream, &flags);
+ ret = mu_streamref_create_abridged (&stream, mime->stream,
+ mime_part->offset,
+ mime_part->offset +
+ mime_part->len - 1);
+ if (ret == 0)
+ {
+ mu_stream_set_flags (stream,
+ MU_STREAM_READ | (flags &
+ (MU_STREAM_SEEK
+ | MU_STREAM_NONBLOCK)));
+ mu_body_set_stream (body, stream, mime_part->msg);
+ mu_message_set_body (mime_part->msg, body, mime_part);
+ mime_part->body_created = 1;
+ }
+ }
+ *msg = mime_part->msg;
+ }
+ }
+ return ret;
+}
+
+int
+mu_mime_get_num_parts (mu_mime_t mime, size_t *nmtp_parts)
+{
+ int ret = 0;
+
+ if ((mime->nmtp_parts == 0 && !mime->boundary)
+ || mime->flags & MIME_PARSER_ACTIVE)
+ {
+ if (mu_mime_is_multipart (mime))
+ {
+ if ((ret = _mime_parse_mpart_message (mime)) != 0)
+ return (ret);
+ }
+ else
+ mime->nmtp_parts = 1;
+ }
+ *nmtp_parts = mime->nmtp_parts;
+ return (ret);
+
+}
+
+int
+mu_mime_add_part (mu_mime_t mime, mu_message_t msg)
+{
+ int ret;
+
+ if (mime == NULL || msg == NULL || (mime->flags & MIME_NEW_MESSAGE) == 0)
+ return EINVAL;
+ if ((ret = _mime_append_part (mime, msg, 0, 0, 0)) == 0)
+ ret = _mime_set_content_type (mime);
+ return ret;
+}
+
+int
+mu_mime_get_message (mu_mime_t mime, mu_message_t *msg)
+{
+ mu_stream_t body_stream;
+ mu_body_t body;
+ int ret = 0;
+
+ if (mime == NULL || msg == NULL)
+ return EINVAL;
+ if (mime->msg == NULL)
+ {
+ if ((mime->flags & MIME_NEW_MESSAGE) == 0)
+ return EINVAL;
+ if ((ret = mu_message_create (&mime->msg, mime)) == 0)
+ {
+ if ((ret = mu_header_create (&mime->hdrs, NULL, 0)) == 0)
+ {
+ mu_message_set_header (mime->msg, mime->hdrs, mime);
+ mu_header_set_value (mime->hdrs, MU_HEADER_MIME_VERSION, "1.0",
+ 0);
+ if ((ret = _mime_set_content_type (mime)) == 0)
+ {
+ if ((ret = mu_body_create (&body, mime->msg)) == 0)
+ {
+ mu_message_set_body (mime->msg, body, mime);
+ mu_body_set_size (body, _mime_body_size, mime->msg);
+ mu_body_set_lines (body, _mime_body_lines, mime->msg);
+ ret = create_mime_body_stream (&body_stream, mime);
+ if (ret == 0)
+ {
+ mu_body_set_stream (body, body_stream, mime->msg);
+ *msg = mime->msg;
+ return 0;
+ }
+ }
+ }
+ }
+ mu_message_destroy (&mime->msg, mime);
+ mime->msg = NULL;
+ }
+ }
+ if (ret == 0)
+ *msg = mime->msg;
+ return ret;
+}
+
+int
+mu_mime_is_multipart (mu_mime_t mime)
+{
+ if (mime->content_type)
+ return (mu_c_strncasecmp ("multipart", mime->content_type,
+ strlen ("multipart")) ? 0 : 1);
+ return 0;
+}

Return to:

Send suggestions and report system problems to the System administrator.