<commit_before>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*  GMime
 *  Copyright (C) 2000-2017 <NAME>
 *
 *  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 <gmime/gmime.h>
#include <gmime/gmime-autocrypt.h>

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

#include "testsuite.h"

extern int verbose;

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

struct _ah_gen_test {
	const char *addr;
	int keydatacount;
	char keybyte;
	gint64 timestamp;
	const char *txt;
};

static GMimeAutocryptHeader*
_gen_header (const struct _ah_gen_test *t)
{
	GMimeAutocryptHeader *ah = NULL;
	GBytes *keydata = NULL;
	if (t == NULL)
		return NULL;
	if (!(ah = g_mime_autocrypt_header_new())) {
		fprintf (stderr, "failed to make a new header");
		return NULL;
	}
	if (t->keydatacount)
		if (!(keydata = g_bytes_new_take ((guint8*) g_strnfill (t->keydatacount, t->keybyte), t->keydatacount))) {
			fprintf (stderr, "failed to make a new keydata");
			g_object_unref (ah);
			return NULL;
		}
	g_mime_autocrypt_header_set_address_from_string (ah, t->addr);
	g_mime_autocrypt_header_set_keydata (ah, keydata);
	if (t->timestamp) {
		GDateTime *ts = g_date_time_new_from_unix_utc (t->timestamp);
		g_mime_autocrypt_header_set_effective_date (ah, ts);
		g_date_time_unref (ts);
	}
	if (keydata)
		g_bytes_unref (keydata);
	return ah;
}

/* generates a header list based on a series with an addr=NULL sentinel value */
static GMimeAutocryptHeaderList*
_gen_header_list (const struct _ah_gen_test **tests)
{
	GMimeAutocryptHeaderList *ret = g_mime_autocrypt_header_list_new ();
	for (; *tests; tests++) {
		GMimeAutocryptHeader *ah = _gen_header (*tests);
		if (!ah) {
			fprintf (stderr, "failed to generate header <%s>", (*tests)->addr);
			g_object_unref (ret);
			return NULL;
		}
		g_mime_autocrypt_header_list_add (ret, ah);
		g_object_unref (ah);
	}
	return ret;
}

const static struct _ah_gen_test gen_test_data[] = {
	{ .addr = "<EMAIL>",
	  .keydatacount = 102,
	  .keybyte = '\013',
	  .txt = "addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL",
	}
};

const static struct _ah_gen_test * no_addrs[] = {
	NULL, /* sentinel */
};


static void
test_ah_generation (void)
{
	GMimeAutocryptHeader *ah = NULL;
	char *str = NULL;
	unsigned int i;
	for (i = 0; i < G_N_ELEMENTS(gen_test_data); i++) {
		try {
			testsuite_check ("Autocrypt header[%u]", i);
			const struct _ah_gen_test *test = gen_test_data + i;
			ah = _gen_header (test);

			str = g_mime_autocrypt_header_to_string (ah, FALSE);
			if (strcmp (test->txt, str)) {
				fprintf (stderr, "expected[%u]: \n%s\n\ngot:\n%s\n", i,
					 test->txt, str);
				throw (exception_new ("failed comparison"));
			}
			GMimeAutocryptHeader *ah2 = g_mime_autocrypt_header_new_from_string (str);
			gint cmp = g_mime_autocrypt_header_compare (ah, ah2);
			if (cmp) {
				char *x = g_mime_autocrypt_header_to_string (ah2, FALSE);
				fprintf (stderr, "after-rebuild[%u] (%d) \nexpected: \n%s\n\ngot:\n%s\n", i,
					 cmp, test->txt, x);
				g_free(x);
				throw (exception_new ("header from string did not match"));
			}
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("autocrypt header creation failed: %s", ex->message);
		} finally;
		if (ah)
			g_object_unref (ah);
		g_free (str);
		str = NULL;
	}
}


struct _ah_parse_test {
	const char *name;
	const struct _ah_gen_test *acheader;
	const struct _ah_gen_test **gossipheaders;
	const char *msg;
	const char *innerpart;
};


struct _ah_inject_test {
	const char *name;
	const struct _ah_gen_test *acheader;
	const struct _ah_gen_test **gossipheaders;
	const char **encrypt_to;
	const char *before;
	const char *after;
	const char *inner_after;
};

