]> git.notmuchmail.org Git - sup/blob - lib/sup/crypto.rb
reply-mode bugfix thanks to Marcus Williams
[sup] / lib / sup / crypto.rb
1 module Redwood
2
3 class CryptoManager
4   include Singleton
5
6   def initialize
7     @mutex = Mutex.new
8     self.class.i_am_the_instance self
9
10     bin = `which gpg`.chomp
11     bin = `which pgp`.chomp unless bin =~ /\S/
12
13     @cmd =
14       case bin
15       when /\S/
16         "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
17       else
18         nil
19       end
20   end
21
22   # returns a cryptosignature
23   def verify payload, signature # both RubyMail::Message objects
24     return unknown_status(cant_find_binary) unless @cmd
25
26     payload_fn = Tempfile.new "redwood.payload"
27     payload_fn.write payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n").gsub(/^MIME-Version: .*\r\n/, "")
28     payload_fn.close
29
30     signature_fn = Tempfile.new "redwood.signature"
31     signature_fn.write signature.decode
32     signature_fn.close
33
34     cmd = "#{@cmd} --verify #{signature_fn.path} #{payload_fn.path} 2> /dev/null"
35
36     #Redwood::log "gpg: running: #{cmd}"
37     gpg_output = `#{cmd}`
38     #Redwood::log "got output: #{gpg_output.inspect}"
39     output_lines = gpg_output.split(/\n/)
40
41     if gpg_output =~ /^gpg: (.* signature from .*$)/
42       if $? == 0
43         Chunk::CryptoNotice.new :valid, $1, output_lines
44       else
45         Chunk::CryptoNotice.new :invalid, $1, output_lines
46       end
47     else
48       unknown_status output_lines
49     end
50   end
51
52   # returns decrypted_message, status, desc, lines
53   def decrypt payload # RubyMail::Message objects
54     return unknown_status(cant_find_binary) unless @cmd
55
56 #    cmd = "#{@cmd} --decrypt 2> /dev/null"
57
58 #    Redwood::log "gpg: running: #{cmd}"
59
60 #    gpg_output =
61 #      IO.popen(cmd, "a+") do |f|
62 #        f.puts payload.to_s
63 #        f.gets
64 #      end
65
66     payload_fn = Tempfile.new "redwood.payload"
67     payload_fn.write payload.to_s
68     payload_fn.close
69
70     cmd = "#{@cmd} --decrypt #{payload_fn.path} 2> /dev/null"
71     Redwood::log "gpg: running: #{cmd}"
72     gpg_output = `#{cmd}`
73     Redwood::log "got output: #{gpg_output.inspect}"
74
75     if $? == 0 # successful decryption
76       decrypted_payload, sig_lines =
77         if gpg_output =~ /\A(.*?)((^gpg: .*$)+)\Z/m
78           [$1, $2]
79         else
80           [gpg_output, nil]
81         end
82       
83       sig = 
84         if sig_lines # encrypted & signed
85           if sig_lines =~ /^gpg: (Good signature from .*$)/
86             Chunk::CryptoNotice.new :valid, $1, sig_lines.split("\n")
87           else
88             Chunk::CryptoNotice.new :invalid, $1, sig_lines.split("\n")
89           end
90         end
91
92       notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display"
93       [RMail::Parser.read(decrypted_payload), sig, notice]
94     else
95       notice = Chunk::CryptoNotice.new :invalid, "This message could not be decrypted", gpg_output.split("\n")
96       [nil, nil, notice]
97     end
98   end
99
100 private
101
102   def unknown_status lines=[]
103     Chunk::CryptoNotice.new :unknown, "Unable to determine validity of cryptographic signature", lines
104   end
105   
106   def cant_find_binary
107     ["Can't find gpg or pgp binary in path"]
108   end
109 end
110 end