[balsa/gmime3: 48/49] GMime3 (3.2, at least) supports Autocrypt headers
- From: Peter Bloomfield <peterb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [balsa/gmime3: 48/49] GMime3 (3.2, at least) supports Autocrypt headers
- Date: Sun, 19 Jan 2020 19:18:47 +0000 (UTC)
commit 4173404b122b68aadc2cd7388a6b201940551a89
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]