sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit adeab88fe422abc3576e2f3421bdf48b1dd71d5e
parent 7619cb9e42ecd21e6a184bfa13661cec3d721bb5
Author: Gaute Hope <eg@gaute.vetsj.com>
Date:   Sat, 31 Aug 2013 18:08:44 +0200

Merge branch 'release-0.14.1-b'

Diffstat:
M CONTRIBUTORS | 14 +++++++-------
M History.txt | 5 +++++
M ReleaseNotes | 4 ++++
M bin/sup | 6 +++---
M doc/Hooks.txt | 3 ++-
M lib/sup.rb | 6 +++---
M lib/sup/account.rb | 2 +-
M lib/sup/buffer.rb | 10 +++++-----
M lib/sup/crypto.rb | 2 +-
M lib/sup/logger.rb | 5 ++++-
M lib/sup/message.rb | 4 ++--
M lib/sup/modes/edit_message_mode.rb | 14 +++++++++-----
M lib/sup/modes/search_list_mode.rb | 6 +++++-
M lib/sup/modes/search_results_mode.rb | 6 +++++-
M lib/sup/search.rb | 23 +++++++++++++++++++++--
M lib/sup/textfield.rb | 2 +-
M lib/sup/util.rb | 12 ++++++------
M lib/sup/version.rb | 2 +-
M sup.gemspec | 7 ++++---
19 files changed, 89 insertions(+), 44 deletions(-)
diff --git a/CONTRIBUTORS b/CONTRIBUTORS
@@ -25,44 +25,44 @@ Richard Brown <rbrown at the exherbo dot orgs>
 Anthony Martinez <pi+sup at the pihost dot uss>
 Marc Hartstein <marc.hartstein at the alum.vassar dot edus>
 Israel Herraiz <israel.herraiz at the gmail dot coms>
+Matthieu Rakotojaona <matthieu.rakotojaona at the gmail dot coms>
 Bo Borgerson <gigabo at the gmail dot coms>
 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>
-Markus Klinik <markus.klinik at the gmx dot des>
 Ico Doornekamp <ico at the pruts dot nls>
+Markus Klinik <markus.klinik at the gmx dot des>
 Adeodato Simó <dato at the net.com.org dot ess>
 Daniel Schoepe <daniel.schoepe at the googlemail dot coms>
 Jason Petsod <jason at the petsod dot orgs>
 Edward Z. Yang <edwardzyang at the thewritingpot dot coms>
-Robin Burchell <viroteck at the viroteck dot nets>
 Steve Goldman <sgoldman at the tower-research dot coms>
+Robin Burchell <viroteck at the viroteck dot nets>
 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>
 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>
+Andrew Pimlott <andrew at the pimlott dot nets>
 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>
 Giorgio Lando <patroclo7 at the gmail dot coms>
 Benoît PIERRE <benoit.pierre at the gmail dot coms>
-Matthieu Rakotojaona <matthieu.rakotojaona at the gmail dot coms>
 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>
-Todd Eisenberger <teisenbe at the andrew.cmu dot edus>
 Adam Lloyd <adam at the alloy-d dot nets>
+Todd Eisenberger <teisenbe at the andrew.cmu dot edus>
+Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
 MichaelRevell <mikearevell at the gmail dot coms>
 Per Andersson <avtobiff at the gmail dot coms>
-Gregor Hoffleit <gregor at the sam.mediasupervision dot des>
 Steven Walter <swalter at the monarch.(none)>
-Jon M. Dugan <jdugan at the es dot nets>
 Matthias Vallentin <vallentin at the icir dot orgs>
+Jon M. Dugan <jdugan at the es dot nets>
 Stefan Lundström <lundst at the snabb.(none)>
 Horacio Sanson <horacio at the skillupjapan.co dot jps>
 Kirill Smelkov <kirr at the landau.phys.spbu dot rus>
diff --git a/History.txt b/History.txt
@@ -1,3 +1,8 @@
+== 0.14.1 / 2013-08-31
+
+* Various bugfixes.
+* Predefined 'All mail' search.
+
 == 0.14.0 / 2013-08-15
 
 * CJK compatability
diff --git a/ReleaseNotes b/ReleaseNotes
@@ -1,3 +1,7 @@
+Release 0.14.1:
+
+Service release to 0.14.0 plus a predefined 'All mail' search.
+
 Release 0.14.0:
 
 CJK-compatability, Psych usage, thread safety, GPGME 2.0 support. Sup is now
diff --git a/bin/sup b/bin/sup
@@ -293,7 +293,7 @@ begin
       b.mode.load_in_background if new
     when :search
       completions = LabelManager.all_labels.map { |l| "label:#{LabelManager.string_for l}" }
