#include "mutt.h"
#include "mutt_curses.h"
+#include "mime.h"
#include "autocrypt.h"
#include "autocrypt_private.h"
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
#include <errno.h>
static int autocrypt_dir_init (int can_create)
mutt_buffer_pool_release (&keydata);
return rv;
}
+
+int mutt_autocrypt_process_autocrypt_header (HEADER *hdr, ENVELOPE *env)
+{
+ AUTOCRYPTHDR *ac_hdr, *valid_ac_hdr = NULL;
+ struct timeval now;
+ AUTOCRYPT_PEER *peer = NULL;
+ AUTOCRYPT_PEER_HISTORY *peerhist = NULL;
+ BUFFER *keyid = NULL;
+ int update_db = 0, insert_db = 0, insert_db_history = 0, import_gpg = 0;
+ int rv = -1;
+
+ if (!option (OPTAUTOCRYPT))
+ return 0;
+
+ if (mutt_autocrypt_init (0))
+ return -1;
+
+ if (!hdr || !hdr->content || !env)
+ return 0;
+
+ /* 1.1 spec says to skip emails with more than one From header */
+ if (!env->from || env->from->next)
+ return 0;
+
+ /* 1.1 spec also says to skip multipart/report emails */
+ if (hdr->content->type == TYPEMULTIPART &&
+ !(ascii_strcasecmp (hdr->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 (hdr->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 (ascii_strcasecmp (env->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 (env->from, &peer) < 0)
+ goto cleanup;
+
+ if (peer)
+ {
+ if (hdr->date_sent <= peer->autocrypt_timestamp)
+ {
+ rv = 0;
+ goto cleanup;
+ }
+
+ if (hdr->date_sent > peer->last_seen)
+ {
+ update_db = 1;
+ peer->last_seen = hdr->date_sent;
+ }
+
+ if (valid_ac_hdr)
+ {
+ update_db = 1;
+ peer->autocrypt_timestamp = hdr->date_sent;
+ peer->prefer_encrypt = valid_ac_hdr->prefer_encrypt;
+ if (mutt_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 = hdr->date_sent;
+ peer->autocrypt_timestamp = hdr->date_sent;
+ peer->keydata = safe_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 (env->from, peer))
+ goto cleanup;
+
+ if (update_db &&
+ mutt_autocrypt_db_peer_update (env->from, peer))
+ goto cleanup;
+
+ if (insert_db_history)
+ {
+ peerhist = mutt_autocrypt_db_peer_history_new ();
+ peerhist->email_msgid = safe_strdup (env->message_id);
+ peerhist->timestamp = hdr->date_sent;
+ peerhist->keydata = safe_strdup (peer->keydata);
+ if (mutt_autocrypt_db_peer_history_insert (env->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 "autocrypt_private.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,
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;
}
+
+AUTOCRYPT_PEER *mutt_autocrypt_db_peer_new (void)
+{
+ return safe_calloc (1, sizeof(AUTOCRYPT_PEER));
+}
+
+void mutt_autocrypt_db_peer_free (AUTOCRYPT_PEER **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); /* __FREE_CHECKED__ */
+}
+
+int mutt_autocrypt_db_peer_get (ADDRESS *addr, AUTOCRYPT_PEER **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 (ADDRESS *addr, AUTOCRYPT_PEER *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 (ADDRESS *addr, AUTOCRYPT_PEER *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;
+}
+
+AUTOCRYPT_PEER_HISTORY *mutt_autocrypt_db_peer_history_new (void)
+{
+ return safe_calloc (1, sizeof(AUTOCRYPT_PEER_HISTORY));
+}
+
+void mutt_autocrypt_db_peer_history_free (AUTOCRYPT_PEER_HISTORY **peerhist)
+{
+ if (!peerhist || !*peerhist)
+ return;
+ FREE (&(*peerhist)->peer_email_addr);
+ FREE (&(*peerhist)->email_msgid);
+ FREE (&(*peerhist)->keydata);
+ FREE (peerhist); /* __FREE_CHECKED__ */
+}
+
+int mutt_autocrypt_db_peer_history_insert (ADDRESS *addr, AUTOCRYPT_PEER_HISTORY *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;
+}