]> granicus.if.org Git - neomutt/commitdiff
Add gossip header processing
authorKevin McCarthy <kevin@8t8.us>
Tue, 16 Jul 2019 01:36:57 +0000 (18:36 -0700)
committerRichard Russon <rich@flatcap.org>
Mon, 19 Aug 2019 23:14:27 +0000 (00:14 +0100)
Co-authored-by: Richard Russon <rich@flatcap.org>
autocrypt/autocrypt.c
autocrypt/autocrypt.h
autocrypt/autocrypt_db.c
autocrypt/autocrypt_private.h
commands.c
email/envelope.c
email/envelope.h
email/parse.c

index 370a8638710cc4e700a9dc3a07537964be18d7a8..3ecebcf2038c533b28b049b55b58c915a8b17374 100644 (file)
@@ -309,3 +309,146 @@ cleanup:
 
   return rv;
 }
+
+static struct Address *matching_gossip_address(struct Envelope *env, const char *addr)
+{
+  struct Address *np = NULL;
+
+  TAILQ_FOREACH(np, &env->to, entries)
+  {
+    if (!mutt_str_strcasecmp(np->mailbox, addr))
+      return np;
+  }
+
+  TAILQ_FOREACH(np, &env->cc, entries)
+  {
+    if (!mutt_str_strcasecmp(np->mailbox, addr))
+      return np;
+  }
+
+  TAILQ_FOREACH(np, &env->reply_to, entries)
+  {
+    if (!mutt_str_strcasecmp(np->mailbox, addr))
+      return np;
+  }
+
+  return NULL;
+}
+
+int mutt_autocrypt_process_gossip_header(struct Email *hdr, struct Envelope *env)
+{
+  struct AutocryptHeader *ac_hdr;
+  struct timeval now;
+  struct AutocryptPeer *peer = NULL;
+  struct AutocryptGossipHistory *gossip_hist = NULL;
+  struct Address *peer_addr;
+  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 (!hdr || !hdr->content || !env)
+    return 0;
+
+  struct Address *from = TAILQ_FIRST(&env->from);
+  if (!from)
+    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;
+
+  keyid = mutt_buffer_pool_get();
+
+  /* To ensure the address headers match the gossip header format */
+  mutt_env_to_intl(env, NULL, NULL);
+
+  for (ac_hdr = env->autocrypt_gossip; ac_hdr; ac_hdr = ac_hdr->next)
+  {
+    if (ac_hdr->invalid)
+      continue;
+
+    peer_addr = matching_gossip_address(env, ac_hdr->addr);
+    if (!peer_addr)
+      continue;
+
+    if (mutt_autocrypt_db_peer_get(from, &peer) < 0)
+      goto cleanup;
+
+    if (peer)
+    {
+      if (hdr->date_sent <= peer->gossip_timestamp)
+      {
+        mutt_autocrypt_db_peer_free(&peer);
+        continue;
+      }
+
+      update_db = 1;
+      peer->gossip_timestamp = hdr->date_sent;
+      if (mutt_str_strcmp(peer->gossip_keydata, ac_hdr->keydata))
+      {
+        import_gpg = 1;
+        insert_db_history = 1;
+        mutt_str_replace(&peer->gossip_keydata, ac_hdr->keydata);
+      }
+    }
+    else
+    {
+      import_gpg = 1;
+      insert_db = 1;
+      insert_db_history = 1;
+    }
+
+    if (!peer)
+    {
+      peer = mutt_autocrypt_db_peer_new();
+      peer->gossip_timestamp = hdr->date_sent;
+      peer->gossip_keydata = mutt_str_strdup(ac_hdr->keydata);
+    }
+
+    if (import_gpg)
+    {
+      if (mutt_autocrypt_gpgme_import_key(peer->gossip_keydata, keyid))
+        goto cleanup;
+      mutt_str_replace(&peer->gossip_keyid, mutt_b2s(keyid));
+    }
+
+    if (insert_db && mutt_autocrypt_db_peer_insert(peer_addr, peer))
+      goto cleanup;
+
+    if (update_db && mutt_autocrypt_db_peer_update(peer_addr, peer))
+      goto cleanup;
+
+    if (insert_db_history)
+    {
+      gossip_hist = mutt_autocrypt_db_gossip_history_new();
+      gossip_hist->sender_email_addr = mutt_str_strdup(from->mailbox);
+      gossip_hist->email_msgid = mutt_str_strdup(env->message_id);
+      gossip_hist->timestamp = hdr->date_sent;
+      gossip_hist->gossip_keydata = mutt_str_strdup(peer->gossip_keydata);
+      if (mutt_autocrypt_db_gossip_history_insert(peer_addr, gossip_hist))
+        goto cleanup;
+    }
+
+    mutt_autocrypt_db_peer_free(&peer);
+    mutt_autocrypt_db_gossip_history_free(&gossip_hist);
+    mutt_buffer_reset(keyid);
+    update_db = insert_db = insert_db_history = import_gpg = 0;
+  }
+
+  rv = 0;
+
+cleanup:
+  mutt_autocrypt_db_peer_free(&peer);
+  mutt_autocrypt_db_gossip_history_free(&gossip_hist);
+  mutt_buffer_pool_release(&keyid);
+
+  return rv;
+}
index 72f35a804833a7d77de5a002b032159df8049938..82dc6a9b1e9b73f397737b41ed7a4d2a704e1ce7 100644 (file)
@@ -58,8 +58,18 @@ struct AutocryptPeerHistory
   char *keydata;
 };
 