static const char * local_recipients[] =
	{
		"0x0D211DC5D9F4567271AC0582D8DECFBFC9346CD4",
		NULL,
	};

const static struct _ah_gen_test alice_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 102,
	  .timestamp = 1508774054,
	  .keybyte = '\013',
	};


const static struct _ah_gen_test alice_incomplete =
	{ .addr = "<EMAIL>",
	  .timestamp = 1508774054,
	};


const static struct _ah_gen_test bob_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 99,
	  .timestamp = 1508774054,
	  .keybyte = '\133' };

const static struct _ah_gen_test carol_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 108,
	  .timestamp = 1508774054,
	  .keybyte = '\131' };

const static struct _ah_gen_test bob_incomplete =
	{ .addr = "<EMAIL>",
	  .timestamp = 1508774054,
	};

const static struct _ah_gen_test *just_bob[] = {
	&bob_addr,
	NULL,
};

const static struct _ah_gen_test *bob_and_carol[] = {
	&bob_addr,
	&carol_addr,
	NULL,
};

const static struct _ah_inject_test inject_test_data[] = {
	{ .name = "simple",
	  .acheader = &alice_addr,
	  .before = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	  .after = "From: <EMAIL>\n"
	  "To: <EMAIL>\n"
	  "Subject: A lovely day\n"
	  "Message-Id: <<EMAIL>>\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\n"
	  "Mime-Version: 1.0\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
	  "Content-Type: text/plain\n"
	  "\n"
	  "Isn't it a lovely day?\n",
	},
	{ .name = "gossip injection",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .encrypt_to = local_recipients,
	  .before = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	  .inner_after = "Content-Type: text/plain\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
	  "\n"
	  "Isn't it a lovely day?\n",
	},
};

