mournmail 1.0.4 → 2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ae379bf40c63688588064eaabc9eebdbff4c393be7ecf8ece7fb882417b0497b
4
- data.tar.gz: 1264ad23254aca9c1550f2f568aac09e298867a7573ad05ddc045e324af0b48b
3
+ metadata.gz: 0470feee9eea6f5ba0071596fe11f01dace923c44ce6c1b33738d4afc7712e07
4
+ data.tar.gz: 242f831af33c0b3fb3eec8958ecb6db122d055f263452f7c4a9a3c5964db000c
5
5
  SHA512:
6
- metadata.gz: 99fe3e9ef1651a1acfb06aa2ce210e7e4342a73f51781c82ab28d8d1a1d940e4233eef8e0ae66d7663dbc14917e121a5d3c268b26ff1ff9a8372bb190c3ab19f
7
- data.tar.gz: 324f77413cf03ddbb281edfabd881dabd97134d6b01c83c81f6560c802dd1c19f66827d391c0779890cbd96d2b8682431b15070f5a2ac745712dc6027abf4f39
6
+ metadata.gz: aaf1a5eec30365b900c65a6d927bbcb5ae340be299190a48acb3107bc3f325e52e52b2045cea6079a315553f631fd97be4e3c1f888ff25a0ca7924975d9320df
7
+ data.tar.gz: 3e99259ee5ac139ca90fb047cab4d0121ddfcbbebb3a007ff30544da2595046178915ca54cb82a9ef458fd71ed8b4df99267ce05ea8028af15180e716e4a83e1
@@ -0,0 +1,48 @@
1
+ name: Publish gem to rubygems.org
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*'
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ push:
13
+ if: github.repository == 'shugo/mournmail'
14
+ runs-on: ubuntu-latest
15
+
16
+ environment:
17
+ name: rubygems.org
18
+ url: https://rubygems.org/gems/mournmail
19
+
20
+ permissions:
21
+ contents: write
22
+ id-token: write
23
+
24
+ steps:
25
+ # Set up
26
+ - name: Harden Runner
27
+ uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
28
+ with:
29
+ egress-policy: audit
30
+
31
+ - uses: actions/checkout@v4
32
+
33
+ - name: Set up Ruby
34
+ uses: ruby/setup-ruby@v1
35
+ with:
36
+ bundler-cache: true
37
+ ruby-version: ruby
38
+
39
+ # Release
40
+ - name: Publish to RubyGems
41
+ uses: rubygems/release-gem@v1
42
+
43
+ - name: Create GitHub release
44
+ run: |
45
+ tag_name="$(git describe --tags --abbrev=0)"
46
+ gh release create "${tag_name}" --verify-tag --draft --generate-notes
47
+ env:
48
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
data/README.md CHANGED
@@ -70,6 +70,67 @@ Type `M-x mail` to send a mail.
70
70
 
71
71
  Type `M-x mournmail` to visit INBOX.
72
72
 
