/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/* GMime
* Copyright (C) 2000-2007 Jeffrey Stedfast
*
* 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <time.h>
#include <config.h>
#include <gmime/gmime.h>
#include "testsuite.h"
extern int verbose;
#define d(x)
#define v(x) if (verbose > 3) x
#define INDENT " "
static void
print_depth (GMimeStream *stream, int depth)
{
int i;
for (i = 0; i < depth; i++)
g_mime_stream_write (stream, INDENT, strlen (INDENT));
}
static void
print_mime_struct (GMimeStream *stream, GMimeObject *part, int depth)
{
const GMimeContentType *type;
print_depth (stream, depth);
type = g_mime_object_get_content_type (part);
g_mime_stream_printf (stream, "Content-Type: %s/%s\n", type->type, type->subtype);
if (GMIME_IS_MULTIPART (part)) {
GList *subpart;
subpart = GMIME_MULTIPART (part)->subparts;
while (subpart) {
print_mime_struct (stream, subpart->data, depth + 1);
subpart = subpart->next;
}
} else if (GMIME_IS_MESSAGE_PART (part)) {
GMimeMessagePart *mpart = (GMimeMessagePart *) part;
if (mpart->message)
print_mime_struct (stream, mpart->message->mime_part, depth + 1);
}
}
static void
header_cb (GMimeParser *parser, const char *header, const char *value, off_t offset, gpointer user_data)
{
GMimeStream *stream = user_data;
g_mime_stream_printf (stream, OFF_T ": %s: %s\n", offset, header, value);
}
static void
test_parser (GMimeParser *parser, GMimeStream *stream)
{
GMimeMessage *message;
off_t start, end;
int nmsg = 0;
char *from;
while (!g_mime_parser_eos (parser)) {
start = g_mime_parser_tell (parser);
if (!(message = g_mime_parser_construct_message (parser)))
throw (exception_new ("failed to parse message #%d", nmsg));
end = g_mime_parser_tell (parser);
g_mime_stream_printf (stream, "message offsets: " OFF_T ", " OFF_T "\n", start, end);
from = g_mime_parser_get_from (parser);
g_mime_stream_printf (stream, "%s\n", from);
g_free (from);
print_mime_struct (stream, message->mime_part, 0);
g_mime_stream_write (stream, "\n", 1);
g_object_unref (message);
nmsg++;
}
}
static gboolean
streams_match (GMimeStream *istream, GMimeStream *ostream)
{
char buf[4096], dbuf[4096], errstr[1024];
size_t totalsize, totalread = 0;
size_t nread, size;
ssize_t n;
v(fprintf (stdout, "Checking if streams match... "));
if (istream->bound_end != -1) {
totalsize = istream->bound_end - istream->position;
} else if ((n = g_mime_stream_length (istream)) == -1) {
sprintf (errstr, "Error: Unable to get length of original stream\n");
goto fail;
} else if (n < (istream->position - istream->bound_start)) {
sprintf (errstr, "Error: Overflow on original stream?\n");
goto fail;
} else {
totalsize = n - (istream->position - istream->bound_start);
}
while (totalread < totalsize) {
if ((n = g_mime_stream_read (istream, buf, sizeof (buf))) <= 0)
break;
size = n;
nread = 0;
totalread += n;
d(fprintf (stderr, "read " SIZE_T " bytes from istream\n", size));
do {
if ((n = g_mime_stream_read (ostream, dbuf + nread, size - nread)) <= 0) {
fprintf (stderr, "ostream's read() returned " SSIZE_T ", EOF\n", n);
break;
}
d(fprintf (stderr, "read " SSIZE_T " bytes from ostream\n", n));
nread += n;
} while (nread < size);
if (nread < size) {
sprintf (errstr, "Error: ostream appears to be truncated, short %u+ bytes\n",
size - nread);
goto fail;
}
if (memcmp (buf, dbuf, size) != 0) {
strcpy (errstr, "Error: content does not match\n");
goto fail;
} else {
d(fprintf (stderr, SIZE_T " bytes identical\n", size));
}
}
if (totalread < totalsize) {
strcpy (errstr, "Error: expected more data from istream\n");
goto fail;
}
if ((n = g_mime_stream_read (ostream, buf, sizeof (buf))) > 0) {
strcpy (errstr, "Error: ostream appears to contain extra content\n");
goto fail;
}
v(fputs ("passed\n", stdout));
return TRUE;
fail:
v(fputs ("failed\n", stdout));
v(fputs (errstr, stderr));
return FALSE;
}
int main (int argc, char **argv)
{
const char *datadir = "data/mbox";
char input[256], output[256], *p, *q;
GMimeStream *istream, *ostream;
GMimeParser *parser;
struct dirent *dent;
const char *path;
struct stat st;
int fd, i;
DIR *dir;
g_mime_init (0);
testsuite_init (argc, argv);
path = datadir;
for (i = 1; i < argc; i++) {
if (argv[i][0] != '-') {
path = argv[i];
break;
}
}
testsuite_start ("Mbox parser");
if (stat (path, &st) == -1)
goto exit;
if (S_ISDIR (st.st_mode)) {
/* automated testsuite */
p = g_stpcpy (input, path);
*p++ = G_DIR_SEPARATOR;
p = g_stpcpy (p, "input");
if (!(dir = opendir (input)))
goto exit;
*p++ = G_DIR_SEPARATOR;
*p = '\0';
q = g_stpcpy (output, path);
*q++ = G_DIR_SEPARATOR;
q = g_stpcpy (q, "output");
*q++ = G_DIR_SEPARATOR;
*q = '\0';
while ((dent = readdir (dir))) {
if (!g_str_has_suffix (dent->d_name, ".mbox"))
continue;
strcpy (p, dent->d_name);
strcpy (q, dent->d_name);
parser = NULL;
istream = NULL;
ostream = NULL;
testsuite_check ("%s", dent->d_name);
try {
if ((fd = open (input, O_RDONLY)) == -1) {
throw (exception_new ("could not open `%s': %s",
input, strerror (errno)));
}
istream = g_mime_stream_fs_new (fd);
if ((fd = open (output, O_RDONLY)) == -1) {
throw (exception_new ("could not open `%s': %s",
output, strerror (errno)));
}
ostream = g_mime_stream_fs_new (fd);
parser = g_mime_parser_new_with_stream (istream);
g_mime_parser_set_persist_stream (parser, TRUE);
g_mime_parser_set_scan_from (parser, TRUE);
g_object_unref (istream);
if (strstr (dent->d_name, "content-length") != NULL)
g_mime_parser_set_respect_content_length (parser, TRUE);
istream = g_mime_stream_mem_new ();
g_mime_parser_set_header_regex (parser, "^Subject$", header_cb, istream);
test_parser (parser, istream);
g_mime_stream_reset (istream);
if (!streams_match (istream, ostream))
throw (exception_new ("streams do not match for `%s'", dent->d_name));
testsuite_check_passed ();
} catch (ex) {
if (parser != NULL)
testsuite_check_failed ("%s: %s", dent->d_name, ex->message);
else
testsuite_check_warn ("%s: %s", dent->d_name, ex->message);
} finally;
if (istream != NULL)
g_object_unref (istream);
if (ostream != NULL)
g_object_unref (ostream);
if (parser != NULL)
g_object_unref (parser);
}
closedir (dir);
} else if (S_ISREG (st.st_mode)) {
/* manually run test on a single file */
if ((fd = open (path, O_RDONLY)) == -1)
goto exit;
istream = g_mime_stream_fs_new (fd);
parser = g_mime_parser_new_with_stream (istream);
g_mime_parser_set_scan_from (parser, TRUE);
g_object_unref (istream);
ostream = g_mime_stream_fs_new (dup (1));
g_mime_parser_set_header_regex (parser, "^Subject$", header_cb, ostream);
testsuite_check ("user-input mbox: `%s'", path);
try {
test_parser (parser, ostream);
testsuite_check_passed ();
} catch (ex) {
testsuite_check_failed ("user-input mbox `%s': %s", path, ex->message);
} finally;
g_object_unref (ostream);
} else {
goto exit;
}
exit:
testsuite_end ();
g_mime_shutdown ();
return testsuite_exit ();
}
syntax highlighted by Code2HTML, v. 0.9.1