lib/sup/modes/edit-message-mode.rb in sup-0.3 vs lib/sup/modes/edit-message-mode.rb in sup-0.4

- old
+ new

@@ -6,10 +6,12 @@ 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) HookManager.register "signature", <<EOS @@ -46,41 +48,71 @@ k.add :edit_subject, "Edit Subject", 's' k.add :edit_message, "Edit message", :enter k.add :save_as_draft, "Save as draft", 'P' k.add :attach_file, "Attach a file", 'a' k.add :delete_attachment, "Delete an attachment", 'd' + k.add :move_cursor_right, "Move selector to the right", :right, 'l' + k.add :move_cursor_left, "Move selector to the left", :left, 'h' end def initialize opts={} @header = opts.delete(:header) || {} @header_lines = [] @body = opts.delete(:body) || [] - @body += sig_lines if $config[:edit_signature] + @body += sig_lines if $config[:edit_signature] && !opts.delete(:have_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 - @skip_top_rows = opts[:skip_top_rows] || 0 + @selectors = [] + @selector_label_width = 0 + + @crypto_selector = + if CryptoManager.have_crypto? + HorizontalSelector.new "Crypto:", [:none] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.keys, ["None"] + CryptoManager::OUTGOING_MESSAGE_OPERATIONS.values + end + add_selector @crypto_selector if @crypto_selector HookManager.run "before-edit", :header => @header, :body => @body super opts regen_text end - def lines; @text.length end - def [] i; @text[i] end + def lines; @text.length + (@selectors.empty? ? 0 : (@selectors.length + DECORATION_LINES)) end + + def [] i + if @selectors.empty? + @text[i] + elsif i < @selectors.length + @selectors[i].line @selector_label_width + elsif i == @selectors.length + "" + else + @text[i - @selectors.length - DECORATION_LINES] + end + end - ## a hook + ## hook for subclasses. i hate this style of programming. def handle_new_text header, body; end def edit_message_or_field - if (curpos - @skip_top_rows) >= @header_lines.length + lines = DECORATION_LINES + @selectors.size + if lines > curpos + return + elsif (curpos - lines) >= @header_lines.length edit_message else - edit_field @header_lines[curpos - @skip_top_rows] + edit_field @header_lines[curpos - lines] end end def edit_to; edit_field "To" end def edit_cc; edit_field "Cc" end @@ -114,24 +146,49 @@ end 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 - @skip_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 protected + def move_cursor_left + if curpos < @selectors.length + @selectors[curpos].roll_left + buffer.mark_dirty + else + col_left + end + end + + def move_cursor_right + if curpos < @selectors.length + @selectors[curpos].roll_right + buffer.mark_dirty + else + col_right + end + end + + def add_selector s + @selectors << s + @selector_label_width = [@selector_label_width, s.label.length].max + end + def update regen_text buffer.mark_dirty if buffer end @@ -143,11 +200,11 @@ @attachment_lines_offset = 0 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 def parse_file fn File.open(fn) do |f| @@ -206,11 +263,10 @@ def send_message return false if !edited? && !BufferManager.ask_yes_or_no("Message unedited. Really send?") return false if $config[:confirm_no_attachments] && mentions_attachments? && @attachments.size == 0 && !BufferManager.ask_yes_or_no("You haven't added any attachments. Really send?")#" stupid ruby-mode return false if $config[:confirm_top_posting] && top_posting? && !BufferManager.ask_yes_or_no("You're top-posting. That makes you a bad person. Really send?") #" stupid ruby-mode - date = Time.now from_email = if @header["From"] =~ /<?(\S+@(\S+?))>?$/ $1 else AccountManager.default_account.email @@ -218,17 +274,19 @@ acct = AccountManager.account_for(from_email) || AccountManager.default_account BufferManager.flash "Sending..." begin - IO.popen(acct.sendmail, "w") { |p| write_full_message_to p, date, false } + date = Time.now + m = build_message date + IO.popen(acct.sendmail, "w") { |p| p.puts m } raise SendmailCommandFailed, "Couldn't execute #{acct.sendmail}" unless $? == 0 - SentManager.write_sent_message(date, from_email) { |f| write_full_message_to f, date, true } + SentManager.write_sent_message(date, from_email) { |f| f.puts sanitize_body(m.to_s) } BufferManager.kill_buffer buffer BufferManager.flash "Message sent!" true - rescue SystemCallError, SendmailCommandFailed => e + rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error => e Redwood::log "Problem sending mail: #{e.message}" BufferManager.flash "Problem sending mail: #{e.message}" false end end @@ -237,43 +295,49 @@ DraftManager.write_draft { |f| write_message f, false } BufferManager.kill_buffer buffer BufferManager.flash "Saved for later editing." end - def write_full_message_to f, date=Time.now, escape=false + def build_message date m = RMail::Message.new + m.header["Content-Type"] = "text/plain; charset=#{$encoding}" + m.body = @body.join + m.body = m.body + m.body += sig_lines.join("\n") unless $config[:edit_signature] + + ## there are attachments, so wrap body in an attachment of its own + unless @attachments.empty? + body_m = m + body_m.header["Content-Disposition"] = "inline" + m = RMail::Message.new + + m.add_part body_m + @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"]).map { |p| PersonManager.person_for(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 when Array v.join ", " end end - m.header["Date"] = date.rfc2822 m.header["Message-Id"] = @message_id m.header["User-Agent"] = "Sup/#{Redwood::VERSION}" - - if @attachments.empty? - m.header["Content-Type"] = "text/plain; charset=#{$encoding}" - m.body = @body.join - m.body = sanitize_body m.body if escape - m.body += sig_lines.join("\n") unless $config[:edit_signature] - else - body_m = RMail::Message.new - body_m.body = @body.join - body_m.body = sanitize_body body_m.body if escape - body_m.body += sig_lines.join("\n") unless $config[:edit_signature] - body_m.header["Content-Type"] = "text/plain; charset=#{$encoding}" - body_m.header["Content-Disposition"] = "inline" - - m.add_part body_m - @attachments.each { |fn| m.add_file_attachment fn.to_s } - end - f.puts m.to_s + m end ## TODO: remove this. redundant with write_full_message_to. ## ## this is going to change soon: draft messages (currently written