imap-backup 14.1.1 → 14.2.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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/bin/imap-backup +3 -2
  3. data/lib/imap/backup/account/client_factory.rb +1 -1
  4. data/lib/imap/backup/account/folder.rb +1 -1
  5. data/lib/imap/backup/cli_coverage.rb +21 -0
  6. data/lib/imap/backup/client/automatic_login_wrapper.rb +1 -1
  7. data/lib/imap/backup/email/mboxrd/message.rb +107 -0
  8. data/lib/imap/backup/email/provider/apple_mail.rb +15 -0
  9. data/lib/imap/backup/email/provider/base.rb +18 -0
  10. data/lib/imap/backup/email/provider/fastmail.rb +11 -0
  11. data/lib/imap/backup/email/provider/gmail.rb +11 -0
  12. data/lib/imap/backup/email/provider/purelymail.rb +15 -0
  13. data/lib/imap/backup/email/provider/unknown.rb +17 -0
  14. data/lib/imap/backup/email/provider.rb +36 -0
  15. data/lib/imap/backup/logger.rb +1 -1
  16. data/lib/imap/backup/retry_on_error.rb +21 -0
  17. data/lib/imap/backup/serializer/appender.rb +1 -1
  18. data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +1 -1
  19. data/lib/imap/backup/serializer/message.rb +1 -1
  20. data/lib/imap/backup/serializer.rb +1 -1
  21. data/lib/imap/backup/setup/email.rb +1 -1
  22. data/lib/imap/backup/setup/folder_chooser.rb +13 -12
  23. data/lib/imap/backup/setup.rb +2 -2
  24. data/lib/imap/backup/text/sanitizer.rb +50 -0
  25. data/lib/imap/backup/version.rb +2 -2
  26. metadata +13 -13
  27. data/lib/cli_coverage.rb +0 -17
  28. data/lib/email/mboxrd/message.rb +0 -103
  29. data/lib/email/provider/apple_mail.rb +0 -11
  30. data/lib/email/provider/base.rb +0 -14
  31. data/lib/email/provider/fastmail.rb +0 -7
  32. data/lib/email/provider/gmail.rb +0 -7
  33. data/lib/email/provider/purelymail.rb +0 -11
  34. data/lib/email/provider/unknown.rb +0 -13
  35. data/lib/email/provider.rb +0 -32
  36. data/lib/retry_on_error.rb +0 -15
  37. data/lib/text/sanitizer.rb +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93047de8b1a28cd27279cae2d5950c1b9856c767b9ac064b57912f5d83812c74
4
- data.tar.gz: 5425573d8f53cffc4624b4467265acd390596ec43ba5314bb7312ed13f41b585
3
+ metadata.gz: 1da04e15e0f6c66398320aff6a7ce638e50089db3b6470a3aed7c6a4b49f0a66
4
+ data.tar.gz: '0261943b048d8a97f334ca58f21010ae26799ff0afab23cd611cf4528ac841c7'
5
5
  SHA512:
6
- metadata.gz: 50abe7600f61974ad45054e6b58b9d2cfcd72c9e25665ede1165637602e0df821506fe0d9b936f1d8f542f8cbd98b6587253496cd941d2ac0430b32cc2558d33
7
- data.tar.gz: cad3792a03accae1f180936b49047822400a8b7644978ff42d1d38c5c7d585f70ea05095849742e3a2bb2289310c64a46605203ec4ff7d87175d2f1db2235f9d
6
+ metadata.gz: 627bcbbdeab8a9bf8cd68cdfd28ae68fbd0961aeb8ce87703428b4a6c1709c3a08a6b45619fc9455d124e31c677af0423e726bbe2c89ad28abc35ddff1fdf2ad
7
+ data.tar.gz: ea19474d8dbe419330b96f46860d44ca930d94188492afde2afde4a7a3018e190904b13af6f820c88295540deca0887abee908987abcef8a2d17d7c21fe239db
data/bin/imap-backup CHANGED
@@ -2,8 +2,9 @@
2
2
 
3
3
  $LOAD_PATH.unshift(File.expand_path("../lib/", __dir__))
4
4
 
5
- require "cli_coverage"
6
- CliCoverage.conditionally_activate
5
+ require "imap/backup/cli_coverage"
6
+
7
+ Imap::Backup::CliCoverage.conditionally_activate
7
8
 
8
9
  require "imap/backup/cli"