-      completions = completions.each { |l| l.fix_encoding }
+      completions = completions.each { |l| l.fix_encoding! }
       completions += Index::COMPL_PREFIXES
       query = BufferManager.ask_many_with_completions :search, "Search all messages (enter for saved searches): ", completions
       unless query.nil?
@@ -307,7 +307,7 @@ begin
       SearchResultsMode.spawn_from_query "is:unread"
     when :list_labels
       labels = LabelManager.all_labels.map { |l| LabelManager.string_for l }
-      labels = labels.each { |l| l.fix_encoding }
+      labels = labels.each { |l| l.fix_encoding! }
 
       user_label = bm.ask_with_completions :label, "Show threads with label (enter for listing): ", labels
       unless user_label.nil?
@@ -375,7 +375,7 @@ ensure
     Index.stop_lock_update_thread
   end
 
-  HookManager.run "shutdown"
+  HookManager.run "shutdown" if HookManager.instantiated?
 
   Index.stop_sync_worker
   Redwood::finish
diff --git a/doc/Hooks.txt b/doc/Hooks.txt
@@ -50,10 +50,11 @@ before-poll:
 mime-decode:
   ## turn text/html attachments into plain text, unless they are part
   ## of a multipart/alternative pair
+  require 'shellwords'
   unless sibling_types.member? "text/plain"
     case content_type
     when "text/html"
-      `/usr/bin/w3m -dump -T #{content_type} '#{filename}'`
+      `/usr/bin/w3m -dump -T #{content_type} '#{Shellwords.escape filename}'`
     end
   end
 
diff --git a/lib/sup.rb b/lib/sup.rb
@@ -271,7 +271,7 @@ EOM
     else
       require 'etc'
       require 'socket'
-      name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first.force_encoding($encoding).fix_encoding rescue nil
+      name = Etc.getpwnam(ENV["USER"]).gecos.split(/,/).first.force_encoding($encoding).fix_encoding! rescue nil
       name ||= ENV["USER"]
       email = ENV["USER"] + "@" +
         begin
