[balsa/gmime3: 52/53] GMime3 (3.2, at least) supports Autocrypt headers



commit 0d738215f00c744236b43bc32509ed21342abaaa
Author: Albrecht Dreß <albrecht dress arcor de>
Date:   Thu Nov 28 14:32:16 2019 -0500

    GMime3 (3.2, at least) supports Autocrypt headers
    
    so we can actually simplify our code a little bit.
    
    Patch details:
    - libbalsa/autocrypt.c:
      * drop obsolete functions scan_autocrypt_headers(),
        parse_autocrypt_header(), eval_autocrypt_attr();
      * simplify autocrypt_from_message() by accessing GMimeAutocryptHeader
      * simplify autocrypt_header() by creating a GMimeAutocryptHeader
      * new function extract_ac_keydata() for validating the gpg key from
        GMimeAutocryptHeader, and for extracting the fingerprint and expiry date
      * add_or_update_user_info(): use data from GMimeAutocryptHeader and
        extract_ac_keydata()
      * update_last_seen(): use data from GMimeAutocryptHeader
    - libbalsa/libbalsa-gpgme-keys.[ch]: change
        libbalsa_gpgme_export_autocrypt_key() to return GBytes as to avoid
        extra conversions
    - libbalsa/message.[ch]: extract and free GMimeAutocryptHeader

 libbalsa/autocrypt.c           | 406 ++++++++++++++---------------------------
 libbalsa/libbalsa-gpgme-keys.c |   6 +-
 libbalsa/libbalsa-gpgme-keys.h |   8 +-
 libbalsa/message.c             |  17 ++
 libbalsa/message.h             |   5 +
 5 files changed, 164 insertions(+), 278 deletions(-)
---
diff --git a/libbalsa/autocrypt.c b/libbalsa/autocrypt.c
index a424f379f..b993ccaed 100644
--- a/libbalsa/autocrypt.c
+++ b/libbalsa/autocrypt.c
@@ -91,23 +91,24 @@ enum {
 };
 
 
+typedef struct {
+       gconstpointer keydata;
+       gsize keysize;
+       gchar *fingerprint;
+       gint64 expires;
+} ac_key_data_t;
+
+
 static void autocrypt_close(void);
-static AutocryptData *scan_autocrypt_headers(GList * const  header_list,
-                                                                                        const gchar   
*from_addr)
-       G_GNUC_WARN_UNUSED_RESULT;
-static AutocryptData *parse_autocrypt_header(const gchar *value)
-       G_GNUC_WARN_UNUSED_RESULT;
-static gboolean eval_autocrypt_attr(const gchar   *attr,
-                                                                       const gchar   *value,
-                                                                       gboolean      *seen,
-                                                                       AutocryptData *target);
-static void add_or_update_user_info(const AutocryptData  *user_info,
-                                                                       time_t                date_header,
-                                                                       gboolean              update,
-                                                                       GError              **error);
-static void update_last_seen(const gchar  *addr,
-                                                        time_t        date_header,
-                                                        GError      **error);
+static gboolean extract_ac_keydata(GMimeAutocryptHeader  *autocrypt_header,
+                                                                  ac_key_data_t         *dest,
+                                                                  GError               **error);
+static void add_or_update_user_info(GMimeAutocryptHeader    *autocrypt_header,
+                                                                       const ac_key_data_t     *ac_key_data,
+                                                                       gboolean                 update,
+                                                                       GError                  **error);
+static void update_last_seen(GMimeAutocryptHeader  *autocrypt_header,
+                                                        GError                   **error);
 static AutocryptData *autocrypt_user_info(const gchar  *mailbox,
                                                                                  GError      **error)
        G_GNUC_WARN_UNUSED_RESULT;