9
10
  require "imap/backup/logger"
@@ -1,9 +1,9 @@
1
1
  require "socket"
2
2
 
3
- require "email/provider"
4
3
  require "imap/backup/client/apple_mail"
5
4
  require "imap/backup/client/automatic_login_wrapper"
6
5
  require "imap/backup/client/default"
6
+ require "imap/backup/email/provider"
7
7
 
8
8
  module Imap; end
9
9
 
@@ -3,7 +3,7 @@ require "logger"
3
3
  require "net/imap"
4
4
 
5
5
  require "imap/backup/logger"
6
- require "retry_on_error"
6
+ require "imap/backup/retry_on_error"
7
7
 
8
8
  module Imap; end
9
9
 
@@ -0,0 +1,21 @@
1
+ module Imap; end
2
+
3
+ module Imap::Backup
4
+ class CliCoverage
5
+ def self.conditionally_activate
6
+ return if !ENV.key?("COVERAGE")
7
+
8
+ # Collect coverage separately
9
+ ENV["SIMPLECOV_COMMAND_NAME"] = "#{ENV.fetch('COVERAGE')} #{ARGV.join(' ')} coverage"
10
+ require "simplecov"
11
+
12
+ # Silence output
13
+ SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter
14
+ SimpleCov.print_error_status = false
15
+
16
+ # Ensure SimpleCov doesn't filter out all out code
17
+ project_root = File.expand_path("..", __dir__)
18
+ SimpleCov.root project_root
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,4 @@
1
- require "retry_on_error"
1
+ require "imap/backup/retry_on_error"
2
2
 
3
3
  module Imap; end
4
4
 
