sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit e9b776f28b42d6a40a512d15c552c51cafbecb5c
parent 7cfbe0536601ef7c62a450ef354433bc848309d7
Author: Rich Lane <rlane@club.cc.cmu.edu>
Date:   Sun, 10 Oct 2010 23:45:30 -0700

Merge branch 'master' into next

Diffstat:
M lib/sup.rb | 3 ++-
M lib/sup/account.rb | 5 +++--
M lib/sup/crypto.rb | 23 +++++++++++++++++++++--
M lib/sup/message.rb | 8 +++++---
4 files changed, 31 insertions(+), 8 deletions(-)
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -261,7 +261,8 @@ EOS
             :email => email,
             :alternates => [],
             :sendmail => "/usr/sbin/sendmail -oem -ti",
-            :signature => File.join(ENV["HOME"], ".signature")
+            :signature => File.join(ENV["HOME"], ".signature"),
+            :gpgkey => ""
           }
         },
         :editor => ENV["EDITOR"] || "/usr/bin/vim -f -c 'setlocal spell spelllang=en_us' -c 'set filetype=mail'",
diff --git a/lib/sup/account.rb b/lib/sup/account.rb
@@ -1,7 +1,7 @@
 module Redwood
 
 class Account < Person
-  attr_accessor :sendmail, :signature
+  attr_accessor :sendmail, :signature, :gpgkey
 
   def initialize h
     raise ArgumentError, "no name for account" unless h[:name]
@@ -9,6 +9,7 @@ class Account < Person
     super h[:name], h[:email]
     @sendmail = h[:sendmail]
     @signature = h[:signature]
+    @gpgkey = h[:gpgkey]
   end
 
   # Default sendmail command for bouncing mail,
@@ -46,7 +47,7 @@ class AccountManager
   def add_account hash, default=false
     raise ArgumentError, "no email specified for account" unless hash[:email]
     unless default
-      [:name, :sendmail, :signature].each { |k| hash[k] ||= @default_account.send(k) }
+      [:name, :sendmail, :signature, :gpgkey].each { |k| hash[k] ||= @default_account.send(k) }
     end
     hash[:alternates] ||= []
 
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
@@ -45,7 +45,8 @@ EOS
 
     sig_fn = Tempfile.new "redwood.signature"; sig_fn.close
 
-    message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --digest-algo sha256 --local-user '#{from}' #{payload_fn.path}", :interactive => true
+    sign_user_opts = gen_sign_user_opts from
+    message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --digest-algo sha256 #{sign_user_opts} #{payload_fn.path}", :interactive => true
     unless $?.success?
       info "Error while running gpg: #{message}"
       raise Error, "GPG command failed. See log for details."
@@ -68,7 +69,8 @@ EOS
     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}'" : ""
+    sign_opts = ""
+    sign_opts = "--sign --digest-algo sha256 " + gen_sign_user_opts(from) if sign
     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}"
@@ -208,6 +210,23 @@ private
     payload.to_s.gsub(/(^|[^\r])\n/, "\\1\r\n").gsub(/^MIME-Version: .*\r\n/, "")
   end
 
+  # logic is:
+  # if    gpgkey set for this account, then use that
+  # elsif only one account,            then leave blank so gpg default will be user
+  # else                                    set --local-user from_email_address
+  def gen_sign_user_opts from
+    account = AccountManager.account_for from
+    if !account.gpgkey.nil?
+      opts = "--local-user '#{account.gpgkey}'"
+    elsif AccountManager.user_emails.length == 1
+      # only one account
+      opts = ""
+    else
+      opts = "--local-user '#{from}'" 
+    end
+    opts
+  end
+
   def run_gpg args, opts={}
     args = HookManager.run("gpg-args", { :args => args }) || args
     cmd = "LC_MESSAGES=C #{@cmd} #{args}"
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -475,10 +475,12 @@ private
       end
     else
       filename =
-        ## first, paw through the headers looking for a filename
-        if m.header["Content-Disposition"] && m.header["Content-Disposition"] =~ /filename="?(.*?[^\\])("|;|$)/
+        ## first, paw through the headers looking for a filename.
+        ## RFC 2183 (Content-Disposition) specifies that disposition-parms are
+        ## separated by ";". So, we match everything up to " and ; (if present).
+        if m.header["Content-Disposition"] && m.header["Content-Disposition"] =~ /filename="?(.*?[^\\])("|;|\z)/m
           $1
-        elsif m.header["Content-Type"] && m.header["Content-Type"] =~ /name="?(.*?[^\\])("|;|$)/i
+        elsif m.header["Content-Type"] && m.header["Content-Type"] =~ /name="?(.*?[^\\])("|;|\z)/im
           $1
 
         ## haven't found one, but it's a non-text message. fake