sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit e42201087f164d72eddd7867faf33efc111e5605
parent 69ec7c32f708e6bc707213a74350503893ac0cd4
Author: Gaute Hope <eg@gaute.vetsj.com>
Date:   Mon, 27 Jan 2014 11:43:42 +0100

Merge branch 'develop'

Diffstat:
M CONTRIBUTORS | 21 +++++++++++----------
M History.txt | 4 ++++
M ReleaseNotes | 4 ++++
M lib/sup.rb | 1 -
M lib/sup/account.rb | 52 ++++++++++------------------------------------------
M lib/sup/crypto.rb | 7 ++++---
M lib/sup/mbox.rb | 2 +-
M lib/sup/message.rb | 17 +++++++++++++----
A test/messages/bad-content-transfer-encoding-1.eml | 8 ++++++++
A test/messages/binary-content-transfer-encoding-2.eml | 21 +++++++++++++++++++++
A test/test_messages_dir.rb | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
11 files changed, 188 insertions(+), 61 deletions(-)
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
@@ -18,8 +18,8 @@ Clint Byrum <clint at the ubuntu dot coms>
 Marcus Williams <marcus-sup at the bar-coded dot nets>
 Lionel Ott <white.magic at the gmx dot des>
 Gaudenz Steinlin <gaudenz at the soziologie dot chs>
-Ingmar Vanhassel <ingmar at the exherbo dot orgs>
 Mark Alexander <marka at the pobox dot coms>
+Ingmar Vanhassel <ingmar at the exherbo dot orgs>
 Edward Z. Yang <ezyang at the mit dot edus>
 Matthieu Rakotojaona <matthieu.rakotojaona at the gmail dot coms>
 Christopher Warrington <chrisw at the rice dot edus>
@@ -33,8 +33,8 @@ Michael Hamann <michael at the content-space dot des>
 Jonathan Lassoff <jof at the thejof dot coms>
 William Erik Baxter <web at the superscript dot coms>
 Grant Hollingworth <grant at the antiflux dot orgs>
-Adeodato Simó <dato at the net.com.org dot ess>
 Markus Klinik <markus.klinik at the gmx dot des>
+Adeodato Simó <dato at the net.com.org dot ess>
 Ico Doornekamp <ico at the pruts dot nls>
 Daniel Schoepe <daniel.schoepe at the googlemail dot coms>
 James Taylor <james at the jamestaylor dot orgs>
@@ -44,30 +44,31 @@ Steve Goldman <sgoldman at the tower-research dot coms>
 Peter Harkins <ph at the malaprop dot orgs>
 Decklin Foster <decklin at the red-bean dot coms>
 Cameron Matheson <cam+sup at the cammunism dot orgs>
-Alex Vandiver <alex at the chmrr dot nets>
 Carl Worth <cworth at the cworth dot orgs>
+Alex Vandiver <alex at the chmrr dot nets>
 Andrew Pimlott <andrew at the pimlott dot nets>
 Jeff Balogh <its.jeff.balogh at the gmail dot coms>
 Matías Aguirre <matiasaguirre at the gmail dot coms>
 Kornilios Kourtis <kkourt at the cslab.ece.ntua dot grs>
-Kevin Riggle <kevinr at the free-dissociation dot coms>
+Lars Fischer <fischer at the wiwi.uni-siegen dot des>
 Giorgio Lando <patroclo7 at the gmail dot coms>
+Kevin Riggle <kevinr at the free-dissociation dot coms>
 Benoît PIERRE <benoit.pierre at the gmail dot coms>
-Steven Lawrance <stl at the koffein dot nets>
 Alvaro Herrera <alvherre at the alvh.no-ip dot orgs>
+Steven Lawrance <stl at the koffein dot nets>
 Jonah <Jonah at the GoodCoffee dot cas>
 ian <itaylor at the uark dot edus>
-Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
-0xACE <0xACE at the users.noreply.github dot coms>
-Adam Lloyd <adam at the alloy-d dot nets>
 Todd Eisenberger <teisenbe at the andrew.cmu dot edus>
 MichaelRevell <mikearevell at the gmail dot coms>