@@ -0,0 +1,107 @@
1
+ require "forwardable"
2
+ require "mail"
3
+
4
+ module Imap; end
5
+
6
+ module Imap::Backup
7
+ module Email; end
8
+
9
+ module Email::Mboxrd
10
+ class Message
11
+ attr_reader :supplied_body
12
+
13
+ def self.clean_serialized(serialized)
14
+ cleaned = serialized.gsub(/^>(>*From)/, "\\1")
15
+ # Serialized messages in this format *should* start with a line
16
+ # From xxx yy zz
17
+ # rubocop:disable Style/IfUnlessModifier
18
+ if cleaned.start_with?("From ")
19
+ cleaned = cleaned.sub(/^From .*[\r\n]*/, "")
20
+ end
21
+ # rubocop:enable Style/IfUnlessModifier
22
+ cleaned
23
+ end
24
+
25
+ def self.from_serialized(serialized)
26
+ new(clean_serialized(serialized))
27
+ end
28
+
29
+ def initialize(supplied_body)
30
+ @supplied_body = supplied_body.clone
31
+ end
32
+
33
+ def to_serialized
34
+ from_line = "From #{from}\n"
35
+ body = mboxrd_body.dup.force_encoding(Encoding::UTF_8)
36
+ from_line + body
37
+ end
38
+
39
+ def date
40
+ parsed.date
41
+ rescue StandardError
42
+ nil
43
+ end
44
+
45
+ def subject
46
+ parsed.subject
47
+ end
48
+
49
+ def imap_body
50
+ supplied_body.gsub(/(?<!\r)\n/, "\r\n")
51
+ end
52
+
53
+ private
54
+
55
+ def parsed
56
+ @parsed ||= Mail.new(supplied_body)
57
+ end
58
+
59
+ def from
60
+ @from ||=
61
+ begin
62
+ from = best_from.dup
63
+ from << " #{asctime}" if asctime != ""
64
+ from
65
+ end
66
+ end
67
+
68
+ def best_from
69
+ return first_from if first_from
70
+ return parsed.sender if parsed.sender
71
+ return parsed.envelope_from if parsed.envelope_from
72
+ return parsed.return_path if parsed.return_path
73
+
74
+ ""
75
+ end
76
+
77
+ def first_from
78
+ return nil if !parsed.from.is_a?(Enumerable)
79
+
80
+ parsed.from.find { |from| from }
81
+ end
82
+
83
+ def mboxrd_body
84
+ @mboxrd_body ||=
85
+ begin
86
+ mboxrd_body = add_extra_quote(supplied_body.gsub("\r\n", "\n"))
87
+ mboxrd_body += "\n" if !mboxrd_body.end_with?("\n")
88
+ mboxrd_body += "\n" if !mboxrd_body.end_with?("\n\n")
89
+ mboxrd_body
90
+ end
91
+ end
92
+
93
+ def add_extra_quote(body)
94
+ # The mboxrd format requires that lines starting with 'From'
95
+ # be prefixed with a '>' so that any remaining lines which start with
96
+ # 'From ' can be taken as the beginning of messages.
97
+ # http://www.digitalpreservation.gov/formats/fdd/fdd000385.shtml
98
+ # Here we add an extra '>' before any "From" or ">From".
99
+ body.gsub(/\n(>*From)/, "\n>\\1")
100
+ end
101
+
102
+ def asctime
103
+ @asctime ||= date ? date.asctime : ""
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,15 @@
1
+ require "imap/backup/email/provider/base"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ class Email::Provider::AppleMail < Email::Provider::Base
7
+ def host
8
+ "imap.mail.me.com"
9
+ end
10
+
11
+ def sets_seen_flags_on_fetch?
12
+ true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ module Imap; end
2
+
3
+ module Imap::Backup
4
+ module Email; end
5
+ class Email::Provider; end
6
+
7
+ class Email::Provider::Base
8
+ def options
9
+ # rubocop:disable Naming/VariableNumber
10
+ {port: 993, ssl: {ssl_version: :TLSv1_2}}
11
+ # rubocop:enable Naming/VariableNumber
12
+ end
13
+
14
+ def sets_seen_flags_on_fetch?
15
+ false
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ require "imap/backup/email/provider/base"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ class Email::Provider::Fastmail < Email::Provider::Base
7
+ def host
8
+ "imap.fastmail.com"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require "imap/backup/email/provider/base"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ class Email::Provider::GMail < Email::Provider::Base
7
+ def host
8
+ "imap.gmail.com"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ require "imap/backup/email/provider/base"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ class Email::Provider::Purelymail < Email::Provider::Base
7
+ def host
8
+ "mailserver.purelymail.com"
9
+ end
10
+
11
+ def sets_seen_flags_on_fetch?
12
+ true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ require "imap/backup/email/provider/base"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ class Email::Provider::Unknown < Email::Provider::Base
7
+ # We don't know how to guess the IMAP server
8
+ def host
9
+ end
10
+
11
+ def options
12
+ # rubocop:disable Naming/VariableNumber
13
+ {port: 993, ssl: {ssl_version: :TLSv1_2}}
14
+ # rubocop:enable Naming/VariableNumber
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ require "imap/backup/email/provider/apple_mail"
2
+ require "imap/backup/email/provider/fastmail"
3
+ require "imap/backup/email/provider/gmail"
4
+ require "imap/backup/email/provider/purelymail"
5
+ require "imap/backup/email/provider/unknown"
6
+
7
+ module Imap; end
8
+
9
+ module Imap::Backup
10
+ module Email; end
11
+
12
+ class Email::Provider
13
+ def self.for_address(address)
14
+ # rubocop:disable Lint/DuplicateBranch
15
+ case
16
+ when address.end_with?("@fastmail.com")
17
+ Email::Provider::Fastmail.new
18
+ when address.end_with?("@fastmail.fm")
19
+ Email::Provider::Fastmail.new
20
+ when address.end_with?("@gmail.com")
21
+ Email::Provider::GMail.new
22
+ when address.end_with?("@icloud.com")
23
+ Email::Provider::AppleMail.new
24
+ when address.end_with?("@mac.com")
25
+ Email::Provider::AppleMail.new
26
+ when address.end_with?("@me.com")
27
+ Email::Provider::AppleMail.new
28
+ when address.end_with?("@purelymail.com")
29
+ Email::Provider::Purelymail.new
30
+ else
31
+ Email::Provider::Unknown.new
32
+ end
33
+ # rubocop:enable Lint/DuplicateBranch
34
+ end
35
+ end
36
+ end
@@ -2,7 +2,7 @@ require "net/imap"
2
2
  require "logger"
3
3
  require "singleton"
4
4
 
5
- require "text/sanitizer"
5
+ require "imap/backup/text/sanitizer"
6
6
 
7
7
  module Imap; end
8
8
 
