imap-backup 14.1.1 → 14.2.0

Sign up to get free protection for your applications and to get access to all the features.
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