mournmail 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/lib/mournmail/commands.rb +11 -2
- data/lib/mournmail/config.rb +8 -0
- data/lib/mournmail/draft_mode.rb +23 -2
- data/lib/mournmail/message_mode.rb +16 -10
- data/lib/mournmail/message_rendering.rb +78 -18
- data/lib/mournmail/summary.rb +2 -1
- data/lib/mournmail/summary_mode.rb +19 -12
- data/lib/mournmail/utils.rb +26 -4
- data/lib/mournmail/version.rb +1 -1
- data/mournmail.gemspec +1 -1
- metadata +4 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 8cbf82ccf54ab48f7596d24aaacb4673ca8f8271f304264df7aa96a932dac2fb
         | 
| 4 | 
            +
              data.tar.gz: facbe695e21c68a1b5c50a9bb9d50894f948e09bab2a436b2174656953b809bf
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 49c6ba544f435ea5eb150b510e26d18ab0bcd0fddd1a1a147dc0aee84bde05656bb153f44628ee0828b2e02c8f6ccc27b75c7e61b34ca0511c0eb2f074240e1e
         | 
| 7 | 
            +
              data.tar.gz: 43accf4207ec48914c79940d2c45ce931beab826759b3e64174257891bf70d741913800aa976b41c19a2b47d1a0c8e32a00215995889058fdee5c82b4bc6e5e0
         | 
    
        data/README.md
    CHANGED
    
    | @@ -33,7 +33,11 @@ CONFIG[:mournmail_accounts] = { | |
| 33 33 | 
             
                },
         | 
| 34 34 | 
             
                spam_mailbox: "spam",
         | 
| 35 35 | 
             
                outbox_mailbox: "outbox",
         | 
| 36 | 
            -
                archive_mailbox_format: "archive/%Y"
         | 
| 36 | 
            +
                archive_mailbox_format: "archive/%Y",
         | 
| 37 | 
            +
                signature: <<~EOF
         | 
| 38 | 
            +
                  -- 
         | 
| 39 | 
            +
                  Shugo Maeda <shugo@example.com>
         | 
| 40 | 
            +
                EOF
         | 
| 37 41 | 
             
              },
         | 
| 38 42 | 
             
              "gmail.com" => {
         | 
| 39 43 | 
             
                from: "Example <example@gmail.com>",
         | 
| @@ -54,7 +58,7 @@ CONFIG[:mournmail_accounts] = { | |
| 54 58 | 
             
                  password: File.read(File.expand_path("~/.textbringer/gmail_passwd")).chomp,
         | 
| 55 59 | 
             
                  ssl: true
         | 
| 56 60 | 
             
                },
         | 
| 57 | 
            -
                spam_mailbox: " | 
| 61 | 
            +
                spam_mailbox: "[Gmail]/迷惑メール",
         | 
| 58 62 | 
             
                archive_mailbox_format: false
         | 
| 59 63 | 
             
              },
         | 
| 60 64 | 
             
            }
         | 
    
        data/lib/mournmail/commands.rb
    CHANGED
    
    | @@ -11,6 +11,14 @@ define_command(:mournmail_visit_mailbox, doc: "Visit mailbox") do | |
| 11 11 | 
             
              end
         | 
| 12 12 | 
             
            end
         | 
| 13 13 |  | 
| 14 | 
            +
            define_command(:mournmail_visit_spam_mailbox, doc: "Visit spam mailbox") do
         | 
| 15 | 
            +
              mailbox = Mournmail.account_config[:spam_mailbox]
         | 
| 16 | 
            +
              if mailbox.nil?
         | 
| 17 | 
            +
                raise EditorError, "spam_mailbox is not specified"
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
              mournmail_visit_mailbox(Net::IMAP.encode_utf7(mailbox))
         | 
| 20 | 
            +
            end
         | 
| 21 | 
            +
             | 
| 14 22 | 
             
            define_command(:mournmail_summary_sync, doc: "Sync summary.") do
         | 
| 15 23 | 
             
              |mailbox = (Mournmail.current_mailbox || "INBOX"),
         | 
| 16 24 | 
             
                all = current_prefix_arg|
         | 
| @@ -61,9 +69,10 @@ define_command(:mail, doc: "Write a new mail.") do | |
| 61 69 | 
             
                User-Agent: Mournmail/#{Mournmail::VERSION} Textbringer/#{Textbringer::VERSION} Ruby/#{RUBY_VERSION}
         | 
| 62 70 | 
             
                --text follows this line--
         | 
| 63 71 | 
             
              EOF
         | 
| 64 | 
            -
               | 