@@ -0,0 +1,21 @@
1
+ require "imap/backup/logger"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ module RetryOnError
7
+ def retry_on_error(errors:, limit: 10, on_error: nil)
8
+ tries ||= 1
9
+ yield
10
+ rescue *errors => e
11
+ if tries < limit
12
+ message = "#{e}, attempt #{tries} of #{limit}"
13
+ Logger.logger.debug message
14
+ on_error&.call
15
+ tries += 1
16
+ retry
17
+ end
18
+ raise e
19
+ end
20
+ end
21
+ end
@@ -1,4 +1,4 @@
1
- require "email/mboxrd/message"
1
+ require "imap/backup/email/mboxrd/message"
2
2
 
3
3
  module Imap; end
4
4
 
@@ -1,4 +1,4 @@
1
- require "email/mboxrd/message"
1
+ require "imap/backup/email/mboxrd/message"
2
2
  require "imap/backup/serializer/imap"
3
3
  require "imap/backup/serializer/mbox"
4
4
  require "imap/backup/serializer/transaction"
@@ -1,4 +1,4 @@
1
- require "email/mboxrd/message"
1
+ require "imap/backup/email/mboxrd/message"
2
2
 
3
3
  module Imap; end
4
4
 
@@ -1,6 +1,6 @@
1
1
  require "forwardable"
2
2
 
3
- require "email/mboxrd/message"
3
+ require "imap/backup/email/mboxrd/message"
4
4
  require "imap/backup/logger"
5
5
  require "imap/backup/naming"
6
6
  require "imap/backup/serializer/appender"
@@ -1,4 +1,4 @@
1
- require "email/provider"
1
+ require "imap/backup/email/provider"
2
2
  require "imap/backup/setup/asker"
3
3
 
4
4
  module Imap; end
@@ -60,16 +60,13 @@ module Imap::Backup
60
60
  end
61
61
 
62
62
  def selected?(folder_name)
63
- config_folders = account.folders
64
- return false if config_folders.nil?
65
-
66
- config_folders.find { |f| f == folder_name }
63
+ account_folders.find { |f| f == folder_name }
67
64
  end
68
65
 
69
66
  def remove_missing
70
67
  removed = []
71
68
  config_folders = []
72
- account.folders.each do |f|
69
+ account_folders.each do |f|
73
70
  found = folder_names.find { |folder| folder == f }
74
71
  if found
75
72
  config_folders << f
@@ -90,13 +87,13 @@ module Imap::Backup
90
87
  end
91
88
 
92
89
  def toggle_selection(folder_name)
93
- if selected?(folder_name)
94
- new_list = account.folders.reject { |f| f == folder_name }
95
- account.folders = new_list
96
- else
97
- existing = account.folders || []
98
- account.folders = existing << folder_name
99
- end
90
+ new_list =
91
+ if selected?(folder_name)
92
+ account_folders.reject { |f| f == folder_name }
93
+ else
94
+ account_folders + [folder_name]
95
+ end
96
+ account.folders = new_list
100
97
  end
101
98
 
102
99
  def client
@@ -117,5 +114,9 @@ module Imap::Backup
117
114
  def helpers
118
115
  Setup::Helpers.new
119
116
  end
117
+
118
+ def account_folders
119
+ account.folders || []
120
+ end
120
121
  end
121
122
  end
@@ -1,7 +1,7 @@
1
1
  require "highline"
2
2
 
3
- require "email/provider"
4
3
  require "imap/backup/account"
4
+ require "imap/backup/email/provider"
5
5
  require "imap/backup/setup/account"
6
6
  require "imap/backup/setup/asker"
7
7
  require "imap/backup/setup/global_options"
@@ -88,7 +88,7 @@ module Imap::Backup
88
88
  password: "",
89
89
  folders: []
90
90
  ).tap do |a|
91
- provider = ::Email::Provider.for_address(username)
91
+ provider = Imap::Backup::Email::Provider.for_address(username)
92
92
  a.server = provider.host if provider.host
93
93
  a.reset_seen_flags_after_fetch = true if provider.sets_seen_flags_on_fetch?
94
94
  end
