@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
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
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
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
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
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={}
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
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
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
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