73
+ ## Key bindings
74
+
75
+ ### Summary
76
+
77
+ |Key |Command |Description |
78
+ |---|---|---|
79
+ |s |mournmail_summary_sync |Sync summary. With C-u sync all mails |
80
+ |SPC |summary_read_command |Read a mail |
81
+ |C-h |summary_scroll_down_command |Scroll down the current message |
82
+ |n |summary_next_command |Display the next mail |
83
+ |w |summary_write_command |Write a new mail |
84
+ |a |summary_reply_command |Reply to the current message |
85
+ |A |summary_reply_command |Reply to the current message |
86
+ |f |summary_forward_command |Forward the current message |
87
+ |u |summary_toggle_seen_command |Toggle Seen |
88
+ |$ |summary_toggle_flagged_command |Toggle Flagged |
89
+ |d |summary_toggle_deleted_command |Toggle Deleted |
90
+ |x |summary_toggle_mark_command |Toggle mark |
91
+ |* a |summary_mark_all_command |Mark all mails |
92
+ |* n |summary_unmark_all_command |Unmark all mails |
93
+ |* r |summary_mark_read_command |Mark read mails |
94
+ |* u |summary_mark_unread_command |Mark unread mails |
95
+ |* s |summary_mark_flagged_command |Mark flagged mails |
96
+ |* t |summary_mark_unflagged_command |Mark unflagged mails |
97
+ |y |summary_archive_command |Archive mails. Archived mails will be deleted or refiled from the server, and only shown by summary_search_command |
98
+ |o |summary_refile_command |Refile marked mails |
99
+ |! |summary_refile_spam_command |Refile marked mails as spam |
100
+ |p |summary_prefetch_command |Prefetch mails |
101
+ |X |summary_expunge_command |Expunge deleted mails |
102
+ |v |summary_view_source_command |View source of a mail |
103
+ |M |summary_merge_partial_command |Merge marked message/partial |
104
+ |q |mournmail_quit |Quit Mournmail |
105
+ |k |previous_line |Move up |
106
+ |j |next_line |Move down |
107
+ |m |mournmail_visit_mailbox |Visit mailbox |
108
+ |S |mournmail_visit_spam_mailbox |Visit spam mailbox |
109
+ |/ |summary_search_command |Search mails |
110
+ |t |summary_show_thread_command |Show the thread of the current mail |
111
+ |@ |summary_change_account_command |Change the current account |
112
+
113
+ ### Message
114
+
115
+ |Key |Command |Description |
116
+ |---|---|---|
117
+ |RET |message_open_link_or_part_command |Open link or MIME part |
118
+ |s |message_save_part_command |Save the MIME part |
119
+ |TAB |message_next_link_or_part_command| Go to the next link or MIME part |
120
+
121
+ ### Draft
122
+
123
+ |Key |Command |Description |
124
+ |---|---|---|
125
+ |C-c C-c |draft_send_command |Send a mail |
126
+ |C-c C-k |draft_kill_command |Kill the draft buffer |
127
+ |C-c C-x TAB |draft_attach_file_command |Attach a file |
128
+ |C-c C-x v |draft_pgp_sign_command |PGP sign |
129
+ |C-c C-x e |draft_pgp_encrypt_command |PGP encrypt |
130
+ |C-c TAB |insert_signature_command |Insert signature |
131
+ |C-c @ |draft_change_account_command |Change account |
132
+ |TAB |draft_complete_or_insert_tab_command |Complete a mail address or insert a tab |
133
+
73
134
  ## Development
74
135
 
75
136
  After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -37,6 +37,7 @@ module Textbringer
37
37
  end
38
38
  CONFIG[:mournmail_addresses_path] = File.expand_path("~/.addresses")
39
39
  CONFIG[:mournmail_signature_regexp] = /^-- /
40
+ CONFIG[:mournmail_summary_lines] = 7
40
41
  CONFIG[:mournmail_allowed_attachment_extensions] = [
41
42
  "txt",
42
43
  "md",
@@ -54,4 +55,18 @@ module Textbringer
54
55
  "ppt",
55
56
  "zip"
56
57
  ]
58
+ CONFIG[:mournmail_forgotten_attachment_re] =
59
+ Regexp.new(
60
+ "^(?!>).*" +
61
+ Regexp.union(
62
+ /I('ve| have) (attached|included)/,
63
+ /See the (attached|attachment)/,
64
+ /Attached file/,
65
+ /添付(する|した|します|しました|いたします|いたしました)/,
66
+ /ファイルを参照/
67
+ ).to_s
68
+ )
69
+ CONFIG[:mournmail_summary_line_limit] = 78
70
+ CONFIG[:mournmail_summary_from_limit] = 16
71
+ CONFIG[:mournmail_summary_use_line_cache] = true
57
72
  end
@@ -21,11 +21,15 @@ module Mournmail
21
21
 
22
22
  define_local_command(:draft_send,
23
23
  doc: "Send a mail and exit from mail buffer.") do
24
- unless y_or_n?("Send this mail?")
25
- return
24
+ s = @buffer.to_s
25
+ if s.match?(CONFIG[:mournmail_forgotten_attachment_re]) &&
26
+ !s.match?(/^Attached-File:/)
27
+ msg = "It seems like you forgot to attach a file. Send anyway?"
28
+ return unless yes_or_no?(msg)
29
+ else
30
+ return unless y_or_n?("Send this mail?")
26
31
  end
27
32
  run_hooks(:mournmail_pre_send_hook)
28
- s = @buffer.to_s
29
33
  charset = CONFIG[:mournmail_charset]
30
34
  begin
31
35
  s.encode(charset)
@@ -11,10 +11,12 @@ module Mournmail
11
11
  MESSAGE_MODE_MAP.define_key("\t", :message_next_link_or_part_command)
12
12
 
13
13
  # See http://nihongo.jp/support/mail_guide/dev_guide.txt
14
- MAILTO_REGEXP = URI.regexp("mailto")
15
- URI_REGEXP = /(https?|ftp):\/\/[^  \t\n>)"]*[^\]  \t\n>.,:)"]+|#{MAILTO_REGEXP}/
14
+ URI_REGEXP = Regexp.union(URI.regexp("http"),
15
+ URI.regexp("https"),
16
+ URI.regexp("ftp"),
17
+ URI.regexp("mailto"))
16
18
  MIME_REGEXP = /^\[(([0-9.]+) [A-Za-z._\-]+\/[A-Za-z._\-]+.*|PGP\/MIME .*)\]$/
