/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 *  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.
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <config.h>
#include <gmime/gmime.h>

#include "testsuite.h"

extern int verbose;

#define v(x) if (verbose > 3) x

#if 0
static void
uputs (const char *str, FILE *out)
{
	register const unsigned char *s = (const unsigned char *) str;
	char buf[4096], *d = buf;
	
	while (*s != '\0') {
		if (!isascii ((int) *s)) {
			d += sprintf (d, "\\x%.2x", *s);
			if (isxdigit ((int) s[1])) {
				*d++ = '"';
				*d++ = ' ';
				*d++ = '"';
			}
		} else
			*d++ = (char) *s;
		s++;
	}
	
	*d = '\0';
	
	fputs (buf, out);
}
#endif

static struct {
	const char *input;
	const char *display;
	const char *encoded;
} addrspec[] = {
	{ "fejj@helixcode.com",
	  "fejj@helixcode.com",
	  "fejj@helixcode.com" },
	{ "Jeffrey Stedfast <fejj@helixcode.com>",
	  "Jeffrey Stedfast <fejj@helixcode.com>",
	  "Jeffrey Stedfast <fejj@helixcode.com>" },
	{ "Jeffrey \"fejj\" Stedfast <fejj@helixcode.com>",
	  "Jeffrey fejj Stedfast <fejj@helixcode.com>",
	  "Jeffrey fejj Stedfast <fejj@helixcode.com>" },
	{ "\"Stedfast, Jeffrey\" <fejj@helixcode.com>",
	  "\"Stedfast, Jeffrey\" <fejj@helixcode.com>",
	  "\"Stedfast, Jeffrey\" <fejj@helixcode.com>" },
	{ "fejj@helixcode.com (Jeffrey Stedfast)",
	  "Jeffrey Stedfast <fejj@helixcode.com>",
	  "Jeffrey Stedfast <fejj@helixcode.com>" },
	{ "Jeff <fejj(recursive (comment) block)@helixcode.(and a comment here)com>",
	  "Jeff <fejj@helixcode.com>",
	  "Jeff <fejj@helixcode.com>" },
	{ "=?iso-8859-1?q?Kristoffer_Br=E5nemyr?= <ztion@swipenet.se>",
	  "Kristoffer Br\xc3\xa5nemyr <ztion@swipenet.se>",
	  "=?iso-8859-1?q?Kristoffer_Br=E5nemyr?= <ztion@swipenet.se>" },
	{ "fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons)",
	  "Fran\xc3\xa7ois Pons <fpons@mandrakesoft.com>",
	  "=?iso-8859-1?q?Fran=E7ois?= Pons <fpons@mandrakesoft.com>" },
	{ "GNOME Hackers: miguel@gnome.org (Miguel de Icaza), Havoc Pennington <hp@redhat.com>;, fejj@helixcode.com",
	  "GNOME Hackers: Miguel de Icaza <miguel@gnome.org>, Havoc Pennington <hp@redhat.com>;, fejj@helixcode.com",
	  "GNOME Hackers: Miguel de Icaza <miguel@gnome.org>, Havoc Pennington <hp@redhat.com>;, fejj@helixcode.com" },
	{ "Local recipients: phil, joe, alex, bob",
	  "Local recipients: phil, joe, alex, bob;",
	  "Local recipients: phil, joe, alex, bob;" },
	{ "\":sysmail\"@  Some-Group. Some-Org,\n Muhammed.(I am  the greatest) Ali @(the)Vegas.WBA",
	  "\":sysmail\"@Some-Group.Some-Org, Muhammed.Ali@Vegas.WBA",
	  "\":sysmail\"@Some-Group.Some-Org, Muhammed.Ali@Vegas.WBA" },
	{ "Charles S. Kerr <charles@foo.com>",
	  "\"Charles S. Kerr\" <charles@foo.com>",
	  "\"Charles S. Kerr\" <charles@foo.com>" },
	{ "Charles \"Likes, to, put, commas, in, quoted, strings\" Kerr <charles@foo.com>",
	  "\"Charles Likes, to, put, commas, in, quoted, strings Kerr\" <charles@foo.com>",
	  "\"Charles Likes, to, put, commas, in, quoted, strings Kerr\" <charles@foo.com>" },
	{ "Charles Kerr, Pan Programmer <charles@superpimp.org>",
	  "\"Charles Kerr, Pan Programmer\" <charles@superpimp.org>",
	  "\"Charles Kerr, Pan Programmer\" <charles@superpimp.org>" },
	{ "Charles Kerr <charles@[127.0.0.1]>",
	  "Charles Kerr <charles@[127.0.0.1]>",
	  "Charles Kerr <charles@[127.0.0.1]>" },
	{ "Charles <charles@[127..0.1]>",
	  "Charles <charles@[127.0.1]>",
	  "Charles <charles@[127.0.1]>" },
	{ "Charles,, likes illegal commas <charles@superpimp.org>",
	  "Charles, likes illegal commas <charles@superpimp.org>",
	  "Charles, likes illegal commas <charles@superpimp.org>" },
	{ "<charles@>",
	  "charles",
	  "charles" },
	{ "<charles@broken.host.com.> (Charles Kerr)",
	  "Charles Kerr <charles@broken.host.com>",
	  "Charles Kerr <charles@broken.host.com>" },
	{ "fpons@mandrakesoft.com (=?iso-8859-1?q?Fran=E7ois?= Pons likes _'s and 	's too)",
	  "\"Fran\xc3\xa7ois Pons likes _'s and 	's too\" <fpons@mandrakesoft.com>",
	  "=?iso-8859-1?q?Fran=E7ois?= Pons likes _'s and 	's too <fpons@mandrakesoft.com>" },
	{ "T\x81\xf5ivo Leedj\x81\xe4rv <leedjarv@interest.ee>",
	  "T\xc2\x81\xc3\xb5ivo Leedj\xc2\x81\xc3\xa4rv <leedjarv@interest.ee>",
	  "=?iso-8859-1?b?VIH1aXZvIExlZWRqgeRydg==?= <leedjarv@interest.ee>" },
	{ "fbosi@mokabyte.it;, rspazzoli@mokabyte.it",
	  "fbosi@mokabyte.it, rspazzoli@mokabyte.it",
	  "fbosi@mokabyte.it, rspazzoli@mokabyte.it" },
	{ "\"Miles (Star Trekkin) O'Brian\" <mobrian@starfleet.org>",
	  "\"Miles (Star Trekkin) O'Brian\" <mobrian@starfleet.org>",
	  "\"Miles (Star Trekkin) O'Brian\" <mobrian@starfleet.org>" },
	{ "undisclosed-recipients: ;",
	  "undisclosed-recipients: ;",
	  "undisclosed-recipients: ;" },
	{ "undisclosed-recipients:;",
	  "undisclosed-recipients: ;",
	  "undisclosed-recipients: ;" },
	{ "undisclosed-recipients:",
	  "undisclosed-recipients: ;",
	  "undisclosed-recipients: ;" },
	{ "undisclosed-recipients",
	  "undisclosed-recipients",
	  "undisclosed-recipients" },
};