| 65 | 
            -
               | 
| 72 | 
            +
              beginning_of_buffer
         | 
| 73 | 
            +
              re_search_forward(/^To: */)
         | 
| 66 74 | 
             
              if run_hooks
         | 
| 75 | 
            +
                Mournmail.insert_signature
         | 
| 67 76 | 
             
                run_hooks(:mournmail_draft_setup_hook)
         | 
| 68 77 | 
             
              end
         | 
| 69 78 | 
             
            end
         | 
    
        data/lib/mournmail/config.rb
    CHANGED
    
    | @@ -14,6 +14,13 @@ module Textbringer | |
| 14 14 | 
             
                "X-Mailer",
         | 
| 15 15 | 
             
                "Content-Type"
         | 
| 16 16 | 
             
              ]
         | 
| 17 | 
            +
              CONFIG[:mournmail_quote_header_fields] = [
         | 
| 18 | 
            +
                "Subject",
         | 
| 19 | 
            +
                "Date",
         | 
| 20 | 
            +
                "From",
         | 
| 21 | 
            +
                "To",
         | 
| 22 | 
            +
                "Cc"
         | 
| 23 | 
            +
              ]
         | 
| 17 24 | 
             
              CONFIG[:mournmail_imap_connect_timeout] = 10
         | 
| 18 25 | 
             
              CONFIG[:mournmail_keep_alive_interval] = 60
         | 
| 19 26 | 
             
              case RUBY_PLATFORM
         | 
| @@ -28,4 +35,5 @@ module Textbringer | |
| 28 35 | 
             
                CONFIG[:mournmail_link_open_comamnd] = "xdg-open"
         | 
| 29 36 | 
             
              end
         | 
| 30 37 | 
             
              CONFIG[:mournmail_addresses_path] = File.expand_path("~/.addresses")
         | 
| 38 | 
            +
              CONFIG[:mournmail_signature_regexp] = /^\n-- /
         | 
| 31 39 | 
             
            end
         | 
    
        data/lib/mournmail/draft_mode.rb
    CHANGED
    
    | @@ -8,6 +8,7 @@ module Mournmail | |
| 8 8 | 
             
                MAIL_MODE_MAP.define_key("\C-c\C-xv", :draft_pgp_sign_command)
         | 
| 9 9 | 
             
                MAIL_MODE_MAP.define_key("\C-c\C-xe", :draft_pgp_encrypt_command)
         | 
| 10 10 | 
             
                MAIL_MODE_MAP.define_key("\C-c\t", :insert_signature_command)
         | 
| 11 | 
            +
                MAIL_MODE_MAP.define_key("\C-c@", :draft_change_account_command)
         | 
| 11 12 |  | 
| 12 13 | 
             
                define_syntax :field_name, /^[A-Za-z\-]+: /
         | 
| 13 14 | 
             
                define_syntax :quotation, /^>.*/
         | 
| @@ -68,12 +69,15 @@ module Mournmail | |
| 68 69 | 
             
                               content: File.read(file),
         | 
| 69 70 | 
             
                               encoding: "binary")
         | 
| 70 71 | 
             
                  end
         | 
| 71 | 
            -
                   | 
| 72 | 
            +
                  account = @buffer[:mournmail_delivery_account] ||
         | 
| 73 | 
            +
                    Mournmail.current_account
         | 
| 74 | 
            +
                  conf = CONFIG[:mournmail_accounts][account]
         | 
| 72 75 | 
             
                  options = @buffer[:mournmail_delivery_options] ||
         | 
| 73 76 | 
             
                    conf[:delivery_options]
         | 
| 74 77 | 
             
                  if options[:authentication] == "gmail"
         | 
| 78 | 
            +
                    token = Mournmail.google_access_token(account)
         | 
| 75 79 | 
             
                    options = options.merge(authentication: "xoauth2",
         | 
| 76 | 
            -
                                            password:  | 
| 80 | 
            +
                                            password: token)
         | 
| 77 81 | 
             
                  end
         | 
