sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit 03763d91b333dbcdd36c2fe4557684c8f06e86cb
parent 17fd0c9bf15e8c114fc05c11a6ba5346154d1730
Author: wmorgan <wmorgan@5c8cc53c-5e98-4d25-b20a-d8db53a31250>
Date:   Mon, 10 Dec 2007 05:56:50 +0000

forward individual attachments from thread-view-mode, and forward messages now forwards all attachments by default

git-svn-id: svn://rubyforge.org/var/svn/sup/trunk@762 5c8cc53c-5e98-4d25-b20a-d8db53a31250

Diffstat:
M lib/sup/modes/edit-message-mode.rb | 22 +++++++++++++++-------
M lib/sup/modes/forward-mode.rb | 51 +++++++++++++++++++++++++++++++++++++++++++--------
M lib/sup/modes/thread-index-mode.rb | 2 +-
M lib/sup/modes/thread-view-mode.rb | 7 +++++--
M lib/sup/util.rb | 59 ++++++++++++++++++++++++++++++++---------------------------
5 files changed, 96 insertions(+), 45 deletions(-)
diff --git a/lib/sup/modes/edit-message-mode.rb b/lib/sup/modes/edit-message-mode.rb
@@ -61,10 +61,16 @@ EOS
     @body = opts.delete(:body) || []
     @body += sig_lines if $config[:edit_signature]
 
-    @attachments = []
+    if opts[:attachments]
+      @attachments = opts[:attachments].values
+      @attachment_names = opts[:attachments].keys
+    else
+      @attachments = []
+      @attachment_names = []
+    end
+
     @message_id = "<#{Time.now.to_i}-sup-#{rand 10000}@#{Socket.gethostname}>"
     @edited = false
-    @reserve_top_rows = opts[:reserve_top_rows] || 0
     @selectors = []
     @selector_label_width = 0
 
@@ -140,14 +146,16 @@ EOS
   def attach_file
     fn = BufferManager.ask_for_filename :attachment, "File name (enter for browser): "
     return unless fn
-    @attachments << Pathname.new(fn)
+    @attachments << RMail::Message.make_file_attachment(fn)
+    @attachment_names << fn
     update
   end
 
   def delete_attachment
-    i = (curpos - @reserve_top_rows) - @attachment_lines_offset
-    if i >= 0 && i < @attachments.size && BufferManager.ask_yes_or_no("Delete attachment #{@attachments[i]}?")
+    i = curpos - @attachment_lines_offset - DECORATION_LINES - 1
+    if i >= 0 && i < @attachments.size && BufferManager.ask_yes_or_no("Delete attachment #{@attachment_names[i]}?")
       @attachments.delete_at i
+      @attachment_names.delete_at i
       update
     end
   end
@@ -186,7 +194,7 @@ protected
     unless @attachments.empty?
       @text += [""]
       @attachment_lines_offset = @text.length
-      @text += @attachments.map { |f| [[:attachment_color, "+ Attachment: #{f} (#{f.human_size})"]] }
+      @text += (0 ... @attachments.size).map { |i| [[:attachment_color, "+ Attachment: #{@attachment_names[i]} (#{@attachments[i].body.size.to_human_size})"]] }
     end
   end
 
@@ -311,7 +319,7 @@ protected
       body_m.header["Content-Disposition"] = "inline"
       
       m.add_part body_m
-      @attachments.each { |fn| m.add_file_attachment fn.to_s }
+      @attachments.each { |a| m.add_part a }
     end
     f.puts m.to_s
   end
diff --git a/lib/sup/modes/forward-mode.rb b/lib/sup/modes/forward-mode.rb
@@ -1,28 +1,63 @@
 module Redwood
 
 class ForwardMode < EditMessageMode
-
-  ## todo: share some of this with reply-mode
-  def initialize m, opts={}
+  ## TODO: share some of this with reply-mode
+  def initialize opts={}
     header = {
       "From" => AccountManager.default_account.full_address,
-      "Subject" => "Fwd: #{m.subj}",
     }
 
+    header["Subject"] = 
+      if opts[:message]
+        "Fwd: " + opts[:message].subj
+      elsif opts[:attachments]
+        "Fwd: " + opts[:attachments].keys.join(", ")
+      end
+
     header["To"] = opts[:to].map { |p| p.full_address }.join(", ") if opts[:to]
     header["Cc"] = opts[:cc].map { |p| p.full_address }.join(", ") if opts[:cc]
     header["Bcc"] = opts[:bcc].map { |p| p.full_address }.join(", ") if opts[:bcc]
 
-    super :header => header, :body => forward_body_lines(m)
+    body =
+      if opts[:message]
+        forward_body_lines(opts[:message]) 
+      elsif opts[:attachments]
+        ["Note: #{opts[:attachments].size.pluralize 'attachment'}."]
+      end
+
+    super :header => header, :body => body, :attachments => opts[:attachments]
   end
 
-  def self.spawn_nicely m, opts={}
+  def self.spawn_nicely opts={}
     to = opts[:to] || BufferManager.ask_for_contacts(:people, "To: ") or return
     cc = opts[:cc] || BufferManager.ask_for_contacts(:people, "Cc: ") or return if $config[:ask_for_cc]
     bcc = opts[:bcc] || BufferManager.ask_for_contacts(:people, "Bcc: ") or return if $config[:ask_for_bcc]
     