+Adam Lloyd <adam at the alloy-d dot nets>
+Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
 Per Andersson <avtobiff at the gmail dot coms>
+0xACE <0xACE at the users.noreply.github dot coms>
 Steven Walter <swalter at the monarch.(none)>
-Jon M. Dugan <jdugan at the es dot nets>
 Matthias Vallentin <vallentin at the icir dot orgs>
-Horacio Sanson <horacio at the skillupjapan.co dot jps>
+Jon M. Dugan <jdugan at the es dot nets>
 Stefan Lundström <lundst at the snabb.(none)>
 akojo <atte.kojo at the gmail dot coms>
+Horacio Sanson <horacio at the skillupjapan.co dot jps>
 Johannes Larsen <johs.a.larsen at the gmail dot coms>
 Kirill Smelkov <kirr at the landau.phys.spbu dot rus>
diff --git a/History.txt b/History.txt
@@ -1,3 +1,7 @@
+== 0.15.3 / 2014-01-27
+
+* Revert non-functioning hidden_alternates and fix some bugs.
+
 == 0.15.2 / 2013-12-20
 
 * Use the form_driver_w routine for inputing multibyte chars when
diff --git a/ReleaseNotes b/ReleaseNotes
@@ -1,3 +1,7 @@
+Release 0.15.3:
+
+Revert non-functioning hidden_alternates option and fix bugs.
+
 Release 0.15.2:
 
 Use form_driver_w when available. New hidden_alternates option.
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -357,7 +357,6 @@ EOM
             :name => name.dup.fix_encoding!,
             :email => email.dup.fix_encoding!,
             :alternates => [],
-            :hidden_alternates => [],
             :sendmail => "/usr/sbin/sendmail -oem -ti",
             :signature => File.join(ENV["HOME"], ".signature"),
             :gpgkey => ""
diff --git a/lib/sup/account.rb b/lib/sup/account.rb
@@ -31,8 +31,6 @@ class AccountManager
 
   def initialize accounts
     @email_map = {}
-    @hidden_email_map = {}
-    @email_map_dirty = false
     @accounts = {}
     @regexen = {}
     @default_account = nil
@@ -42,7 +40,7 @@ class AccountManager
   end
 
   def user_accounts; @accounts.keys; end
-  def user_emails(type = :all); email_map(type).keys.select { |e| String === e }; end
+  def user_emails; @email_map.keys.select { |e| String === e }; end
 
   ## must be called first with the default account. fills in missing
   ## values from the default account.
@@ -52,9 +50,7 @@ class AccountManager
       [:name, :sendmail, :signature, :gpgkey].each { |k| hash[k] ||= @default_account.send(k) }
     end
     hash[:alternates] ||= []
-    hash[:hidden_alternates] ||= []
     fail "alternative emails are not an array: #{hash[:alternates]}" unless hash[:alternates].kind_of? Array
-    fail "hidden alternative emails are not an array: #{hash[:hidden_alternates]}" unless hash[:hidden_alternates].kind_of? Array
 
     [:name, :signature].each { |x| hash[x] ? hash[x].fix_encoding! : nil }
 
@@ -67,11 +63,8 @@ class AccountManager
     end
 
     ([hash[:email]] + hash[:alternates]).each do |email|
-      add_email_to_map(:shown, email, a)
-    end
-
-    hash[:hidden_alternates].each do |email|
-      add_email_to_map(:hidden, email, a)
+      next if @email_map.member? email
+      @email_map[email] = a
     end
 
     hash[:regexen].each do |re|
@@ -79,44 +72,19 @@ class AccountManager
     end if hash[:regexen]
   end
 
-  def is_account? p;    is_account_email? p.email       end
+  def is_account? p; is_account_email? p.email end
   def is_account_email? email; !account_for(email).nil? end
-
   def account_for email
-    a = email_map[email]
-    a.nil? ? @regexen.argfind { |re, a| re =~ email && a } : a
+    if(a = @email_map[email])
+      a
+    else
+      @regexen.argfind { |re, a| re =~ email && a }
+    end
   end
-
   def full_address_for email
     a = account_for email
     Person.full_address a.name, email
   end
