commit 4812245a2f975370290fb4235b05e9eca793e318
parent 1b2c7b1dfee5a3959da702c17d104ead5e2f4bab
Author: William Morgan <wmorgan-sup@masanjin.net>
Date: Sun, 11 Oct 2009 13:23:25 -0700
Merge branch 'interactive-crypto' into next
Diffstat:
2 files changed, 75 insertions(+), 55 deletions(-)
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -757,6 +757,7 @@ EOS
end
private
+
def default_status_bar buf
" [#{buf.mode.name}] #{buf.title} #{buf.mode.status}"
end
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
@@ -15,15 +15,14 @@ class CryptoManager
@mutex = Mutex.new
bin = `which gpg`.chomp
- @cmd =
- case bin
- when /\S/
- debug "crypto: detected gpg binary in #{bin}"
- "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
- else
- debug "crypto: no gpg binary detected"
- nil
- end
+ @cmd = case bin
+ when /\S/
+ debug "crypto: detected gpg binary in #{bin}"
+ "#{bin} --quiet --batch --no-verbose --logger-fd 1 --use-agent"
+ else
+ debug "crypto: no gpg binary detected"
+ nil
+ end
end
def have_crypto?; !@cmd.nil? end
@@ -33,15 +32,19 @@ class CryptoManager
payload_fn.write format_payload(payload)
payload_fn.close
- output = run_gpg "--output - --armor --detach-sign --textmode --local-user '#{from}' #{payload_fn.path}"
+ sig_fn = Tempfile.new "redwood.signature"; sig_fn.close
- raise Error, (output || "gpg command failed: #{cmd}") unless $?.success?
+ message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --local-user '#{from}' #{payload_fn.path}", :interactive => true
+ unless $?.success?
+ info "Error while running gpg: #{message}"
+ raise Error, "GPG command failed. See log for details."
+ end
envelope = RMail::Message.new
envelope.header["Content-Type"] = 'multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha1'
envelope.add_part payload
- signature = RMail::Message.make_attachment output, "application/pgp-signature", nil, "signature.asc"
+ signature = RMail::Message.make_attachment IO.read(sig_fn.path), "application/pgp-signature", nil, "signature.asc"
envelope.add_part signature
envelope
end
@@ -51,15 +54,20 @@ class CryptoManager
payload_fn.write format_payload(payload)
payload_fn.close
+ encrypted_fn = Tempfile.new "redwood.encrypted"; encrypted_fn.close
+
recipient_opts = (to + [ from ] ).map { |r| "--recipient '<#{r}>'" }.join(" ")
sign_opts = sign ? "--sign --local-user '#{from}'" : ""
- gpg_output = run_gpg "--output - --armor --encrypt --textmode #{sign_opts} #{recipient_opts} #{payload_fn.path}"
- raise Error, (gpg_output || "gpg command failed: #{cmd}") unless $?.success?
+ message = run_gpg "--output #{encrypted_fn.path} --yes --armor --encrypt --textmode #{sign_opts} #{recipient_opts} #{payload_fn.path}", :interactive => true
+ unless $?.success?
+ info "Error while running gpg: #{message}"
+ raise Error, "GPG command failed. See log for details."
+ end
encrypted_payload = RMail::Message.new
encrypted_payload.header["Content-Type"] = "application/octet-stream"
encrypted_payload.header["Content-Disposition"] = 'inline; filename="msg.asc"'
- encrypted_payload.body = gpg_output
+ encrypted_payload.body = IO.read(encrypted_fn.path)
control = RMail::Message.new
control.header["Content-Type"] = "application/pgp-encrypted"
@@ -111,46 +119,48 @@ class CryptoManager
payload_fn.write payload.to_s
payload_fn.close
- output = run_gpg "--decrypt #{payload_fn.path}"
+ output_fn = Tempfile.new "redwood.output"
+ output_fn.close
- if $?.success?
- decrypted_payload, sig_lines = if output =~ /\A(.*?)((^gpg: .*$)+)\Z/m
- [$1, $2]
- else
- [output, nil]
- end
+ message = run_gpg "--output #{output_fn.path} --yes --decrypt #{payload_fn.path}", :interactive => true
- sig = if sig_lines # encrypted & signed
- if sig_lines =~ /^gpg: (Good signature from .*$)/
- Chunk::CryptoNotice.new :valid, $1, sig_lines.split("\n")
- else
- Chunk::CryptoNotice.new :invalid, $1, sig_lines.split("\n")
- end
- end
+ unless $?.success?
+ info "Error while running gpg: #{message}"
+ return Chunk::CryptoNotice.new(:invalid, "This message could not be decrypted", message.split("\n"))
+ end
- # This is gross. This decrypted payload could very well be a multipart
- # element itself, as opposed to a simple payload. For example, a
- # multipart/signed element, like those generated by Mutt when encrypting
- # and signing a message (instead of just clearsigning the body).
- # Supposedly, decrypted_payload being a multipart element ought to work
- # out nicely because Message::multipart_encrypted_to_chunks() runs the
- # decrypted message through message_to_chunks() again to get any
- # children. However, it does not work as intended because these inner
- # payloads need not carry a MIME-Version header, yet they are fed to
- # RMail as a top-level message, for which the MIME-Version header is
- # required. This causes for the part not to be detected as multipart,
- # hence being shown as an attachment. If we detect this is happening,
- # we force the decrypted payload to be interpreted as MIME.
- msg = RMail::Parser.read(decrypted_payload)
- if msg.header.content_type =~ %r{^multipart/} and not msg.multipart?
- decrypted_payload = "MIME-Version: 1.0\n" + decrypted_payload
- msg = RMail::Parser.read(decrypted_payload)
- end
- notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display"
- [notice, sig, msg]
- else
- Chunk::CryptoNotice.new :invalid, "This message could not be decrypted", output.split("\n")
+ output = IO.read output_fn.path
+
+ ## there's probably a better way to do this, but we're using the output to
+ ## look for a valid signature being present.
+
+ sig = case message
+ when /^gpg: (Good signature from .*$)/i
+ Chunk::CryptoNotice.new :valid, $1, message.split("\n")
+ when /^gpg: (Bad signature from .*$)/i
+ Chunk::CryptoNotice.new :invalid, $1, message.split("\n")
end
+
+ # This is gross. This decrypted payload could very well be a multipart
+ # element itself, as opposed to a simple payload. For example, a
+ # multipart/signed element, like those generated by Mutt when encrypting
+ # and signing a message (instead of just clearsigning the body).
+ # Supposedly, decrypted_payload being a multipart element ought to work
+ # out nicely because Message::multipart_encrypted_to_chunks() runs the
+ # decrypted message through message_to_chunks() again to get any
+ # children. However, it does not work as intended because these inner
+ # payloads need not carry a MIME-Version header, yet they are fed to
+ # RMail as a top-level message, for which the MIME-Version header is
+ # required. This causes for the part not to be detected as multipart,
+ # hence being shown as an attachment. If we detect this is happening,
+ # we force the decrypted payload to be interpreted as MIME.
+ msg = RMail::Parser.read output
+ if msg.header.content_type =~ %r{^multipart/} && !msg.multipart?
+ output = "MIME-Version: 1.0\n" + output
+ msg = RMail::Parser.read output
+ end
+ notice = Chunk::CryptoNotice.new :valid, "This message has been decrypted for display"
+ [notice, sig, msg]
end
private
@@ -169,10 +179,19 @@ private
payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n").gsub(/^MIME-Version: .*\r\n/, "")
end
- def run_gpg args
- cmd = "#{@cmd} #{args} 2> /dev/null"
- output = `#{cmd}`
- output
+ def run_gpg args, opts={}
+ cmd = "#{@cmd} #{args}"
+ if opts[:interactive] && BufferManager.instantiated?
+ output_fn = Tempfile.new "redwood.output"
+ output_fn.close
+ cmd += " > #{output_fn.path} 2> /dev/null"
+ debug "crypto: running: #{cmd}"
+ BufferManager.shell_out cmd
+ IO.read(output_fn.path) rescue "can't read output"
+ else
+ debug "crypto: running: #{cmd}"
+ `#{cmd} 2> /dev/null`
+ end
end
end
end