const static struct _ah_parse_test parse_test_data[] = {
	{ .name = "simple",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	},
	
	{ .name = "simple+onegossip",
	  .acheader = &alice_addr,
	  .gossipheaders = just_bob,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "\r\n"
	  "Isn't a lovely day?  Now Carol can encrypt to Bob, hopefully.\r\n",
	},
	
	{ .name = "simple+nogossip",
	  .acheader = &alice_addr,
	  .gossipheaders = no_addrs,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't a lovely day?  I sure hope Bob and Carol have each other's info\r\n"
	  "because otherwise they won't be able to Reply All.\r\n",
	},
	
	{ .name = "simple+fullgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  "\r\n"
	  "Isn't a lovely day?  Now Carol and Bob can now both Reply All, hopefully.\r\n",
	},
		
	{ .name = "actually encrypted, fullgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "hQGMA4xh3ftdkAY8AQv+NU4HDHKzqSk309FOoGCNfTIM16+LrT3TY+pwdQ+BHZNh\r\n"
	  "v62TfRrG3PFd46tvH3zInIHjow7Usb3Au+nz1fF0HgIkOg7IEGXUle0OuPgQt38i\r\n"
	  "J2B+7EhksG86aaGmlsCq7Y8v9QnBH/UsX95xSHOTpIgWDamdGed2nnqW0fdOtapK\r\n"
	  "QyfOWkmti8vUnzvDPxiEMLr2VW5UWtyJQiu6BwyEpme15KkmO0TJUNJ71N8cWKfD\r\n"
	  "+jK2qlQzlgKHeSy3cWmu6ejhkTqPOghxsgb6lGHNu4+/vHufZkZCKBOYrPq/6pLr\r\n"
	  "zySDS6p8+LsDf5WwbR3u1TENxUz1YfNDmFi0FcVRPgdbx6NsUe0EQgTudqMRJ7q4\r\n"
	  "<KEY>"
	  "PfKWNCC8dOw7ABcbMrATGnaDenoSr0mrQWR4S7UeNeJUyB3as4iaTkc9inOHeUvr\r\n"
	  "<KEY>"
	  "<KEY>"
	  "kGP9B6WeE53tFZKihq7fAgGKg8wOHKSlEKM42nI2V2+0XOqHySHgZbuS8gnhjG9O\r\n"
	  "Nc90XqYNWZUMDaUsSGeOvJzrpAM29kk9Vy2TdbWd3IvWsDMDtQRQcQfruAGiJCf9\r\n"
	  "mGH0HIKmGfHqMnIQZp+H/HOmNpEHPkEIVj5JT0XzHz/QXzuitsuV1ApGIu/lV7Ht\r\n"
	  "gdJzmTbrijjrinZE4kPsqNJQcQbuSw==\r\n"
	  "=/f+w\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	},
		
	{ .name = "simple+excessgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = just_bob,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  "\r\n"
	  "Recipients of this message should not accept carol's public key for gossip, since\r\n"
	  "the message was not addressed to her\r\n"
	},
	
	{ .name = "simple+badgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "There are at least two headers here which will be ignored.\r\n",
	},
	
	{ .name = "duplicate",
	  .acheader = &alice_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Duplicate Autocrypt headers should cause none to match?\r\n",
	},

	{ .name = "unrecognized critical attribute",
	  .acheader = &alice_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
	},

	{ .name = "unrecognized critical attribute + simple",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Unknown Autocrypt critical attribute should cause nothing to match but should not block a classic type header\r\n",
	},
	
	{ .name = "unrecognized non-critical attribute",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; _not_an_emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
	},

	{ .name = "no From: at all",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has no sender at all\r\n",
	},

	{ .name = "with Sender: header",
	  .acheader = &bob_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "Sender: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has an Autocrypt header that matches the Sender: attribute but not the From:\r\n",
	},

	{ .name = "no senders",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "From: undisclosed sender\r\n"
	  "To: <EMAIL>.org\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has no sender at all\r\n",
	},

	{ .name = "two senders",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>, <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "When Alice and Bob are both present, we should not update Autocrypt state at all\r\n",
	},
};




static void
test_ah_injection (void)
{
	unsigned int i;
	for (i = 0; i < G_N_ELEMENTS(inject_test_data); i++) {
		GMimeAutocryptHeader *ah = NULL;
		char *str = NULL;
		GMimeStream *stream = NULL;
		GMimeParser *parser = NULL;
		GMimeMessage *before = NULL;
		GMimeStream *afterstream = NULL;
		GByteArray *after = NULL;
		GMimeAutocryptHeaderList *ahl = NULL;
		GMimeCryptoContext *ctx = NULL;
		GPtrArray *recip = NULL;
		GMimeMultipartEncrypted *encrypted = NULL;
		GMimeObject *cleartext = NULL;
		GMimeStream *innerafterstream = NULL;
		GByteArray *innerafter = NULL;
		GError *err = NULL;
		int r, ix;
		
		try {
			const struct _ah_inject_test *test = inject_test_data + i;
			testsuite_check ("Autocrypt injection[%u] (%s)", i, test->name);
			
			stream = g_mime_stream_mem_new_with_buffer (test->before, strlen(test->before));
			parser = g_mime_parser_new_with_stream (stream);
			before = g_mime_parser_construct_message (parser, NULL);

			if (test->acheader) {
				ah = _gen_header (test->acheader);
			        g_mime_object_set_header (GMIME_OBJECT (before), "Autocrypt",
						         g_mime_autocrypt_header_to_string (ah, FALSE), NULL);
			}

			if (test->encrypt_to) {
				ctx = g_mime_gpg_context_new ();
				recip = g_ptr_array_new ();
				for (r = 0; test->encrypt_to[r]; r++)
					g_ptr_array_add (recip, (gpointer)(test->encrypt_to[r]));
				/* get_mime_part is "transfer none" so mainpart does not need to be cleaned up */
				GMimeObject *mainpart = g_mime_message_get_mime_part (before);
				if (!mainpart) {
					throw (exception_new ("failed to find main part!\n"));
				}
				if (test->gossipheaders) {
					ahl = _gen_header_list (test->gossipheaders);
					for (ix = 0; ix < g_mime_autocrypt_header_list_get_count (ahl); ix++) {
						g_mime_object_append_header (mainpart, "Autocrypt-Gossip",
									     g_mime_autocrypt_header_to_string (g_mime_autocrypt_header_list_get_header_at (ahl, ix), TRUE), NULL);
					}
				}
				
				encrypted = g_mime_multipart_encrypted_encrypt (ctx, mainpart, FALSE, NULL,
										GMIME_ENCRYPT_ALWAYS_TRUST,
										recip, &err);
				if (!encrypted) {
					throw (exception_new ("failed to encrypt: %s", err->message));
				}
				g_mime_message_set_mime_part (before, GMIME_OBJECT (encrypted));
			}

			if (test->after) {
				afterstream = g_mime_stream_mem_new ();
				g_mime_object_write_to_stream (GMIME_OBJECT (before), NULL, afterstream);

				after = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (afterstream));
				
				gchar *got = (gchar*)after->data;
				if (memcmp (got, test->after, strlen(test->after))) {
					fprintf (stderr, "Expected: %s\nGot: %s\n", test->after, got);
					throw (exception_new ("failed to match"));
				}
			}
			if (test->inner_after) {
				if (!encrypted) {
					throw (exception_new ("inner_after, but no encrypted part!\n"));
				}
				
				cleartext = g_mime_multipart_encrypted_decrypt (encrypted,
										GMIME_DECRYPT_NONE,
										NULL, NULL, &err);
				if (!cleartext) {
					throw (exception_new ("decryption failed: %s!\n", err->message));
				}
				innerafterstream = g_mime_stream_mem_new ();
				g_mime_object_write_to_stream (cleartext, NULL, innerafterstream);

				innerafter = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (innerafterstream));
				
				gchar *got = (gchar*)innerafter->data;
				if (memcmp (got, test->inner_after, strlen(test->inner_after))) {
					fprintf (stderr, "Expected: %s\nGot: %s\n", test->inner_after, got);
					throw (exception_new ("failed to match"));
				}
			}
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("autocrypt header injection failed: %s", ex->message);
		} finally;
		if (ah)
			g_object_unref (ah);
		if (stream)
			g_object_unref (stream);
		if (parser)
			g_object_unref (parser);
		if (before)
			g_object_unref (before);
		if (afterstream)
			g_object_unref (afterstream);
		if (after)
			g_byte_array_unref (after);
		if (ahl)
			g_object_unref (ahl);
		if (ctx)
			g_object_unref (ctx);
		if (recip)
			g_ptr_array_unref (recip);
		if (encrypted)
			g_object_unref (encrypted);
		if (cleartext)
			g_object_unref (cleartext);
		if (err)
			g_error_free (err);
		if (innerafterstream)
			g_object_unref (innerafterstream);
		if (innerafter)
			g_byte_array_unref (innerafter);
		g_free (str);
		str = NULL;
	}
}