static void
test_addrspec (void)
{
	InternetAddressList *addrlist;
	char *str;
	guint i;
	
	for (i = 0; i < G_N_ELEMENTS (addrspec); i++) {
		addrlist = NULL;
		str = NULL;
		
		testsuite_check ("addrspec[%u]", i);
		try {
			if (!(addrlist = internet_address_parse_string (addrspec[i].input)))
				throw (exception_new ("could not parse addr-spec", i));
			
			str = internet_address_list_to_string (addrlist, FALSE);
			if (strcmp (addrspec[i].display, str) != 0)
				throw (exception_new ("display addr-spec does not match"));
			g_free (str);
			
			str = internet_address_list_to_string (addrlist, TRUE);
			if (strcmp (addrspec[i].encoded, str) != 0)
				throw (exception_new ("encoded addr-spec does not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("addrspec[%u]: %s", i, ex->message);
		} finally;
		
		g_free (str);
		if (addrlist)
			internet_address_list_destroy (addrlist);
	}
}


static struct {
	const char *in;
	const char *out;
	time_t date;
	int tzone;
} dates[] = {
	{ "Mon, 17 Jan 1994 11:14:55 -0500",
	  "Mon, 17 Jan 1994 11:14:55 -0500",
	  758823295, -500 },
	{ "Mon, 17 Jan 01 11:14:55 -0500",
	  "Wed, 17 Jan 2001 11:14:55 -0500",
	  979748095, -500 },
	{ "Tue, 30 Mar 2004 13:01:38 +0000",
	  "Tue, 30 Mar 2004 13:01:38 +0000",
	  1080651698, 0 },
	{ "Sat Mar 24 21:23:03 EDT 2007",
	  "Sat, 24 Mar 2007 21:23:03 -0400",
	  1174785783, -400 },
	{ "Sat, 24 Mar 2007 21:23:03 EDT",
	  "Sat, 24 Mar 2007 21:23:03 -0400",
	  1174785783, -400 },
	{ "Sat, 24 Mar 2007 21:23:03 GMT",
	  "Sat, 24 Mar 2007 21:23:03 +0000",
	  1174771383, 0 },
};

static void
test_date_parser (void)
{
	time_t date;
	int tzone;
	char *buf;
	guint i;
	
	for (i = 0; i < G_N_ELEMENTS (dates); i++) {
		buf = NULL;
		testsuite_check ("Date: '%s'", dates[i].in);
		try {
			date = g_mime_utils_header_decode_date (dates[i].in, &tzone);
			
			if (date != dates[i].date)
				throw (exception_new ("time_t's do not match"));
			
			if (tzone != dates[i].tzone)
				throw (exception_new ("timezones do not match"));
			
			buf = g_mime_utils_header_format_date (date, tzone);
			if (strcmp (dates[i].out, buf) != 0)
				throw (exception_new ("date strings do not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("Date: '%s': %s", dates[i].in,
						ex->message);
		} finally;
		
		g_free (buf);
	}
}

static struct {
	const char *input;
	const char *decoded;
	const char *encoded;
} rfc2047_text[] = {
#if 0
	{ "=?iso-8859-1?Q?blah?=:\t=?iso-8859-1?Q?I_am_broken?=",
	  "blah:\tI am broken",
	  "blah:\tI am broken" },
#endif
	{ "=?iso-8859-1?Q?Copy_of_Rapport_fra_Norges_R=E5fisklag=2Edoc?=",
	  "Copy of Rapport fra Norges R\xc3\xa5" "fisklag.doc",
	  "Copy =?iso-8859-1?q?of_Rapport_fra_Norges_R=E5fisklag=2Edoc?=" },
	{ "=?iso-8859-1?Q?Copy_of_Rapport_fra_Norges_R=E5fisklag.doc?=",
	  "Copy of Rapport fra Norges R\xc3\xa5" "fisklag.doc",
	  "Copy =?iso-8859-1?q?of_Rapport_fra_Norges_R=E5fisklag=2Edoc?=" },
	{ "=?iso-8859-1?B?dGVzdOb45S50eHQ=?=",
	  "test\xc3\xa6\xc3\xb8\xc3\xa5.txt",
	  "=?iso-8859-1?b?dGVzdOb45S50eHQ=?=" },
	{ "Re: !!! =?windows-1250?Q?Nab=EDz=EDm_scanov=E1n=ED_negativ=F9?= =?windows-1250?Q?=2C_p=F8edloh_do_A4=2C_=E8/b_lasertov=FD_ti?= =?windows-1250?Q?sk_a_=E8/b_inkoutov=FD_tisk_do_A2!!!?=",
	  "Re: !!! Nab\xc3\xadz\xc3\xadm scanov\xc3\xa1n\xc3\xad negativ\xc5\xaf, p\xc5\x99" "edloh do A4, \xc4\x8d/b lasertov\xc3\xbd tisk a \xc4\x8d/b inkoutov\xc3\xbd tisk do A2!!!",
	  "Re: =?iso-8859-2?q?!!!_Nab=EDz=EDm_scanov=E1n=ED_negativ=F9=2C_?= =?iso-8859-2?q?p=F8edloh_do_A4=2C_=E8=2Fb_lasertov=FD_?= =?iso-8859-2?q?tisk_a_=E8=2Fb_inkoutov=FD?= tisk do A2!!!" },
	{ "Re: =?iso-8859-2?q?!!!_Nab=EDz=EDm_scanov=E1n=ED_negativ=F9=2C_?= =?iso-8859-2?q?p=F8edloh_do_A4=2C_=E8=2Fb_lasertov=FD_?= =?iso-8859-2?q?tisk_a_=E8=2Fb_inkoutov=FD?= tisk do A2!!!",
	  "Re: !!! Nab\xc3\xadz\xc3\xadm scanov\xc3\xa1n\xc3\xad negativ\xc5\xaf, p\xc5\x99" "edloh do A4, \xc4\x8d/b lasertov\xc3\xbd tisk a \xc4\x8d/b inkoutov\xc3\xbd tisk do A2!!!",
	  "Re: =?iso-8859-2?q?!!!_Nab=EDz=EDm_scanov=E1n=ED_negativ=F9=2C_?= =?iso-8859-2?q?p=F8edloh_do_A4=2C_=E8=2Fb_lasertov=FD_?= =?iso-8859-2?q?tisk_a_=E8=2Fb_inkoutov=FD?= tisk do A2!!!" },
	{ "OT - ich =?iso-8859-1?b?d2Vp3yw=?= trotzdem",
	  "OT - ich wei\xc3\x9f, trotzdem",
	  "=?iso-8859-1?q?OT_-_ich_wei=DF=2C?= trotzdem" },
	{ "=?iso-8859-5?b?tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2trY=?=",
	  "\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96\xd0\x96",
	  "=?iso-8859-5?b?tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2trY=?= =?iso-8859-5?b?tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2trY=?= =?iso-8859-5?b?tra2tg==?=" },
#ifdef ENABLE_RFC2047_WORKAROUNDS
	{ "Copy of Rapport fra Norges R=?iso-8859-1?Q?=E5?=fisklag.doc",
	  "Copy of Rapport fra Norges R\xc3\xa5" "fisklag.doc",
	  "Copy =?iso-8859-1?q?of_Rapport_fra_Norges_R=E5fisklag=2Edoc?=" },
	{ "Copy of Rapport fra Norges =?iso-8859-1?Q?R=E5?=fisklag.doc",
	  "Copy of Rapport fra Norges R\xc3\xa5" "fisklag.doc",
	  "Copy =?iso-8859-1?q?of_Rapport_fra_Norges_R=E5fisklag=2Edoc?=" },
#endif /* ENABLE_RFC2047_WORKAROUNDS */
};

static struct {
	const char *input;
	const char *decoded;
	const char *encoded;
} rfc2047_phrase[] = {
	/* FIXME: add some phrase tests */
};

static void
test_rfc2047 (void)
{
	char *enc, *dec;
	guint i;
	
	for (i = 0; i < G_N_ELEMENTS (rfc2047_text); i++) {
		dec = enc = NULL;
		testsuite_check ("rfc2047_text[%u]", i);
		try {
			dec = g_mime_utils_header_decode_text (rfc2047_text[i].input);
			if (strcmp (rfc2047_text[i].decoded, dec) != 0)
				throw (exception_new ("decoded text does not match"));
			
			enc = g_mime_utils_header_encode_text (dec);
			if (strcmp (rfc2047_text[i].encoded, enc) != 0)
				throw (exception_new ("encoded text does not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("rfc2047_text[%u]: %s", i, ex->message);
		} finally;
		
		g_free (dec);
		g_free (enc);
	}
	
#if 0
	for (i = 0; i < G_N_ELEMENTS (rfc2047_phrase); i++) {
		dec = enc = NULL;
		testsuite_check ("rfc2047_phrase[%u]", i);
		try {
			dec = g_mime_utils_header_decode_phrase (rfc2047_phrase[i].input);
			if (strcmp (rfc2047_phrase[i].decoded, dec) != 0)
				throw (exception_new ("decoded phrase does not match"));
			
			enc = g_mime_utils_header_encode_phrase (dec);
			if (strcmp (rfc2047_phrase[i].encoded, enc) != 0)
				throw (exception_new ("encoded phrase does not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("rfc2047_phrase[%u]: %s", i, ex->message);
		} finally;
		
		g_free (dec);
		g_free (enc);
	}
#endif
}


static struct {
	const char *input;
	const char *encoded;
} rfc2184[] = {
	{ "this is a really really long filename that should force gmime to rfc2184 encode it - yay!.html",
	  "Content-Disposition: attachment;\n\t"
	  "filename*0*=iso-8859-1''this%20is%20a%20really%20really%20long%20filename;\n\t"
	  "filename*1*=%20that%20should%20force%20gmime%20to%20rfc2184%20encode%20it;\n\t"
	  "filename*2*=%20-%20yay!.html\n" },
};

static void
test_rfc2184 (void)
{
	GMimeParam param, *params;
	GString *str;
	size_t n;
	guint i;
	
	param.next = NULL;
	param.name = "filename";
	
	str = g_string_new ("Content-Disposition: attachment");
	n = str->len;
	
	for (i = 0; i < G_N_ELEMENTS (rfc2184); i++) {
		params = NULL;
		
		param.value = (char *) rfc2184[i].input;
		
		testsuite_check ("rfc2184[%u]", i);
		try {
			g_mime_param_write_to_string (&param, TRUE, str);
			if (strcmp (rfc2184[i].encoded, str->str) != 0)
				throw (exception_new ("encoded param does not match"));
			
			if (!(params = g_mime_param_new_from_string (str->str + n + 2)))
				throw (exception_new ("could not parse encoded param list"));
			
			if (params->next != NULL)
				throw (exception_new ("parsed more than a single param?"));
			
			if (strcmp (rfc2184[i].input, params->value) != 0)
				throw (exception_new ("parsed param value does not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("rfc2184[%u]: %s", i, ex->message);
		} finally;
		
		if (params != NULL)
			g_mime_param_destroy (params);
		
		g_string_truncate (str, n);
	}
	
	g_string_free (str, TRUE);
}


static struct {
	const char *input;
	const char *unquoted;
	const char *quoted;
} qstrings[] = {
	{ "this is a \\\"quoted\\\" string",
	  "this is a \"quoted\" string",
	  "this is a \"quoted\" string" },
	{ "\\\"this\\\" and \\\"that\\\"",
	  "\"this\" and \"that\"",
	  "\"this\" and \"that\"" },
	{ "\"Dr. A. Cula\"",
	  "Dr. A. Cula",
	  "\"Dr. A. Cula\"" },
};

static void
test_qstring (void)
{
	char *str;
	guint i;
	
	for (i = 0; i < G_N_ELEMENTS (qstrings); i++) {
		str = NULL;
		
		testsuite_check ("qstrings[%u]", i);
		try {
			str = g_strdup (qstrings[i].input);
			g_mime_utils_unquote_string (str);
			if (strcmp (qstrings[i].unquoted, str) != 0)
				throw (exception_new ("unquoted string does not match"));
			
			g_free (str);
			str = g_mime_utils_quote_string (qstrings[i].unquoted);
			if (strcmp (qstrings[i].quoted, str) != 0)
				throw (exception_new ("quoted string does not match"));
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("qstrings[%u]: %s", i, ex->message);
		} finally;
		
		g_free (str);
	}
}


int main (int argc, char **argv)
{
	g_mime_init (0);
	
	testsuite_init (argc, argv);
	
	testsuite_start ("addr-spec parser");
	test_addrspec ();
	testsuite_end ();
	
	testsuite_start ("date parser");
	test_date_parser ();
	testsuite_end ();
	
	testsuite_start ("rfc2047 encoding/decoding");
	test_rfc2047 ();
	testsuite_end ();
	
	testsuite_start ("rfc2184 encoding/decoding");
	test_rfc2184 ();
	testsuite_end ();
	
	testsuite_start ("quoted-strings");
	test_qstring ();
	testsuite_end ();
	
	g_mime_shutdown ();
	
	return testsuite_exit ();
}


syntax highlighted by Code2HTML, v. 0.9.1