[gmime: 13/27] Autocrypt update to spec Level 1.0.0rc1 (#27)



commit 9770d9b545201d39eb3bd98f4676dceabb90279c
Author: dkg <dkg fifthhorseman net>
Date:   Wed Nov 8 22:44:34 2017 +0100

    Autocrypt update to spec Level 1.0.0rc1  (#27)
    
    * Drop Autocrypt type value
    
    At the Autocrypt meeting this weekend, we determined that we can
    completely drop the explicit "type=" attribute for the Autocrypt
    header.
    
    This change removes the type from the gmime API, simplifying it.
    Hopefully it is ok because there has been no release with this API.
    
    * Move _get_autocrypt_headers to gmime-object.c
    
    This prepares for a shift in API so that we can get gossip headers
    from the child of a GMimeMultipartEncrypted object instead of from a
    GMimeMessage object.
    
    * Only a single Autocrypt header can be returned from a GMimeMessage
    
    As the Autocrypt spec reached its first release, we've clarified that
    only a single address is ever valid from an e-mail message with
    respect to Autocrypt.
    
    See https://github.com/autocrypt/autocrypt/issues/138 and
    https://github.com/autocrypt/autocrypt/pull/144 for discussion and
    Autocrypt spec commit 45df7b1ea8fe6e83bbe8abae09dc495e4f7ab784 for
    the clarification.
    
    This commit adjusts the GMime interface to match the spec.
    
    * autocrypt: add several more test messages
    
    * Autocrypt: introduce gm_message_get_autocrypt_headers_from_inner_part
    
    Autocrypt Level 1 has clarified that gossip headers must only be found
    within the encrypted layer.  If a message has no encryption layer,
    there should be no gossip processing.
    
    See discussion at https://github.com/autocrypt/autocrypt/issues/135
    and Autocrypt commit 1636162ee10df2d61d614e3903a5ff35cb953a8c for the
    specific changes in the spec.
    
    This changeset updates the retrieval of gossip headers so anyone who
    has decrypted the message can retrieve them from the inner part, once
    decrypted.  This is an API change against the currently unreleased
    Autocrypt API.
    
    * Autocrypt: added OpenPGP secret key to test in preparation for actual decryption.
    
    * Autocrypt: fetch gossip headers from encrypted part.
    
    This changeset adds a convenience function for any implementer to
    extract key gossip information from an encrypted message, without
    having to do the decryption manually.
    
    We also add a new element to the test suite to exercise it and make
    sure it works as expected.

 docs/reference/gmime-sections.txt |    5 +-
 docs/reference/gmime.hierarchy    |   59 ----
 gmime/gmime-autocrypt.c           |   68 +----
 gmime/gmime-autocrypt.h           |    5 -
 gmime/gmime-message.c             |  204 +++++++-----
 gmime/gmime-message.h             |    6 +-
 gmime/gmime-object.c              |   56 ++++
 gmime/gmime-object.h              |    5 +
 tests/test-autocrypt.c            |  614 ++++++++++++++++++++++++++++++++-----
 9 files changed, 725 insertions(+), 297 deletions(-)
---
diff --git a/docs/reference/gmime-sections.txt b/docs/reference/gmime-sections.txt
index 952fb33..8cad81e 100644
--- a/docs/reference/gmime-sections.txt
+++ b/docs/reference/gmime-sections.txt
@@ -1001,7 +1001,8 @@ g_mime_message_set_mime_part
 g_mime_message_get_mime_part
 g_mime_message_foreach
 g_mime_message_get_body
-g_mime_message_get_autocrypt_headers
+g_mime_message_get_autocrypt_header
+g_mime_message_get_autocrypt_gossip_headers_from_inner_part
 g_mime_message_get_autocrypt_gossip_headers
 
 <SUBSECTION Private>
@@ -1599,8 +1600,6 @@ GMimeAutocryptPreferEncrypt
 GMimeAutocryptHeader
 g_mime_autocrypt_header_new
 g_mime_autocrypt_header_new_from_string
-g_mime_autocrypt_header_get_actype
-g_mime_autocrypt_header_set_actype
 g_mime_autocrypt_header_get_address
 g_mime_autocrypt_header_set_address
 g_mime_autocrypt_header_set_address_from_string
diff --git a/gmime/gmime-autocrypt.c b/gmime/gmime-autocrypt.c
index 92f1c13..40e3411 100644
--- a/gmime/gmime-autocrypt.c
+++ b/gmime/gmime-autocrypt.c
@@ -85,7 +85,6 @@ g_mime_autocrypt_header_class_init (GMimeAutocryptHeaderClass *klass)
 static void
 g_mime_autocrypt_header_init (GMimeAutocryptHeader *ah, GMimeAutocryptHeaderClass *klass)
 {
-       ah->actype = 1;
        ah->address = NULL;
        ah->prefer_encrypt = GMIME_AUTOCRYPT_PREFER_ENCRYPT_NONE;
        ah->keydata = NULL;
@@ -133,7 +132,8 @@ g_mime_autocrypt_header_new (void)
  * e-mail headers, but cannot be extracted from the raw Autocrypt:
  * header itself.
  * 
- * Returns: (transfer full): a new #GMimeAutocryptHeader object, or %NULL on error.
+ * Returns: (transfer full): a new #GMimeAutocryptHeader object, or
+ * %NULL on error.
  **/
 GMimeAutocryptHeader *
 g_mime_autocrypt_header_new_from_string (const char *string)
@@ -152,12 +152,11 @@ g_mime_autocrypt_header_new_from_string (const char *string)
 
        struct _attr { char *val; const char *name; size_t sz; int count; };
 #define attr(n, str) struct _attr n = { .name = str, .sz = sizeof(str)-1 }
-       attr(t, "type");
        attr(k, "keydata");
        attr(p, "prefer-encrypt");
        attr(a, "addr");
 #undef attr
-       struct _attr *attrs[] = { &t, &k, &p, &a };
+       struct _attr *attrs[] = { &k, &p, &a };
        
        gchar **vals = g_strsplit (string, ";", -1);
        gchar **x;
@@ -188,27 +187,15 @@ g_mime_autocrypt_header_new_from_string (const char *string)
                goto done;
        if (a.count != 1)
                goto done;
-       if (t.count > 1)
-               goto done;
        if (p.count > 1)
                goto done;
-       gchar *endptr=NULL;
-       guint64 newtype = 1;
        GMimeAutocryptPreferEncrypt newpref = GMIME_AUTOCRYPT_PREFER_ENCRYPT_NONE;
 
-       if (t.count) {
-               newtype = g_ascii_strtoull (t.val, &endptr, 10);
-               if (*endptr) /* should be NULL after the unsigned int conversion */
-                       goto done;
-               if (newtype > G_MAXINT)
-                       goto done;
-       }
        if (p.count && (g_ascii_strcasecmp ("mutual", p.val) == 0))
                newpref = GMIME_AUTOCRYPT_PREFER_ENCRYPT_MUTUAL;
        
        ret = g_object_new (GMIME_TYPE_AUTOCRYPT_HEADER, NULL);
        g_mime_autocrypt_header_set_address_from_string (ret, a.val);
-       g_mime_autocrypt_header_set_actype (ret, newtype);
        g_mime_autocrypt_header_set_prefer_encrypt (ret, newpref);
        ksplit = g_strsplit_set (k.val, " \r\n\t", -1);
        kjoined = g_strjoinv ("", ksplit);
@@ -231,38 +218,6 @@ g_mime_autocrypt_header_new_from_string (const char *string)
 
 
 /**
- * g_mime_autocrypt_header_set_actype:
- * @ah: a #GMimeAutocryptHeader object
- * @type: an integer value associated with the type
- *
- * Set the type associated with the Autocrypt header.
- **/
-void
-g_mime_autocrypt_header_set_actype (GMimeAutocryptHeader *ah, gint actype)
-{
-       g_return_if_fail (GMIME_IS_AUTOCRYPT_HEADER (ah));
-
-       ah->actype = actype;
-}
-
-/**
- * g_mime_autocrypt_header_get_actype:
- * @ah: a #GMimeAutocryptHeader object
- *
- * Gets the type of the Autocrypt header.
- *
- * Returns: the type of the Autocrypt header
- **/
-gint
-g_mime_autocrypt_header_get_actype (GMimeAutocryptHeader *ah)
-{
-       g_return_val_if_fail (GMIME_IS_AUTOCRYPT_HEADER (ah), 0);
-
-       return ah->actype;
-}
-
-
-/**
  * g_mime_autocrypt_header_set_address_from_string:
  * @ah: a #GMimeAutocryptHeader object
  * @address: a %NULL-terminated string that is a raw e-mail address
@@ -417,7 +372,8 @@ g_mime_autocrypt_header_set_effective_date (GMimeAutocryptHeader *ah, GDateTime
                g_date_time_unref (ah->effective_date);
        
        ah->effective_date = effective_date;
-       g_date_time_ref (effective_date);
+       if (effective_date)
+               g_date_time_ref (effective_date);
 }
 
 /**
@@ -463,7 +419,7 @@ g_mime_autocrypt_header_is_complete (GMimeAutocryptHeader *ah)
  * Gets the string representation of the Autocrypt header, or %NULL on
  * error.  For example, it might return:
  *
- *     type=1; prefer-encrypt=mutual; addr=bob\@example.com keydata=AAAB15BE...
+ *     prefer-encrypt=mutual; addr=bob\@example.com; keydata=AAAB15BE...
  *
  * Returns: (transfer full): the string representation of the
  * Autocrypt header.
@@ -480,8 +436,8 @@ g_mime_autocrypt_header_get_string (GMimeAutocryptHeader *ah)
        const char *addr = internet_address_mailbox_get_addr (ah->address);
        GPtrArray *lines = g_ptr_array_new_with_free_func (g_free);
 
-       gchar * first = g_strdup_printf("addr=%s; type=%d; %skeydata=",
-                                       addr, ah->actype, pe);
+       gchar * first = g_strdup_printf("addr=%s; %skeydata=",
+                                       addr, pe);
        size_t n = strlen (first);
        const size_t maxwid = 72;
        const size_t firstline = maxwid - sizeof ("Autocrypt:");
@@ -521,7 +477,6 @@ g_mime_autocrypt_header_get_string (GMimeAutocryptHeader *ah)
  * sorting headers by:
  *
  *  - address
- *  - actype
  *  - effective_date
  *  - keydata
  *  - prefer_encrypt
@@ -543,11 +498,6 @@ g_mime_autocrypt_header_compare (GMimeAutocryptHeader *ah1, GMimeAutocryptHeader
                        return ret;
        }
        
-       if (ah1->actype < ah2->actype)
-               return -1;
-       if (ah1->actype > ah2->actype)
-               return 1;
-       
        if (!ah1->effective_date && ah2->effective_date)
                return -1;
        if (ah1->effective_date && !ah2->effective_date)
@@ -591,8 +541,6 @@ g_mime_autocrypt_header_compare (GMimeAutocryptHeader *ah1, GMimeAutocryptHeader
 void
 g_mime_autocrypt_header_clone (GMimeAutocryptHeader *dst, GMimeAutocryptHeader *src)
 {
-       if (dst->actype != src->actype)
-               return;
        if (!dst->address || !src->address)
                return;
        if (g_strcmp0 (internet_address_mailbox_get_idn_addr (dst->address), 
internet_address_mailbox_get_idn_addr (src->address)))
diff --git a/gmime/gmime-autocrypt.h b/gmime/gmime-autocrypt.h
index d2405e4..4595c97 100644
--- a/gmime/gmime-autocrypt.h
+++ b/gmime/gmime-autocrypt.h
@@ -66,7 +66,6 @@ typedef enum {
  * GMimeAutocryptHeader:
  * @parent_object: parent #GObject
  * @address: the #InternetAddressMailbox associated with this Autocrypt header.
- * @actype: the Autocrypt type parameter (defaults to 1).
  * @prefer_encrypt: a #GMimeAutocryptPreferEncrypt value (defaults to @GMIME_AUTOCRYPT_PREFER_ENCRYPT_NONE).
  * @keydata: the raw binary form of the encoded key.
  * @effective_date: the date associated with the Autocrypt header in this message.
@@ -79,7 +78,6 @@ typedef enum {
 struct _GMimeAutocryptHeader {
        GObject parent_object;
 
-       gint actype;
        InternetAddressMailbox *address;
        GMimeAutocryptPreferEncrypt prefer_encrypt;
        GByteArray *keydata;
@@ -95,9 +93,6 @@ GType g_mime_autocrypt_header_get_type (void);
 GMimeAutocryptHeader *g_mime_autocrypt_header_new (void);
 GMimeAutocryptHeader *g_mime_autocrypt_header_new_from_string (const char* header);
 
-void g_mime_autocrypt_header_set_actype (GMimeAutocryptHeader *ah, gint actype);
-gint g_mime_autocrypt_header_get_actype (GMimeAutocryptHeader *ah);
-
 void g_mime_autocrypt_header_set_address (GMimeAutocryptHeader *ah, InternetAddressMailbox *address);
 InternetAddressMailbox *g_mime_autocrypt_header_get_address (GMimeAutocryptHeader *ah);
 void g_mime_autocrypt_header_set_address_from_string (GMimeAutocryptHeader *ah, const char *address);
diff --git a/gmime/gmime-message.c b/gmime/gmime-message.c
index 15fea3c..ab2ae43 100644
--- a/gmime/gmime-message.c
+++ b/gmime/gmime-message.c
@@ -1177,39 +1177,30 @@ g_mime_message_get_body (GMimeMessage *message)
 }
 
 
-static GMimeAutocryptHeaderList *
-_get_autocrypt_headers (GMimeMessage *message, gint actype, GDateTime *now, const char *matchheader,
-                       InternetAddressList *addresses, gboolean keep_incomplete);
-
-
 /**
- * g_mime_message_get_autocrypt_headers:
+ * g_mime_message_get_autocrypt_header:
  * @message: a #GMimeMessage object.
- * @actype: the desired version of autocrypt headers
  * @now: a #GDateTime object, or %NULL
  *
- * Creates a new #GMimeAutocryptHeaderList of relevant headers of the
- * given type based on the sender(s) of an e-mail message.
+ * Creates a new #GMimeAutocryptHeader base on the relevant Autocrypt
+ * header associated with the sender of an e-mail message.
  *
- * Each header in the list will have a valid address and will be of
- * the type requested.
+ * If the message has no sender in the From: field, or has more than
+ * one sender, then this function will return %NULL.  Autocrypt should
+ * ignore the message entirely.
  *
- * If no Autocrypt header is found for a sender, the returned
- * #GMimeAutocryptHeader associated with that address will be in the
- * list, but it will not be complete (see
+ * If there is one sender, but no single Autocrypt header is found
+ * that matches that e-mail address, a #GMimeAutocryptHeader will be
+ * returned for the sender, but it will be incomplete (see
  * #g_mime_autocrypt_header_is_complete).
  *
  * Note that the following types of Autocrypt headers will not be
  * returned by this function:
  *
- *  - headers of an unrequested type
  *  - headers that do not match an address in "From:"
  *  - unparseable headers
  *  - headers with unknown critical attributes
- *  - duplicate valid headers for a given address
- * 
- * On error (e.g. if this version of GMime cannot handle the requested
- * Autocrypt type), returns %NULL
+ *  - duplicate valid headers for the sender's address
  *
  * The returned Autocrypt headers will have their effective_date set
  * to the earliest of either:
@@ -1217,25 +1208,60 @@ _get_autocrypt_headers (GMimeMessage *message, gint actype, GDateTime *now, cons
  * - the Date: header of the message or 
  * - @now (or the current time, if @now is %NULL)
  *
- * Returns: (transfer full): a new #GMimeAutocryptHeaderList object
+ * Returns: (transfer full): a new #GMimeAutocryptHeaderList object,
+ * or %NULL if the message should be ignored for purposes of
+ * Autocrypt.
  **/
-GMimeAutocryptHeaderList *
-g_mime_message_get_autocrypt_headers (GMimeMessage *message, gint actype, GDateTime *now)
+GMimeAutocryptHeader *
+g_mime_message_get_autocrypt_header (GMimeMessage *message, GDateTime *now)
 {
-       return _get_autocrypt_headers (message, actype, now, "autocrypt", 
message->addrlists[GMIME_ADDRESS_TYPE_FROM], TRUE);
+       g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
+
+       GMimeAutocryptHeaderList *retlist = NULL;
+       GMimeAutocryptHeader *ret = NULL;
+       GDateTime *newnow = NULL;
+       GDateTime *effective_date = NULL;
+       if (now == NULL)
+               now = newnow = g_date_time_new_now_utc ();
+       effective_date = now;
+       if (message->date && g_date_time_compare (message->date, now) < 0)
+               effective_date = message->date;
+       retlist = g_mime_object_get_autocrypt_headers (GMIME_OBJECT (message),
+                                                      effective_date,
+                                                      "autocrypt",
+                                                      message->addrlists[GMIME_ADDRESS_TYPE_FROM],
+                                                      TRUE);
+       if (newnow)
+               g_date_time_unref (newnow);
+       if (retlist) {
+               if (g_mime_autocrypt_header_list_get_count (retlist) == 1) {
+                       ret = g_mime_autocrypt_header_list_get_header_at (retlist, 0);
+                       g_object_ref (ret);
+               }
+               g_object_unref (retlist);
+       }
+       return ret;
 }
 
 
 /**
- * g_mime_message_get_autocrypt_gossip_headers:
+ * g_mime_message_get_autocrypt_gossip_headers_from_inner_part:
  * @message: a #GMimeMessage object.
- * @actype: the desired version of autocrypt headers
  * @now: a #GDateTime object, or %NULL
+ * @inner_part: a #GMimeObject which is the cleartext part of the inner message
  *
  * Creates a new #GMimeAutocryptHeaderList of relevant headers of the
  * given type based on the recipient(s) of an e-mail message.
+ * 
+ * You must pass the decrypted inner part of the message to this
+ * function, since Autocrypt-Gossip headers are only stored within the
+ * encrypted layer.
  *
- * Each header in the list will:
+ * If you don't already have the decrypted inner part available to
+ * you, you probably want to use
+ * #g_mime_message_get_autocrypt_gossip_headers instead.
+ *
+ * Each header in the returned list will:
  *
  *  - have a valid address
  *  - be of the type requested
@@ -1254,7 +1280,8 @@ g_mime_message_get_autocrypt_headers (GMimeMessage *message, gint actype, GDateT
  *  - duplicate valid headers for a given address
  * 
  * On error (e.g. if this version of GMime cannot handle the requested
- * Autocrypt type), returns %NULL
+ * Autocrypt type, or if a parameter is missing or malformed), returns
+ * %NULL
  *
  * The returned Autocrypt headers will have their effective_date set
  * to the earliest of either:
@@ -1262,80 +1289,85 @@ g_mime_message_get_autocrypt_headers (GMimeMessage *message, gint actype, GDateT
  * - the Date: header of the message or 
  * - @now (or the current time, if @now is %NULL)
  * 
- * Returns: (transfer full): a new #GMimeAutocryptHeaderList object
+ * Returns: (transfer full): a new #GMimeAutocryptHeaderList object, or %NULL on error.
  **/
 GMimeAutocryptHeaderList *
-g_mime_message_get_autocrypt_gossip_headers (GMimeMessage *message, gint actype, GDateTime *now)
+g_mime_message_get_autocrypt_gossip_headers_from_inner_part (GMimeMessage *message, GDateTime *now, 
GMimeObject *inner_part)
 {
        g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
+       g_return_val_if_fail (GMIME_IS_OBJECT (inner_part), NULL);
        InternetAddressList *addresses = g_mime_message_get_all_recipients (message);
-       GMimeAutocryptHeaderList *ret = _get_autocrypt_headers (message, actype, now, "autocrypt-gossip", 
addresses, FALSE);
+       GDateTime *newnow = NULL;
+       GDateTime *effective_date = NULL;
+       GMimeAutocryptHeaderList *ret = NULL;
+       if (now == NULL)
+               now = newnow = g_date_time_new_now_utc ();
+       effective_date = now;
+       if (message->date && g_date_time_compare (message->date, now) < 0)
+               effective_date = message->date;
+       ret = g_mime_object_get_autocrypt_headers (inner_part,
+                                                  effective_date,
+                                                  "autocrypt-gossip",
+                                                  addresses, FALSE);
        g_object_unref (addresses);
+       if (newnow)
+               g_date_time_unref (newnow);
        return ret;
 }
 
 
-static GMimeAutocryptHeaderList *
-_get_autocrypt_headers (GMimeMessage *message, gint actype, GDateTime *now, const char *matchheader,
-                       InternetAddressList *addresses, gboolean keep_incomplete)
+/**
+ * g_mime_message_get_autocrypt_gossip_headers:
+ * @message: a #GMimeMessage object, which is expected to be encrypted.
+ * @now: a #GDateTime object, or %NULL
+ * @flags: a #GMimeDecryptFlags, to be used during decryption
+ * @session_key: session key to use or %NULL
+ * @err: a #GError (can be %NULL)
+ *
+ * Creates a new #GMimeAutocryptHeaderList of relevant headers of the
+ * given type based on the recipient(s) of an e-mail message.
+ * 
+ * Returns the same object as
+ * #g_mime_message_get_autocrypt_gossip_headers_with_inner_part , but
+ * handles decryption and cleanup automatically.
+ *
+ * @flags and @session_key are passed through to
+ * #g_mime_multipart_encrypted_decrypt, as needed.
+ *
+ * If the message is not actually an encrypted message, returns %NULL:
+ * it should be ignored for purposes of evaluating gossip.
+ *
+ * If decryption fails, returns %NULL.  In this case, an exception
+ * will be set on @err to provide information about the decryption
+ * failure.
+ *
+ * Returns: (transfer full): a new #GMimeAutocryptHeaderList object,
+ * or %NULL on error.
+ **/
+GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_gossip_headers (GMimeMessage *message,
+                                                                      GDateTime *now,
+                                                                      GMimeDecryptFlags flags,
+                                                                      const char *session_key,
+                                                                      GError **err)
 {
        g_return_val_if_fail (GMIME_IS_MESSAGE (message), NULL);
-       if (actype != 1)
+       GMimeAutocryptHeaderList *ret = NULL;
+       GMimeObject *top_level = NULL;
+       GMimeObject *inner_part = NULL;
+
+       top_level = g_mime_message_get_mime_part (message);
+       if (!GMIME_IS_MULTIPART_ENCRYPTED (top_level))
                return NULL;
 
-       GMimeObject *mime_part = GMIME_OBJECT (message);
-       int i;
-
-       GMimeAutocryptHeaderList *ret = g_mime_autocrypt_header_list_new ();
-       guint count = g_mime_autocrypt_header_list_add_missing_addresses (ret, addresses);
-       if (!count)
-               return ret;
-       
-       /* scan for Autocrypt headers whose type=actype, and addr=
-        * attribute matches the From: header.  calculate
-        * effective_date based on now and the Date: header. */
-       
-       GMimeHeaderList *headers = g_mime_object_get_header_list(mime_part);
-       for (i = 0; i < g_mime_header_list_get_count (headers); i++) {
-               GMimeHeader *header = g_mime_header_list_get_header_at (headers, i);
-               if (g_ascii_strcasecmp (matchheader, header->name) == 0) {
-                       GMimeAutocryptHeader *ah = g_mime_autocrypt_header_new_from_string 
(g_mime_header_get_value (header));
-                       if (!ah || ah->actype != actype || ! g_mime_autocrypt_header_is_complete (ah))
-                               goto done;
-                       GMimeAutocryptHeader *prev = g_mime_autocrypt_header_list_get_header_for_address 
(ret, ah->address);
-                       if (!prev) /* not a valid address (was not in From:) */
-                               goto done;
-                       if (g_mime_autocrypt_header_is_complete (prev)) {
-                               /* this is a duplicate (we use actype=0 as an internal marker for this) */
-                               prev->actype = 0;
-                       } else {
-                               g_mime_autocrypt_header_clone (prev, ah);
-                       }
-               done:
-                       if (ah)
-                               g_object_unref (ah);
-               }
-                       
-       }
-       GDateTime *newnow = NULL;
-       if (now == NULL)
-               now = newnow = g_date_time_new_now_utc ();
-       GDateTime *effective = now;
-       if (message->date && g_date_time_compare (message->date, now) < 0)
-               effective = message->date;
-       for (i = 0; i < g_mime_autocrypt_header_list_get_count (ret); i++) {
-               GMimeAutocryptHeader *ah = g_mime_autocrypt_header_list_get_header_at (ret, i);
-               g_mime_autocrypt_header_set_effective_date (ah, effective);
-               /* drop keydata from duplicates */
-               if (ah->actype == 0) {
-                       g_mime_autocrypt_header_set_keydata (ah, NULL);
-                       ah->actype = actype;
-               }
+       inner_part = g_mime_multipart_encrypted_decrypt (GMIME_MULTIPART_ENCRYPTED (top_level),
+                                                        flags,
+                                                        session_key,
+                                                        NULL, /* we do not care about decryptresult */
+                                                        err);
+       if (inner_part) {
+               ret = g_mime_message_get_autocrypt_gossip_headers_from_inner_part (message, now, inner_part);
+               g_object_unref (inner_part);
        }
 
-       if (!keep_incomplete)
-               g_mime_autocrypt_header_list_remove_incomplete (ret);
-       if (newnow)
-               g_date_time_unref (newnow);
        return ret;
 }
diff --git a/gmime/gmime-message.h b/gmime/gmime-message.h
index 5125a37..657eb07 100644
--- a/gmime/gmime-message.h
+++ b/gmime/gmime-message.h
@@ -31,6 +31,7 @@
 #include <gmime/gmime-header.h>
 #include <gmime/gmime-stream.h>
 #include <gmime/gmime-autocrypt.h>
+#include <gmime/gmime-crypto-context.h>
 
 G_BEGIN_DECLS
 
@@ -123,8 +124,9 @@ const char *g_mime_message_get_message_id (GMimeMessage *message);
 GMimeObject *g_mime_message_get_mime_part (GMimeMessage *message);
 void g_mime_message_set_mime_part (GMimeMessage *message, GMimeObject *mime_part);
 
-GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_headers (GMimeMessage *part, gint actype, GDateTime 
*now);
-GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_gossip_headers (GMimeMessage *part, gint actype, 
GDateTime *now);
+GMimeAutocryptHeader *g_mime_message_get_autocrypt_header (GMimeMessage *message, GDateTime *now);
+GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_gossip_headers (GMimeMessage *message, GDateTime 
*now, GMimeDecryptFlags flags, const char *session_key, GError **err);
+GMimeAutocryptHeaderList *g_mime_message_get_autocrypt_gossip_headers_from_inner_part (GMimeMessage 
*message, GDateTime *now, GMimeObject *inner_part);
 
 /* convenience functions */
 
diff --git a/gmime/gmime-object.c b/gmime/gmime-object.c
index 3678f7d..54fa34a 100644
--- a/gmime/gmime-object.c
+++ b/gmime/gmime-object.c
@@ -1101,3 +1101,59 @@ g_mime_object_type_registry_init (void)
        
        type_hash = g_hash_table_new (g_mime_strcase_hash, g_mime_strcase_equal);
 }
+
+
+
+GMimeAutocryptHeaderList *
+g_mime_object_get_autocrypt_headers (GMimeObject *mime_part, GDateTime *effective_date,
+                                    const char *matchheader, InternetAddressList *addresses,
+                                    gboolean keep_incomplete)
+{
+       g_return_val_if_fail (GMIME_IS_OBJECT (mime_part), NULL);
+
+       int i;
+
+       GMimeAutocryptHeaderList *ret = g_mime_autocrypt_header_list_new ();
+       guint count = g_mime_autocrypt_header_list_add_missing_addresses (ret, addresses);
+       if (!count)
+               return ret;
+
+       /* scan for Autocrypt headers whose addr= attribute matches
+        * the From: header. */
+       
+       GMimeHeaderList *headers = g_mime_object_get_header_list(mime_part);
+       for (i = 0; i < g_mime_header_list_get_count (headers); i++) {
+               GMimeHeader *header = g_mime_header_list_get_header_at (headers, i);
+               if (g_ascii_strcasecmp (matchheader, header->name) == 0) {
+                       GMimeAutocryptHeader *ah = g_mime_autocrypt_header_new_from_string 
(g_mime_header_get_value (header));
+                       if (!ah || ! g_mime_autocrypt_header_is_complete (ah))
+                               goto done;
+                       g_mime_autocrypt_header_set_effective_date (ah, effective_date);
+                       GMimeAutocryptHeader *prev = g_mime_autocrypt_header_list_get_header_for_address 
(ret, ah->address);
+                       if (!prev) /* not a valid address (was not in From:) */
+                               goto done;
+                       if (g_mime_autocrypt_header_is_complete (prev)) {
+                               /* this is a duplicate (we use effective_date=NULL as an internal marker for 
this) */
+                               g_mime_autocrypt_header_set_effective_date (prev, NULL);
+                       } else {
+                               g_mime_autocrypt_header_clone (prev, ah);
+                       }
+               done:
+                       if (ah)
+                               g_object_unref (ah);
+               }
+                       
+       }
+       for (i = 0; i < g_mime_autocrypt_header_list_get_count (ret); i++) {
+               GMimeAutocryptHeader *ah = g_mime_autocrypt_header_list_get_header_at (ret, i);
+               /* drop keydata from duplicates */
+               if (ah->effective_date == NULL) {
+                       g_mime_autocrypt_header_set_keydata (ah, NULL);
+                       g_mime_autocrypt_header_set_effective_date (ah, effective_date);
+               }
+       }
+
+       if (!keep_incomplete)
+               g_mime_autocrypt_header_list_remove_incomplete (ret);
+       return ret;
+}
diff --git a/gmime/gmime-object.h b/gmime/gmime-object.h
index c29534b..6daf30f 100644
--- a/gmime/gmime-object.h
+++ b/gmime/gmime-object.h
@@ -32,6 +32,7 @@
 #include <gmime/gmime-encodings.h>
 #include <gmime/gmime-stream.h>
 #include <gmime/gmime-header.h>
+#include <gmime/gmime-autocrypt.h>
 
 G_BEGIN_DECLS
 
@@ -140,6 +141,10 @@ void g_mime_object_encode (GMimeObject *object, GMimeEncodingConstraint constrai
 /* Internal API */
 G_GNUC_INTERNAL void g_mime_object_type_registry_init (void);
 G_GNUC_INTERNAL void g_mime_object_type_registry_shutdown (void);
+G_GNUC_INTERNAL GMimeAutocryptHeaderList *
+g_mime_object_get_autocrypt_headers(GMimeObject *mime_part, GDateTime *effective_date,
+                                   const char *matchheader, InternetAddressList *addresses,
+                                   gboolean keep_incomplete);
 
 G_END_DECLS
 
diff --git a/tests/test-autocrypt.c b/tests/test-autocrypt.c
index 900d1b6..6f3f984 100644
--- a/tests/test-autocrypt.c
+++ b/tests/test-autocrypt.c
@@ -48,6 +48,8 @@ _gen_header (const struct _ah_gen_test *t)
 {
        GMimeAutocryptHeader *ah = NULL;
        GByteArray *keydata = NULL;
+       if (t == NULL)
+               return NULL;
        if (!(ah = g_mime_autocrypt_header_new())) {
                fprintf (stderr, "failed to make a new header");
                return NULL;
@@ -72,13 +74,13 @@ _gen_header (const struct _ah_gen_test *t)
 
 /* 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)
+_gen_header_list (const struct _ah_gen_test **tests)
 {
        GMimeAutocryptHeaderList *ret = g_mime_autocrypt_header_list_new ();
-       for (; tests->addr; tests++) {
-               GMimeAutocryptHeader *ah = _gen_header (tests);
+       for (; *tests; tests++) {
+               GMimeAutocryptHeader *ah = _gen_header (*tests);
                if (!ah) {
-                       fprintf (stderr, "failed to generate header <%s>", tests->addr);
+                       fprintf (stderr, "failed to generate header <%s>", (*tests)->addr);
                        g_object_unref (ret);
                        return NULL;
                }
@@ -92,14 +94,14 @@ const static struct _ah_gen_test gen_test_data[] = {
        { .addr = "test example org",
          .keydatacount = 102,
          .keybyte = '\013',
-         .txt = "addr=test example org; type=1; keydata=CwsLCwsLCwsLCwsLCwsL\r\n"
+         .txt = "addr=test example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
-         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL",
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL",
        }
 };
 
-const static struct _ah_gen_test no_addrs[] = {
-       { .addr = NULL }, /* sentinel */
+const static struct _ah_gen_test * no_addrs[] = {
+       NULL, /* sentinel */
 };
 
 
@@ -145,38 +147,58 @@ test_ah_generation (void)
 
 struct _ah_parse_test {
        const char *name;
-       const struct _ah_gen_test *acheaders;
-       const struct _ah_gen_test *gossipheaders;
+       const struct _ah_gen_test *acheader;
+       const struct _ah_gen_test **gossipheaders;
        const char *msg;
+       const char *innerpart;
 };
 
-const static struct _ah_gen_test alice_addr[] = {
+const static struct _ah_gen_test alice_addr =
        { .addr = "alice example org",
          .keydatacount = 102,
          .timestamp = 1508774054,
-         .keybyte = '\013' },
-       { .addr = NULL }, /* sentinel */
-};
+         .keybyte = '\013',
+       };
 
-const static struct _ah_gen_test alice_incomplete[] = {
+
+const static struct _ah_gen_test alice_incomplete =
        { .addr = "alice example org",
          .timestamp = 1508774054,
-       },
-       { .addr = NULL }, /* sentinel */
-};
+       };
 
-const static struct _ah_gen_test bob_addr[] = {
+
+const static struct _ah_gen_test bob_addr =
        { .addr = "bob example org",
          .keydatacount = 99,
          .timestamp = 1508774054,
-         .keybyte = '\133' },
-       { .addr = NULL }, /* sentinel */
+         .keybyte = '\133' };
+
+const static struct _ah_gen_test carol_addr =
+       { .addr = "carol example org",
+         .keydatacount = 108,
+         .timestamp = 1508774054,
+         .keybyte = '\131' };
+
+const static struct _ah_gen_test bob_incomplete =
+       { .addr = "bob example org",
+         .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_parse_test parse_test_data[] = {
        { .name = "simple",
-         .acheaders = alice_addr,
-         .gossipheaders = no_addrs,
+         .acheader = &alice_addr,
+         .gossipheaders = NULL,
          .msg = "From: alice example org\r\n"
          "To: bob example org\r\n"
          "Subject: A lovely day\r\n"
@@ -191,32 +213,235 @@ const static struct _ah_parse_test parse_test_data[] = {
          "Isn't it a lovely day?\r\n",
        },
        
-       { .name = "simple+gossip",
-         .acheaders = alice_addr,
-         .gossipheaders = bob_addr,
+       { .name = "simple+onegossip",
+         .acheader = &alice_addr,
+         .gossipheaders = just_bob,
          .msg = "From: alice example org\r\n"
          "To: bob example org, carol example org\r\n"
          "Subject: A gossipy lovely day\r\n"
-         "Message-Id: <lovely-gossip-day example net>\r\n"
+         "Message-Id: <simple-one-gossip example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
          "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " 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=bob example org; keydata=W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
          " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
          " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
-         "Mime-Version: 1.0\r\n"
-         "Content-Type: text/plain\r\n"
          "\r\n"
          "Isn't a lovely day?  Now Carol can encrypt to Bob, hopefully.\r\n",
        },
        
-       { .name = "simple+badgossip",
-         .acheaders = alice_addr,
+       { .name = "simple+nogossip",
+         .acheader = &alice_addr,
          .gossipheaders = no_addrs,
          .msg = "From: alice example org\r\n"
          "To: bob example org, carol example org\r\n"
          "Subject: A gossipy lovely day\r\n"
+         "Message-Id: <simple-no-gossip example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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: alice example org\r\n"
+         "To: bob example org, carol example org\r\n"
+         "Subject: A gossipy lovely day\r\n"
+         "Message-Id: <simple-full-gossip example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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=bob example org; keydata=W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         "Autocrypt-Gossip: addr=carol example org; keydata=WVlZWVlZWVlZWVlZWVlZWVlZWVlZ\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: alice example org\r\n"
+         "To: bob example org, carol example org\r\n"
+         "Subject: A gossipy lovely day\r\n"
+         "Message-Id: <encrypted-full-gossip example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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"
+         "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: alice example org\r\n"
+         "To: bob example org\r\n"
+         "Subject: A gossipy lovely day\r\n"
+         "Message-Id: <simple-excess-gossip example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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=bob example org; keydata=W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         " W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tb\r\n"
+         "Autocrypt-Gossip: addr=carol example org; keydata=WVlZWVlZWVlZWVlZWVlZWVlZWVlZ\r\n"
+         " 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: alice example org\r\n"
+         "To: bob example org, carol example org\r\n"
+         "Subject: A gossipy lovely day\r\n"
          "Message-Id: <lovely-badgossip-day example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
          "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
@@ -235,8 +460,8 @@ const static struct _ah_parse_test parse_test_data[] = {
        },
        
        { .name = "duplicate",
-         .acheaders = alice_incomplete,
-         .gossipheaders = no_addrs,
+         .acheader = &alice_incomplete,
+         .gossipheaders = NULL,
          .msg = "From: alice example org\r\n"
          "To: bob example org\r\n"
          "Subject: A lovely day\r\n"
@@ -253,53 +478,53 @@ const static struct _ah_parse_test parse_test_data[] = {
          "\r\n"
          "Duplicate Autocrypt headers should cause none to match?\r\n",
        },
-       
-       { .name = "unknown type",
-         .acheaders = alice_incomplete,
-         .gossipheaders = no_addrs,
+
+       { .name = "unrecognized critical attribute",
+         .acheader = &alice_incomplete,
+         .gossipheaders = NULL,
          .msg = "From: alice example org\r\n"
          "To: bob example org\r\n"
          "Subject: A lovely day\r\n"
-         "Message-Id: <unknown-type example net>\r\n"
+         "Message-Id: <unknown-critical-attribute example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
-         "Autocrypt: addr=alice example org; type=1532633; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         "Autocrypt: addr=alice example org; emergency=true; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          "Mime-Version: 1.0\r\n"
          "Content-Type: text/plain\r\n"
          "\r\n"
-         "Unknown Autocrypt type value should cause nothing to match\r\n",
+         "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
        },
-       
-       { .name = "unknown type + type1",
-         .acheaders = alice_addr,
-         .gossipheaders = no_addrs,
+
+       { .name = "unrecognized critical attribute + simple",
+         .acheader = &alice_addr,
+         .gossipheaders = NULL,
          .msg = "From: alice example org\r\n"
          "To: bob example org\r\n"
          "Subject: A lovely day\r\n"
-         "Message-Id: <unknown-type+type1 example net>\r\n"
+         "Message-Id: <unknown-critical+simple example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
          "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
-         "Autocrypt: addr=alice example org; type=1532633; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         "Autocrypt: addr=alice example org; emergency=true; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          "Mime-Version: 1.0\r\n"
          "Content-Type: text/plain\r\n"
          "\r\n"
-         "Unknown Autocrypt type value should cause nothing to match but should not block type=1\r\n",
+         "Unknown Autocrypt critical attribute should cause nothing to match but should not block a classic 
type header\r\n",
        },
        
-       { .name = "unrecognized critical attribute",
-         .acheaders = alice_incomplete,
-         .gossipheaders = no_addrs,
+       { .name = "unrecognized non-critical attribute",
+         .acheader = &alice_addr,
+         .gossipheaders = NULL,
          .msg = "From: alice example org\r\n"
          "To: bob example org\r\n"
          "Subject: A lovely day\r\n"
          "Message-Id: <unknown-critical-attribute example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
-         "Autocrypt: addr=alice example org; emergency=true; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         "Autocrypt: addr=alice example org; _not_an_emergency=true; 
keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          "Mime-Version: 1.0\r\n"
@@ -307,22 +532,76 @@ const static struct _ah_parse_test parse_test_data[] = {
          "\r\n"
          "An unrecognized attribute that does not start with _ is critical and should not cause a match\r\n",
        },
-       
-       { .name = "unrecognized non-critical attribute",
-         .acheaders = alice_addr,
-         .gossipheaders = no_addrs,
-         .msg = "From: alice example org\r\n"
-         "To: bob example org\r\n"
+
+       { .name = "no From: at all",
+         .acheader = NULL,
+         .gossipheaders = NULL,
+         .msg = "To: carol example org\r\n"
          "Subject: A lovely day\r\n"
-         "Message-Id: <unknown-critical-attribute example net>\r\n"
+         "Message-Id: <no-from example net>\r\n"
          "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
-         "Autocrypt: addr=alice example org; _not_an_emergency=true; 
keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
          " 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",
+         "This message has no sender at all\r\n",
+       },
+
+       { .name = "with Sender: header",
+         .acheader = &bob_incomplete,
+         .gossipheaders = NULL,
+         .msg = "From: bob example org\r\n"
+         "Sender: alice example org\r\n"
+         "To: carol example org\r\n"
+         "Subject: A lovely day\r\n"
+         "Message-Id: <with-sender-header example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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: carol example org\r\n"
+         "Subject: A lovely day\r\n"
+         "Message-Id: <no-senders example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " 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: alice example org, bob example org\r\n"
+         "To: carol example org\r\n"
+         "Subject: A lovely day\r\n"
+         "Message-Id: <two-senders example net>\r\n"
+         "Date: Mon, 23 Oct 2017 11:54:14 -0400\r\n"
+         "Autocrypt: addr=alice example org; keydata=CwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         " CwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsL\r\n"
+         "Autocrypt: addr=bob example org; 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",
        },
 };
 
@@ -360,46 +639,83 @@ test_ah_message_parse (void)
 {
        guint i;
        for (i = 0; i < G_N_ELEMENTS (parse_test_data); i++) {
-               GMimeAutocryptHeaderList *ahl_expected = NULL;
-               GMimeAutocryptHeaderList *ahl_got = NULL;
+               GMimeAutocryptHeader *ah_expected = NULL;
+               GMimeAutocryptHeader *ah_got = NULL;
                GMimeAutocryptHeaderList *gossip_expected = NULL;
                GMimeAutocryptHeaderList *gossip_got = NULL;
                GMimeMessage *message = NULL;
+               GMimeObject *innerpart = NULL;
                const struct _ah_parse_test *test = parse_test_data + i;
                try {
                        testsuite_check ("Autocrypt message[%u] (%s)", i, test->name);
 
-                       ahl_expected = _gen_header_list (test->acheaders);
-                       gossip_expected = _gen_header_list (test->gossipheaders);
-
                        /* make GMimeMessage from test->msg */
                        GMimeStream *stream = g_mime_stream_mem_new_with_buffer (test->msg, 
strlen(test->msg));
                        GMimeParser *parser = g_mime_parser_new_with_stream (stream);
                        message = g_mime_parser_construct_message (parser, NULL);
                        g_object_unref (parser);
                        g_object_unref (stream);
+                       /* make GMimeObject from test->innerpart */
+                       if (test->innerpart) {
+                               stream = g_mime_stream_mem_new_with_buffer (test->innerpart, 
strlen(test->innerpart));
+                               parser = g_mime_parser_new_with_stream (stream);
+                               innerpart = g_mime_parser_construct_part (parser, NULL);
+                               g_object_unref (parser);
+                               g_object_unref (stream);
+                       }
+                       
+
+                       /* check on autocrypt header */
+                       ah_expected = _gen_header (test->acheader);
+                       ah_got = g_mime_message_get_autocrypt_header (message, NULL);
+                       if (!ah_got && ah_expected)
+                               throw (exception_new ("failed to extract Autocrypt header from message!"));
+                       if (ah_got && !ah_expected)
+                               throw (exception_new ("extracted Autocrypt header when we shouldn't!\n%s\n",
+                                                     g_mime_autocrypt_header_get_string (ah_got)));
+                       if (ah_expected)
+                               if (g_mime_autocrypt_header_compare (ah_expected, ah_got))
+                                       throw (exception_new ("Autocrypt header did not match"));
 
-                       ahl_got = g_mime_message_get_autocrypt_headers (message, 1, NULL);
-                       if (!ahl_got)
-                               throw (exception_new ("failed to extract headers from message!"));
-                       gossip_got = g_mime_message_get_autocrypt_gossip_headers (message, 1, NULL);
-                       if (!gossip_got)
-                               throw (exception_new ("failed to extract gossip headers from message!"));
-                       gchar *err = NULL;
-                       err = _acheaderlists_compare (ahl_expected, ahl_got);
-                       if (err)
-                               throw (exception_new ("sender headers: %s", err));
-                       err = _acheaderlists_compare (gossip_expected, gossip_got);
-                       if (err)
-                               throw (exception_new ("gossip headers: %s", err));
+                       /* check on gossip */
+                       if (test->gossipheaders)
+                               gossip_expected = _gen_header_list (test->gossipheaders);
+                       if (innerpart) {
+                               gossip_got = g_mime_message_get_autocrypt_gossip_headers_from_inner_part 
(message, NULL, innerpart);
+                       } else {
+                               GMimeObject *obj = g_mime_message_get_mime_part (message);
+                               if (GMIME_IS_MULTIPART_ENCRYPTED (obj)) {
+#ifdef ENABLE_CRYPTO
+                                       GError *err = NULL;
+                                       gossip_got = g_mime_message_get_autocrypt_gossip_headers (message, 
NULL, GMIME_DECRYPT_NONE, NULL, &err);
+                                       if (!gossip_got && err) {
+                                               fprintf (stderr, "%s", test->msg);
+                                               Exception *ex;
+                                               ex = exception_new ("%s", err->message);
+                                               g_error_free (err);
+                                               throw (ex);
+                                       }
+#endif
+                               }
+                       }
+                       if (!gossip_got && gossip_expected)
+                               throw (exception_new ("failed to extract Autocrypt gossip headers from 
message!"));
+                       if (gossip_got && !gossip_expected)
+                               throw (exception_new ("extracted Autocrypt gossip headers when we 
shouldn't!\n"));
+                       if (gossip_expected) {
+                               gchar *err = NULL;
+                               err = _acheaderlists_compare (gossip_expected, gossip_got);
+                               if (err)
+                                       throw (exception_new ("gossip headers: %s", err));
+                       }
                        testsuite_check_passed ();
                } catch (ex) {
                        testsuite_check_failed ("autocrypt message parse[%u] (%s) failed: %s", i, test->name, 
ex->message);
                } finally;
-               if (ahl_expected)
-                       g_object_unref (ahl_expected);
-               if (ahl_got)
-                       g_object_unref (ahl_got);
+               if (ah_expected)
+                       g_object_unref (ah_expected);
+               if (ah_got)
+                       g_object_unref (ah_got);
                if (gossip_expected)
                        g_object_unref (gossip_expected);
                if (gossip_got)
@@ -408,15 +724,144 @@ test_ah_message_parse (void)
 }
 
 
+static void
+import_secret_key (void)
+{
+       /* generated with GnuPG via:
+        *
+        * export GNUPGHOME=$(mktemp -d)
+        * gpg --pinentry-mode loopback --passphrase '' --batch --quick-gen-key $(uuidgen)@autocrypt.org
+        * gpg --command-fd 3 --edit-key autocrypt.org expire  3<<<0
+        * gpg --command-fd 3 --edit-key autocrypt.org clean
+        * gpg --armor --export-secret-keys autocrypt.org | sed -e 's/^/\t\t"/' -e 's/$/\\n"/'
+        */
+       const char secret_key[] =
+               "-----BEGIN PGP PRIVATE KEY BLOCK-----\n"
+               "\n"
+               "lQVYBFoDAZIBDAC6ZV+A42SHxfH5W4e6QM2PlVwrF6cRDbxCg0SFUNgsNtQknqcB\n"
+               "iwdujOnc0rgqW2BMek+mfZaACSA4l5rEiDfowQFV7aogkySDcH9/2gDJ+0b/j8z0\n"
+               "q0cM3nDzYggsrHdgGE3KkIW2+tf8eaB5Mjxt1VekT4AkS/IvES6Qoo22G39XG51V\n"
+               "HS4wproDgiwxpVX/L9m+IXDJmpD90UQlIJ1kpWX3Tt9eiX+vFeo3ohxyVy8ICn9n\n"
+               "K5Ve42e93dfXfcLQg3EKOdxI/jop7J+IOOEyHmQXZ3QrVeiIRYsUpc+Clk6TM5Nw\n"
+               "JpwtNy1DFUt+bCqx90X30L1IlboK2eNte8n+x6DyhopBpQwjHAvV4qX87X2NYETl\n"
+               "1iua051J6chXFZcP1b/Re0CtiKmv26d1HOsPF2ocAf2Ssx7e7aeXP0vF2r/btY02\n"
+               "Cndrj0gy6xMbQ+EymE4cs+1h/anesuYE38XCLJdTjzbnBuG3sCHZ7v83Xn0d7XTy\n"
+               "mgZVaxbaYJlIGq8AEQEAAQAL/0xL9n3BloLlCZkyWCprIDlnv+R7uA0I/EiVhtSz\n"
+               "NOlUQB4FOwMsr4wW7htPvcbIxHBJmJTjz1j1Y1UG6XkM8SW66xsLP5o54LZUtDvX\n"
+               "Nn929abia9iyy1B/NOjK9eGjbvHMwPrrkXBG2WYlOwShBY9HxqohSKiS1b1iYRcf\n"
+               "Era6JrO3P/15BlEvzfBltkVUEhF0usJS2eIL/NGIeUZhRUvPUB+dD12ZFsTKSacg\n"
+               "GljLSxsVgPTwKCJBH1PenN0+Qm6FqUUJzhhwqHvU6Qf8qZIr3cKq9XCAyRlFCZR4\n"
+               "42WXMMA9b0R4ZhE5l2iNN0B8lyhr1UpRrb8//8E80nDVPiWaT3c9vxACDNS24+Sc\n"
+               "1KIXX1Owl7+V15kJKefL5Lh6nCfQFz5iVI1m/8W40wgRO6rak/WiJdfOFXlWdzGN\n"
+               "1PVY6U7Rk96FygCEPkTCy3NgUhMikkfC/+jEpz75M2k7hYX6vft4u3zRBKufEWpx\n"
+               "PFMbp4i3DckEyURbhDisQFDsoQYAxovBi/jl3LMJe5efkV0W/kUCZJl5AGgWzgET\n"
+               "1EJo91dW6YCUoikIxByBeyzQ3c/uu1G6ly5e9eSiqXdixtQoOFDsITqCAcxrbquQ\n"
+               "0RIrcyAVOMO2v5DqPBRLqxzg5pJfzerx8jyO3xOCrCNdWP25HFpYM3rvdrHXh3TO\n"
+               "wGvqmLUPO+jKEiAspRM9U74RLmfIRc+6/V9OwnmaCBM4nCCS0FLo40E/KFne6kZn\n"
+               "xnqlK2o92IYrD/SrEVI4yZQAhj5HBgDwVY1hQH5jmjdgH6CVS0Zxtk1iyhYpDnDi\n"
+               "iZstDLuCk0k0G9u5vlbCS8+zBanPi07xwE5DRVLhtI4twHJb4Qh84gka/tOc12rW\n"
+               "QyooBWRjyHhGt70x1yzh6vqAuQy629hR48ogbXI70xAuS4LrlW9nd0iu2UhNjYd5\n"
+               "3ZPhoz1qxuwA0w3Xf47yjTs+v4dG932vVvFFL1QrxfJL/4X+FescJGwhzBQhROIB\n"
+               "sSL6I+qLERlYAbVWruCvXzGVf9e17FkGAN5AWq9OcFsMpOmUmngSjydIpphKN/u3\n"
+               "OuReO4M4f52HShxFPi0aA+IieJGEA0wNaDHsQG8G2W7anKwYETg0l9FpoHNijS94\n"
+               "GUCCaIuDtRI8y26d4OnFxrL3A5Nnez/i4uzI5PKoiHw8n4CthEQB7Ucqsq1BbkEC\n"
+               "wFQhTK9FhGqhnrEPxDJsEhyPoNPIa9lqqXi3rq24w+AXnSmAqByTrIWj/H6s4DJq\n"
+               "c1qIc9F9i/aJIyq1+fTrBPMRgC82GFB/NdpYtDI0YmMzODg4ZS0wNTZiLTRkYTkt\n"
+               "YmQxMi0wMmE4ZWRiZTIzYmJAYXV0b2NyeXB0Lm9yZ4kBzgQTAQoAOAIbAwULCQgH\n"
+               "AgYVCgkICwIEFgIDAQIeAQIXgBYhBA0hHcXZ9FZycawFgtjez7/JNGzUBQJaAwG3\n"
+               "AAoJENjez7/JNGzUxaAMAJMFg7xwU2fAI4kF21edZiT5gah1cbsSTmAQz5PMz2BL\n"
+               "6iufDkdhBseMjMc4ZFCgfBRH/n0ZJPqSgKHieaxBLUlyQITuyrLVV0UslPMe5PLu\n"
+               "x2FMMoxDQoIuPbb0yMDIs9kxiPViAgOQwRhsud1K1u0S3u/isix6SdYor2sEfbr/\n"
+               "JReZ5LFyA2PZebKpYRSMBOASeneYhQ8q4AZZKUQgxMQSQTHP/0ABVg/80o6NDqGp\n"
+               "Ll5pFGCQQlwmHMZhXZ5DWMqbqwEdB1LjvmNhAfl1Pw6Q8V3lPz1gjB+FbG/sj74G\n"
+               "HgI+Yn0R57DxR0JWS68lHcCHXn+d6GkBrVLKSRc9QA8GAFjntxQM8y5fUETYJ9bs\n"
+               "EFzrPOkxUM0tTgtN4gTspTMdGv89Cd3MPU9recRzcMlqAXH/R0P6Oz8+l91sI/kN\n"
+               "K8rFbqiGTPZB0uUiB397YkFDnZNT5mvnxMSVP4QwtM5wb1PPqgxQI5oDUepa3r56\n"
+               "g5vq6SecDkEFP0Cm0qbbsp0FWARaAwGSAQwAoa7hUcD18PsB4QbvzUGBL9uwcdeD\n"
+               "2m9yEY/ZNQscAYlipAYrmKBhIEIy7DLgDncM+IQ47Gf0tcIFRxT0bQxgUEAHlgRN\n"
+               "D6aPCoswnX+IsPy9M5ZHh4LldkMldmVgs/iAtJ8+esi6V39073FhL191coBxuBFB\n"
+               "fMo0iW+HmMBnX1jhTffSRUntdQTRMEGYGsmPkcFBgL6UFLePP2bwNOs9v0gdgnEK\n"
+               "9u7l+y5cLc0HnbN6sEKCjT+HWQBFeBS4Nhff+pcw6ToFm+2LrxUpgt+URePC0wnr\n"
+               "WWmsCPEeNs4SreYvn6zglDoqhfBSg7f+8DXY2rL2M+KKFPIS11t+e2Irzy0Xj7Iv\n"
+               "V5NvCv/DrC4oBFzreL0jP4u4+z2GoadpUiqwPMq99TLY3v87KD3Zds5+W+jEzs1r\n"
+               "py+UEZdQI1n46oFrvzh97/ASkxmuhXu0A/As1T9nxY6V7+Y1SuGSpDnhrWSHq04O\n"
+               "bsqZoFa8sIciY5tNAP+NpACKcMuQUkfEwNWjABEBAAEAC/sFbXFy5R9cb5ColSsH\n"
+               "oONNT/qkV6+9bXBO1p1cAnt2Mb518x8TiI66Hn7HHw4WbjipPwcKKJM2ZsT2leV+\n"
+               "o6O4De4zQwGzPMwgdnuzTcyw5EsVqD3Odr5tMePYDZ8pa3YmmhHm3UYkGcs7Hns8\n"
+               "s9+lcFpg63NfVQpecrgCkLLnqHwnueH9IXYvL3I2RH1uqMWBBxPD1qHx8BeG8VWu\n"
+               "8RapjSowbssHbw2ZWP0PLIrM6HF96T2osDFC85dhaJCmgDae7IO06K/akf1qNUMR\n"
+               "kFsXBBnduADzNNxiZofeThejDurnBsOPKH/wxiKEV+vDeyXGghyqy7WuWhBFAjZX\n"
+               "xNQD+Un9pTc4V6EIX4RZex7gfu7ylXIiDZr+yZO3L2OsbnvUSyGYOJnEQNvl/bPN\n"
+               "OBS10U/BW1wgkJmDwOXkleH3lSVHC7p7A5nxlUrT0mvrOu04cl6Tc5/hO2+/N9Oy\n"
+               "9z5TrVECgv7HGG5tA0jWS2LeXyvOtUZuUtcdMirQUq+W8gEGAMA7PuGIreU09Dl5\n"
+               "9Agg09+rHvLHRjuDQtyv9M5tnzOS01FWDCMBNnqj4hwJCzwZ1/s6y/ng6K2KEWmi\n"
+               "XHR07gFBBRyNiQ6sAtZj5Ve5xdG4dW/t49ZabO++eJrLOfU7RFNhbyFm6aZcebva\n"
+               "adGhDbisG4exQIB+ggiNOxP2iSTs1jgVWfuAtu4p9SfXXyESRer4VDpUN/N5lwIO\n"
+               "jpWCsPNT3N4fkUxNNjiHV3anpY7jEr0fNx+cGPswfIGr+NgUgwYA11FmOmGpQTZE\n"
+               "HrhiTLtq1CmkmFzdh1j103qdNWn/6UWiXQoxxmTPlyiufM+Fdd18fbyuxoegO67q\n"
+               "Q4sg3ZkE7PSv5bzAome6ZtBheNJdH1kfSIfzInlSDNN57G7vndkMosiBoyJHF7+Y\n"
+               "mOLDFkvvfF501huHyQz9DHt2KuBYy0TkKtw2uCYrkIuahCEagx4VEWxtKihKzX4x\n"
+               "DyhR7cqWyXdz5dmgRBg1KE8uHmKcRvvuLWiVZnSRGyiTD0U7bbBhBgC19HjukV37\n"
+               "shzXIjuLVwMeuLdx2NS4PyHjwroFcORO4wzZCUIUqv1IgXb0kWra0lmxEnSCkngq\n"
+               "YpYpUFDjXM+mKYvumuPZzAT/3P3+aKMq4QpCYKqVdLHCNWpFJpGzyH+wvvBK4YQe\n"
+               "PSVKZi1yu4aRtg7JHjGVIdesP3PdE/EMvglWaFCJjdz2ehVH0f7JYCSMcxcwvoTE\n"
+               "PQK2z66Y2xFjbk3s+rJrg1txsNLryreGJMWk0OO+uEbcM085rXSUUi7ekokBtgQY\n"
+               "AQoAIBYhBA0hHcXZ9FZycawFgtjez7/JNGzUBQJaAwGSAhsMAAoJENjez7/JNGzU\n"
+               "KFMMAJLojGZv1C5nj6UC1tOq993wUVtq09gevHCl9/wzFw/bf85TjGOqo2hC7jb7\n"
+               "KrzyhJSb3rxMWs8kfbyfZdcLPI3qtq3S6WcdCPLzaJsa+YcnAnE7dvpXavjF9cHz\n"
+               "EAIGkxhBGB4xZOOLQecx60tUDQE64AhoDsIsi+ofMZFJgePTBrlLhHxChZqb8S6d\n"
+               "SYYvs2k2r1gdhansk2o93G8nYksCe0ukZ7tqSywtmgce/ruPDRz+PI1OpS7SNX7D\n"
+               "05YotAuuJD6D5yQaxpaqD5FxXQzPcvUU20mscQwS9MtjgOfyy3EauAS5BS/peB2O\n"
+               "Gvg8DPfF5P2+/Eez1lEPNLYcbjzFOAItEUYrgdgpB5vg2VCRkLznZdWFXhh2KmvU\n"
+               "NOiQoDWThtanLPNFXe3vxr+g0lSgMkJaT2yo+TciqZOvPRUs+TzDwWRMzVQe77PZ\n"
+               "aJ/L2xxnGXtLsZ2V9rf/4VaXeky2HYl5UmwM5kr/3jiN5MdNtVh2sfIwOvzLRKaF\n"
+               "7MLMmw==\n"
+               "=HQjA\n"
+               "-----END PGP PRIVATE KEY BLOCK-----\n"
+               ;
+       GMimeCryptoContext *ctx = g_mime_gpg_context_new ();
+       GMimeStream *stream = g_mime_stream_mem_new_with_buffer (secret_key, sizeof(secret_key));
+       GError *err = NULL;
+
+       testsuite_check ("Importing secret key");
+       
+       g_mime_crypto_context_import_keys (ctx, stream, &err);
+       g_object_unref (stream);
+       g_object_unref (ctx);
+
+       if (err != NULL) {
+               Exception *ex;
+               ex = exception_new ("%s", err->message);
+               g_error_free (err);
+               throw (ex);
+       }
+       testsuite_check_passed ();
+}
+
 int main (int argc, char **argv)
 {
+       char *gpg;
+
        g_mime_init ();
        
        testsuite_init (argc, argv);
+
+       if (!(gpg = g_find_program_in_path ("gpg2")))
+               if (!(gpg = g_find_program_in_path ("gpg")))
+                       return EXIT_FAILURE;
+       
+       if (testsuite_setup_gpghome (gpg) != 0)
+               return EXIT_FAILURE;
        
        testsuite_start ("Autocrypt: generate headers");
        test_ah_generation ();
        testsuite_end ();
+
+#ifdef ENABLE_CRYPTO
+       testsuite_start ("Autocrypt: import OpenPGP secret key");
+       import_secret_key ();
+       testsuite_end ();
+#endif
        
        testsuite_start ("Autocrypt: parse messages");
        test_ah_message_parse ();
@@ -424,5 +869,10 @@ int main (int argc, char **argv)
        
        g_mime_shutdown ();
        
+#ifdef ENABLE_CRYPTO
+       if (testsuite_destroy_gpghome () != 0)
+               return EXIT_FAILURE;
+#endif
+       
        return testsuite_exit ();
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]