/* returns a non-NULL error if they're not the same */
char *
_acheaderlists_compare (GMimeAutocryptHeaderList *expected, GMimeAutocryptHeaderList *got)
{
	if (g_mime_autocrypt_header_list_get_count (expected) != g_mime_autocrypt_header_list_get_count (got))
		return g_strdup_printf ("header counts: expected: %d, got: %d",<commit_msg>Cleaned up autocrypt unit tests to not double-unref stuff<commit_after>/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*  GMime
 *  Copyright (C) 2000-2017 <NAME>
 *
 *  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 <gmime/gmime.h>
#include <gmime/gmime-autocrypt.h>

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

#include "testsuite.h"

extern int verbose;

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

struct _ah_gen_test {
	const char *addr;
	int keydatacount;
	char keybyte;
	gint64 timestamp;
	const char *txt;
};

static GMimeAutocryptHeader*
_gen_header (const struct _ah_gen_test *t)
{
	GMimeAutocryptHeader *ah = NULL;
	GBytes *keydata = NULL;
	if (t == NULL)
		return NULL;
	if (!(ah = g_mime_autocrypt_header_new())) {
		fprintf (stderr, "failed to make a new header");
		return NULL;
	}
	if (t->keydatacount)
		if (!(keydata = g_bytes_new_take ((guint8*) g_strnfill (t->keydatacount, t->keybyte), t->keydatacount))) {
			fprintf (stderr, "failed to make a new keydata");
			g_object_unref (ah);
			return NULL;
		}
	g_mime_autocrypt_header_set_address_from_string (ah, t->addr);
	g_mime_autocrypt_header_set_keydata (ah, keydata);
	if (t->timestamp) {
		GDateTime *ts = g_date_time_new_from_unix_utc (t->timestamp);
		g_mime_autocrypt_header_set_effective_date (ah, ts);
		g_date_time_unref (ts);
	}
	if (keydata)
		g_bytes_unref (keydata);
	return ah;
}

/* generates a header list based on a series with an addr=NULL sentinel value */
static GMimeAutocryptHeaderList*
_gen_header_list (const struct _ah_gen_test **tests)
{
	GMimeAutocryptHeaderList *ret = g_mime_autocrypt_header_list_new ();
	for (; *tests; tests++) {
		GMimeAutocryptHeader *ah = _gen_header (*tests);
		if (!ah) {
			fprintf (stderr, "failed to generate header <%s>", (*tests)->addr);
			g_object_unref (ret);
			return NULL;
		}
		g_mime_autocrypt_header_list_add (ret, ah);
		g_object_unref (ah);
	}
	return ret;
}

const static struct _ah_gen_test gen_test_data[] = {
	{ .addr = "<EMAIL>",
	  .keydatacount = 102,
	  .keybyte = '\013',
	  .txt = "addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL",
	}
};

const static struct _ah_gen_test * no_addrs[] = {
	NULL, /* sentinel */
};


static void
test_ah_generation (void)
{
	GMimeAutocryptHeader *ah = NULL;
	char *str = NULL;
	unsigned int i;
	for (i = 0; i < G_N_ELEMENTS(gen_test_data); i++) {
		try {
			testsuite_check ("Autocrypt header[%u]", i);
			const struct _ah_gen_test *test = gen_test_data + i;
			ah = _gen_header (test);

			str = g_mime_autocrypt_header_to_string (ah, FALSE);
			if (strcmp (test->txt, str)) {
				fprintf (stderr, "expected[%u]: \n%s\n\ngot:\n%s\n", i,
					 test->txt, str);
				throw (exception_new ("failed comparison"));
			}
			GMimeAutocryptHeader *ah2 = g_mime_autocrypt_header_new_from_string (str);
			gint cmp = g_mime_autocrypt_header_compare (ah, ah2);
			if (cmp) {
				char *x = g_mime_autocrypt_header_to_string (ah2, FALSE);
				fprintf (stderr, "after-rebuild[%u] (%d) \nexpected: \n%s\n\ngot:\n%s\n", i,
					 cmp, test->txt, x);
				g_free(x);
				throw (exception_new ("header from string did not match"));
			}
			
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("autocrypt header creation failed: %s", ex->message);
		} finally;
		if (ah)
			g_object_unref (ah);
		g_free (str);
		str = NULL;
	}
}


struct _ah_parse_test {
	const char *name;
	const struct _ah_gen_test *acheader;
	const struct _ah_gen_test **gossipheaders;
	const char *msg;
	const char *innerpart;
};


struct _ah_inject_test {
	const char *name;
	const struct _ah_gen_test *acheader;
	const struct _ah_gen_test **gossipheaders;
	const char **encrypt_to;
	const char *before;
	const char *after;
	const char *inner_after;
};

static const char * local_recipients[] =
	{
		"0x0D211DC5D9F4567271AC0582D8DECFBFC9346CD4",
		NULL,
	};

const static struct _ah_gen_test alice_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 102,
	  .timestamp = 1508774054,
	  .keybyte = '\013',
	};


const static struct _ah_gen_test alice_incomplete =
	{ .addr = "<EMAIL>",
	  .timestamp = 1508774054,
	};


const static struct _ah_gen_test bob_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 99,
	  .timestamp = 1508774054,
	  .keybyte = '\133' };