| 78 82 | 
             
                  m.delivery_method(@buffer[:mournmail_delivery_method] ||
         | 
| 79 83 | 
             
                                    conf[:delivery_method],
         | 
| @@ -186,6 +190,23 @@ module Mournmail | |
| 186 190 | 
             
                  @buffer.insert(CONFIG[:signature])
         | 
| 187 191 | 
             
                end
         | 
| 188 192 |  | 
| 193 | 
            +
                define_local_command(:draft_change_account, doc: "Change account.") do
         | 
| 194 | 
            +
                  |account = Mournmail.read_account_name("Change account: ")|
         | 
| 195 | 
            +
                  from = CONFIG[:mournmail_accounts][account][:from]
         | 
| 196 | 
            +
                  @buffer[:mournmail_delivery_account] = account
         | 
| 197 | 
            +
                  @buffer.save_excursion do
         | 
| 198 | 
            +
                    @buffer.beginning_of_buffer
         | 
| 199 | 
            +
                    @buffer.re_search_forward(/^From:.*/)
         | 
| 200 | 
            +
                    @buffer.replace_match("From: " + from)
         | 
| 201 | 
            +
                    @buffer.end_of_buffer
         | 
| 202 | 
            +
                    if @buffer.re_search_backward(CONFIG[:mournmail_signature_regexp],
         | 
| 203 | 
            +
                                                  raise_error: false)
         | 
| 204 | 
            +
                      @buffer.delete_region(@buffer.point, @buffer.point_max)
         | 
| 205 | 
            +
                    end
         | 
| 206 | 
            +
                    Mournmail.insert_signature
         | 
| 207 | 
            +
                  end
         | 
| 208 | 
            +
                end
         | 
| 209 | 
            +
             | 
| 189 210 | 
             
                private
         | 
| 190 211 |  | 
| 191 212 | 
             
                def end_of_header
         | 
| @@ -12,7 +12,7 @@ module Mournmail | |
| 12 12 |  | 
| 13 13 | 
             
                # See http://nihongo.jp/support/mail_guide/dev_guide.txt
         | 
| 14 14 | 
             
                MAILTO_REGEXP = URI.regexp("mailto")
         | 
| 15 | 
            -
                URI_REGEXP = /(https?|ftp):\/\/[^  \t\n>)"]*[ | 
| 15 | 
            +
                URI_REGEXP = /(https?|ftp):\/\/[^  \t\n>)"]*[^\]  \t\n>.,:)"]+|#{MAILTO_REGEXP}/
         | 
| 16 16 | 
             
                MIME_REGEXP = /^\[(([0-9.]+) [A-Za-z._\-]+\/[A-Za-z._\-]+.*|PGP\/MIME .*)\]$/
         | 
| 17 17 | 
             
                URI_OR_MIME_REGEXP = /#{URI_REGEXP}|#{MIME_REGEXP}/
         | 
| 18 18 |  | 
| @@ -24,6 +24,7 @@ module Mournmail | |
| 24 24 | 
             
                def initialize(buffer)
         | 
| 25 25 | 
             
                  super(buffer)
         | 
| 26 26 | 
             
                  buffer.keymap = MESSAGE_MODE_MAP
         | 
| 27 | 
            +
                  @attached_file = nil
         | 
| 27 28 | 
             
                end
         | 