+struct AutocryptGossipHistory
+{
+  char *peer_email_addr;
+  char *sender_email_addr;
+  char *email_msgid;
+  sqlite3_int64 timestamp;
+  char *gossip_keydata;
+};
+
 int mutt_autocrypt_init (int);
 void mutt_autocrypt_cleanup (void);
 int mutt_autocrypt_process_autocrypt_header (struct Email *hdr, struct Envelope *env);
+int mutt_autocrypt_process_gossip_header (struct Email *hdr, struct Envelope *env);
 
 #endif /* MUTT_AUTOCRYPT_AUTOCRYPT_H */
index 1c2e1d5c312342ee93469921178d659c18992c9d..e9c964176415f1475f56578ac90d36a6db24f61f 100644 (file)
@@ -37,6 +37,7 @@ static sqlite3_stmt *PeerGetStmt;
 static sqlite3_stmt *PeerInsertStmt;
 static sqlite3_stmt *PeerUpdateStmt;
 static sqlite3_stmt *PeerHistoryInsertStmt;
+static sqlite3_stmt *GossipHistoryInsertStmt;
 
 static int autocrypt_db_create(const char *db_path)
 {
@@ -112,6 +113,9 @@ void mutt_autocrypt_db_close(void)
   sqlite3_finalize(PeerHistoryInsertStmt);
   PeerHistoryInsertStmt = NULL;
 
+  sqlite3_finalize(GossipHistoryInsertStmt);
+  GossipHistoryInsertStmt = NULL;
+
   sqlite3_close_v2(AutocryptDB);
   AutocryptDB = NULL;
 }
@@ -495,3 +499,66 @@ cleanup:
   sqlite3_reset(PeerHistoryInsertStmt);
   return rv;
 }
+
+struct AutocryptGossipHistory *mutt_autocrypt_db_gossip_history_new(void)
+{
+  return mutt_mem_calloc(1, sizeof(struct AutocryptGossipHistory));
+}
+
+void mutt_autocrypt_db_gossip_history_free(struct AutocryptGossipHistory **gossip_hist)
+{
+  if (!gossip_hist || !*gossip_hist)
+    return;
+  FREE(&(*gossip_hist)->peer_email_addr);
+  FREE(&(*gossip_hist)->sender_email_addr);
+  FREE(&(*gossip_hist)->email_msgid);
+  FREE(&(*gossip_hist)->gossip_keydata);
+  FREE(gossip_hist);
+}
+
+int mutt_autocrypt_db_gossip_history_insert(struct Address *addr,
+                                            struct AutocryptGossipHistory *gossip_hist)
+{
+  int rv = -1;
+  char *email = NULL;
+
+  email = normalize_email_addr(addr);
+
+  if (!GossipHistoryInsertStmt)
+  {
+    if (sqlite3_prepare_v3(AutocryptDB,
+                           "INSERT INTO gossip_history "
+                           "(peer_email_addr, "
+                           "sender_email_addr, "
+                           "email_msgid, "
+                           "timestamp, "
+                           "gossip_keydata) "
+                           "VALUES (?, ?, ?, ?, ?);",
+                           -1, SQLITE_PREPARE_PERSISTENT,
+                           &GossipHistoryInsertStmt, NULL) != SQLITE_OK)
+      goto cleanup;
+  }
+
+  if (sqlite3_bind_text(GossipHistoryInsertStmt, 1, email, -1, SQLITE_STATIC) != SQLITE_OK)
+    goto cleanup;
+  if (sqlite3_bind_text(GossipHistoryInsertStmt, 2, gossip_hist->sender_email_addr,
+                        -1, SQLITE_STATIC) != SQLITE_OK)
+    if (sqlite3_bind_text(GossipHistoryInsertStmt, 3, gossip_hist->email_msgid,
+                          -1, SQLITE_STATIC) != SQLITE_OK)
+      goto cleanup;
+  if (sqlite3_bind_int64(GossipHistoryInsertStmt, 4, gossip_hist->timestamp) != SQLITE_OK)
+    goto cleanup;
+  if (sqlite3_bind_text(GossipHistoryInsertStmt, 5, gossip_hist->gossip_keydata,
+                        -1, SQLITE_STATIC) != SQLITE_OK)
+    goto cleanup;
+
+  if (sqlite3_step(GossipHistoryInsertStmt) != SQLITE_DONE)
+    goto cleanup;
+
+  rv = 0;
+
+cleanup:
+  FREE(&email);
+  sqlite3_reset(GossipHistoryInsertStmt);
+  return rv;
+}
index e63fbf352c5ab3bb7bbdffea592de0374329efb9..76a30292647036555a8617b0ef3d183f24c58600 100644 (file)
@@ -49,6 +49,10 @@ struct AutocryptPeerHistory *mutt_autocrypt_db_peer_history_new (void);
 void mutt_autocrypt_db_peer_history_free (struct AutocryptPeerHistory **peerhist);
 int mutt_autocrypt_db_peer_history_insert (struct Address *addr, struct AutocryptPeerHistory *peerhist);
 