const static struct _ah_gen_test carol_addr =
	{ .addr = "<EMAIL>",
	  .keydatacount = 108,
	  .timestamp = 1508774054,
	  .keybyte = '\131' };

const static struct _ah_gen_test bob_incomplete =
	{ .addr = "<EMAIL>",
	  .timestamp = 1508774054,
	};

const static struct _ah_gen_test *just_bob[] = {
	&bob_addr,
	NULL,
};

const static struct _ah_gen_test *bob_and_carol[] = {
	&bob_addr,
	&carol_addr,
	NULL,
};

const static struct _ah_inject_test inject_test_data[] = {
	{ .name = "simple",
	  .acheader = &alice_addr,
	  .before = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	  .after = "From: <EMAIL>\n"
	  "To: <EMAIL>\n"
	  "Subject: A lovely day\n"
	  "Message-Id: <<EMAIL>>\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\n"
	  "Mime-Version: 1.0\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\n"
	  "Content-Type: text/plain\n"
	  "\n"
	  "Isn't it a lovely day?\n",
	},
	{ .name = "gossip injection",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .encrypt_to = local_recipients,
	  .before = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	  .inner_after = "Content-Type: text/plain\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\n"
	  "\n"
	  "Isn't it a lovely day?\n",
	},
};

const static struct _ah_parse_test parse_test_data[] = {
	{ .name = "simple",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't it a lovely day?\r\n",
	},
	
	{ .name = "simple+onegossip",
	  .acheader = &alice_addr,
	  .gossipheaders = just_bob,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "\r\n"
	  "Isn't a lovely day?  Now Carol can encrypt to Bob, hopefully.\r\n",
	},
	
	{ .name = "simple+nogossip",
	  .acheader = &alice_addr,
	  .gossipheaders = no_addrs,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Isn't a lovely day?  I sure hope Bob and Carol have each other's info\r\n"
	  "because otherwise they won't be able to Reply All.\r\n",
	},
	
	{ .name = "simple+fullgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>ZWVlZWVlZWVlZWVlZ\r\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  "\r\n"
	  "Isn't a lovely day?  Now Carol and Bob can now both Reply All, hopefully.\r\n",
	},
		
	{ .name = "actually encrypted, fullgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = bob_and_carol,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "hQGMA4xh3ftdkAY8AQv+NU4HDHKzqSk309FOoGCNfTIM16+LrT3TY+pwdQ+BHZNh\r\n"
	  "v62TfRrG3PFd46tvH3zInIHjow7Usb3Au+nz1fF0HgIkOg7IEGXUle0OuPgQt38i\r\n"
	  "J2B+7EhksG86aaGmlsCq7Y8v9QnBH/UsX95xSHOTpIgWDamdGed2nnqW0fdOtapK\r\n"
	  "QyfOWkmti8vUnzvDPxiEMLr2VW5UWtyJQiu6BwyEpme15K<KEY>cWKfD\r\n"
	  "+jK2qlQzlgKHeSy3cWmu6ejhkTqPOghxsgb6lGHNu4+/vHufZkZCKBOYrPq/6pLr\r\n"
	  "zySDS6p8+LsDf5WwbR3u1TENxUz1YfNDmFi0FcVRPgdbx6NsUe0EQgTudqMRJ7q4\r\n"
	  "6uID8HLG3p/i3nX3QbuJJZD5qz62AEypnNnuV2FsrZiQNkL/77uuBYrpruhNM6LZ\r\n"
	  "PfKWNCC8dOw7ABcbMrATGnaDenoSr0mrQWR4S7UeNeJUyB3as4iaTkc9inOHeUvr\r\n"
	  "3tck7qz96YII5gZzeo/40sA0AegT+pidzQ0xAe9llNHznJU/vqA5lV0gYpr6jCOh\r\n"
	  "46qWO/r4GEmwgKGDyakrifTOlO9DBM5A57FuWdFsnBX5dSgBuQrfaMhwVkeYN7jE\r\n"
	  "kGP9B6WeE53tFZKihq7fAgGKg8wOHKSlEKM42nI2V2+0XOqHySHgZbuS8gnhjG9O\r\n"
	  "Nc90XqYNWZUMDaUsSGeOvJzrpAM29kk9Vy2TdbWd3IvWsDMDtQRQcQfruAGiJCf9\r\n"
	  "mGH0HIKmGfHqMnIQZp+H/HOmNpEHPkEIVj5JT0XzHz/QXzuitsuV1ApGIu/lV7Ht\r\n"
	  "gdJzmTbrijjrinZE4kPsqNJQcQbuSw==\r\n"
	  "=/f+w\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	},
		
	{ .name = "simple+excessgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = just_bob,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=C<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: multipart/encrypted;\r\n"
	  " protocol=\"application/pgp-encrypted\";\r\n"
	  " boundary=\"boundary\"\r\n"
	  "\r\n"
	  "This is an OpenPGP/MIME encrypted message (RFC 4880 and 3156)\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/pgp-encrypted\r\n"
	  "Content-Description: PGP/MIME version identification\r\n"
	  "\r\n"
	  "Version: 1\r\n"
	  "\r\n"
	  "--boundary\r\n"
	  "Content-Type: application/octet-stream; name=\"encrypted.asc\"\r\n"
	  "Content-Description: OpenPGP encrypted message\r\n"
	  "Content-Disposition: inline; filename=\"encrypted.asc\"\r\n"
	  "\r\n"
	  "-----BEGIN PGP MESSAGE-----\r\n"
	  "\r\n"
	  "NOTREALLYOPENPGPJUSTATEST\r\n"
	  "-----END PGP MESSAGE-----\r\n"
	  "\r\n"
	  "--boundary--\r\n",
	  .innerpart = "Content-Type: text/plain\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  " WVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
	  "\r\n"
	  "Recipients of this message should not accept carol's public key for gossip, since\r\n"
	  "the message was not addressed to her\r\n"
	},
	
	{ .name = "simple+badgossip",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>, <EMAIL>\r\n"
	  "Subject: A gossipy lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt-Gossip: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "There are at least two headers here which will be ignored.\r\n",
	},
	
	{ .name = "duplicate",
	  .acheader = &alice_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Duplicate Autocrypt headers should cause none to match?\r\n",
	},

	{ .name = "unrecognized critical attribute",
	  .acheader = &alice_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
	},

	{ .name = "unrecognized critical attribute + simple",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>@<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "Unknown Autocrypt critical attribute should cause nothing to match but should not block a classic type header\r\n",
	},
	
	{ .name = "unrecognized non-critical attribute",
	  .acheader = &alice_addr,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; _not_an_emergency=true; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
	},

	{ .name = "no From: at all",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has no sender at all\r\n",
	},

	{ .name = "with Sender: header",
	  .acheader = &bob_incomplete,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>\r\n"
	  "Sender: <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has an Autocrypt header that matches the Sender: attribute but not the From:\r\n",
	},

	{ .name = "no senders",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "From: undisclosed sender\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "This message has no sender at all\r\n",
	},

	{ .name = "two senders",
	  .acheader = NULL,
	  .gossipheaders = NULL,
	  .msg = "From: <EMAIL>, <EMAIL>\r\n"
	  "To: <EMAIL>\r\n"
	  "Subject: A lovely day\r\n"
	  "Message-Id: <<EMAIL>>\r\n"
	  "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
	  "Autocrypt: addr=<EMAIL>; keydata=<KEY>"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
	  "Mime-Version: 1.0\r\n"
	  "Content-Type: text/plain\r\n"
	  "\r\n"
	  "When Alice and Bob are both present, we should not update Autocrypt state at all\r\n",
	},
};




static void
test_ah_injection (void)
{
	unsigned int i;
	
	for (i = 0; i < G_N_ELEMENTS (inject_test_data); i++) {
		GMimeMultipartEncrypted *encrypted = NULL;
		GMimeMessage *before = NULL;
		GMimeStream *stream = NULL;
		GMimeParser *parser = NULL;
		GError *err = NULL;
		char *str = NULL;
		int r, ix;
		
		try {
			const struct _ah_inject_test *test = inject_test_data + i;
			testsuite_check ("Autocrypt injection[%u] (%s)", i, test->name);
			
			stream = g_mime_stream_mem_new_with_buffer (test->before, strlen (test->before));
			parser = g_mime_parser_new_with_stream (stream);
			g_object_unref (stream);
			
			before = g_mime_parser_construct_message (parser, NULL);
			g_object_unref (parser);
			
			if (test->acheader) {
				GMimeAutocryptHeader *ah = _gen_header (test->acheader);
				
			        g_mime_object_set_header (GMIME_OBJECT (before), "Autocrypt",
						         g_mime_autocrypt_header_to_string (ah, FALSE), NULL);
				g_object_unref (ah);
			}
			
			if (test->encrypt_to) {
				/* get_mime_part is "transfer none" so mainpart does not need to be cleaned up */
				GMimeObject *mainpart = g_mime_message_get_mime_part (before);
				
				if (!mainpart)
					throw (exception_new ("failed to find main part!\n"));
				
				if (test->gossipheaders) {
					GMimeAutocryptHeaderList *ahl = _gen_header_list (test->gossipheaders);
					
					for (ix = 0; ix < g_mime_autocrypt_header_list_get_count (ahl); ix++) {
						g_mime_object_append_header (mainpart, "Autocrypt-Gossip",
									     g_mime_autocrypt_header_to_string (g_mime_autocrypt_header_list_get_header_at (ahl, ix), TRUE), NULL);
					}
					
					g_object_unref (ahl);
				}
				
				GMimeCryptoContext *ctx = g_mime_gpg_context_new ();
				GPtrArray *recip = g_ptr_array_new ();
				
				for (r = 0; test->encrypt_to[r]; r++)
					g_ptr_array_add (recip, (gpointer) test->encrypt_to[r]);
				
				encrypted = g_mime_multipart_encrypted_encrypt (ctx, mainpart, FALSE, NULL,
										GMIME_ENCRYPT_ALWAYS_TRUST,
										recip, &err);
				g_ptr_array_free (recip, TRUE);
				g_object_unref (ctx);
				
				if (!encrypted)
					throw (exception_new ("failed to encrypt: %s", err->message));
				
				g_mime_message_set_mime_part (before, GMIME_OBJECT (encrypted));
				g_object_unref (encrypted);
			}
			
			if (test->after) {
				stream = g_mime_stream_mem_new ();
				g_mime_object_write_to_stream (GMIME_OBJECT (before), NULL, stream);
				
				GByteArray *bytes = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream));
				char *got = (char *) bytes->data;
				
				if (memcmp (got, test->after, strlen (test->after)) != 0) {
					fprintf (stderr, "Expected: %s\nGot: %s\n", test->after, got);
					g_object_unref (stream);
					
					throw (exception_new ("failed to match"));
				} else {
					g_object_unref (stream);
				}
			}
			
			if (test->inner_after) {
				if (!encrypted)
					throw (exception_new ("inner_after, but no encrypted part!\n"));
				
				GMimeObject *cleartext = g_mime_multipart_encrypted_decrypt (encrypted,
											     GMIME_DECRYPT_NONE,
											     NULL, NULL, &err);
				if (!cleartext)
					throw (exception_new ("decryption failed: %s!\n", err->message));
				
				stream = g_mime_stream_mem_new ();
				g_mime_object_write_to_stream (cleartext, NULL, stream);
				g_object_unref (cleartext);
				
				GByteArray *bytes = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream));
				char *got = (char*) bytes->data;
				
				if (memcmp (got, test->inner_after, strlen (test->inner_after)) != 0) {
					fprintf (stderr, "Expected: %s\nGot: %s\n", test->inner_after, got);
					g_object_unref (stream);
					
					throw (exception_new ("failed to match"));
				} else {
					g_object_unref (stream);
				}
			}
			testsuite_check_passed ();
		} catch (ex) {
			testsuite_check_failed ("autocrypt header injection failed: %s", ex->message);
		} finally;
		
		if (before)
			g_object_unref (before);
		if (err)
			g_error_free (err);
		g_free (str);
		str = NULL;
	}
}


/* returns a non-NULL error if they're not the same */
char *
_acheaderlists_compare (GMimeAutocryptHeaderList *expected, GMimeAutocryptHeaderList *got)
{
	if (g_mime_autocrypt_header_list_get_count (expected) != g_mime_autocrypt_header_list_get_count (got))
		return g_strdup_printf ("header counts: expected: %d, got: %d",
{'fraction_non_alphanumeric': 0.1522419572907599, 'fraction_numerical': 0.029337136295445706, 'mean_word_length': 3.6478436773693725, 'pattern_counts': {'":': 0, '<': 309, '<?xml version=': 0, '>': 375, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6237723', 'n_tokens_mistral': 27025, 'n_tokens_neox': 23924, 'n_words': 4127}