| 28 29 |  | 
| 29 30 | 
             
                define_local_command(:message_open_link_or_part,
         | 
| @@ -52,6 +53,9 @@ module Mournmail | |
| 52 53 |  | 
| 53 54 | 
             
                define_local_command(:message_next_link_or_part,
         | 
| 54 55 | 
             
                                     doc: "Go to the next link or MIME part.") do
         | 
| 56 | 
            +
                  if @buffer.looking_at?(URI_OR_MIME_REGEXP)
         | 
| 57 | 
            +
                    @buffer.forward_char
         | 
| 58 | 
            +
                  end
         | 
| 55 59 | 
             
                  if @buffer.re_search_forward(URI_OR_MIME_REGEXP, raise_error: false)
         | 
| 56 60 | 
             
                    goto_char(@buffer.match_beginning(0))
         | 
| 57 61 | 
             
                  else
         | 
| @@ -76,8 +80,8 @@ module Mournmail | |
| 76 80 |  | 
| 77 81 | 
             
                def part_file_name(part)
         | 
| 78 82 | 
             
                  file_name =
         | 
| 79 | 
            -
                    part["content-disposition"]&.parameters&.[]("filename") ||
         | 
| 80 | 
            -
                    part["content-type"]&.parameters&.[]("name") ||
         | 
| 83 | 
            +
                    (part["content-disposition"]&.parameters&.[]("filename") rescue nil) ||
         | 
| 84 | 
            +
                    (part["content-type"]&.parameters&.[]("name") rescue nil) ||
         | 
| 81 85 | 
             
                    part_default_file_name(part)
         | 
| 82 86 | 
             
                  decoded_file_name = Mail::Encodings.decode_encode(file_name, :decode)
         | 
| 83 87 | 
             
                  if /\A([A-Za-z0-9_\-]+)'(?:[A-Za-z0-9_\-])*'(.*)/ =~ decoded_file_name
         | 
| @@ -130,18 +134,20 @@ module Mournmail | |
| 130 134 | 
             
                  else
         | 
| 131 135 | 
             
                    file_name = "mournmail"
         | 
| 132 136 | 
             
                  end
         | 
| 133 | 
            -
                   | 
| 137 | 
            +
                  @attached_file = Tempfile.open(file_name, binmode: true)
         | 
| 134 138 | 
             
                  s = part.decoded
         | 
| 135 | 
            -
                  if part. | 
| 139 | 
            +
                  if part.content_type == "text/html"
         | 
| 140 | 
            +
                    s = s.sub(/<meta http-equiv="content-type".*?>/i, "")
         | 
| 141 | 
            +
                  elsif part.charset
         | 
| 136 142 | 
             
                    s = s.encode(part.charset)
         | 
| 137 143 | 
             
                  end
         | 
| 138 | 
            -
                   | 
| 139 | 
            -
                   | 
| 140 | 
            -
                  if  | 
| 141 | 
            -
                    find_file( | 
| 144 | 
            +
                  @attached_file.write(s)
         | 
| 145 | 
            +
                  @attached_file.close
         | 
| 146 | 
            +
                  if part.main_type == "text" && part.sub_type != "html"
         | 
| 147 | 
            +
                    find_file(@attached_file.path)
         | 
| 142 148 | 
             
                  else
         | 
| 143 149 | 
             
                    background do
         | 
| 144 | 
            -
                      system(*CONFIG[:mournmail_file_open_comamnd],  | 
| 150 | 
            +
                      system(*CONFIG[:mournmail_file_open_comamnd], @attached_file.path,
         | 
| 145 151 | 
             
                             out: File::NULL, err: File::NULL)
         | 
| 146 152 | 
             
                    end
         | 
| 147 153 | 
             
                  end
         | 
| @@ -8,8 +8,8 @@ module Mournmail | |
| 8 8 | 
             
                    render_header + "\n" + render_body(indices)
         | 
| 9 9 | 
             
                  end
         | 
| 10 10 |  | 
| 11 | 
            -
                  def render_header
         | 
| 12 | 
            -
                     | 
| 11 | 
            +
                  def render_header(fields = CONFIG[:mournmail_display_header_fields])
         | 
| 12 | 
            +
                    fields.map { |name|
         | 
| 13 13 | 
             
                      val = self[name]&.to_s&.gsub(/\t/, " ")
         | 
| 14 14 | 
             
                      val ? "#{name}: #{val}\n" : ""
         | 
| 15 15 | 
             
                    }.join
         | 
| @@ -41,11 +41,36 @@ module Mournmail | |
| 41 41 | 
             
                    else
         | 
| 42 42 | 
             
                      type = Mail::Encodings.decode_encode(self["content-type"].to_s,
         | 
| 43 43 | 
             
                                                           :decode) rescue
         | 
| 44 | 
            -
             | 
| 44 | 
            +
                        "broken/type; error=\"#{$!} (#{$!.class})\""
         | 
| 45 45 | 
             
                      "[0 #{type}]\n"
         | 
| 46 46 | 
             
                    end + pgp_signature
         | 
| 47 47 | 
             
                  end
         | 
| 48 48 |  | 
| 49 | 
            +
                  def render_text(indices = [])
         | 
| 50 | 
            +
                    if HAVE_MAIL_GPG && encrypted?
         | 
| 51 | 
            +
                      mail = decrypt(verify: true)
         | 
| 52 | 
            +
                      return mail.render_text(indices)
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                    if multipart?
         | 
| 55 | 
            +
                      parts.each_with_index.map { |part, i|
         | 
| 56 | 
            +
                        if sub_type == "alternative" && i > 0
         | 
| 57 | 
            +
                          ""
         | 
| 58 | 
            +
                        else
         | 
| 59 | 
            +
                          part.render_text([*indices, i])
         | 
| 60 | 
            +
                        end
         | 
| 61 | 
            +
                      }.join("\n")
         | 
| 62 | 
            +
                    elsif main_type.nil? || main_type == "text"
         | 
| 63 | 
            +
                      s = Mournmail.to_utf8(body.decoded, charset)
         | 
| 64 | 
            +
                      if sub_type == "html"
         | 
| 65 | 
            +
                        Html2Text.convert(s)
         | 
| 66 | 
            +
                      else
         | 
| 67 | 
            +
                        s
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
                    else
         | 
| 70 | 
            +
                      ""
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
                  end
         | 
| 73 | 
            +
             | 
| 49 74 | 
             
                  def dig_part(i, *rest_indices)
         | 
| 50 75 | 
             
                    if HAVE_MAIL_GPG && encrypted?
         | 
| 51 76 | 
             
                      mail = decrypt(verify: true)
         | 
| @@ -91,7 +116,39 @@ module Mournmail | |
| 91 116 | 
             
                                                         :decode) rescue
         | 
| 92 117 | 
             
                      "broken/type; error=\"#{$!} (#{$!.class})\""
         | 
| 93 118 | 
             
                    "[#{index} #{type}]\n" +
         | 
| 94 | 
            -
                       | 
| 119 | 
            +
                      render_content(indices, no_content)
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  def render_text(indices)
         | 
| 123 | 
            +
                    if multipart?
         | 
| 124 | 
            +
                      parts.each_with_index.map { |part, i|
         | 
| 125 | 
            +
                        if sub_type == "alternative" && i > 0
         | 
| 126 | 
            +
                          ""
         | 
| 127 | 
            +
                        else
         | 
| 128 | 
            +
                          part.render_text([*indices, i])
         | 
| 129 | 
            +
                        end
         | 
| 130 | 
            +
                      }.join("\n")
         | 
| 131 | 
            +
                    else
         | 
| 132 | 
            +
                      if main_type == "message" && sub_type == "rfc822"
         | 
| 133 | 
            +
                        mail = Mail.new(body.raw_source)
         | 
| 134 | 
            +
                        mail.render_header(CONFIG[:mournmail_quote_header_fields]) +
         | 
| 135 | 
            +
                          "\n" + mail.render_text(indices)
         | 
| 136 | 
            +
                      elsif attachment?
         | 
| 137 | 
            +
                        ""
         | 
| 138 | 
            +
                      else
         | 
| 139 | 
            +
                        if main_type == "text"
         | 
| 140 | 
            +
                          if sub_type == "html"
         | 
| 141 | 
            +
                            Html2Text.convert(decoded).sub(/(?<!\n)\z/, "\n")
         | 
| 142 | 
            +
                          else
         | 
| 143 | 
            +
                            decoded.sub(/(?<!\n)\z/, "\n").gsub(/\r\n/, "\n")
         | 
| 144 | 
            +
                          end
         | 
| 145 | 
            +
                        else
         | 
| 146 | 
            +
                          ""
         | 
| 147 | 
            +
                        end
         | 
| 148 | 
            +
                      end
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
                  rescue => e
         | 
| 151 | 
            +
                    ""
         | 
| 95 152 | 
             
                  end
         | 
| 96 153 |  | 
| 97 154 | 
             
                  def dig_part(i, *rest_indices)
         | 
| @@ -110,26 +167,29 @@ module Mournmail | |
| 110 167 |  | 
| 111 168 | 
             
                  private
         | 
| 112 169 |  | 
| 113 | 
            -
                  def render_content(indices)
         | 
| 170 | 
            +
                  def render_content(indices, no_content)
         | 
| 114 171 | 
             
                    if multipart?
         | 
| 115 172 | 
             
                      parts.each_with_index.map { |part, i|
         | 
| 116 | 
            -
                         | 
| 117 | 
            -
             | 
| 173 | 
            +
                        part.render([*indices, i],
         | 
| 174 | 
            +
                                    no_content || sub_type == "alternative" && i > 0)
         | 
| 118 175 | 
             
                      }.join
         | 
| 119 | 
            -
                    elsif main_type == "message" && sub_type == "rfc822"
         | 
| 120 | 
            -
                      mail = Mail.new(body.raw_source)
         | 
| 121 | 
            -
                      mail.render(indices)
         | 
| 122 | 
            -
                    elsif attachment?
         | 
| 123 | 
            -
                      ""
         | 
| 124 176 | 
             
                    else
         | 
| 125 | 
            -
                       | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 177 | 
            +
                      return "" if no_content
         | 
| 178 | 
            +
                      if main_type == "message" && sub_type == "rfc822"
         | 
| 179 | 
            +
                        mail = Mail.new(body.raw_source)
         | 
| 180 | 
            +
                        mail.render(indices)
         | 
| 181 | 
            +
                      elsif attachment?
         | 
| 182 | 
            +
                        ""
         | 
| 183 | 
            +
                      else
         | 
| 184 | 
            +
                        if main_type == "text"
         | 
| 185 | 
            +
                          if sub_type == "html"
         | 
| 186 | 
            +
                            Html2Text.convert(decoded).sub(/(?<!\n)\z/, "\n")
         | 
| 187 | 
            +
                          else
         | 
| 188 | 
            +
                            decoded.sub(/(?<!\n)\z/, "\n").gsub(/\r\n/, "\n")
         | 
| 189 | 
            +
                          end
         | 
| 128 190 | 
             
                        else
         | 
| 129 | 
            -
                           | 
| 191 | 
            +
                          ""
         | 
| 130 192 | 
             
                        end
         | 
| 131 | 
            -
                      else
         | 
| 132 | 
            -
                        ""
         | 
| 133 193 | 
             
                      end
         | 
| 134 194 | 
             
                    end
         | 
| 135 195 | 
             
                  rescue => e
         | 
    
        data/lib/mournmail/summary.rb
    CHANGED
    
    | @@ -141,7 +141,8 @@ module Mournmail | |
| 141 141 | 
             
                        end
         | 
| 142 142 | 
             
                        s = data[0].attr["BODY[]"]
         | 
| 143 143 | 
             
                        mail = Mournmail.parse_mail(s)
         | 
| 144 | 
            -
                         | 
| 144 | 
            +
                        spam_mailbox = Mournmail.account_config[:spam_mailbox]
         | 
| 145 | 
            +
                        if @mailbox != Net::IMAP.encode_utf7(spam_mailbox)
         | 
| 145 146 | 
             
                          item.cache_id = Mournmail.write_mail_cache(s)
         | 
| 146 147 | 
             
                          Mournmail.index_mail(item.cache_id, mail)
         | 
| 147 148 | 
             
                        end
         | 
| @@ -25,6 +25,7 @@ module Mournmail | |
| 25 25 | 
             
                SUMMARY_MODE_MAP.define_key("*t", :summary_mark_unflagged_command)
         | 
| 26 26 | 
             
                SUMMARY_MODE_MAP.define_key("y", :summary_archive_command)
         | 
| 27 27 | 
             
                SUMMARY_MODE_MAP.define_key("o", :summary_refile_command)
         | 
| 28 | 
            +
                SUMMARY_MODE_MAP.define_key("!", :summary_refile_spam_command)
         | 
| 28 29 | 
             
                SUMMARY_MODE_MAP.define_key("p", :summary_prefetch_command)
         | 
| 29 30 | 
             
                SUMMARY_MODE_MAP.define_key("X", :summary_expunge_command)
         | 
| 30 31 | 
             
                SUMMARY_MODE_MAP.define_key("v", :summary_view_source_command)
         | 
| @@ -33,6 +34,7 @@ module Mournmail | |
| 33 34 | 
             
                SUMMARY_MODE_MAP.define_key("k", :previous_line)
         | 
| 34 35 | 
             
                SUMMARY_MODE_MAP.define_key("j", :next_line)
         | 
| 35 36 | 
             
                SUMMARY_MODE_MAP.define_key("m", :mournmail_visit_mailbox)
         | 
| 37 | 
            +
                SUMMARY_MODE_MAP.define_key("S", :mournmail_visit_spam_mailbox)
         | 
| 36 38 | 
             
                SUMMARY_MODE_MAP.define_key("/", :summary_search_command)
         | 
| 37 39 | 
             
                SUMMARY_MODE_MAP.define_key("t", :summary_show_thread_command)
         | 
| 38 40 | 
             
                SUMMARY_MODE_MAP.define_key("@", :summary_change_account_command)
         | 
| @@ -98,7 +100,7 @@ module Mournmail | |
| 98 100 | 
             
                  |reply_all = current_prefix_arg|
         | 
| 99 101 | 
             
                  Mournmail.background do
         | 
| 100 102 | 
             
                    mail = read_current_mail[0]
         | 
| 101 | 
            -
                    body = mail. | 
| 103 | 
            +
                    body = mail.render_text
         | 
| 102 104 | 
             
                    foreground do
         | 
| 103 105 | 
             
                      Window.current = Mournmail.message_window
         | 
| 104 106 | 
             
                      Commands.mail(run_hooks: false)
         | 
| @@ -137,6 +139,7 @@ module Mournmail | |
| 137 139 | 
             
                    On #{mail['date']}
         | 
| 138 140 | 
             
                    #{mail['from']} wrote:
         | 
| 139 141 | 
             
                  EOF
         | 
| 142 | 
            +
                      Mournmail.insert_signature
         | 
| 140 143 | 
             
                      exchange_point_and_mark
         | 
| 141 144 | 
             
                      run_hooks(:mournmail_draft_setup_hook)
         | 
| 142 145 | 
             
                    end
         | 
| @@ -373,10 +376,23 @@ module Mournmail | |
| 373 376 | 
             
                  end
         | 
| 374 377 | 
             
                end
         | 
| 375 378 |  | 
| 379 | 
            +
                define_local_command(:summary_refile_spam,
         | 
| 380 | 
            +
                                     doc: "Refile marked mails as spam.") do
         | 
| 381 | 
            +
                  mailbox = Mournmail.account_config[:spam_mailbox]
         | 
| 382 | 
            +
                  if mailbox.nil?
         | 
| 383 | 
            +
                    raise EditorError, "spam_mailbox is not specified"
         | 
| 384 | 
            +
                  end
         | 
| 385 | 
            +
                  summary_refile(Net::IMAP.encode_utf7(mailbox))
         | 
| 386 | 
            +
                end
         | 
| 387 | 
            +
             | 
| 376 388 | 
             
                define_local_command(:summary_prefetch,
         | 
| 377 389 | 
             
                                     doc: "Prefetch mails.") do
         | 
| 378 390 | 
             
                  summary = Mournmail.current_summary
         | 
| 379 391 | 
             
                  mailbox = Mournmail.current_mailbox
         | 
| 392 | 
            +
                  spam_mailbox = Mournmail.account_config[:spam_mailbox]
         | 
| 393 | 
            +
                  if mailbox == Net::IMAP.encode_utf7(spam_mailbox)
         | 
| 394 | 
            +
                    raise EditorError, "Can't prefetch spam"
         | 
| 395 | 
            +
                  end
         | 
| 380 396 | 
             
                  target_uids = @buffer.to_s.scan(/^ *\d+/).map { |s|
         | 
| 381 397 | 
             
                    s.to_i
         | 
| 382 398 | 
             
                  }.select { |uid|
         | 
| @@ -394,9 +410,7 @@ module Mournmail | |
| 394 410 | 
             
                            s = i.attr["BODY[]"]
         | 
| 395 411 | 
             
                            if s
         | 
| 396 412 | 
             
                              cache_id = Mournmail.write_mail_cache(s)
         | 
| 397 | 
            -
                               | 
| 398 | 
            -
                                Mournmail.index_mail(cache_id, Mail.new(s))
         | 
| 399 | 
            -
                              end
         | 
| 413 | 
            +
                              Mournmail.index_mail(cache_id, Mail.new(s))
         | 
| 400 414 | 
             
                              summary[uid].cache_id = cache_id
         | 
| 401 415 | 
             
                            end
         | 
| 402 416 | 
             
                          end
         | 
| @@ -450,7 +464,7 @@ module Mournmail | |
| 450 464 |  | 
| 451 465 | 
             
                define_local_command(:summary_change_account,
         | 
| 452 466 | 
             
                                     doc: "Change the current account.") do
         | 
| 453 | 
            -
                  |account = read_account_name("Change account: ")|
         | 
| 467 | 
            +
                  |account = Mournmail.read_account_name("Change account: ")|
         | 
| 454 468 | 
             
                  unless CONFIG[:mournmail_accounts].key?(account)
         | 
| 455 469 | 
             
                    raise EditorError, "No such account: #{account}"
         | 
| 456 470 | 
             
                  end
         | 
| @@ -651,13 +665,6 @@ module Mournmail | |
| 651 665 | 
             
                  message
         | 
| 652 666 | 
             
                end
         | 
| 653 667 |  | 
| 654 | 
            -
                def read_account_name(prompt, **opts)
         | 
| 655 | 
            -
                  f = ->(s) {
         | 
| 656 | 
            -
                    complete_for_minibuffer(s, CONFIG[:mournmail_accounts].keys)
         | 
| 657 | 
            -
                  }
         | 
| 658 | 
            -
                  read_from_minibuffer(prompt, completion_proc: f, **opts)
         | 
| 659 | 
            -
                end
         | 
| 660 | 
            -
             | 
| 661 668 | 
             
                def delete_from_summary(summary, uids, msg)
         | 
| 662 669 | 
             
                  summary.delete_item_if do |item|
         | 
| 663 670 | 
             
                    uids.include?(item.uid)
         | 
    
        data/lib/mournmail/utils.rb
    CHANGED
    
    | @@ -225,15 +225,16 @@ module Mournmail | |
| 225 225 | 
             
                end
         | 
| 226 226 | 
             
              end
         | 
| 227 227 |  | 
| 228 | 
            -
              def self.google_access_token
         | 
| 229 | 
            -
                auth_path = File.expand_path("cache/#{ | 
| 228 | 
            +
              def self.google_access_token(account = current_account)
         | 
| 229 | 
            +
                auth_path = File.expand_path("cache/#{account}/google_auth.json",
         | 
| 230 230 | 
             
                                             CONFIG[:mournmail_directory])
         | 
| 231 231 | 
             
                FileUtils.mkdir_p(File.dirname(auth_path))
         | 
| 232 232 | 
             
                store = Google::APIClient::FileStore.new(auth_path)
         | 
| 233 233 | 
             
                storage = Google::APIClient::Storage.new(store)
         | 
| 234 234 | 
             
                storage.authorize
         | 
| 235 235 | 
             
                if storage.authorization.nil?
         | 
| 236 | 
            -
                   | 
| 236 | 
            +
                  conf = CONFIG[:mournmail_accounts][account]
         | 
| 237 | 
            +
                  path = File.expand_path(conf[:client_secret_path])
         | 
| 237 238 | 
             
                  client_secrets = Google::APIClient::ClientSecrets.load(path)
         | 
| 238 239 | 
             
                  auth_client = client_secrets.to_authorization
         | 
| 239 240 | 
             
                  auth_client.update!(
         | 
| @@ -382,7 +383,8 @@ module Mournmail | |
| 382 383 | 
             
                dir = File.dirname(path)
         | 
| 383 384 | 
             
                base = File.basename(path)
         | 
| 384 385 | 
             
                begin
         | 
| 385 | 
            -
                  f = Tempfile.create(["#{base}-", ".tmp"], dir | 
| 386 | 
            +
                  f = Tempfile.create(["#{base}-", ".tmp"], dir,
         | 
| 387 | 
            +
                                      external_encoding: "ASCII-8BIT", binmode: true)
         | 
| 386 388 | 
             
                  begin
         | 
| 387 389 | 
             
                    f.write(s)
         | 
| 388 390 | 
             
                  ensure
         | 
| @@ -554,4 +556,24 @@ module Mournmail | |
| 554 556 | 
             
              def self.parse_mail(s)
         | 
| 555 557 | 
             
                Mail.new(s.scrub("??"))
         | 
| 556 558 | 
             
              end
         | 
| 559 | 
            +
             | 
| 560 | 
            +
              def self.read_account_name(prompt, **opts)
         | 
| 561 | 
            +
                f = ->(s) {
         | 
| 562 | 
            +
                  complete_for_minibuffer(s, CONFIG[:mournmail_accounts].keys)
         | 
| 563 | 
            +
                }
         | 
| 564 | 
            +
                read_from_minibuffer(prompt, completion_proc: f, **opts)
         | 
| 565 | 
            +
              end
         | 
| 566 | 
            +
             | 
| 567 | 
            +
              def self.insert_signature
         | 
| 568 | 
            +
                account = Buffer.current[:mournmail_delivery_account] ||
         | 
| 569 | 
            +
                  Mournmail.current_account
         | 
| 570 | 
            +
                signature = CONFIG[:mournmail_accounts][account][:signature]
         | 
| 571 | 
            +
                if signature
         | 
| 572 | 
            +
                  Buffer.current.save_excursion do
         | 
| 573 | 
            +
                    end_of_buffer
         | 
| 574 | 
            +
                    insert("\n")
         | 
| 575 | 
            +
                    insert(signature)
         | 
| 576 | 
            +
                  end
         | 
| 577 | 
            +
                end
         | 
| 578 | 
            +
              end
         | 
| 557 579 | 
             
            end
         | 
    
        data/lib/mournmail/version.rb
    CHANGED
    
    
    
        data/mournmail.gemspec
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: mournmail
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 1.0.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Shugo Maeda
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: exe
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2021- | 
| 11 | 
            +
            date: 2021-06-11 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: textbringer
         | 
| @@ -126,14 +126,14 @@ dependencies: | |
| 126 126 | 
             
              name: rake
         | 
| 127 127 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 128 128 | 
             
                requirements:
         | 
| 129 | 
            -
                - - " | 
| 129 | 
            +
                - - ">="
         | 
| 130 130 | 
             
                  - !ruby/object:Gem::Version
         | 
| 131 131 | 
             
                    version: '12.0'
         | 
| 132 132 | 
             
              type: :development
         | 
| 133 133 | 
             
              prerelease: false
         | 
| 134 134 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 135 135 | 
             
                requirements:
         | 
| 136 | 
            -
                - - " | 
| 136 | 
            +
                - - ">="
         | 
| 137 137 | 
             
                  - !ruby/object:Gem::Version
         | 
| 138 138 | 
             
                    version: '12.0'
         | 
| 139 139 | 
             
            description: A message user agent for Textbringer.
         |