-    mode = ForwardMode.new m, :to => to, :cc => cc, :bcc => bcc
-    BufferManager.spawn "Forwarding #{m.subj}", mode
+    attachment_hash = {}
+    attachments = opts[:attachments] || []
+
+    if(m = opts[:message])
+      m.load_from_source! # read the full message in. you know, maybe i should just make Message#chunks do this....
+      attachments += m.chunks.select { |c| c.is_a?(Chunk::Attachment) && !c.quotable? }
+    end
+
+    attachments.each do |c|
+      mime_type = MIME::Types[c.content_type].first || MIME::Types["application/octet-stream"]
+      attachment_hash[c.filename] = RMail::Message.make_attachment c.raw_content, mime_type.content_type, mime_type.encoding, c.filename
+    end
+
+    mode = ForwardMode.new :message => opts[:message], :to => to, :cc => cc, :bcc => bcc, :attachments => attachment_hash
+
+    title = "Forwarding " +
+      if opts[:message]
+        opts[:message].subj
+      elsif attachments
+        attachment_hash.keys.join(", ")
+      else
+        "something"
+      end
+
+    BufferManager.spawn title, mode
     mode.edit_message
   end
 
diff --git a/lib/sup/modes/thread-index-mode.rb b/lib/sup/modes/thread-index-mode.rb
@@ -407,7 +407,7 @@ EOS
     m = t.latest_message
     return if m.nil? # probably won't happen
     m.load_from_source!
-    ForwardMode.spawn_nicely m
+    ForwardMode.spawn_nicely :message => m
   end
 
   def load_n_threads_background n=LOAD_MORE_THREAD_NUM, opts={}
diff --git a/lib/sup/modes/thread-view-mode.rb b/lib/sup/modes/thread-view-mode.rb
@@ -136,8 +136,11 @@ EOS
   end
 
   def forward
-    m = @message_lines[curpos] or return
-    ForwardMode.spawn_nicely m
+    if(chunk = @chunk_lines[curpos]) && chunk.is_a?(Chunk::Attachment)
+      ForwardMode.spawn_nicely :attachments => [chunk]
+    elsif(m = @message_lines[curpos])
+      ForwardMode.spawn_nicely :message => m
+    end
   end
 
   include CanAliasContacts
diff --git a/lib/sup/util.rb b/lib/sup/util.rb
@@ -37,16 +37,7 @@ class Pathname
       rescue SystemCallError
         return "?"
       end
-
-    if s < 1024
-      s.to_s + "b"
-    elsif s < (1024 * 1024)
-      (s / 1024).to_s + "k"
-    elsif s < (1024 * 1024 * 1024)
-      (s / 1024 / 1024).to_s + "m"
-    else
-      (s / 1024 / 1024 / 1024).to_s + "g"
-    end
+    s.to_human_size
   end
 
   def human_time
@@ -63,33 +54,35 @@ module RMail
   class EncodingUnsupportedError < StandardError; end
 
   class Message
-    def add_file_attachment fn
+    def self.make_file_attachment fn
       bfn = File.basename fn
-      a = Message.new
       t = MIME::Types.type_for(bfn).first || MIME::Types.type_for("exe").first
+      make_attachment IO.read(fn), t.content_type, t.encoding, bfn.to_s
+    end
+
+    def charset
+      if header.field?("content-type") && header.fetch("content-type") =~ /charset="?(.*?)"?(;|$)/
+        $1
+      end
+    end
 
-      a.header.add "Content-Disposition", "attachment; filename=#{bfn.to_s.inspect}"
-      a.header.add "Content-Type", "#{t.content_type}; name=#{bfn.to_s.inspect}"
-      a.header.add "Content-Transfer-Encoding", t.encoding
+    def self.make_attachment payload, mime_type, encoding, filename
+      a = Message.new
+      a.header.add "Content-Disposition", "attachment; filename=#{filename.inspect}"
+      a.header.add "Content-Type", "#{mime_type}; name=#{filename.inspect}"
+      a.header.add "Content-Transfer-Encoding", encoding
       a.body =
-        case t.encoding
+        case encoding
         when "base64"
-          [IO.read(fn)].pack "m"
+          [payload].pack "m"
         when "quoted-printable"
-          [IO.read(fn)].pack "M"
+          [payload].pack "M"
         when "7bit", "8bit"
-          IO.read(fn)
+          payload
         else
           raise EncodingUnsupportedError, t.encoding
         end
-
-      add_part a
-    end
-
-    def charset
-      if header.field?("content-type") && header.fetch("content-type") =~ /charset="?(.*?)"?(;|$)/
-        $1
-      end
+      a
     end
   end
 end
@@ -294,6 +287,18 @@ class Numeric
   end
 
   def in? range; range.member? self; end
+
+  def to_human_size
+    if self < 1024
+      to_s + "b"
+    elsif self < (1024 * 1024)
+      (self / 1024).to_s + "k"
+    elsif self < (1024 * 1024 * 1024)
+      (self / 1024 / 1024).to_s + "m"
+    else
+      (self / 1024 / 1024 / 1024).to_s + "g"
+    end
+  end
 end
 
 class Fixnum