sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 16901db9836d2c07d8143c7acf5975cc411904bb
parent c62aec33a8c98ceb90d052e211574fa54d701bd7
Author: Matthieu Rakotojaona <matthieu.rakotojaona@gmail.com>
Date:   Sun, 25 Jan 2015 11:39:54 +0100

Add key binding for fetching key and re-verifying message

Diffstat:
M lib/sup/crypto.rb | 29 +++++++++++++++++++++++++++--
M lib/sup/message.rb | 6 ++++++
M lib/sup/message_chunks.rb | 5 +++--
M lib/sup/modes/thread_view_mode.rb | 22 ++++++++++++++++++++++
4 files changed, 58 insertions(+), 4 deletions(-)
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
@@ -16,6 +16,9 @@ class CryptoManager
     [:encrypt, "Encrypt only"]
   )
 
+  KEY_PATTERN = /(-----BEGIN PGP PUBLIC KEY BLOCK.*-----END PGP PUBLIC KEY BLOCK)/m
+  KEYSERVER_URL = "http://pool.sks-keyservers.net:11371/pks/lookup"
+
   HookManager.register "gpg-options", <<EOS
 Runs before gpg is called, allowing you to modify the options (most
 likely you would want to add something to certain commands, like
@@ -212,9 +215,10 @@ EOS
     unknown = false
     all_output_lines = []
     all_trusted = true
+    unknown_fingerprint = nil
 
     verify_result.signatures.each do |signature|
-      output_lines, trusted = sig_output_lines signature
+      output_lines, trusted, unknown_fingerprint = sig_output_lines signature
       all_output_lines << output_lines
       all_output_lines.flatten!
       all_trusted &&= trusted
@@ -242,6 +246,8 @@ EOS
       end
     elsif !unknown
       Chunk::CryptoNotice.new(:invalid, summary_line, all_output_lines)
+    elsif unknown_fingerprint
+      Chunk::CryptoNotice.new(:unknown_key, "Unable to determine validity of cryptographic signature", all_output_lines, unknown_fingerprint)
     else
       unknown_status all_output_lines
     end
@@ -351,6 +357,24 @@ EOS
     [notice, sig, msg]
   end
 
+  def retrieve fingerprint
+    require 'net/http'
+    uri = URI(KEYSERVER_URL)
+    fingerprint = "0x" + fingerprint unless fingerprint[0..1] == "0x"
+    params = {op: "get", search: fingerprint}
+    uri.query = URI.encode_www_form(params)
+
+    res = Net::HTTP.get_response(uri)
+    return "Couldn't contact keyserver pool" unless res.is_a?(Net::HTTPSuccess)
+
+    match = KEY_PATTERN.match(res.body)
+    return "No key found" unless match && match.length > 0
+
+    GPGME::Key.import(match[0])
+
+    return nil
+  end
+
 private
 
   def unknown_status lines=[]
@@ -394,6 +418,7 @@ private
     rescue EOFError
       from_key = nil
       first_sig = "No public key available for #{signature.fingerprint}"
+      unknown_fpr = signature.fingerprint
     end
 
     time_line = "Signature made " + signature.timestamp.strftime("%a %d %b %Y %H:%M:%S %Z") +
@@ -422,7 +447,7 @@ private
       output_lines << HookManager.run("sig-output",
                                {:signature => signature, :from_key => from_key})
     end
-    return output_lines, trusted
+    return output_lines, trusted, unknown_fpr
   end
 
   def key_type key, fpr
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -279,6 +279,12 @@ class Message
       end
   end
 
+  def reload_from_source!
+    @chunks = nil
+    load_from_source!
+  end
+
+
   def error_message
     <<EOS
 #@snippet...
diff --git a/lib/sup/message_chunks.rb b/lib/sup/message_chunks.rb
@@ -307,12 +307,13 @@ EOS
   end
 
   class CryptoNotice
-    attr_reader :lines, :status, :patina_text
+    attr_reader :lines, :status, :patina_text, :unknown_fingerprint
 
-    def initialize status, description, lines=[]
+    def initialize status, description, lines=[], unknown_fingerprint=nil
       @status = status
       @patina_text = description
       @lines = lines
+      @unknown_fingerprint = unknown_fingerprint
     end
 
     def patina_color
diff --git a/lib/sup/modes/thread_view_mode.rb b/lib/sup/modes/thread_view_mode.rb
@@ -89,6 +89,7 @@ EOS
     k.add :toggle_wrap, "Toggle wrapping of text", 'w'
 
     k.add :goto_uri, "Goto uri under cursor", 'g'
+    k.add :fetch_and_verify, "Fetch the PGP key on poolserver and re-verify message", "v"
 
     k.add_multi "(a)rchive/(d)elete/mark as (s)pam/mark as u(N)read:", '.' do |kk|
       kk.add :archive_and_kill, "Archive this thread and kill buffer", 'a'
@@ -793,6 +794,27 @@ EOS
     BufferManager.flash "No URI found." unless found
   end
 
+  def fetch_and_verify
+    message = @message_lines[curpos]
+    crypto_chunk = message.chunks.select {|chunk| chunk.is_a?(Chunk::CryptoNotice)}.first
+    return unless crypto_chunk
+    return unless crypto_chunk.unknown_fingerprint
+
+    BufferManager.flash "Retrieving key #{crypto_chunk.unknown_fingerprint} ..."
+
+    error = CryptoManager.retrieve crypto_chunk.unknown_fingerprint
+
+    if error
+      BufferManager.flash "Couldn't retrieve key: #{error.to_s}"
+    else
+      BufferManager.flash "Key #{crypto_chunk.unknown_fingerprint} successfully retrieved !"
+    end
+
+    # Re-trigger gpg verification
+    message.reload_from_source!
+    update
+  end
+
 private
 
   def initial_state_for m