-
-  private
-
-  def add_email_to_map(type, email, acc)
-    type = :shown if type != :hidden
-    m = email_map(type)
-    unless m.member? email
-      m[email] = acc
-      @email_map_dirty = true
-    end
-  end
-
-  def email_map(type = nil)
-    case type
-    when :shown, :public  then @email_map
-    when :hidden          then @hidden_email_map
-    else
-      if @email_map_dirty
-        @email_map_all = @hidden_email_map.merge(@email_map)
-        if @email_map_all.count != @email_map.count + @hidden_email_map.count
-          @hidden_email_map.reject! { |m| @email_map.member? m }
-        end
-      end
-      @email_map_all ||= {}
-    end
-  end
-end # class AccountManager
+end
 
 end
diff --git a/lib/sup/crypto.rb b/lib/sup/crypto.rb
@@ -128,7 +128,6 @@ EOS
     gpg_opts.merge!(gen_sign_user_opts(from))
     gpg_opts = HookManager.run("gpg-options",
                                {:operation => "sign", :options => gpg_opts}) || gpg_opts
-
     begin
       if GPGME.respond_to?('detach_sign')
         sig = GPGME.detach_sign(format_payload(payload), gpg_opts)
@@ -443,16 +442,18 @@ private
   # 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
+  # NOTE: multiple signers doesn't seem to work with gpgme (2.0.2, 1.0.8)
+  #       
   def gen_sign_user_opts from
     account = AccountManager.account_for from
     account ||= AccountManager.default_account
     if !account.gpgkey.nil?
-      opts = {:signers => account.gpgkey}
+      opts = {:signer => account.gpgkey}
     elsif AccountManager.user_emails.length == 1
       # only one account
       opts = {}
     else
-      opts = {:signers => from}
+      opts = {:signer => from}
     end
     opts
   end
diff --git a/lib/sup/mbox.rb b/lib/sup/mbox.rb
@@ -161,7 +161,7 @@ class MBox < Source
   ## TODO optimize this by iterating over allterms list backwards or
   ## storing source_info negated
   def last_indexed_message
-    benchmark(:mbox_read_index) { Enumerator.new(Index.instance, :each_source_info, self.id).map(&:to_i).max }
+    benchmark(:mbox_read_index) { Index.instance.enum_for(:each_source_info, self.id).map(&:to_i).max }
   end
 
   ## offset of first new message or nil
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -263,9 +263,9 @@ class Message
         message_to_chunks rmsg
       rescue SourceError, SocketError, RMail::EncodingUnsupportedError => e
         warn "problem reading message #{id}"
-        [Chunk::Text.new(error_message.split("\n"))]
-
         debug "could not load message: #{location.inspect}, exception: #{e.inspect}"
+
+        [Chunk::Text.new(error_message.split("\n"))]
       end
   end
 
@@ -497,13 +497,21 @@ private
       ## they have no MIME multipart and just set the body content type to
       ## application/pgp. this handles that.
       ##
-      ## TODO: unduplicate code between here and multipart_encrypted_to_chunks
+      ## TODO 1: unduplicate code between here and
+      ##         multipart_encrypted_to_chunks
+      ## TODO 2: this only tries to decrypt. it cannot handle inline PGP
       notice, sig, decryptedm = CryptoManager.decrypt m.body
       if decryptedm # managed to decrypt
         children = message_to_chunks decryptedm, true
         [notice, sig].compact + children
       else
-        [notice]
+        ## try inline pgp signed
+      	chunks = inline_gpg_to_chunks m.body, $encoding, (m.charset || $encoding)
+        if chunks
+          chunks
+        else
+          [notice]
+        end
       end
     else
       filename =
@@ -579,6 +587,7 @@ private
     # -----END PGP SIGNED MESSAGE-----
     #
     # In some cases, END PGP SIGNED MESSAGE doesn't appear
+    # (and may leave strange -----BEGIN PGP SIGNATURE----- ?)
     gpg = lines.between(GPG_SIGNED_START, GPG_SIGNED_END)
     # between does not check if GPG_END actually exists
     # Reference: http://permalink.gmane.org/gmane.mail.sup.devel/641