@@ -0,0 +1,50 @@
1
+ require "forwardable"
2
+
3
+ module Imap; end
4
+
5
+ module Imap::Backup
6
+ module Text; end
7
+
8
+ class Text::Sanitizer
9
+ extend Forwardable
10
+
11
+ attr_reader :output
12
+
13
+ delegate puts: :output
14
+ delegate write: :output
15
+
16
+ def initialize(output)
17
+ @output = output
18
+ @current = ""
19
+ end
20
+
21
+ def print(*args)
22
+ @current << args.join
23
+ loop do
24
+ line, newline, rest = @current.partition("\n")
25
+ break if newline != "\n"
26
+
27
+ clean = sanitize(line)
28
+ output.puts clean
29
+ @current = rest
30
+ end
31
+ end
32
+
33
+ def flush
34
+ return if @current == ""
35
+
36
+ clean = sanitize(@current)
37
+ output.puts clean
38
+ end
39
+
40
+ private
41
+
42
+ def sanitize(text)
43
+ # Hide password in Net::IMAP debug output
44
+ text.gsub(
45
+ /\A(C: RUBY\d+ LOGIN \S+) \S+/,
46
+ "\\1 [PASSWORD REDACTED]"
47
+ )
48
+ end
49
+ end
50
+ end
@@ -2,8 +2,8 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 14
5
- MINOR = 1
6
- REVISION = 1
5
+ MINOR = 2
6
+ REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
9
9
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 14.1.1
4
+ version: 14.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-10-12 00:00:00.000000000 Z
11
+ date: 2023-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -138,15 +138,6 @@ files:
138
138
  - docs/migrate-server-keep-address.md
139
139
  - docs/performance.md
140
140
  - imap-backup.gemspec
141
- - lib/cli_coverage.rb
142
- - lib/email/mboxrd/message.rb
143
- - lib/email/provider.rb
144
- - lib/email/provider/apple_mail.rb
145
- - lib/email/provider/base.rb
146
- - lib/email/provider/fastmail.rb
147
- - lib/email/provider/gmail.rb
148
- - lib/email/provider/purelymail.rb
149
- - lib/email/provider/unknown.rb
150
141
  - lib/imap/backup/account.rb
151
142
  - lib/imap/backup/account/backup.rb
152
143
  - lib/imap/backup/account/backup_folders.rb
@@ -171,12 +162,21 @@ files:
171
162
  - lib/imap/backup/cli/stats.rb
172
163
  - lib/imap/backup/cli/transfer.rb
173
164
  - lib/imap/backup/cli/utils.rb
165
+ - lib/imap/backup/cli_coverage.rb
174
166
  - lib/imap/backup/client/apple_mail.rb
175
167
  - lib/imap/backup/client/automatic_login_wrapper.rb
176
168
  - lib/imap/backup/client/default.rb
177
169
  - lib/imap/backup/configuration.rb
178
170
  - lib/imap/backup/configuration_not_found.rb
179
171
  - lib/imap/backup/downloader.rb
172
+ - lib/imap/backup/email/mboxrd/message.rb
173
+ - lib/imap/backup/email/provider.rb
174
+ - lib/imap/backup/email/provider/apple_mail.rb
175
+ - lib/imap/backup/email/provider/base.rb
176
+ - lib/imap/backup/email/provider/fastmail.rb
177
+ - lib/imap/backup/email/provider/gmail.rb
178
+ - lib/imap/backup/email/provider/purelymail.rb
179
+ - lib/imap/backup/email/provider/unknown.rb
180
180
  - lib/imap/backup/file_mode.rb
181
181
  - lib/imap/backup/flag_refresher.rb
182
182
  - lib/imap/backup/local_only_message_deleter.rb
@@ -185,6 +185,7 @@ files:
185
185
  - lib/imap/backup/mirror.rb
186
186
  - lib/imap/backup/mirror/map.rb
187
187
  - lib/imap/backup/naming.rb
188
+ - lib/imap/backup/retry_on_error.rb
188
189
  - lib/imap/backup/serializer.rb
189
190
  - lib/imap/backup/serializer/appender.rb
190
191
  - lib/imap/backup/serializer/delayed_metadata_serializer.rb
@@ -210,11 +211,10 @@ files:
210
211
  - lib/imap/backup/setup/global_options.rb
211
212
  - lib/imap/backup/setup/global_options/download_strategy_chooser.rb
212
213
  - lib/imap/backup/setup/helpers.rb
214
+ - lib/imap/backup/text/sanitizer.rb
213
215
  - lib/imap/backup/thunderbird/mailbox_exporter.rb
214
216
  - lib/imap/backup/uploader.rb
215
217
  - lib/imap/backup/version.rb
216
- - lib/retry_on_error.rb
217
- - lib/text/sanitizer.rb
218
218
  homepage: https://github.com/joeyates/imap-backup
219
219
  licenses:
