lib/sup/modes/edit-message-mode.rb in sup-0.7 vs lib/sup/modes/edit-message-mode.rb in sup-0.8

- old
+ new

@@ -1,20 +1,21 @@ require 'tempfile' require 'socket' # just for gethostname! require 'pathname' require 'rmail' +require 'jcode' # for RE_UTF8 module Redwood class SendmailCommandFailed < StandardError; end class EditMessageMode < LineCursorMode DECORATION_LINES = 1 FORCE_HEADERS = %w(From To Cc Bcc Subject) MULTI_HEADERS = %w(To Cc Bcc) - NON_EDITABLE_HEADERS = %w(Message-Id Date) + NON_EDITABLE_HEADERS = %w(Message-id Date) HookManager.register "signature", <<EOS Generates a message signature. Variables: header: an object that supports string-to-string hashtable-style access @@ -143,10 +144,12 @@ def killable? !edited? || BufferManager.ask_yes_or_no("Discard message?") end + def unsaved?; edited? end + def attach_file fn = BufferManager.ask_for_filename :attachment, "File name (enter for browser): " return unless fn begin @attachments << RMail::Message.make_file_attachment(fn) @@ -166,10 +169,33 @@ end end protected + def mime_encode string + string = [string].pack('M') # basic quoted-printable + string.gsub!(/=\n/,'') # .. remove trailing newline + string.gsub!(/_/,'=96') # .. encode underscores + string.gsub!(/\?/,'=3F') # .. encode question marks + string.gsub!(/ /,'_') # .. translate space to underscores + "=?utf-8?q?#{string}?=" + end + + def mime_encode_subject string + return string unless string.match(String::RE_UTF8) + mime_encode string + end + + RE_ADDRESS = /(.+)( <.*@.*>)/ + + # Encode "bælammet mitt <user@example.com>" into + # "=?utf-8?q?b=C3=A6lammet_mitt?= <user@example.com> + def mime_encode_address string + return string unless string.match(String::RE_UTF8) + string.sub(RE_ADDRESS) { |match| mime_encode($1) + $2 } + end + def move_cursor_left if curpos < @selectors.length @selectors[curpos].roll_left buffer.mark_dirty else @@ -210,11 +236,11 @@ end end def parse_file fn File.open(fn) do |f| - header = MBox::read_header f + header = Source.parse_raw_email_header(f).inject({}) { |h, (k, v)| h[k.capitalize] = v; h } # lousy HACK body = f.readlines.map { |l| l.chomp } header.delete_if { |k, v| NON_EDITABLE_HEADERS.member? k } header.each { |k, v| header[k] = parse_header k, v } @@ -255,11 +281,11 @@ things.map_with_index do |name, i| raise "an array: #{name.inspect} (things #{things.inspect})" if Array === name if i == 0 header + " " + name else - (" " * (header.length + 1)) + name + (" " * (header.display_length + 1)) + name end + (i == things.length - 1 ? "" : ",") end end end end @@ -319,30 +345,32 @@ @attachments.each { |a| m.add_part a } end ## do whatever crypto transformation is necessary if @crypto_selector && @crypto_selector.val != :none - from_email = PersonManager.person_for(@header["From"]).email - to_email = [@header["To"], @header["Cc"], @header["Bcc"]].flatten.compact.map { |p| PersonManager.person_for(p).email } + from_email = Person.from_address(@header["From"]).email + to_email = [@header["To"], @header["Cc"], @header["Bcc"]].flatten.compact.map { |p| Person.from_address(p).email } m = CryptoManager.send @crypto_selector.val, from_email, to_email, m end ## finally, set the top-level headers @header.each do |k, v| next if v.nil? || v.empty? m.header[k] = case v when String - v + k.match(/subject/i) ? mime_encode_subject(v) : mime_encode_address(v) when Array - v.join ", " + v.map { |v| mime_encode_address v }.join ", " end end + m.header["Date"] = date.rfc2822 m.header["Message-Id"] = @message_id m.header["User-Agent"] = "Sup/#{Redwood::VERSION}" + m.header["Content-Transfer-Encoding"] = '8bit' m end ## TODO: remove this. redundant with write_full_message_to. ## @@ -388,11 +416,11 @@ @header[field] end contacts = BufferManager.ask_for_contacts :people, "#{field}: ", default if contacts - text = contacts.map { |s| s.longname }.join(", ") + text = contacts.map { |s| s.full_address }.join(", ") @header[field] = parse_header field, text update end end end @@ -410,10 +438,10 @@ def top_posting? @body.join("\n") =~ /(\S+)\s*Excerpts from.*\n(>.*\n)+\s*\Z/ end def sig_lines - p = PersonManager.person_for(@header["From"]) + p = Person.from_address(@header["From"]) from_email = p && p.email ## first run the hook hook_sig = HookManager.run "signature", :header => @header, :from_email => from_email