diff --git a/test/messages/bad-content-transfer-encoding-1.eml b/test/messages/bad-content-transfer-encoding-1.eml
@@ -0,0 +1,8 @@
+From: foo@example.org
+MIME-Version: 1.0
+Subject: Content-Transfer-Encoding:-bug in sup
+Content-Type: message/rfc822
+Content-Transfer-Encoding: nosuchcontenttransferencoding
+
+foo
+
diff --git a/test/messages/binary-content-transfer-encoding-2.eml b/test/messages/binary-content-transfer-encoding-2.eml
@@ -0,0 +1,21 @@
+From: foo@example.org
+MIME-Version: 1.0
+Content-type: multipart/report; boundary="======11647==82899======"; report-type="spam-notification"
+Subject: Important
+
+This is a multi-part message in MIME format...
+
+--======11647==82899======
+Content-Type: text/plain; charset="ISO-8859-1"
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+
+--======11647==82899======
+Content-Type: message/rfc822
+Content-Disposition: attachment
+Content-Transfer-Encoding: binary
+
+
+--======11647==82899======--
+
diff --git a/test/test_messages_dir.rb b/test/test_messages_dir.rb
@@ -0,0 +1,112 @@
+#!/usr/bin/ruby
+
+require 'test_helper'
+require 'sup'
+require 'stringio'
+
+require 'dummy_source'
+
+# override File.exists? to make it work with StringIO for testing.
+# FIXME: do aliasing to avoid breaking this when sup moves from
+# File.exists? to File.exist?
+
+class File
+
+  def File.exists? file
+    # puts "fake File::exists?"
+
+    if file.is_a?(StringIO)
+      return false
+    end
+    # use the different function
+    File.exist?(file)
+  end
+
+end
+
+module Redwood
+
+class TestMessagesDir < ::Minitest::Unit::TestCase
+
+  def setup
+    @path = Dir.mktmpdir
+    Redwood::HookManager.init File.join(@path, 'hooks')
+  end
+
+  def teardown
+    Redwood::HookManager.deinstantiate!
+    FileUtils.rm_r @path
+  end
+
+  def test_binary_content_transfer_encoding
+    message = ''
+    File.open 'test/messages/binary-content-transfer-encoding-2.eml' do |f|
+      message = f.read
+    end
+
+    source = DummySource.new("sup-test://test_messages")
+    source.messages = [ message ]
+    source_info = 0
+
+    sup_message = Message.build_from_source(source, source_info)
+    sup_message.load_from_source!
+
+    from = sup_message.from
+    # "from" is just a simple person item
+
+    assert_equal("foo@example.org", from.email)
+    #assert_equal("Fake Sender", from.name)
+
+    subj = sup_message.subj
+    assert_equal("Important", subj)
+
+    chunks = sup_message.load_from_source!
+    indexable_chunks = sup_message.indexable_chunks
+
+    # there should be only one chunk
+    #assert_equal(1, chunks.length)
+
+    lines = chunks[0].lines
+
+    # lines should contain an error message
+    assert (lines.join.include? "An error occurred while loading this message."), "This message should not load successfully"
+  end
+
+  def test_bad_content_transfer_encoding
+    message = ''
+    File.open 'test/messages/bad-content-transfer-encoding-1.eml' do |f|
+      message = f.read
+    end
+
+    source = DummySource.new("sup-test://test_messages")
+    source.messages = [ message ]
+    source_info = 0
+
+    sup_message = Message.build_from_source(source, source_info)
+    sup_message.load_from_source!
+
+    from = sup_message.from
+    # "from" is just a simple person item
+
+    assert_equal("foo@example.org", from.email)
+    #assert_equal("Fake Sender", from.name)
+
+    subj = sup_message.subj
+    assert_equal("Content-Transfer-Encoding:-bug in sup", subj)
+
+    chunks = sup_message.load_from_source!
+    indexable_chunks = sup_message.indexable_chunks
+
+    # there should be only one chunk
+    #assert_equal(1, chunks.length)
+
+    lines = chunks[0].lines
+
+    # lines should contain an error message
+    assert (lines.join.include? "An error occurred while loading this message."), "This message should not load successfully"
+  end
+end
+
+end
+
+# vim:noai:ts=2:sw=2: