#include <errno.h>
#include <string.h>
#include <sys/stat.h>
+#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include "autocrypt_private.h"
#include "address/lib.h"
+#include "email/lib.h"
#include "mutt.h"
#include "autocrypt.h"
#include "curs_lib.h"
mutt_buffer_pool_release(&keydata);
return rc;
}
+
+int mutt_autocrypt_process_autocrypt_header(struct Email *e, struct Envelope *env)
+{
+ struct AutocryptHeader *ac_hdr, *valid_ac_hdr = NULL;
+ struct timeval now;
+ struct AutocryptPeer *peer = NULL;
+ struct AutocryptPeerHistory *peerhist = NULL;
+ struct Buffer *keyid = NULL;
+ int update_db = 0, insert_db = 0, insert_db_history = 0, import_gpg = 0;
+ int rv = -1;
+
+ if (!C_Autocrypt)
+ return 0;
+
+ if (mutt_autocrypt_init(0))
+ return -1;
+
+ if (!e || !e->content || !env)
+ return 0;
+
+ /* 1.1 spec says to skip emails with more than one From header */
+ struct Address *from = TAILQ_FIRST(&env->from);
+ if (!from || TAILQ_NEXT(from, entries))
+ return 0;
+
+ /* 1.1 spec also says to skip multipart/report emails */
+ if ((e->content->type == TYPE_MULTIPART) &&
+ !(mutt_str_strcasecmp(e->content->subtype, "report")))
+ return 0;
+
+ /* Ignore emails that appear to be more than a week in the future,
+ * since they can block all future updates during that time. */
+ gettimeofday(&now, NULL);
+ if (e->date_sent > (now.tv_sec + 7 * 24 * 60 * 60))
+ return 0;
+
+ for (ac_hdr = env->autocrypt; ac_hdr; ac_hdr = ac_hdr->next)
+ {
+ if (ac_hdr->invalid)
+ continue;
+
+ /* NOTE: this assumes the processing is occurring right after
+ * mutt_parse_rfc822_line() and the from ADDR is still in the same
+ * form (intl) as the autocrypt header addr field */
+ if (mutt_str_strcasecmp(from->mailbox, ac_hdr->addr))
+ continue;
+
+ /* 1.1 spec says ignore all, if more than one valid header is found. */
+ if (valid_ac_hdr)
+ {
+ valid_ac_hdr = NULL;
+ break;
+ }
+ valid_ac_hdr = ac_hdr;
+ }
+
+ if (mutt_autocrypt_db_peer_get(from, &peer) < 0)
+ goto cleanup;
+
+ if (peer)
+ {
+ if (e->date_sent <= peer->autocrypt_timestamp)
+ {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (e->date_sent > peer->last_seen)
+ {
+ update_db = 1;
+ peer->last_seen = e->date_sent;
+ }
+
+ if (valid_ac_hdr)
+ {
+ update_db = 1;
+ peer->autocrypt_timestamp = e->date_sent;
+ peer->prefer_encrypt = valid_ac_hdr->prefer_encrypt;
+ if (mutt_str_strcmp(peer->keydata, valid_ac_hdr->keydata))
+ {
+ import_gpg = 1;
+ insert_db_history = 1;
+ mutt_str_replace(&peer->keydata, valid_ac_hdr->keydata);
+ }
+ }
+ }
+ else if (valid_ac_hdr)
+ {
+ import_gpg = 1;
+ insert_db = 1;
+ insert_db_history = 1;
+ }
+
+ if (!(import_gpg || insert_db || update_db))
+ {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (!peer)
+ {
+ peer = mutt_autocrypt_db_peer_new();
+ peer->last_seen = e->date_sent;
+ peer->autocrypt_timestamp = e->date_sent;
+ peer->keydata = mutt_str_strdup(valid_ac_hdr->keydata);
+ peer->prefer_encrypt = valid_ac_hdr->prefer_encrypt;
+ }
+
+ if (import_gpg)
+ {
+ keyid = mutt_buffer_pool_get();
+ if (mutt_autocrypt_gpgme_import_key(peer->keydata, keyid))
+ goto cleanup;
+ mutt_str_replace(&peer->keyid, mutt_b2s(keyid));
+ }
+
+ if (insert_db && mutt_autocrypt_db_peer_insert(from, peer))
+ goto cleanup;
+
+ if (update_db && mutt_autocrypt_db_peer_update(from, peer))
+ goto cleanup;
+
+ if (insert_db_history)
+ {
+ peerhist = mutt_autocrypt_db_peer_history_new();
+ peerhist->email_msgid = mutt_str_strdup(env->message_id);
+ peerhist->timestamp = e->date_sent;
+ peerhist->keydata = mutt_str_strdup(peer->keydata);
+ if (mutt_autocrypt_db_peer_history_insert(from, peerhist))
+ goto cleanup;
+ }
+
+ rv = 0;
+
+cleanup:
+ mutt_autocrypt_db_peer_free(&peer);
+ mutt_autocrypt_db_peer_history_free(&peerhist);
+ mutt_buffer_pool_release(&keyid);
+
+ return rv;
+}
#include "autocrypt.h"
#include "globals.h"
+/* Prepared statements */
+static sqlite3_stmt *AccountGetStmt;
+static sqlite3_stmt *AccountInsertStmt;
+static sqlite3_stmt *PeerGetStmt;
+static sqlite3_stmt *PeerInsertStmt;
+static sqlite3_stmt *PeerUpdateStmt;
+static sqlite3_stmt *PeerHistoryInsertStmt;
+
static int autocrypt_db_create(const char *db_path)
{
if (sqlite3_open_v2(db_path, &AutocryptDB,
sqlite3_finalize(AccountInsertStmt);
AccountInsertStmt = NULL;
+ sqlite3_finalize(PeerGetStmt);
+ PeerGetStmt = NULL;
+ sqlite3_finalize(PeerInsertStmt);
+ PeerInsertStmt = NULL;
+ sqlite3_finalize(PeerUpdateStmt);
+ PeerUpdateStmt = NULL;
+
+ sqlite3_finalize(PeerHistoryInsertStmt);
+ PeerHistoryInsertStmt = NULL;
+
sqlite3_close_v2(AutocryptDB);
AutocryptDB = NULL;
}
sqlite3_reset(AccountInsertStmt);
return rv;
}
+
+struct AutocryptPeer *mutt_autocrypt_db_peer_new(void)
+{
+ return mutt_mem_calloc(1, sizeof(struct AutocryptPeer));
+}
+
+void mutt_autocrypt_db_peer_free(struct AutocryptPeer **peer)
+{
+ if (!peer || !*peer)
+ return;
+ FREE(&(*peer)->email_addr);
+ FREE(&(*peer)->keyid);
+ FREE(&(*peer)->keydata);
+ FREE(&(*peer)->gossip_keyid);
+ FREE(&(*peer)->gossip_keydata);
+ FREE(peer);
+}
+
+int mutt_autocrypt_db_peer_get(struct Address *addr, struct AutocryptPeer **peer)
+{
+ int rv = -1, result;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+ *peer = NULL;
+
+ if (!PeerGetStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "SELECT "
+ "email_addr, "
+ "last_seen, "
+ "autocrypt_timestamp, "
+ "keyid, "
+ "keydata, "
+ "prefer_encrypt, "
+ "gossip_timestamp, "
+ "gossip_keyid, "
+ "gossip_keydata "
+ "FROM peer "
+ "WHERE email_addr = ?",
+ -1, &PeerGetStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_text(PeerGetStmt, 1, email, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+
+ result = sqlite3_step(PeerGetStmt);
+ if (result != SQLITE_ROW)
+ {
+ if (result == SQLITE_DONE)
+ rv = 0;
+ goto cleanup;
+ }
+
+ *peer = mutt_autocrypt_db_peer_new();
+ (*peer)->email_addr = strdup_column_text(PeerGetStmt, 0);
+ (*peer)->last_seen = sqlite3_column_int64(PeerGetStmt, 1);
+ (*peer)->autocrypt_timestamp = sqlite3_column_int64(PeerGetStmt, 2);
+ (*peer)->keyid = strdup_column_text(PeerGetStmt, 3);
+ (*peer)->keydata = strdup_column_text(PeerGetStmt, 4);
+ (*peer)->prefer_encrypt = sqlite3_column_int(PeerGetStmt, 5);
+ (*peer)->gossip_timestamp = sqlite3_column_int64(PeerGetStmt, 6);
+ (*peer)->gossip_keyid = strdup_column_text(PeerGetStmt, 7);
+ (*peer)->gossip_keydata = strdup_column_text(PeerGetStmt, 8);
+
+ rv = 1;
+
+cleanup:
+ FREE(&email);
+ sqlite3_reset(PeerGetStmt);
+ return rv;
+}
+
+int mutt_autocrypt_db_peer_insert(struct Address *addr, struct AutocryptPeer *peer)
+{
+ int rv = -1;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+
+ if (!PeerInsertStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "INSERT INTO peer "
+ "(email_addr, "
+ "last_seen, "
+ "autocrypt_timestamp, "
+ "keyid, "
+ "keydata, "
+ "prefer_encrypt, "
+ "gossip_timestamp, "
+ "gossip_keyid, "
+ "gossip_keydata) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);",
+ -1, &PeerInsertStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_text(PeerInsertStmt, 1, email, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerInsertStmt, 2, peer->last_seen) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerInsertStmt, 3, peer->autocrypt_timestamp) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerInsertStmt, 4, peer->keyid, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerInsertStmt, 5, peer->keydata, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int(PeerInsertStmt, 6, peer->prefer_encrypt) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerInsertStmt, 7, peer->gossip_timestamp) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerInsertStmt, 8, peer->gossip_keyid, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerInsertStmt, 9, peer->gossip_keydata, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+
+ if (sqlite3_step(PeerInsertStmt) != SQLITE_DONE)
+ goto cleanup;
+
+ rv = 0;
+
+cleanup:
+ FREE(&email);
+ sqlite3_reset(PeerInsertStmt);
+ return rv;
+}
+
+int mutt_autocrypt_db_peer_update(struct Address *addr, struct AutocryptPeer *peer)
+{
+ int rv = -1;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+
+ if (!PeerUpdateStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "UPDATE peer SET "
+ "last_seen = ?, "
+ "autocrypt_timestamp = ?, "
+ "keyid = ?, "
+ "keydata = ?, "
+ "prefer_encrypt = ?, "
+ "gossip_timestamp = ?, "
+ "gossip_keyid = ?, "
+ "gossip_keydata = ? "
+ "WHERE email_addr = ?;",
+ -1, &PeerUpdateStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_int64(PeerUpdateStmt, 1, peer->last_seen) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerUpdateStmt, 2, peer->autocrypt_timestamp) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerUpdateStmt, 3, peer->keyid, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerUpdateStmt, 4, peer->keydata, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int(PeerUpdateStmt, 5, peer->prefer_encrypt) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerUpdateStmt, 6, peer->gossip_timestamp) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerUpdateStmt, 7, peer->gossip_keyid, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerUpdateStmt, 8, peer->gossip_keydata, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerUpdateStmt, 9, email, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+
+ if (sqlite3_step(PeerUpdateStmt) != SQLITE_DONE)
+ goto cleanup;
+
+ rv = 0;
+
+cleanup:
+ FREE(&email);
+ sqlite3_reset(PeerUpdateStmt);
+ return rv;
+}
+
+struct AutocryptPeerHistory *mutt_autocrypt_db_peer_history_new(void)
+{
+ return mutt_mem_calloc(1, sizeof(struct AutocryptPeerHistory));
+}
+
+void mutt_autocrypt_db_peer_history_free(struct AutocryptPeerHistory **peerhist)
+{
+ if (!peerhist || !*peerhist)
+ return;
+ FREE(&(*peerhist)->peer_email_addr);
+ FREE(&(*peerhist)->email_msgid);
+ FREE(&(*peerhist)->keydata);
+ FREE(peerhist);
+}
+
+int mutt_autocrypt_db_peer_history_insert(struct Address *addr,
+ struct AutocryptPeerHistory *peerhist)
+{
+ int rv = -1;
+ char *email = NULL;
+
+ email = normalize_email_addr(addr);
+
+ if (!PeerHistoryInsertStmt)
+ {
+ if (sqlite3_prepare_v2(AutocryptDB,
+ "INSERT INTO peer_history "
+ "(peer_email_addr, "
+ "email_msgid, "
+ "timestamp, "
+ "keydata) "
+ "VALUES (?, ?, ?, ?);",
+ -1, &PeerHistoryInsertStmt, NULL) != SQLITE_OK)
+ goto cleanup;
+ }
+
+ if (sqlite3_bind_text(PeerHistoryInsertStmt, 1, email, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerHistoryInsertStmt, 2, peerhist->email_msgid, -1,
+ SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_int64(PeerHistoryInsertStmt, 3, peerhist->timestamp) != SQLITE_OK)
+ goto cleanup;
+ if (sqlite3_bind_text(PeerHistoryInsertStmt, 4, peerhist->keydata, -1, SQLITE_STATIC) != SQLITE_OK)
+ goto cleanup;
+
+ if (sqlite3_step(PeerHistoryInsertStmt) != SQLITE_DONE)
+ goto cleanup;
+
+ rv = 0;
+
+cleanup:
+ FREE(&email);
+ sqlite3_reset(PeerHistoryInsertStmt);
+ return rv;
+}