17
- URI_OR_MIME_REGEXP = /#{URI_REGEXP}|#{MIME_REGEXP}/
19
+ URI_OR_MIME_REGEXP = Regexp.union(URI_REGEXP, MIME_REGEXP)
18
20
 
19
21
  define_syntax :field_name, /^[A-Za-z\-]+: /
20
22
  define_syntax :quotation, /^>.*/
@@ -128,7 +130,7 @@ module Mournmail
128
130
  if part.multipart?
129
131
  raise EditorError, "Can't open a multipart entity."
130
132
  end
131
- ext = part_file_name(part).slice(/\.([^.]+)\z/, 1)
133
+ ext = part_file_name(part).slice(/\.([^.]+)\z/, 1).downcase
132
134
  if part.main_type != "text" || part.sub_type == "html"
133
135
  if ext.nil?
134
136
  raise EditorError, "The extension of the filename is not specified"
@@ -1,4 +1,5 @@
1
1
  require "mail"
2
+ require "nokogiri"
2
3
  require "html2text"
3
4
 
4
5
  module Mournmail
@@ -34,7 +35,9 @@ module Mournmail
34
35
  elsif main_type.nil? || main_type == "text"
35
36
  s = Mournmail.to_utf8(body.decoded, charset)
36
37
  if sub_type == "html"
37
- "[0 text/html]\n" + Html2Text.convert(s)
38
+ doc = Nokogiri::HTML(s)
39
+ doc.css("script, style, link").each { |node| node.remove }
40
+ "[0 text/html]\n" + doc.css("body").text.squeeze(" \n")
38
41
  else
39
42
  s
40
43
  end
@@ -115,6 +118,10 @@ module Mournmail
115
118
  type = Mail::Encodings.decode_encode(self["content-type"].to_s,
116
119
  :decode) rescue
117
120
  "broken/type; error=\"#{$!} (#{$!.class})\""
121
+ filename = self["content-disposition"]&.filename
122
+ if filename && !self["content-type"]&.filename
123
+ type += "; filename=#{filename}"
124
+ end
118
125
  "[#{index} #{type}]\n" +
119
126
  render_content(indices, no_content)
120
127
  end
@@ -213,8 +213,12 @@ module Mournmail
213
213
  }
214
214
  end
215
215
 
216
- def to_s(limit = 78, from_limit = 16, level = 0)
217
- @line ||= format_line(limit, from_limit, level)
216
+ def to_s(limit = CONFIG[:mournmail_summary_line_limit],
217
+ from_limit = CONFIG[:mournmail_summary_from_limit],
218
+ level = 0)
219
+ if @line.nil? || !CONFIG[:mournmail_summary_use_line_cache]
220
+ @line = format_line(limit, from_limit, level)
221
+ end
218
222
  return @line if @replies.empty?
219
223
  s = @line.dup
220
224
  child_level = level + 1
@@ -247,7 +251,9 @@ module Mournmail
247
251
 
248
252
  private