@@ -199,17 +200,22 @@ void
 autocrypt_from_message(LibBalsaMessage  *message,
                                           GError          **error)
 {
-       const gchar *from_addr;
-       AutocryptData *autocrypt;
-        LibBalsaMessageHeaders *headers;
+       LibBalsaMessageHeaders *headers;
+       ac_key_data_t ac_key_data;
+       time_t ac_header_time;
 
        g_return_if_fail(LIBBALSA_IS_MESSAGE(message));
-        headers = libbalsa_message_get_headers(message);
+       headers = libbalsa_message_get_headers(message);
        g_return_if_fail(headers != NULL);
-       g_return_if_fail(headers->from != NULL);
-       g_return_if_fail(headers->content_type != NULL);
        g_return_if_fail(autocrypt_db != NULL);
 
+       /* return silently if there is no gmime autocrypt header
+        * note that it will *always* contain a valid sender address and effective date if it exists, so 
there is no need to validate
+        * the data returned from the g_mime_autocrypt_header_get_* accessor functions, except for the key 
data */
+       if (headers->autocrypt_hdr == NULL) {
+               return;
+       }
+
        // FIXME - we should ignore spam - how can we detect it?
 
        /* check for content types which shall be ignored
@@ -221,47 +227,36 @@ autocrypt_from_message(LibBalsaMessage  *message,
                return;
        }
 
-       /* check for exactly one From: mailbox address - others shall be ignored */
-       if ((internet_address_list_length(headers->from) != 1) ||
-               !INTERNET_ADDRESS_IS_MAILBOX(internet_address_list_get_address(headers->from, 0))) {
-               g_debug("require exactly one From: address, ignored");
-               return;
-       }
-
-       /* ignore messages without a Date: header or with a date in the future */
-       if ((headers->date == 0) || (headers->date > time(NULL))) {
-               g_debug("no Date: header or value in the future, ignored");
-               return;
-       }
-
-       /* get the From: address (is a mailbox, checked above) */
-       from_addr =
-               
internet_address_mailbox_get_addr(INTERNET_ADDRESS_MAILBOX(internet_address_list_get_address(headers->from, 
0)));
-       g_debug("message from '%s', date %ld", from_addr, headers->date);
-
-       /* scan for Autocrypt headers */
-       autocrypt = scan_autocrypt_headers(headers->user_hdrs, from_addr);
+    /* ignore messages without a Date: header or with a date in the future */
+       ac_header_time = 
g_date_time_to_unix(g_mime_autocrypt_header_get_effective_date(headers->autocrypt_hdr));
+    if (ac_header_time > time(NULL)) {
+       g_debug("no Date: header or value in the future, ignored");
+       return;
+    }
 
     /* update the database */
     G_LOCK(db_mutex);
-    if (autocrypt != NULL) {
+    if (extract_ac_keydata(headers->autocrypt_hdr, &ac_key_data, error)) {
        AutocryptData *db_info;
 
-       db_info = autocrypt_user_info(autocrypt->addr, error);
+       db_info = autocrypt_user_info(g_mime_autocrypt_header_get_address_as_string(headers->autocrypt_hdr), 
error);
        if (db_info != NULL) {
-               if (headers->date > db_info->ac_timestamp) {
-                       add_or_update_user_info(autocrypt, headers->date, TRUE, error);
+               if (ac_header_time > db_info->ac_timestamp) {
+                       add_or_update_user_info(headers->autocrypt_hdr, &ac_key_data, TRUE, error);
                } else {
                        g_info("message timestamp %ld not newer than autocrypt db timestamp %ld, ignore 
message",
                                (long) headers->date, (long) db_info->ac_timestamp);
                }
                autocrypt_free(db_info);
        } else {
-               add_or_update_user_info(autocrypt, headers->date, FALSE, error);
+               add_or_update_user_info(headers->autocrypt_hdr, &ac_key_data, FALSE, error);
        }
-       autocrypt_free(autocrypt);
+       g_free(ac_key_data.fingerprint);
     } else {
-       update_last_seen(from_addr, headers->date, error);
+       /* note: we update the last seen db field if there is no key (i.e. the message did not contain an 
Autocrypt: header) *and*
+        * if the key data is broken, or gpgme failed to handle it for some other reason.  We /might/ want to 
distinguish between
+        * these two cases. */
+        update_last_seen(headers->autocrypt_hdr, error);
     }
     G_UNLOCK(db_mutex);
 }
@@ -274,9 +269,9 @@ autocrypt_header(LibBalsaIdentity *identity, GError **error)
        const gchar *mailbox;
        gchar *use_fpr = NULL;
        gchar *result = NULL;
-        InternetAddress *ia;
-        const gchar *force_gpg_key_id;
-        AutocryptMode autocrypt_mode;
+       InternetAddress *ia;
+       const gchar *force_gpg_key_id;
+       AutocryptMode autocrypt_mode;
 
        g_return_val_if_fail(identity != NULL, NULL);
        autocrypt_mode = libbalsa_identity_get_autocrypt_mode(identity);
@@ -286,7 +281,7 @@ autocrypt_header(LibBalsaIdentity *identity, GError **error)
        mailbox = internet_address_mailbox_get_addr(INTERNET_ADDRESS_MAILBOX(ia));
 
        /* no key fingerprint has been passed - try to find the fingerprint of a secret key matching the 
passed mailbox */
-        force_gpg_key_id = libbalsa_identity_get_force_gpg_key_id(identity);
+       force_gpg_key_id = libbalsa_identity_get_force_gpg_key_id(identity);
        if ((force_gpg_key_id == NULL) || (force_gpg_key_id[0] == '\0')) {
                gpgme_ctx_t ctx;
 
@@ -317,25 +312,24 @@ autocrypt_header(LibBalsaIdentity *identity, GError **error)
        }
 
        if (use_fpr != NULL) {
-               gchar *keydata;
+               GBytes *keydata;
 
                keydata = libbalsa_gpgme_export_autocrypt_key(use_fpr, mailbox, error);
                g_free(use_fpr);
                if (keydata != NULL) {
-                       GString *buffer;
-                       gssize ins_fws;
+                       GMimeAutocryptHeader *header;
 
-                       buffer = g_string_new(NULL);
-                       g_string_append_printf(buffer, "addr=%s;", mailbox);
+                       header = g_mime_autocrypt_header_new();
+                       g_mime_autocrypt_header_set_address_from_string(header, mailbox);
                        if (autocrypt_mode == AUTOCRYPT_PREFER_ENCRYPT) {
-                               g_string_append(buffer, "prefer-encrypt=mutual;");
-                       }
-                       g_string_append_printf(buffer, "keydata=%s", keydata);
-                       for (ins_fws = 66; ins_fws < (gssize) buffer->len; ins_fws += 78) {
-                               g_string_insert(buffer, ins_fws, "\n\t");
+                               g_mime_autocrypt_header_set_prefer_encrypt(header, 
GMIME_AUTOCRYPT_PREFER_ENCRYPT_MUTUAL);
+                       } else {
+                               g_mime_autocrypt_header_set_prefer_encrypt(header, 
GMIME_AUTOCRYPT_PREFER_ENCRYPT_NONE);
                        }
-                       result = g_string_free(buffer, FALSE);
-                       g_free(keydata);
+                       g_mime_autocrypt_header_set_keydata(header, keydata);
+                       g_bytes_unref(keydata);
+                       result = g_mime_autocrypt_header_to_string(header, FALSE);
+                       g_object_unref(header);
                }
        }
 
@@ -627,102 +621,58 @@ autocrypt_close(void)
 }
 
 
-/** \brief Extract Autocrypt data from message headers
- *
- * \param header_list list of headers pointing to gchar** (name, value) pairs
- * \param from_addr sender mailbox extracted from the From: header
- * \return the data extracted from the Autocrypt header, or NULL if no valid data is present
- *
- * The following rules apply according to the Autocrypt Level 1 standard:
- * - invalid Autocrypt headers are just discarded, but checking for more Autocrypt headers continues (see 
section 2.1 <em>The
- *   Autocrypt Header</em>, https://autocrypt.org/level1.html#the-autocrypt-header);
- * - if the \em addr attribute of an otherwise valid Autocrypt header does not match the mailbox extracted 
from the From: message
- *   header, the Autocrypt header shall be treated as being invalid and discarded (see section 2.1);
- * - if more than one valid Autocrypt header is present, \em all Autocrypt headers shall be discarded (see 
section 2.3 <em>Updating
- *   Autocrypt Peer State</em>, https://autocrypt.org/level1.html#updating-autocrypt-peer-state);
- *
- * Thus, this function returns a newly allocated Autocrypt data structure iff the passed headers list 
contains exactly \em one valid
- * Autocrypt header.
- */
 static AutocryptData *
-scan_autocrypt_headers(GList * const header_list, const gchar *from_addr)
+autocrypt_user_info(const gchar *mailbox, GError **error)
 {
-       GList *header;
-       AutocryptData *result = NULL;
-
-       for (header = header_list; header != NULL; header = header->next) {
-               const gchar **header_parts = (const gchar **) header->data;
-
-               if ((g_ascii_strcasecmp(header_parts[0], "Autocrypt") == 0) && (header_parts[1] != NULL)) {
-                       AutocryptData *new_data;
-
-                       new_data = parse_autocrypt_header(header_parts[1]);
-                       if (new_data != NULL) {
-                               if (result == NULL) {
-                               if (g_ascii_strcasecmp(new_data->addr, from_addr) != 0) {
-                                       g_info("Autocrypt header for '%s' in message from '%s', ignore 
header", new_data->addr, from_addr);
-                                       autocrypt_free(new_data);
-                               } else {
-                                       result = new_data;
-                               }
-                               } else {
-                                       g_info("more than one valid Autocrypt header");
-                                       autocrypt_free(result);
-                                       autocrypt_free(new_data);
-                                       return NULL;
-                               }
-                       }
-               }
-       }
+       int sqlite_res;
+       AutocryptData *user_info = NULL;
 
-       return result;
-}
+       g_return_val_if_fail((mailbox != NULL) && (autocrypt_db != NULL), NULL);
 
+       sqlite_res = sqlite3_bind_text(query[0], 1, mailbox, -1, SQLITE_STATIC);
+       if (sqlite_res == SQLITE_OK) {
+               sqlite_res = sqlite3_step(query[0]);
+               if (sqlite_res == SQLITE_ROW) {
+                       user_info = g_new0(AutocryptData, 1U);
+                       user_info->addr = g_strdup((const gchar *) sqlite3_column_text(query[0], 0));
+                       user_info->last_seen = sqlite3_column_int64(query[0], 1);
+                       user_info->ac_timestamp = sqlite3_column_int64(query[0], 2);
+                       user_info->keydata = g_bytes_new(sqlite3_column_blob(query[0], 3), 
sqlite3_column_bytes(query[0], 3));
+                       user_info->fingerprint = g_strdup((const gchar *) sqlite3_column_text(query[0], 4));
+                       user_info->expires = sqlite3_column_int64(query[0], 5);
+                       user_info->prefer_encrypt = (sqlite3_column_int(query[0], 6) != 0);
+                       sqlite_res = sqlite3_step(query[0]);
+               }
 
-static AutocryptData *
-parse_autocrypt_header(const gchar *value)
-{
-       gchar **attributes;
-       gboolean attr_seen[3] = { FALSE, FALSE, FALSE };
-       AutocryptData *new_data;
-       gint n;
-       gboolean broken;
-
-       new_data = g_new0(AutocryptData, 1U);
-       attributes = g_strsplit(value, ";", -1);
-       if (attributes == NULL) {
-               g_info("empty Autocrypt header");
-               broken = TRUE;
+               if (sqlite_res != SQLITE_DONE) {
+                       g_set_error(error, AUTOCRYPT_ERROR_QUARK, sqlite_res, _("error reading Autocrypt data 
for “%s”: %s"), mailbox,
+                               sqlite3_errmsg(autocrypt_db));
+                       autocrypt_free(user_info);
+                       user_info = NULL;
+               }
        } else {
-               broken = FALSE;
+               g_set_error(error, AUTOCRYPT_ERROR_QUARK, sqlite_res, _("error reading Autocrypt data for 
“%s”: %s"), mailbox,
+                       sqlite3_errmsg(autocrypt_db));
        }
+       sqlite3_reset(query[0]);
 
-       for (n = 0; !broken && (attributes[n] != NULL); n++) {
-               gchar **items;
+       return user_info;
+}
 
-               items = g_strsplit(attributes[n], "=", 2);
-               if ((items == NULL) || (items[0] == NULL) || (items[1] == NULL)) {
-                       g_info("bad Autocrypt header attribute");
-                       broken = TRUE;
-               } else {
-                       broken = !eval_autocrypt_attr(g_strstrip(items[0]), g_strstrip(items[1]), attr_seen, 
new_data);
-               }
-               g_strfreev(items);
-       }
-       g_strfreev(attributes);
 
-       if (!broken) {
-               if (!attr_seen[0] || !attr_seen[2]) {
-                       g_info("missing mandatory Autocrypt header attribute");
-                       broken = TRUE;
-               }
-       }
+static gboolean
+extract_ac_keydata(GMimeAutocryptHeader *autocrypt_header, ac_key_data_t *dest, GError **error)
+{
+       GBytes *keydata;
+       gboolean success = FALSE;
 
-       /* try to import the key into a temporary context */
-       if (!broken) {
-               gboolean success = FALSE;
+       keydata = g_mime_autocrypt_header_get_keydata(autocrypt_header);
+       if (keydata) {
                gpgme_ctx_t ctx;
 
+               dest->keydata = g_bytes_get_data(keydata, &dest->keysize);
+
+               /* try to import the key into a temporary context: validate, get fingerprint and expiry date 
*/
                ctx = libbalsa_gpgme_new_with_proto(GPGME_PROTOCOL_OpenPGP, NULL, NULL, NULL);
                if (ctx != NULL) {
                        gchar *temp_dir = NULL;
@@ -731,24 +681,25 @@ parse_autocrypt_header(const gchar *value)
                                g_warning("Failed to create a temporary folder");
                        } else {
                                GList *keys = NULL;
-                               GError *error = NULL;
+                               GError *gpg_error = NULL;
                                guint bad_keys = 0U;
 
-                               success = libbalsa_gpgme_ctx_set_home(ctx, temp_dir, &error) &&
-                                       libbalsa_gpgme_import_bin_key(ctx, new_data->keydata, NULL, &error) &&
-                                       libbalsa_gpgme_list_keys(ctx, &keys, &bad_keys, NULL, FALSE, FALSE, 
FALSE, &error);
+                               success = libbalsa_gpgme_ctx_set_home(ctx, temp_dir, &gpg_error) &&
+                                       libbalsa_gpgme_import_bin_key(ctx, keydata, NULL, &gpg_error) &&
+                                       libbalsa_gpgme_list_keys(ctx, &keys, &bad_keys, NULL, FALSE, FALSE, 
FALSE, &gpg_error);
                                if (success && (keys != NULL) && (keys->next == NULL)) {
                                        gpgme_key_t key = (gpgme_key_t) keys->data;
 
                                        if ((key != NULL) && (key->subkeys != NULL)) {
-                                               new_data->fingerprint = g_strdup(key->subkeys->fpr);
-                                               new_data->expires = key->subkeys->expires;
+                                               dest->fingerprint = g_strdup(key->subkeys->fpr);
+                                               dest->expires = key->subkeys->expires;
                                        }
                                } else {
-                                       g_warning("Failed to import or list key data for '%s': %s (%u keys, 
%u bad)", new_data->addr,
-                                               (error != NULL) ? error->message : "unknown", (keys != NULL) 
? g_list_length(keys) : 0U, bad_keys);
+                                       g_warning("Failed to import or list key data for '%s': %s (%u keys, 
%u bad)",
+                                               
g_mime_autocrypt_header_get_address_as_string(autocrypt_header),
+                                               (gpg_error != NULL) ? gpg_error->message : "unknown", (keys 
!= NULL) ? g_list_length(keys) : 0U, bad_keys);
                                }
-                               g_clear_error(&error);
+                               g_clear_error(&gpg_error);
 
                                g_list_free_full(keys, (GDestroyNotify) gpgme_key_release);
                                libbalsa_delete_directory_contents(temp_dir);
@@ -759,139 +710,52 @@ parse_autocrypt_header(const gchar *value)
                }
        }
 
-       /* check if a broken header has been detected, or if importing the key failed */
-       if (broken || (new_data->fingerprint == NULL)) {
-               autocrypt_free(new_data);
-               new_data = NULL;
-       } else {
-               g_debug("valid Autocrypt header for '%s', prefer encrypt %d, key fingerprint %s", 
new_data->addr, new_data->prefer_encrypt,
-                       new_data->fingerprint);
-       }
-
-       return new_data;
-}
-
-
-static gboolean
-eval_autocrypt_attr(const gchar *attr, const gchar *value, gboolean *seen, AutocryptData *target)
-{
-       gboolean result = FALSE;
-
-       if (seen[2]) {
-               g_info("broken Autocrypt header, extra attribute after keydata");
-       } else if (strcmp(attr, "addr") == 0) {
-               if (seen[0]) {
-                       g_info("duplicated Autocrypt header attribute 'addr'");
-               } else {
-                       seen[0] = TRUE;
-                       /* note: not exactly the canonicalisation as required by the Autocrypt standard, but 
should work in all practical use
-                        * cases... */
-                       target->addr = g_ascii_strdown(value, -1);
-                       result = TRUE;
-               }
-       } else if (strcmp(attr, "prefer-encrypt") == 0) {
-               if (seen[1]) {
-                       g_info("duplicated Autocrypt header attribute 'addr'");
-               } else {
-                       seen[1] = TRUE;
-                       if (strcmp(value, "mutual") == 0) {
-                               target->prefer_encrypt = TRUE;
-                               result = TRUE;
-                       } else {
-                               g_info("bad value '%s' for Autocrypt header attribute 'prefer-encrypt'", 
value);
-                       }
-               }
-       } else if (strcmp(attr, "keydata") == 0) {
-               guchar *data;
-               gsize len;
-
-               seen[2] = TRUE;
-               data = g_base64_decode(value, &len);
-               if (data == NULL) {
-                       g_info("invalid keydata in Autocrypt header");
-               } else {
-                       target->keydata = g_bytes_new_take(data, len);
-                       result = TRUE;
-               }
-       } else if (attr[0] == '_') {
-               g_debug("ignoring non-critical Autocrypt header attribute '%s'", attr);
-               result = TRUE;          /* note that this is no error */
-       } else {
-               g_info("unexpected Autocrypt header attribute '%s'", attr);
-       }
-
-       return result;
+       return success;
 }
 
 
-static AutocryptData *
-autocrypt_user_info(const gchar *mailbox, GError **error)
+static void
+add_or_update_user_info(GMimeAutocryptHeader *autocrypt_header, const ac_key_data_t *ac_key_data, gboolean 
update, GError **error)
 {
-       int sqlite_res;
-       AutocryptData *user_info = NULL;
-
-       g_return_val_if_fail((mailbox != NULL) && (autocrypt_db != NULL), NULL);
+       guint query_idx;
+       const gchar *addr;
+       gint64 date_header;
+       gint prefer_encrypt;
 
-       sqlite_res = sqlite3_bind_text(query[0], 1, mailbox, -1, SQLITE_STATIC);
-       if (sqlite_res == SQLITE_OK) {
-               sqlite_res = sqlite3_step(query[0]);
-               if (sqlite_res == SQLITE_ROW) {
-                       user_info = g_new0(AutocryptData, 1U);
-                       user_info->addr = g_strdup((const gchar *) sqlite3_column_text(query[0], 0));
-                       user_info->last_seen = sqlite3_column_int64(query[0], 1);
-                       user_info->ac_timestamp = sqlite3_column_int64(query[0], 2);
-                       user_info->keydata = g_bytes_new(sqlite3_column_blob(query[0], 3), 
sqlite3_column_bytes(query[0], 3));
-                       user_info->fingerprint = g_strdup((const gchar *) sqlite3_column_text(query[0], 4));
-                       user_info->expires = sqlite3_column_int64(query[0], 5);
-                       user_info->prefer_encrypt = (sqlite3_column_int(query[0], 6) != 0);
-                       sqlite_res = sqlite3_step(query[0]);
-               }
+       query_idx = update ? 2 : 1;
 
-               if (sqlite_res != SQLITE_DONE) {
-                       g_set_error(error, AUTOCRYPT_ERROR_QUARK, sqlite_res, _("error reading Autocrypt data 
for “%s”: %s"), mailbox,
-                               sqlite3_errmsg(autocrypt_db));
-                       autocrypt_free(user_info);
-                       user_info = NULL;
-               }
+       addr = g_mime_autocrypt_header_get_address_as_string(autocrypt_header);
+       date_header = g_date_time_to_unix(g_mime_autocrypt_header_get_effective_date(autocrypt_header));
+       if (g_mime_autocrypt_header_get_prefer_encrypt(autocrypt_header) == 
GMIME_AUTOCRYPT_PREFER_ENCRYPT_MUTUAL) {
+               prefer_encrypt = (gint) AUTOCRYPT_PREFER_ENCRYPT;
        } else {
-               g_set_error(error, AUTOCRYPT_ERROR_QUARK, sqlite_res, _("error reading Autocrypt data for 
“%s”: %s"), mailbox,
-                       sqlite3_errmsg(autocrypt_db));
+               prefer_encrypt = (gint) AUTOCRYPT_NOPREFERENCE;
        }
-       sqlite3_reset(query[0]);
-
-       return user_info;
-}
 
-
-static void
-add_or_update_user_info(const AutocryptData *user_info, time_t date_header, gboolean update, GError **error)
-{
-       guint query_idx;
-       gconstpointer keyvalue;
-       gsize keysize;
-
-       query_idx = update ? 2 : 1;
-       keyvalue = g_bytes_get_data(user_info->keydata, &keysize);
-       if ((sqlite3_bind_text(query[query_idx], 1, user_info->addr, -1, SQLITE_STATIC) != SQLITE_OK) ||
+       if ((sqlite3_bind_text(query[query_idx], 1, addr, -1, SQLITE_STATIC) != SQLITE_OK) ||
                (sqlite3_bind_int64(query[query_idx], 2, date_header) != SQLITE_OK) ||
-               (sqlite3_bind_blob(query[query_idx], 3, keyvalue, keysize, SQLITE_STATIC) != SQLITE_OK) ||
-               (sqlite3_bind_text(query[query_idx], 4, user_info->fingerprint, -1, SQLITE_STATIC) != 
SQLITE_OK) ||
-               (sqlite3_bind_int64(query[query_idx], 5, user_info->expires) != SQLITE_OK) ||
-               (sqlite3_bind_int(query[query_idx], 6, user_info->prefer_encrypt) != SQLITE_OK) ||
+               (sqlite3_bind_blob(query[query_idx], 3, ac_key_data->keydata, ac_key_data->keysize, 
SQLITE_STATIC) != SQLITE_OK) ||
+               (sqlite3_bind_text(query[query_idx], 4, ac_key_data->fingerprint, -1, SQLITE_STATIC) != 
SQLITE_OK) ||
+               (sqlite3_bind_int64(query[query_idx], 5, ac_key_data->expires) != SQLITE_OK) ||
+               (sqlite3_bind_int(query[query_idx], 6, prefer_encrypt) != SQLITE_OK) ||
                (sqlite3_step(query[query_idx]) != SQLITE_DONE)) {
-               g_set_error(error, AUTOCRYPT_ERROR_QUARK, -1,
-                        update ? _("update user “%s” failed: %s") : _("insert user “%s” failed: %s"),
-                       user_info->addr, sqlite3_errmsg(autocrypt_db));
+               g_set_error(error, AUTOCRYPT_ERROR_QUARK, -1, update ? _("update user “%s” failed: %s") : 
_("insert user “%s” failed: %s"),
+                       addr, sqlite3_errmsg(autocrypt_db));
        } else {
-               g_debug("%s user '%s': %d", update ? "updated" : "inserted", user_info->addr, 
sqlite3_changes(autocrypt_db));
+               g_debug("%s user '%s': %d", update ? "updated" : "inserted", addr, 
sqlite3_changes(autocrypt_db));
        }
        sqlite3_reset(query[query_idx]);
 }
 
 
 static void
-update_last_seen(const gchar *addr, time_t date_header, GError **error)
+update_last_seen(GMimeAutocryptHeader *autocrypt_header, GError **error)
 {
+       const gchar *addr;
+       time_t date_header;
+
+       addr = g_mime_autocrypt_header_get_address_as_string(autocrypt_header);
+       date_header = g_date_time_to_unix(g_mime_autocrypt_header_get_effective_date(autocrypt_header));
        if ((sqlite3_bind_text(query[3], 1, addr, -1, SQLITE_STATIC) != SQLITE_OK) ||
                (sqlite3_bind_int64(query[3], 2, date_header) != SQLITE_OK) ||
                (sqlite3_step(query[3]) != SQLITE_DONE)) {
diff --git a/libbalsa/libbalsa-gpgme-keys.c b/libbalsa/libbalsa-gpgme-keys.c
index e9bef45f1..78a6de986 100644
--- a/libbalsa/libbalsa-gpgme-keys.c
+++ b/libbalsa/libbalsa-gpgme-keys.c
@@ -271,13 +271,13 @@ libbalsa_gpgme_export_key(gpgme_ctx_t   ctx,
 
 
 /* documentation: see header file */
-gchar *
+GBytes *
 libbalsa_gpgme_export_autocrypt_key(const gchar *fingerprint, const gchar *mailbox, GError **error)
 {
        gchar *export_args[10] = { "", "--export", "--export-options", "export-minimal,no-export-attributes",
                NULL, NULL, NULL, NULL, NULL, NULL };
        gpgme_ctx_t ctx;
-       gchar *result = NULL;
+       GBytes *result = NULL;
 
        g_return_val_if_fail((fingerprint != NULL) && (mailbox != NULL), NULL);
 
@@ -323,7 +323,7 @@ libbalsa_gpgme_export_autocrypt_key(const gchar *fingerprint, const gchar *mailb
                                if ((keydata == NULL) || (keysize == 0U)) {
                                        g_set_error(error, GPGME_ERROR_QUARK, -1, _("cannot export minimal 
key for “%s”"), mailbox);
                                } else {
-                                       result = g_base64_encode(keydata, keysize);
+                                       result = g_bytes_new(keydata, keysize);
                                }
                                gpgme_free(keydata);
                        }
diff --git a/libbalsa/libbalsa-gpgme-keys.h b/libbalsa/libbalsa-gpgme-keys.h
index 9141132a7..238d2021c 100644
--- a/libbalsa/libbalsa-gpgme-keys.h
+++ b/libbalsa/libbalsa-gpgme-keys.h
@@ -120,15 +120,15 @@ gchar *libbalsa_gpgme_export_key(gpgme_ctx_t   ctx,
  * \param fingerprint key fingerprint, may be NULL
  * \param mailbox key uid
  * \param error filled with error information on error
- * \return a newly allocated string containing the BASE64-encoded key on success, NULL on error
+ * \return a newly allocated buffer containing the key on success, NULL on error
  *
  * Export the minimal key for using it in a Autocrypt: header.  If specified, the key is selected by the 
passed fingerprint,
  * otherwise the first key matching the passed mailbox is used.  Depending on the gpg backend version, all 
other uid's and all
  * subkeys which are not required are stripped.
  */
-gchar *libbalsa_gpgme_export_autocrypt_key(const gchar  *fingerprint,
-                                                                                  const gchar  *mailbox,
-                                                                                  GError      **error)
+GBytes *libbalsa_gpgme_export_autocrypt_key(const gchar  *fingerprint,
+                                                                                   const gchar  *mailbox,
+                                                                                   GError      **error)
        G_GNUC_WARN_UNUSED_RESULT;
 
 /** \brief Import an ASCII-armoured key
diff --git a/libbalsa/message.c b/libbalsa/message.c
index 09685a254..1889954eb 100644
--- a/libbalsa/message.c
+++ b/libbalsa/message.c
@@ -274,6 +274,13 @@ libbalsa_message_headers_destroy(LibBalsaMessageHeaders *headers)
 
     lb_message_headers_extra_destroy(headers);
 
+#if defined ENABLE_AUTOCRYPT
+    if (headers->autocrypt_hdr != NULL) {
+       g_object_unref(headers->autocrypt_hdr);
+       headers->autocrypt_hdr = NULL;
+}
+#endif
+
     g_free(headers);
 }
 
@@ -1067,6 +1074,16 @@ libbalsa_message_headers_from_gmime(LibBalsaMessageHeaders *headers,
 {
     lb_message_headers_basic_from_gmime(headers, mime_msg);
     lb_message_headers_extra_from_gmime(headers, mime_msg);
+#if defined ENABLE_AUTOCRYPT
+    if (headers->autocrypt_hdr == NULL) {
+       GDateTime *reftime;
+
+       /* set the reference date far in the future so we can ignore messages w/o Date: header */
+       reftime = g_date_time_new_from_unix_utc(time(NULL) + (365 * 24 * 60 * 60));
+       headers->autocrypt_hdr = g_mime_message_get_autocrypt_header(mime_msg, reftime);
+       g_date_time_unref(reftime);
+    }
+#endif
 }
 
 /* Populate message and message->headers from the info in mime_msg,
diff --git a/libbalsa/message.h b/libbalsa/message.h
index 7841a90d7..11c52a572 100644
--- a/libbalsa/message.h
+++ b/libbalsa/message.h
@@ -144,6 +144,11 @@ struct _LibBalsaMessageHeaders {
 
     /* other headers */
     GList *user_hdrs;
+
+#if defined ENABLE_AUTOCRYPT
+    /* received Autocrypt header */
+    GMimeAutocryptHeader *autocrypt_hdr;
+#endif
 };
 
 /** FREE_HEADER_LIST() frees user_hdrs */


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