@@ -283,8 +283,8 @@ EOM
       config = {
         :accounts => {
           :default => {
-            :name => name.fix_encoding,
-            :email => email.fix_encoding,
+            :name => name.dup.fix_encoding!,
+            :email => email.dup.fix_encoding!,
             :alternates => [],
             :sendmail => "/usr/sbin/sendmail -oem -ti",
             :signature => File.join(ENV["HOME"], ".signature"),
diff --git a/lib/sup/account.rb b/lib/sup/account.rb
@@ -52,7 +52,7 @@ class AccountManager
     hash[:alternates] ||= []
     fail "alternative emails are not an array: #{hash[:alternates]}" unless hash[:alternates].kind_of? Array
 
-    [:name, :signature].each { |x| hash[x] ? hash[x].fix_encoding : nil }
+    [:name, :signature].each { |x| hash[x] ? hash[x].fix_encoding! : nil }
 
     a = Account.new hash
     @accounts[a] = true
diff --git a/lib/sup/buffer.rb b/lib/sup/buffer.rb
@@ -449,7 +449,7 @@ EOS
 
   def ask_with_completions domain, question, completions, default=nil
     ask domain, question, default do |s|
-      s.fix_encoding
+      s.fix_encoding!
       completions.select { |x| x =~ /^#{Regexp::escape s}/iu }.map { |x| [x, x] }
     end
   end
@@ -466,8 +466,8 @@ EOS
           raise "william screwed up completion: #{partial.inspect}"
         end
 
-      prefix.fix_encoding
-      target.fix_encoding
+      prefix.fix_encoding!
+      target.fix_encoding!
       completions.select { |x| x =~ /^#{Regexp::escape target}/iu }.map { |x| [prefix + x, x] }
     end
   end
@@ -476,10 +476,10 @@ EOS
     ask domain, question, default do |partial|
       prefix, target = partial.split_on_commas_with_remainder
       target ||= prefix.pop || ""
-      target.fix_encoding
+      target.fix_encoding!
 
       prefix = prefix.join(", ") + (prefix.empty? ? "" : ", ")
-      prefix.fix_encoding
+      prefix.fix_encoding!
 
       completions.select { |x| x =~ /^#{Regexp::escape target}/iu }.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
@@ -340,7 +340,7 @@ EOS
       msg = RMail::Parser.read output
       if msg.header.content_type =~ %r{^multipart/} && !msg.multipart?
         output = "MIME-Version: 1.0\n" + output
-        output.fix_encoding
+        output.fix_encoding!
         msg = RMail::Parser.read output
       end
     end
diff --git a/lib/sup/logger.rb b/lib/sup/logger.rb
@@ -60,7 +60,10 @@ private
   ## actually distribute the message
   def send_message m
     @mutex.synchronize do
-      @sinks.each { |sink| sink << m }
+      @sinks.each do |sink|
+        sink << m
+        sink.flush if sink.respond_to?(:flush) and level == "debug"
+      end
       @buf << m
     end
   end
diff --git a/lib/sup/message.rb b/lib/sup/message.rb
@@ -112,7 +112,7 @@ class Message
     end
 
     subj = header["subject"]
-    subj = subj ? subj.fix_encoding : nil
+    subj = subj ? subj.fix_encoding! : nil
     @subj = subj ? subj.gsub(/\s+/, " ").gsub(/\s+$/, "") : DEFAULT_SUBJECT
     @to = Person.from_address_list header["to"]
     @cc = Person.from_address_list header["cc"]
@@ -309,7 +309,7 @@ EOS
   end
 
   def indexable_chunks
-    chunks.select { |c| c.is_a? Chunk::Text }
+    chunks.select { |c| c.is_a? Chunk::Text } || []
   end
 
   def indexable_subject
diff --git a/lib/sup/modes/edit_message_mode.rb b/lib/sup/modes/edit_message_mode.rb
@@ -178,7 +178,7 @@ EOS
   def handle_new_text header, body; end
 
   def edit_message_or_field
-    lines = DECORATION_LINES + @selectors.size
+    lines = (@selectors.empty? ? 0 : DECORATION_LINES) + @selectors.size
     if lines > curpos
       return
     elsif (curpos - lines) >= @header_lines.length
@@ -489,7 +489,7 @@ protected
               return false
         end
       else
-        IO.popen(acct.sendmail, "w") { |p| p.puts m }
+        IO.popen(acct.sendmail, "w:UTF-8") { |p| p.puts m }
         raise SendmailCommandFailed, "Couldn't execute #{acct.sendmail}" unless $? == 0
       end
 
@@ -517,6 +517,7 @@ protected
     m.body += "\n" + sig_lines.join("\n") unless @sig_edited
     ## body must end in a newline or GPG signatures will be WRONG!
     m.body += "\n" unless m.body =~ /\n\Z/
+    m.body = m.body.fix_encoding!
 
     ## there are attachments, so wrap body in an attachment of its own
     unless @attachments.empty?
@@ -525,7 +526,10 @@ protected
       m = RMail::Message.new
 
       m.add_part body_m
-      @attachments.each { |a| m.add_part a }
+      @attachments.each do |a|
+        a.body = a.body.fix_encoding! if a.body.kind_of? String
+        m.add_part a
+      end
     end
 
     ## do whatever crypto transformation is necessary
@@ -547,9 +551,9 @@ protected
       m.header[k] =
         case v
         when String
-          k.match(/subject/i) ? mime_encode_subject(v) : mime_encode_address(v)
+          (k.match(/subject/i) ? mime_encode_subject(v) : mime_encode_address(v)).fix_encoding!
         when Array
-          v.map { |v| mime_encode_address v }.join ", "
+          (v.map { |v| mime_encode_address v }.join ", ").fix_encoding!
         end
     end
 
diff --git a/lib/sup/modes/search_list_mode.rb b/lib/sup/modes/search_list_mode.rb
@@ -86,7 +86,11 @@ protected
     counted = searches.map do |name|
       search_string = SearchManager.search_string_for name
       begin
-        query = Index.parse_query search_string
+        if SearchManager.predefined_queries.has_key? search_string
+          query = SearchManager.predefined_queries[search_string]
+        else
+          query = Index.parse_query search_string
+        end
         total = Index.num_results_for :qobj => query[:qobj]
         unread = Index.num_results_for :qobj => query[:qobj], :label => :unread
       rescue Index::ParseError => e
diff --git a/lib/sup/modes/search_results_mode.rb b/lib/sup/modes/search_results_mode.rb
@@ -40,7 +40,11 @@ class SearchResultsMode < ThreadIndexMode
 
   def self.spawn_from_query text
     begin
-      query = Index.parse_query(text)
+      if SearchManager.predefined_queries.has_key? text
+        query = SearchManager.predefined_queries[text]
+      else
+        query = Index.parse_query(text)
+      end
       return unless query
       short_text = text.length < 20 ? text : text[0 ... 20] + "..."
       mode = SearchResultsMode.new query
diff --git a/lib/sup/search.rb b/lib/sup/search.rb
@@ -1,3 +1,5 @@
+# encoding: utf-8
+
 module Redwood
 
 class SearchManager
@@ -15,10 +17,27 @@ class SearchManager
       end
     end
     @modified = false
+
+    @predefined_searches = { 'All mail' => 'Search all mail.' }
+    @predefined_queries  = { 'All mail'.to_sym => { :qobj => Xapian::Query.new('Kmail'),
+                                                    :load_spam => false,
+                                                    :load_deleted => false,
+                                                    :load_killed => false,
+                                                    :text => 'Search all mail.'}
+    }
+    @predefined_searches.each do |k,v|
+      @searches[k] = v
+    end
   end
 
+  def predefined_queries; return @predefined_queries; end
   def all_searches; return @searches.keys.sort; end
-  def search_string_for name; return @searches[name]; end
+  def search_string_for name;
+    if @predefined_searches.keys.member? name
+      return name.to_sym
+    end
+    return @searches[name];
+  end
   def valid_name? name; name =~ /^[\w-]+$/; end
   def name_format_hint; "letters, numbers, underscores and dashes only"; end
 
@@ -65,7 +84,7 @@ class SearchManager
 
   def save
     return unless @modified
-    File.open(@fn, "w") { |f| @searches.sort.each { |(n, s)| f.puts "#{n}: #{s}" } }
+    File.open(@fn, "w:UTF-8") { |f| (@searches - @predefined_searches.keys).sort.each { |(n, s)| f.puts "#{n}: #{s}" } }
     @modified = false
   end
 end
diff --git a/lib/sup/textfield.rb b/lib/sup/textfield.rb
@@ -177,7 +177,7 @@ private
     # encoding) will produce erronous results, but will also do that for
     # a log of other programs since it is impossible to detect which is
     # which and what encoding the inputted byte chars are supposed to have.
-    v.force_encoding($encoding).fix_encoding
+    v.force_encoding($encoding).fix_encoding!
   end
 
   def remove_extra_space
diff --git a/lib/sup/util.rb b/lib/sup/util.rb
@@ -125,7 +125,7 @@ module RMail
       class << self
         def parse(field)
           field = field.dup.to_s
-          field = field.fix_encoding.ascii
+          field = field.fix_encoding!.ascii
           if field =~ EXTRACT_FIELD_NAME_RE
             [ $1, $'.chomp ]
           else
@@ -256,7 +256,7 @@ end
 
 class String
   def display_length
-    @display_length ||= Unicode.width(self, false)
+    @display_length ||= Unicode.width(self.fix_encoding!, false)
   end
 
   def slice_by_display_length len
@@ -366,7 +366,7 @@ class String
   # user encoding.
   #
   # Not Ruby 1.8 compatible
-  def fix_encoding
+  def fix_encoding!
     # first try to encode to utf-8 from whatever current encoding
     encode!('UTF-8', :invalid => :replace, :undef => :replace)
 
@@ -402,7 +402,7 @@ class String
 
     rescue Encoding::ConverterNotFoundError
       debug "Encoding converter not found for #{from_encoding.inspect} or #{to_encoding.inspect}, fixing string: '#{self.to_s}', but expect weird characters."
-      fix_encoding
+      fix_encoding!
     end
 
     fail "Could not create valid #{to_encoding.inspect} string out of: '#{self.to_s}'." unless valid_encoding?
@@ -411,7 +411,7 @@ class String
   end
 
   def normalize_whitespace
-    fix_encoding
+    fix_encoding!
     gsub(/\t/, "    ").gsub(/\r/, "")
   end
 
@@ -453,7 +453,7 @@ class String
         out << b.chr
       end
     end
-    out = out.fix_encoding # this should now be an utf-8 string of ascii
+    out = out.fix_encoding! # this should now be an utf-8 string of ascii
                            # compat chars.
   end
 
diff --git a/lib/sup/version.rb b/lib/sup/version.rb
@@ -1,3 +1,3 @@
 module Redwood
-  VERSION = "0.14.0"
+  VERSION = "0.14.1"
 end
diff --git a/sup.gemspec b/sup.gemspec
@@ -36,10 +36,11 @@ DESC
     s.license = 'GPL-2'
     # TODO: might want to add index migrating script here, too
     s.post_install_message = <<-EOF
-SUP: Please run `sup-psych-ify-config-files` to migrate from 0.13 to 0.14.
+SUP: If you are upgrading Sup from before version 0.14.0: Please
+     run `sup-psych-ify-config-files` to migrate from 0.13 to 0.14.
 
-SUP: Check https://github.com/sup-heliotrope/sup/wiki/Migration-0.13-to-0.14
-     for more detailed up-to-date instructions.
+     Check https://github.com/sup-heliotrope/sup/wiki/Migration-0.13-to-0.14
+     for more detailed and up-to-date instructions.
     EOF
     s.files = SUP_FILES
     s.executables = SUP_EXECUTABLES