249
253
 
250
- def format_line(limit = 78, from_limit = 16, level = 0)
254
+ def format_line(limit = CONFIG[:mournmail_summary_line_limit],
255
+ from_limit = CONFIG[:mournmail_summary_from_limit],
256
+ level = 0)
251
257
  space = " " * (level < 8 ? level : 8)
252
258
  s = +""
253
259
  s << format("%6d %s%s %s[ %s ] ",
@@ -262,6 +268,7 @@ module Mournmail
262
268
  width = 0
263
269
  str = +""
264
270
  s.each_char do |c|
271
+ next if c == "\n"
265
272
  w = Buffer.display_width(c)
266
273
  width += w
267
274
  if width > n
@@ -390,7 +390,7 @@ module Mournmail
390
390
  summary = Mournmail.current_summary
391
391
  mailbox = Mournmail.current_mailbox
392
392
  spam_mailbox = Mournmail.account_config[:spam_mailbox]
393
- if mailbox == Net::IMAP.encode_utf7(spam_mailbox)
393
+ if spam_mailbox && mailbox == Net::IMAP.encode_utf7(spam_mailbox)
394
394
  raise EditorError, "Can't prefetch spam"
395
395
  end
396
396
  target_uids = @buffer.to_s.scan(/^ *\d+/).map { |s|
@@ -478,6 +478,11 @@ module Mournmail
478
478
 
479
479
  private
480
480
 
481
+ def get_summary_item(uid)
482
+ summary = Mournmail.current_summary
483
+ summary && summary[uid]
484
+ end
485
+
481
486
  def selected_uid
482
487
  uid = @buffer.save_excursion {
483
488
  @buffer.beginning_of_line
@@ -538,7 +543,7 @@ module Mournmail
538
543
  end
539
544
 
540
545
  def mark_as_seen(uid, update_server)
541
- summary_item = Mournmail.current_summary[uid]
546
+ summary_item = get_summary_item(uid)
542
547
  if summary_item && !summary_item.flags.include?(:Seen)
543
548
  summary_item.set_flag(:Seen, update_server: update_server)
544
549
  Mournmail.current_summary.save
@@ -547,7 +552,7 @@ module Mournmail
547
552
  end
548
553
 
549
554
  def toggle_flag(uid, flag)
550
- summary_item = Mournmail.current_summary[uid]
555
+ summary_item = get_summary_item(uid)
551
556
  if summary_item
552
557
  Mournmail.background do
553
558
  summary_item.toggle_flag(flag)
@@ -594,10 +599,16 @@ module Mournmail
594
599
  def show_search_result(messages,
595
600
  query: nil, buffer_name: "*search result*")
596
601
  summary_text = messages.map { |m|
597
- format("%s [ %s ] %s\n",
598
- m.date.strftime("%m/%d %H:%M"),
599
- ljust(m.from.to_s, 16),
600
- ljust(m.subject.to_s, 45))
602
+ s = +""
603
+ s << format("%s [ %s ] ",
604
+ m.date.strftime("%m/%d %H:%M"),
605
+ ljust(m.from.to_s.gsub(/\n/, ""),
606
+ CONFIG[:mournmail_summary_from_limit]))
607
+ s << ljust(m.subject.to_s.gsub(/\n/, ""),
608
+ CONFIG[:mournmail_summary_line_limit] - Buffer.display_width(s))
609
+ s << "\n"
610
+ s
611
+
601
612
  }.join
602
613
  buffer = Buffer.find_or_new(buffer_name, undo_limit: 0,
603
614
  read_only: true)
@@ -657,7 +668,7 @@ module Mournmail
657
668
 
658
669
  def current_message
659
670
  uid = selected_uid
660
- item = Mournmail.current_summary[uid]
671
+ item = get_summary_item(uid)
661
672
  message = Groonga["Messages"][item.cache_id]
662
673
  if message.nil?
663
674
  raise EditorError, "No message found"
@@ -13,15 +13,28 @@ require 'google/api_client/auth/storages/file_store'
13
13
  require 'launchy'
14
14
  require "socket"
15
15
 
16
- class Net::SMTP
17
- def auth_xoauth2(user, secret)
18
- check_auth_args user, secret
19
- res = critical {
20
- s = Net::IMAP::XOauth2Authenticator.new(user, secret).process("")
21
- get_response('AUTH XOAUTH2 ' + base64_encode(s))
22
- }
23
- check_auth_response res
24
- res
16
+ if defined?(Net::SMTP::Authenticator)
17
+ class Net::SMTP
18
+ class AuthXOAuth2 < Net::SMTP::Authenticator
19
+ auth_type :xoauth2
20
+
21
+ def auth(user, secret)
22
+ s = Net::IMAP::XOauth2Authenticator.new(user, secret).process("")
23
+ finish('AUTH XOAUTH2 ' + base64_encode(s))
24
+ end
25
+ end
26
+ end
27
+ else
28
+ class Net::SMTP
29
+ def auth_xoauth2(user, secret)
30
+ check_auth_args user, secret
31
+ res = critical {
32
+ s = Net::IMAP::XOauth2Authenticator.new(user, secret).process("")
33
+ get_response('AUTH XOAUTH2 ' + base64_encode(s))
34
+ }
35
+ check_auth_response res
36
+ res
37
+ end
25
38
  end
26
39
  end
27
40
 
@@ -116,7 +129,8 @@ module Mournmail
116
129
  def self.message_window
117
130
  if Window.list.size == 1
118
131
  split_window
119
- shrink_window(Window.current.lines - 8)
132
+ n = Window.current.lines - (CONFIG[:mournmail_summary_lines] + 1)
133
+ shrink_window(n)
120
134
  end
121
135
  windows = Window.list
122
136
  i = (windows.index(Window.current) + 1) % windows.size
@@ -184,7 +198,7 @@ module Mournmail
184
198
  end
185
199
  Timeout.timeout(CONFIG[:mournmail_imap_connect_timeout]) do
186
200
  @imap = Net::IMAP.new(conf[:imap_host],
187
- conf[:imap_options])
201
+ conf[:imap_options].except(:auth_type, :user_name, :password))
188
202
  @imap.authenticate(auth_type, conf[:imap_options][:user_name],
189
203
  password)
190
204
  @mailboxes = @imap.list("", "*").map { |mbox|
@@ -1,3 +1,3 @@
1
1
  module Mournmail
2
- VERSION = "1.0.4"
2
+ VERSION = "2"
3
3
  end
data/mournmail.gemspec CHANGED
@@ -29,6 +29,7 @@ Gem::Specification.new do |spec|
29
29
  spec.add_runtime_dependency "rroonga"
30
30
  spec.add_runtime_dependency "google-apis-core"
31
31
  spec.add_runtime_dependency "launchy"
32
+ spec.add_runtime_dependency "nokogiri"
32
33
  spec.add_runtime_dependency "html2text"
33
34
 
34
35
  spec.add_development_dependency "bundler"
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mournmail
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: '2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shugo Maeda
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2022-11-18 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: textbringer
@@ -122,6 +121,20 @@ dependencies:
122
121
  - - ">="
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: nokogiri
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
125
138
  - !ruby/object:Gem::Dependency
126
139
  name: html2text
127
140
  requirement: !ruby/object:Gem::Requirement
@@ -172,6 +185,7 @@ executables:
172
185
  extensions: []
173
186
  extra_rdoc_files: []
174
187
  files:
188
+ - ".github/workflows/push_gem.yml"
175
189
  - ".gitignore"
176
190
  - Gemfile
177
191
  - LICENSE.txt
@@ -199,7 +213,6 @@ homepage: https://github.com/shugo/mournmail
199
213
  licenses:
200
214
  - MIT
201
215
  metadata: {}
202
- post_install_message:
203
216
  rdoc_options: []
204
217
  require_paths:
205
218
  - lib
@@ -214,8 +227,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
214
227
  - !ruby/object:Gem::Version
215
228
  version: '0'
216
229
  requirements: []
217
- rubygems_version: 3.4.0.dev
218
- signing_key:
230
+ rubygems_version: 3.6.7
219
231
  specification_version: 4
220
232
  summary: A message user agent for Textbringer.
221
233
  test_files: []