sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 270d56c86219b9a44b52e97171faeff568380cb0
parent 04d8cfe1079a456cbe95faf6170d2bb9ee4dd51e
Author: Rich Lane <rlane@club.cc.cmu.edu>
Date:   Fri,  8 Oct 2010 15:44:28 -0400

Merge commit 'mainline/master'

Diffstat:
M lib/sup/buffer.rb | 4 ++++
M lib/sup/crypto.rb | 6 +++---
M lib/sup/index.rb | 6 ++++++
M lib/sup/message.rb | 3 ++-
M lib/sup/modes/edit-message-mode.rb | 25 +++++++++++++++++++++++--
M lib/sup/modes/line-cursor-mode.rb | 1 +
M lib/sup/modes/reply-mode.rb | 15 ++++++---------
M lib/sup/modes/thread-index-mode.rb | 4 ++--
8 files changed, 47 insertions(+), 17 deletions(-)
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -453,6 +453,7 @@ EOS
 
   def ask_with_completions domain, question, completions, default=nil
     ask domain, question, default do |s|
+      s.force_encoding 'UTF-8' if s.methods.include?(:encoding)
       completions.select { |x| x =~ /^#{Regexp::escape s}/i }.map { |x| [x, x] }
     end
   end
@@ -469,6 +470,8 @@ EOS
           raise "william screwed up completion: #{partial.inspect}"
         end
 
+      prefix.force_encoding 'UTF-8' if prefix.methods.include?(:encoding)
+      target.force_encoding 'UTF-8' if target.methods.include?(:encoding)
       completions.select { |x| x =~ /^#{Regexp::escape target}/i }.map { |x| [prefix + x, x] }
     end
   end
@@ -477,6 +480,7 @@ EOS
     ask domain, question, default do |partial|
       prefix, target = partial.split_on_commas_with_remainder
       target ||= prefix.pop || ""
+      target.force_encoding 'UTF-8' if target.methods.include?(:encoding)
       prefix = prefix.join(", ") + (prefix.empty? ? "" : ", ")
       completions.select { |x| x =~ /^#{Regexp::escape target}/i }.sort_by { |c| [ContactManager.contact_for(c) ? 0 : 1, c] }.map { |x| [prefix + x, x] }
     end
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
@@ -45,14 +45,14 @@ EOS
 
     sig_fn = Tempfile.new "redwood.signature"; sig_fn.close
 
-    message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --local-user '#{from}' #{payload_fn.path}", :interactive => true
+    message = run_gpg "--output #{sig_fn.path} --yes --armor --detach-sign --textmode --digest-algo sha256 --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.header["Content-Type"] = 'multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha256'
 
     envelope.add_part payload
     signature = RMail::Message.make_attachment IO.read(sig_fn.path), "application/pgp-signature", nil, "signature.asc"
@@ -144,7 +144,7 @@ EOS
     output_fn = Tempfile.new "redwood.output"
     output_fn.close
 
-    message = run_gpg "--output #{output_fn.path} --skip-verify --yes --decrypt #{payload_fn.path}", :interactive => true
+    message = run_gpg "--output #{output_fn.path} --multifile --skip-verify --yes --decrypt #{payload_fn.path}", :interactive => true
 
     unless $?.success?
       info "Error while running gpg: #{message}"
diff --git a/lib/sup/index.rb b/lib/sup/index.rb
@@ -331,6 +331,12 @@ EOS
       end
     end
 
+    ## labels are stored lower-case in the index
+    subs = subs.gsub(/\blabel:(\S+)\b/) do
+      label = $1
+      "label:#{label.downcase}"
+    end
+
     ## if we see a label:deleted or a label:spam term anywhere in the query
     ## string, we set the extra load_spam or load_deleted options to true.
     ## bizarre? well, because the query allows arbitrary parenthesized boolean
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -559,7 +559,8 @@ private
     end
 
     gpg = lines.between(GPG_START, GPG_END)
-    if !gpg.empty?
+    # between does not check if GPG_END actually exists
+    if !gpg.empty? && !lines.index(GPG_END).nil?
       msg = RMail::Message.new
       msg.body = gpg.join("\n")
 
diff --git a/lib/sup/modes/edit-message-mode.rb b/lib/sup/modes/edit-message-mode.rb
@@ -58,6 +58,18 @@ Return value:
      none
 EOS
 
+  HookManager.register "sendmail", <<EOS
+Sends the given mail. If this hook doesn't exist, the sendmail command
+configured for the account is used.
+The message will be saved after this hook is run, so any modification to it
+will be recorded.
+Variables:
+    message: RMail::Message instance of the mail to send
+    account: Account instance matching the From address
+Return value:
+     True if mail has been sent successfully, false otherwise.
+EOS
+
   attr_reader :status
   attr_accessor :body, :header
   bool_reader :edited
@@ -341,8 +353,17 @@ protected
     begin
       date = Time.now
       m = build_message date
-      IO.popen(acct.sendmail, "w") { |p| p.puts m }
-      raise SendmailCommandFailed, "Couldn't execute #{acct.sendmail}" unless $? == 0
+
+      if HookManager.enabled? "sendmail"
+    if not HookManager.run "sendmail", :message => m, :account => acct
+          warn "Sendmail hook was not successful"
+          return false
+    end
+      else
+        IO.popen(acct.sendmail, "w") { |p| p.puts m }
+        raise SendmailCommandFailed, "Couldn't execute #{acct.sendmail}" unless $? == 0
+      end
+
       SentManager.write_sent_message(date, from_email) { |f| f.puts sanitize_body(m.to_s) }
       BufferManager.kill_buffer buffer
       BufferManager.flash "Message sent!"
diff --git a/lib/sup/modes/line-cursor-mode.rb b/lib/sup/modes/line-cursor-mode.rb
@@ -66,6 +66,7 @@ protected
     return if @curpos == p
     @curpos = p.clamp @cursor_top, lines
     buffer.mark_dirty
+    set_status
   end
 
   ## override search behavior to be cursor-based. this is a stupid
diff --git a/lib/sup/modes/reply-mode.rb b/lib/sup/modes/reply-mode.rb
@@ -65,18 +65,15 @@ EOS
     ## if we have a value from a hook, use it.
     from = if hook_reply_from
       hook_reply_from
-    ## otherwise, if the original email had an envelope-to header, try and use
-    ## it, and look up the corresponding name form the list of accounts.
-    ##
+    ## otherwise, try and find an account somewhere in the list of to's
+    ## and cc's and look up the corresponding name form the list of accounts.
+    ## if this does not succeed use the recipient_email (=envelope-to) instead.
     ## this is for the case where mail is received from a mailing lists (so the
     ## To: is the list id itself). if the user subscribes via a particular
     ## alias, we want to use that alias in the reply.
-    elsif @m.recipient_email && (a = AccountManager.account_for(@m.recipient_email))
-      Person.new a.name, @m.recipient_email
-    ## otherwise, try and find an account somewhere in the list of to's
-    ## and cc's.
-    elsif(b = (@m.to + @m.cc).find { |p| AccountManager.is_account? p })
-      b
+    elsif(b = (@m.to.collect {|t| t.email} + @m.cc.collect {|c| c.email} + [@m.recipient_email] ).find { |p| AccountManager.is_account_email? p })
+      a = AccountManager.account_for(b)
+      Person.new a.name, b
     ## if all else fails, use the default
     else
       AccountManager.default_account
diff --git a/lib/sup/modes/thread-index-mode.rb b/lib/sup/modes/thread-index-mode.rb
@@ -792,8 +792,8 @@ protected
     authors = t.map do |m, *o|
       next unless m && m.from
       new[m.from] ||= m.has_label?(:unread)
-      next if seen[m.from]
-      seen[m.from] = true
+      next if seen[m.from.mediumname]
+      seen[m.from.mediumname] = true
       m.from
     end.compact