+struct AutocryptGossipHistory *mutt_autocrypt_db_gossip_history_new (void);
+void mutt_autocrypt_db_gossip_history_free (struct AutocryptGossipHistory **gossip_hist);
+int mutt_autocrypt_db_gossip_history_insert (struct Address *addr, struct AutocryptGossipHistory *gossip_hist);
+
 int mutt_autocrypt_schema_init (void);
 int mutt_autocrypt_schema_update (void);
 
index c7160eb73c929ec37779a2e0c5909bd3cc3e7ef8..2d369c866499053805c274a27908349f13842b88 100644 (file)
@@ -78,6 +78,9 @@
 #ifdef ENABLE_NLS
 #include <libintl.h>
 #endif
+#ifdef USE_AUTOCRYPT
+#include "autocrypt/autocrypt.h"
+#endif
 
 /* These Config Variables are only used in commands.c */
 unsigned char C_CryptVerifySig; ///< Config: Verify PGP or SMIME signatures
@@ -95,16 +98,22 @@ static const char *ExtPagerProgress = "all";
 static char LastSaveFolder[PATH_MAX] = "";
 
 /**
- * update_protected_headers - Get the protected header and update the index
+ * process_protected_headers - Get the protected header and update the index
  * @param e Email to update
  */
-static void update_protected_headers(struct Email *e)
+static void process_protected_headers(struct Email *e)
 {
   struct Envelope *prot_headers = NULL;
   regmatch_t pmatch[1];
 
-  if (!C_CryptProtectedHeadersRead)
+  if (!C_CryptProtectedHeadersRead
+#ifdef USE_AUTOCRYPT
+      && !C_Autocrypt
+#endif
+  )
+  {
     return;
+  }
 
   /* Grab protected headers to update in the index */
   if (e->security & SEC_SIGN)
@@ -142,7 +151,7 @@ static void update_protected_headers(struct Email *e)
   }
 
   /* Update protected headers in the index and header cache. */
-  if (prot_headers && prot_headers->subject &&
+  if (C_CryptProtectedHeadersRead && prot_headers && prot_headers->subject &&
       mutt_str_strcmp(e->env->subject, prot_headers->subject))
   {
     if (Context->mailbox->subj_hash && e->env->real_subj)
@@ -168,6 +177,13 @@ static void update_protected_headers(struct Email *e)
       Context->mailbox->changed = 1;
     }
   }
+
+#ifdef USE_AUTOCRYPT
+  if (C_Autocrypt && (e->security & SEC_ENCRYPT) && prot_headers && prot_headers->autocrypt_gossip)
+  {
+    mutt_autocrypt_process_gossip_header(e, e->env);
+  }
+#endif
 }
 
 /**
@@ -302,8 +318,8 @@ int mutt_display_message(struct Mailbox *m, struct Email *e)
      * are color patterns for both ~g and ~V */
     e->pair = 0;
 
-    /* Grab protected headers and update the header and index */
-    update_protected_headers(e);
+    /* Process protected headers and autocrypt gossip headers */
+    process_protected_headers(e);
   }
 
   if (builtin)
index f81859cd2c327a77187eff8b66a919075bfb6a30..fa8732e8e01ac456a800c91b57850850d24830d9 100644 (file)
@@ -117,6 +117,7 @@ void mutt_env_free(struct Envelope **p)
 
 #ifdef USE_AUTOCRYPT
   mutt_free_autocrypthdr(&(*p)->autocrypt);
+  mutt_free_autocrypthdr(&(*p)->autocrypt_gossip);
 #endif
 
   FREE(p);
index 7cf51dcdb115571c3d74b3b761a37bcd22e9be46..73ec268a709e0be7a69be24811643257c927be1e 100644 (file)
@@ -80,6 +80,7 @@ struct Envelope
   struct ListHead userhdrs;            ///< user defined headers
 #ifdef USE_AUTOCRYPT
   struct AutocryptHeader *autocrypt;
+  struct AutocryptHeader *autocrypt_gossip;
 #endif
   unsigned char changed;               ///< Changed fields, e.g. #MUTT_ENV_CHANGED_SUBJECT
 };
index e1a4969a08add7ec3f65249a7e6ec44731e74457..469437e20e9880c13e2295b332159e6e01416a9f 100644 (file)
@@ -953,6 +953,14 @@ int mutt_rfc822_parse_line(struct Envelope *env, struct Email *e, char *line,
           matched = 1;
         }
       }
+      else if (mutt_str_strcasecmp(line + 1, "utocrypt-gossip") == 0)
+      {
+        if (C_Autocrypt)
+        {
+          env->autocrypt_gossip = parse_autocrypt(env->autocrypt_gossip, p);
+          matched = 1;
+        }
+      }
 #endif
       break;