220
220
  - MIT
data/lib/cli_coverage.rb DELETED
@@ -1,17 +0,0 @@
1
- class CliCoverage
2
- def self.conditionally_activate
3
- return if !ENV.key?("COVERAGE")
4
-
5
- # Collect coverage separately
6
- ENV["SIMPLECOV_COMMAND_NAME"] = "#{ENV.fetch('COVERAGE')} #{ARGV.join(' ')} coverage"
7
- require "simplecov"
8
-
9
- # Silence output
10
- SimpleCov.formatter = SimpleCov::Formatter::SimpleFormatter
11
- SimpleCov.print_error_status = false
12
-
13
- # Ensure SimpleCov doesn't filter out all out code
14
- project_root = File.expand_path("..", __dir__)
15
- SimpleCov.root project_root
16
- end
17
- end
@@ -1,103 +0,0 @@
1
- require "forwardable"
2
- require "mail"
3
-
4
- module Email; end
5
-
6
- module Email::Mboxrd
7
- class Message
8
- attr_reader :supplied_body
9
-
10
- def self.clean_serialized(serialized)
11
- cleaned = serialized.gsub(/^>(>*From)/, "\\1")
12
- # Serialized messages in this format *should* start with a line
13
- # From xxx yy zz
14
- # rubocop:disable Style/IfUnlessModifier
15
- if cleaned.start_with?("From ")
16
- cleaned = cleaned.sub(/^From .*[\r\n]*/, "")
17
- end
18
- # rubocop:enable Style/IfUnlessModifier
19
- cleaned
20
- end
21
-
22
- def self.from_serialized(serialized)
23
- new(clean_serialized(serialized))
24
- end
25
-
26
- def initialize(supplied_body)
27
- @supplied_body = supplied_body.clone
28
- end
29
-
30
- def to_serialized
31
- from_line = "From #{from}\n"
32
- body = mboxrd_body.dup.force_encoding(Encoding::UTF_8)
33
- from_line + body
34
- end
35
-
36
- def date
37
- parsed.date
38
- rescue StandardError
39
- nil
40
- end
41
-
42
- def subject
43
- parsed.subject
44
- end
45
-
46
- def imap_body
47
- supplied_body.gsub(/(?<!\r)\n/, "\r\n")
48
- end
49
-
50
- private
51
-
52
- def parsed
53
- @parsed ||= Mail.new(supplied_body)
54
- end
55
-
56
- def from
57
- @from ||=
58
- begin
59
- from = best_from.dup
60
- from << " #{asctime}" if asctime != ""
61
- from
62
- end
63
- end
64
-
65
- def best_from
66
- return first_from if first_from
67
- return parsed.sender if parsed.sender
68
- return parsed.envelope_from if parsed.envelope_from
69
- return parsed.return_path if parsed.return_path
70
-
71
- ""
72
- end
73
-
74
- def first_from
75
- return nil if !parsed.from.is_a?(Enumerable)
76
-
77
- parsed.from.find { |from| from }
78
- end
79
-
80
- def mboxrd_body
81
- @mboxrd_body ||=
82
- begin
83
- mboxrd_body = add_extra_quote(supplied_body.gsub("\r\n", "\n"))
84
- mboxrd_body += "\n" if !mboxrd_body.end_with?("\n")
85
- mboxrd_body += "\n" if !mboxrd_body.end_with?("\n\n")
86
- mboxrd_body
87
- end
88
- end
89
-
90
- def add_extra_quote(body)
91
- # The mboxrd format requires that lines starting with 'From'
92
- # be prefixed with a '>' so that any remaining lines which start with
93
- # 'From ' can be taken as the beginning of messages.
94
- # http://www.digitalpreservation.gov/formats/fdd/fdd000385.shtml
95
- # Here we add an extra '>' before any "From" or ">From".
96
- body.gsub(/\n(>*From)/, "\n>\\1")
97
- end
98
-
99
- def asctime
100
- @asctime ||= date ? date.asctime : ""
101
- end
102
- end
103
- end
@@ -1,11 +0,0 @@
1
- require "email/provider/base"
2
-
3
- class Email::Provider::AppleMail < Email::Provider::Base
4
- def host
5
- "imap.mail.me.com"
6
- end
7
-
8
- def sets_seen_flags_on_fetch?
9
- true
10
- end
11
- end
@@ -1,14 +0,0 @@
1
- module Email; end
2
- class Email::Provider; end
3
-
4
- class Email::Provider::Base
5
- def options
6
- # rubocop:disable Naming/VariableNumber
7
- {port: 993, ssl: {ssl_version: :TLSv1_2}}
8
- # rubocop:enable Naming/VariableNumber
9
- end
10
-
11
- def sets_seen_flags_on_fetch?
12
- false
13
- end
14
- end
@@ -1,7 +0,0 @@
1
- require "email/provider/base"
2
-
3
- class Email::Provider::Fastmail < Email::Provider::Base
4
- def host
5
- "imap.fastmail.com"
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- require "email/provider/base"
2
-
3
- class Email::Provider::GMail < Email::Provider::Base
4
- def host
5
- "imap.gmail.com"
6
- end
7
- end
@@ -1,11 +0,0 @@
1
- require "email/provider/base"
2
-
3
- class Email::Provider::Purelymail < Email::Provider::Base
4
- def host
5
- "mailserver.purelymail.com"
6
- end
7
-
8
- def sets_seen_flags_on_fetch?
9
- true
10
- end
11
- end
@@ -1,13 +0,0 @@
1
- require "email/provider/base"
2
-
3
- class Email::Provider::Unknown < Email::Provider::Base
4
- # We don't know how to guess the IMAP server
5
- def host
6
- end
7
-
8
- def options
9
- # rubocop:disable Naming/VariableNumber
10
- {port: 993, ssl: {ssl_version: :TLSv1_2}}
11
- # rubocop:enable Naming/VariableNumber
12
- end
13
- end
@@ -1,32 +0,0 @@
1
- require "email/provider/apple_mail"
2
- require "email/provider/fastmail"
3
- require "email/provider/gmail"
4
- require "email/provider/purelymail"
5
- require "email/provider/unknown"
6
-
7
- module Email; end
8
-
9
- class Email::Provider
10
- def self.for_address(address)
11
- # rubocop:disable Lint/DuplicateBranch
12
- case
13
- when address.end_with?("@fastmail.com")
14
- Email::Provider::Fastmail.new
15
- when address.end_with?("@fastmail.fm")
16
- Email::Provider::Fastmail.new
17
- when address.end_with?("@gmail.com")
18
- Email::Provider::GMail.new
19
- when address.end_with?("@icloud.com")
20
- Email::Provider::AppleMail.new
21
- when address.end_with?("@mac.com")
22
- Email::Provider::AppleMail.new
23
- when address.end_with?("@me.com")
24
- Email::Provider::AppleMail.new
25
- when address.end_with?("@purelymail.com")
26
- Email::Provider::Purelymail.new
27
- else
28
- Email::Provider::Unknown.new
29
- end
30
- # rubocop:enable Lint/DuplicateBranch
31
- end
32
- end
@@ -1,15 +0,0 @@
1
- module RetryOnError
2
- def retry_on_error(errors:, limit: 10, on_error: nil)
3
- tries ||= 1
4
- yield
5
- rescue *errors => e
6
- if tries < limit
7
- message = "#{e}, attempt #{tries} of #{limit}"
8
- Imap::Backup::Logger.logger.debug message
9
- on_error&.call
10
- tries += 1
11
- retry
12
- end
13
- raise e
14
- end
15
- end
@@ -1,46 +0,0 @@
1
- require "forwardable"
2
-
3
- module Text; end
4
-
5
- class Text::Sanitizer
6
- extend Forwardable
7
-
8
- attr_reader :output
9
-
10
- delegate puts: :output
11
- delegate write: :output
12
-
13
- def initialize(output)
14
- @output = output
15
- @current = ""
16
- end
17
-
18
- def print(*args)
19
- @current << args.join
20
- loop do
21
- line, newline, rest = @current.partition("\n")
22
- break if newline != "\n"
23
-
24
- clean = sanitize(line)
25
- output.puts clean
26
- @current = rest
27
- end
28
- end
29
-
30
- def flush
31
- return if @current == ""
32
-
33
- clean = sanitize(@current)
34
- output.puts clean
35
- end
36
-
37
- private
38
-
39
- def sanitize(text)
40
- # Hide password in Net::IMAP debug output
41
- text.gsub(
42
- /\A(C: RUBY\d+ LOGIN \S+) \S+/,
43
- "\\1 [PASSWORD REDACTED]"
44
- )
45
- end
46
- end