/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* Authors: Jeffrey Stedfast <fejj@ximian.com>
*
* Copyright 2000-2002 Ximian, Inc. (www.ximian.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "strlib.h"
#include <ctype.h>
#include "gmime-parser.h"
#include "gmime-stream-mem.h"
#ifndef HAVE_ISBLANK
#define isblank(c) ((c) == ' ' || (c) == '\t')
#endif /* HAVE_ISBLANK */
#define d(x)
typedef struct _GMimeParser GMimeParser;
static GMimeParser *parser_new (GMimeStream *stream);
static void parser_destroy (GMimeParser *parser);
static void parser_init (GMimeParser *parser, GMimeStream *stream);
static void parser_close (GMimeParser *parser);
static GMimePart *parser_construct_leaf_part (GMimeParser *parser, GMimeContentType *content_type,
int *found);
static GMimePart *parser_construct_multipart (GMimeParser *parser, GMimeContentType *content_type,
int *found);
#define SCAN_BUF 4096 /* size of read buffer */
#define SCAN_HEAD 128 /* headroom guaranteed to be before each read buffer */
enum {
GMIME_PARSER_STATE_INIT,
GMIME_PARSER_STATE_FROM,
GMIME_PARSER_STATE_HEADERS,
GMIME_PARSER_STATE_HEADERS_END,
GMIME_PARSER_STATE_CONTENT,
};
struct _GMimeParser {
int state;
GMimeStream *stream;
off_t offset;
/* i/o buffers */
unsigned char realbuf[SCAN_HEAD + SCAN_BUF + 1];
unsigned char *inbuf;
unsigned char *inptr;
unsigned char *inend;
/* header buffer */
unsigned char *headerbuf;
unsigned char *headerptr;
unsigned int headerleft;
off_t headers_start;
off_t header_start;
unsigned int unstep:30;
unsigned int midline:1;
unsigned int seekable:1;
GMimeContentType *content_type;
struct _header_raw *headers;
struct _boundary_stack *bounds;
};
struct _boundary_stack {
struct _boundary_stack *parent;
unsigned char *boundary;
unsigned int boundarylen;
unsigned int boundarylenfinal;
unsigned int boundarylenmax;
};
static void
parser_push_boundary (GMimeParser *parser, const char *boundary)
{
struct _boundary_stack *s;
unsigned int max;
max = parser->bounds ? parser->bounds->boundarylenmax : 0;
s = g_new (struct _boundary_stack, 1);
s->parent = parser->bounds;
parser->bounds = s;
s->boundary = g_strdup_printf ("--%s--", boundary);
s->boundarylen = strlen (boundary) + 2;
s->boundarylenfinal = strlen (s->boundary);
s->boundarylenmax = MAX (s->boundarylenfinal, max);
}
static void
parser_pop_boundary (GMimeParser *parser)
{
struct _boundary_stack *s;
if (!parser->bounds) {
g_warning ("boundary stack underflow");
return;
}
s = parser->bounds;
parser->bounds = parser->bounds->parent;
g_free (s->boundary);
g_free (s);
}
struct _header_raw {
struct _header_raw *next;
char *name;
char *value;
off_t offset;
};
static const char *
header_raw_find (struct _header_raw *headers, const char *name, off_t *offset)
{
struct _header_raw *h;
h = headers;
while (h) {
if (!strcasecmp (h->name, name)) {
if (offset)
*offset = h->offset;
return h->value;
}
h = h->next;
}
return NULL;
}
static void
header_raw_clear (struct _header_raw **headers)
{
struct _header_raw *h, *n;
h = *headers;
while (h) {
n = h->next;
g_free (h->name);
g_free (h->value);
g_free (h);
h = n;
}
*headers = NULL;
}
static GMimeParser *
parser_new (GMimeStream *stream)
{
GMimeParser *parser;
parser = g_new (GMimeParser, 1);
parser_init (parser, stream);
return parser;
}
static void
parser_destroy (GMimeParser *parser)
{
if (parser) {
parser_close (parser);
g_free (parser);
}
}
static void
parser_init (GMimeParser *parser, GMimeStream *stream)
{
off_t offset = -1;
if (stream) {
g_mime_stream_ref (stream);
offset = g_mime_stream_tell (stream);
}
parser->state = GMIME_PARSER_STATE_INIT;
parser->stream = stream;
parser->offset = offset;
parser->inbuf = parser->realbuf + SCAN_HEAD;
parser->inptr = parser->inbuf;
parser->inend = parser->inbuf;
parser->headerbuf = g_malloc (SCAN_HEAD + 1);
parser->headerptr = parser->headerbuf;
parser->headerleft = SCAN_HEAD;
parser->headers_start = -1;
parser->header_start = -1;
parser->unstep = 0;
parser->midline = FALSE;
parser->seekable = offset != -1;
parser->headers = NULL;
parser->bounds = NULL;
}
static void
parser_close (GMimeParser *parser)
{
if (parser->stream)
g_mime_stream_unref (parser->stream);
if (parser->headerbuf)
g_free (parser->headerbuf);
header_raw_clear (&parser->headers);
while (parser->bounds)
parser_pop_boundary (parser);
}
static void
parser_unstep (GMimeParser *parser)
{
parser->unstep++;
}
static ssize_t
parser_fill (GMimeParser *parser)
{
unsigned char *inbuf, *inptr, *inend;
size_t inlen, atleast = SCAN_HEAD;
ssize_t nread;
inbuf = parser->inbuf;
inptr = parser->inptr;
inend = parser->inend;
inlen = inend - inptr;
atleast = MAX (atleast, parser->bounds ? parser->bounds->boundarylenmax : 0);
if (inlen > atleast)
return inlen;
inbuf = parser->realbuf;
memmove (inbuf, inptr, inlen);
parser->inptr = inbuf;
/* start reading into inbuf */
inbuf += inlen;
parser->inend = inbuf;
inend = parser->realbuf + SCAN_HEAD + SCAN_BUF - 1;
nread = g_mime_stream_read (parser->stream, inbuf, inend - inbuf);
if (nread > 0)
parser->inend += nread;
parser->offset = g_mime_stream_tell (parser->stream);
return parser->inend - parser->inptr;
}
static off_t
parser_offset (GMimeParser *parser, unsigned char *cur)
{
unsigned char *inptr = cur;
if (!inptr)
inptr = parser->inptr;
return (parser->offset - (parser->inend - inptr));
}
#define header_backup(parser, start, len) G_STMT_START { \
if (parser->headerleft <= len) { \
unsigned int hlen, hoff; \
\
hlen = hoff = parser->headerptr - parser->headerbuf; \
hlen = hlen ? hlen : 1; \
\
while (hlen < hoff + len) \
hlen <<= 1; \
\
parser->headerbuf = g_realloc (parser->headerbuf, hlen + 1); \
parser->headerptr = parser->headerbuf + hoff; \
parser->headerleft = hlen - hoff; \
} \
\
memcpy (parser->headerptr, start, len); \
parser->headerptr += len; \
parser->headerleft -= len; \
} G_STMT_END
#define header_parse(parser, hend) G_STMT_START { \
register unsigned char *colon; \
struct _header_raw *header; \
unsigned int hlen; \
\
header = g_new (struct _header_raw, 1); \
header->next = NULL; \
\
*parser->headerptr = '\0'; \
colon = parser->headerbuf; \
while (*colon && *colon != ':') \
colon++; \
\
hlen = colon - parser->headerbuf; \
\
header->name = g_strndup (parser->headerbuf, hlen); \
g_strstrip (header->name); \
if (*colon != ':') { \
g_warning ("Invalid header: %s", header->name); \
header->value = header->name; \
header->name = g_strdup ("X-Invalid-Header"); \
} else { \
header->value = g_strdup (colon + 1); \
g_strstrip (header->value); \
} \
header->offset = parser->header_start; \
\
hend->next = header; \
hend = header; \
\
parser->headerleft += parser->headerptr - parser->headerbuf; \
parser->headerptr = parser->headerbuf; \
} G_STMT_END
static int
parser_step_headers (GMimeParser *parser)
{
register unsigned char *inptr;
unsigned char *start, *inend;
struct _header_raw *hend;
size_t len = 0;
parser->midline = FALSE;
hend = (struct _header_raw *) &parser->headers;
parser->headers_start = parser_offset (parser, NULL);
parser->header_start = parser_offset (parser, NULL);
inptr = parser->inptr;
inend = parser->inend;
do {
refill:
if (parser_fill (parser) <= len)
break;
inptr = parser->inptr;
inend = parser->inend;
/* Note: see optimization comment [1] */
*inend = '\n';
g_assert (inptr <= inend);
while (inptr < inend) {
start = inptr;
/* Note: see optimization comment [1] */
while (*inptr != '\n')
inptr++;
if (inptr + 1 >= inend) {
/* we don't have enough data to tell if we
got all of the header or not... */
parser->inptr = start;
len = inend - start;
goto refill;
}
/* check to see if we've reached the end of the headers */
if (!parser->midline && inptr == start)
goto headers_end;
len = inptr - start;
header_backup (parser, start, len);
if (inptr < inend) {
/* inptr has to be less than inend - 1 */
inptr++;
if (*inptr == ' ' || *inptr == '\t') {
parser->midline = TRUE;
} else {
parser->midline = FALSE;
header_parse (parser, hend);
parser->header_start = parser_offset (parser, inptr);
}
} else {
parser->midline = TRUE;
}
}
parser->inptr = inptr;
len = inend - inptr;
} while (1);
inptr = parser->inptr;
inend = parser->inend;
header_backup (parser, inptr, inend - inptr);
/*header_parse (parser, hend);*/
headers_end:
if (parser->headerptr > parser->headerbuf)
header_parse (parser, hend);
parser->state = GMIME_PARSER_STATE_HEADERS_END;
g_assert (inptr <= parser->inend);
parser->inptr = inptr;
return 0;
}
static GMimeContentType *
parser_content_type (GMimeParser *parser)
{
const char *content_type;
content_type = header_raw_find (parser->headers, "Content-Type", NULL);
if (content_type)
return g_mime_content_type_new_from_string (content_type);
return NULL;
}
static int
parser_step (GMimeParser *parser)
{
if (!parser->unstep) {
switch (parser->state) {
case GMIME_PARSER_STATE_INIT:
parser->state = GMIME_PARSER_STATE_HEADERS;
case GMIME_PARSER_STATE_HEADERS:
parser_step_headers (parser);
break;
default:
g_assert_not_reached ();
break;
}
} else {
parser->unstep--;
}
return parser->state;
}
static void
parser_skip_line (GMimeParser *parser)
{
register unsigned char *inptr;
unsigned char *inend;
inptr = parser->inptr;
do {
if (parser_fill (parser) <= 0) {
inptr = parser->inptr;
break;
}
inptr = parser->inptr;
inend = parser->inend;
*inend = '\n';
while (*inptr != '\n')
inptr++;
if (inptr < inend)
break;
parser->inptr = inptr;
} while (1);
parser->midline = FALSE;
parser->inptr = MIN (inptr + 1, parser->inend);
}
enum {
FOUND_EOS,
FOUND_BOUNDARY,
FOUND_END_BOUNDARY
};
#define content_save(content, start, len) G_STMT_START { \
if (content) \
g_byte_array_append (content, start, len); \
} G_STMT_END
#define possible_boundary(start, len) (len >= 2 && (start[0] == '-' && start[1] == '-'))
/* Optimization Notes:
*
* 1. By making the parser->realbuf char array 1 extra char longer, we
* can safely set '*inend' to '\n' and not fear an ABW. Setting *inend
* to '\n' means that we can eliminate having to check that inptr <
* inend every trip through our inner while-loop. This cuts the number
* of instructions down from ~7 to ~4, assuming the compiler does its
* job correctly ;-)
**/
static int
parser_scan_content (GMimeParser *parser, GByteArray *content)
{
register unsigned char *inptr;
unsigned char *start, *inend;
gboolean found_eos = FALSE;
size_t nleft, len;
int found;
d(printf ("scan-content\n"));
parser->midline = FALSE;
g_assert (parser->inptr <= parser->inend);
inptr = parser->inptr;
do {
refill:
nleft = parser->inend - inptr;
if (parser_fill (parser) <= 0) {
start = parser->inptr;
found = FOUND_EOS;
break;
}
inptr = parser->inptr;
inend = parser->inend;
/* Note: see optimization comment [1] */
*inend = '\n';
if (inend - inptr == nleft)
found_eos = TRUE;
while (inptr < inend) {
start = inptr;
/* Note: see optimization comment [1] */
while (*inptr != '\n')
inptr++;
len = inptr - start;
if (inptr < inend) {
inptr++;
if (possible_boundary (start, len)) {
struct _boundary_stack *s;
d(printf ("checking boundary '%.*s'\n", len, start));
s = parser->bounds;
while (s) {
if (len == s->boundarylenfinal &&
!strncmp (s->boundary, start,
s->boundarylenfinal)) {
found = FOUND_END_BOUNDARY;
goto boundary;
}
if (len == s->boundarylen &&
!strncmp (s->boundary, start,
s->boundarylen)) {
found = FOUND_BOUNDARY;
goto boundary;
}
s = s->parent;
}
d(printf ("'%.*s' not a boundary\n", len, start));
}
len++;
} else if (!found_eos) {
/* not enough to tell if we found a boundary */
parser->inptr = start;
goto refill;
}
content_save (content, start, len);
}
parser->inptr = inptr;
} while (1);
boundary:
/* don't chew up the boundary */
parser->inptr = start;
return found;
}
static void
parser_scan_mime_part_content (GMimeParser *parser, GMimePart *mime_part, int *found)
{
GMimePartEncodingType encoding;
GByteArray *content = NULL;
GMimeDataWrapper *wrapper;
GMimeStream *stream;
off_t start, end;
if (parser->seekable)
start = parser_offset (parser, NULL);
else
content = g_byte_array_new ();
*found = parser_scan_content (parser, content);
if (*found != FOUND_EOS) {
/* last '\n' belongs to the boundary */
if (parser->seekable)
end = parser_offset (parser, NULL) - 1;
else
g_byte_array_set_size (content, MAX (content->len - 1, 0));
} else if (parser->seekable) {
end = parser_offset (parser, NULL);
}
encoding = g_mime_part_get_encoding (mime_part);
if (parser->seekable) {
stream = g_mime_stream_substream (parser->stream, start, end);
} else {
stream = g_mime_stream_mem_new_with_byte_array (content);
}
wrapper = g_mime_data_wrapper_new_with_stream (stream, encoding);
g_mime_part_set_content_object (mime_part, wrapper);
g_mime_stream_unref (stream);
}
enum {
CONTENT_TYPE = 0,
CONTENT_TRANSFER_ENCODING,
CONTENT_DISPOSITION,
CONTENT_DESCRIPTION,
CONTENT_LOCATION,
CONTENT_MD5,
CONTENT_ID,
CONTENT_UNKNOWN
};
static char *content_headers[] = {
"Content-Type",
"Content-Transfer-Encoding",
"Content-Disposition",
"Content-Description",
"Content-Location",
"Content-Md5",
"Content-Id",
NULL
};
static void
mime_part_set_content_headers (GMimePart *mime_part, struct _header_raw *headers)
{
struct _header_raw *header;
header = headers;
while (header) {
GMimePartEncodingType encoding;
GMimeDisposition *disposition;
char *text;
int i;
for (i = 0; content_headers[i]; i++)
if (!strcasecmp (content_headers[i], header->name))
break;
g_strstrip (header->value);
switch (i) {
case CONTENT_DESCRIPTION:
text = g_mime_utils_8bit_header_decode (header->value);
g_strstrip (text);
g_mime_part_set_content_description (mime_part, text);
g_free (text);
break;
case CONTENT_LOCATION:
g_mime_part_set_content_location (mime_part, header->value);
break;
case CONTENT_MD5:
g_mime_part_set_content_md5 (mime_part, header->value);
break;
case CONTENT_ID:
g_mime_part_set_content_id (mime_part, header->value);
break;
case CONTENT_TRANSFER_ENCODING:
encoding = g_mime_part_encoding_from_string (header->value);
g_mime_part_set_encoding (mime_part, encoding);
break;
case CONTENT_TYPE:
/* no-op, this has already been decoded */
break;
case CONTENT_DISPOSITION:
disposition = g_mime_disposition_new (header->value);
g_mime_part_set_content_disposition_object (mime_part, disposition);
break;
default:
/* possibly save the header */
if (!strncasecmp ("Content-", header->name, 8))
g_mime_part_set_content_header (mime_part, header->name, header->value);
break;
}
header = header->next;
}
}
static GMimePart *
parser_construct_leaf_part (GMimeParser *parser, GMimeContentType *content_type, int *found)
{
GMimePart *mime_part;
/* get the headers */
while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
;
if (!content_type) {
content_type = parser_content_type (parser);
if (!content_type)
content_type = g_mime_content_type_new ("text", "plain");
}
mime_part = g_mime_part_new_with_type (content_type->type, content_type->subtype);
mime_part_set_content_headers (mime_part, parser->headers);
header_raw_clear (&parser->headers);
g_mime_part_set_content_type (mime_part, content_type);
/* skip empty line after headers */
parser_skip_line (parser);
parser_scan_mime_part_content (parser, mime_part, found);
return mime_part;
}
static int
parser_scan_multipart_face (GMimeParser *parser, GMimePart *multipart, gboolean preface)
{
GByteArray *buffer;
const char *face;
int len, found;
buffer = g_byte_array_new ();
found = parser_scan_content (parser, buffer);
/* last '\n' belongs to the boundary */
if (buffer->len) {
buffer->data[buffer->len - 1] = '\0';
face = buffer->data;
} else
face = NULL;
#if 0
/* FIXME: allow setting of these? */
if (preface)
g_mime_part_set_preface (multipart, face);
else
g_mime_part_set_postface (multipart, face);
#endif
g_byte_array_free (buffer, TRUE);
return found;
}
#define parser_scan_multipart_preface(parser, multipart) parser_scan_multipart_face (parser, multipart, TRUE)
#define parser_scan_multipart_postface(parser, multipart) parser_scan_multipart_face (parser, multipart, FALSE)
static int
parser_scan_multipart_subparts (GMimeParser *parser, GMimePart *multipart)
{
GMimeContentType *content_type;
struct _header_raw *header;
GMimePart *subpart;
int found;
do {
/* skip over the boundary marker */
parser_skip_line (parser);
/* get the headers */
parser_step_headers (parser);
content_type = parser_content_type (parser);
if (!content_type)
content_type = g_mime_content_type_new ("text", "plain");
parser_unstep (parser);
if (g_mime_content_type_is_type (content_type, "multipart", "*")) {
subpart = parser_construct_multipart (parser, content_type, &found);
} else {
subpart = parser_construct_leaf_part (parser, content_type, &found);
}
g_mime_part_add_subpart (multipart, subpart);
g_mime_object_unref (GMIME_OBJECT (subpart));
} while (found == FOUND_BOUNDARY);
return found;
}
static GMimePart *
parser_construct_multipart (GMimeParser *parser, GMimeContentType *content_type, int *found)
{
struct _header_raw *header;
GMimePart *multipart;
const char *boundary;
/* get the headers */
while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
;
multipart = g_mime_part_new_with_type (content_type->type, content_type->subtype);
mime_part_set_content_headers (multipart, parser->headers);
header_raw_clear (&parser->headers);
g_mime_part_set_content_type (multipart, content_type);
/* skip empty line after headers */
parser_skip_line (parser);
boundary = g_mime_content_type_get_parameter (content_type, "boundary");
if (boundary) {
parser_push_boundary (parser, boundary);
*found = parser_scan_multipart_preface (parser, multipart);
if (*found == FOUND_BOUNDARY)
*found = parser_scan_multipart_subparts (parser, multipart);
parser_pop_boundary (parser);
/* eat end boundary */
parser_skip_line (parser);
if (*found == FOUND_END_BOUNDARY)
*found = parser_scan_multipart_postface (parser, multipart);
} else {
g_warning ("multipart without boundary encountered");
/* this will scan everything into the preface */
*found = parser_scan_multipart_preface (parser, multipart);
}
return multipart;
}
static GMimePart *
parser_construct_part (GMimeParser *parser)
{
GMimeContentType *content_type;
GMimePart *part;
int found;
/* get the headers */
while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
;
content_type = parser_content_type (parser);
if (!content_type)
content_type = g_mime_content_type_new ("text", "plain");
parser_unstep (parser);
if (g_mime_content_type_is_type (content_type, "multipart", "*")) {
part = parser_construct_multipart (parser, content_type, &found);
} else {
part = parser_construct_leaf_part (parser, content_type, &found);
}
return part;
}
/**
* g_mime_parser_construct_part:
* @stream: raw MIME Part stream
*
* Constructs a GMimePart object based on @stream.
*
* Returns a GMimePart object based on the data.
**/
GMimePart *
g_mime_parser_construct_part (GMimeStream *stream)
{
GMimeParser *parser;
GMimePart *part;
g_return_val_if_fail (stream != NULL, NULL);
parser = parser_new (stream);
part = parser_construct_part (parser);
parser_destroy (parser);
return part;
}
static int
content_header (const char *field)
{
int i;
for (i = 0; content_headers[i]; i++)
if (!strcasecmp (field, content_headers[i]))
return i;
return -1;
}
enum {
HEADER_FROM = 0,
HEADER_REPLY_TO,
HEADER_TO,
HEADER_CC,
HEADER_BCC,
HEADER_SUBJECT,
HEADER_DATE,
HEADER_MESSAGE_ID,
HEADER_UNKNOWN
};
static char *message_headers[] = {
"From",
"Reply-To",
"To",
"Cc",
"Bcc",
"Subject",
"Date",
"Message-Id",
NULL
};
static gboolean
special_header (const char *header)
{
return (!strcasecmp (header, "MIME-Version") || content_header (header) != -1);
}
static GMimeMessage *
parser_construct_message (GMimeParser *parser)
{
GMimeContentType *content_type;
struct _header_raw *header;
GMimeMessage *message;
int i, offset, found;
GMimePart *part;
time_t date;
char *raw;
/* get the headers */
while (parser_step (parser) != GMIME_PARSER_STATE_HEADERS_END)
;
message = g_mime_message_new (FALSE);
header = parser->headers;
while (header) {
for (i = 0; message_headers[i]; i++)
if (!strcasecmp (message_headers[i], header->name))
break;
g_strstrip (header->value);
switch (i) {
case HEADER_FROM:
raw = g_mime_utils_8bit_header_decode (header->value);
g_mime_message_set_sender (message, raw);
g_free (raw);
break;
case HEADER_REPLY_TO:
raw = g_mime_utils_8bit_header_decode (header->value);
g_mime_message_set_reply_to (message, raw);
g_free (raw);
break;
case HEADER_TO:
g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_TO,
header->value);
break;
case HEADER_CC:
g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_CC,
header->value);
break;
case HEADER_BCC:
g_mime_message_add_recipients_from_string (message, GMIME_RECIPIENT_TYPE_BCC,
header->value);
break;
case HEADER_SUBJECT:
raw = g_mime_utils_8bit_header_decode (header->value);
g_mime_message_set_subject (message, raw);
g_free (raw);
break;
case HEADER_DATE:
date = g_mime_utils_header_decode_date (header->value, &offset);
g_mime_message_set_date (message, date, offset);
break;
case HEADER_MESSAGE_ID:
raw = g_mime_utils_8bit_header_decode (header->value);
g_mime_message_set_message_id (message, raw);
g_free (raw);
break;
case HEADER_UNKNOWN:
default:
/* save the raw header if it's not a mime-part header */
if (!special_header (header->name)) {
g_mime_message_add_header (message, header->name, header->value);
}
break;
}
header = header->next;
}
content_type = parser_content_type (parser);
if (!content_type)
content_type = g_mime_content_type_new ("text", "plain");
parser_unstep (parser);
if (content_type && g_mime_content_type_is_type (content_type, "multipart", "*")) {
part = parser_construct_multipart (parser, content_type, &found);
} else {
part = parser_construct_leaf_part (parser, content_type, &found);
}
g_mime_message_set_mime_part (message, part);
g_mime_object_unref (GMIME_OBJECT (part));
return message;
}
/**
* g_mime_parser_construct_message:
* @stream: an rfc0822 message stream
*
* Constructs a GMimeMessage object based on @stream.
*
* Returns a GMimeMessage object based on the rfc0822 message stream.
**/
GMimeMessage *
g_mime_parser_construct_message (GMimeStream *stream)
{
GMimeMessage *message;
GMimeParser *parser;
g_return_val_if_fail (stream != NULL, NULL);
parser = parser_new (stream);
message = parser_construct_message (parser);
parser_destroy (parser);
return message;
}
syntax highlighted by Code2HTML, v. 0.9.1