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.
- checksums.yaml +4 -4
- data/bin/imap-backup +3 -2
- data/lib/imap/backup/account/client_factory.rb +1 -1
- data/lib/imap/backup/account/folder.rb +1 -1
- data/lib/imap/backup/cli_coverage.rb +21 -0
- data/lib/imap/backup/client/automatic_login_wrapper.rb +1 -1
- data/lib/imap/backup/email/mboxrd/message.rb +107 -0
- data/lib/imap/backup/email/provider/apple_mail.rb +15 -0
- data/lib/imap/backup/email/provider/base.rb +18 -0
- data/lib/imap/backup/email/provider/fastmail.rb +11 -0
- data/lib/imap/backup/email/provider/gmail.rb +11 -0
- data/lib/imap/backup/email/provider/purelymail.rb +15 -0
- data/lib/imap/backup/email/provider/unknown.rb +17 -0
- data/lib/imap/backup/email/provider.rb +36 -0
- data/lib/imap/backup/logger.rb +1 -1
- data/lib/imap/backup/retry_on_error.rb +21 -0
- data/lib/imap/backup/serializer/appender.rb +1 -1
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +1 -1
- data/lib/imap/backup/serializer/message.rb +1 -1
- data/lib/imap/backup/serializer.rb +1 -1
- data/lib/imap/backup/setup/email.rb +1 -1
- data/lib/imap/backup/setup/folder_chooser.rb +13 -12
- data/lib/imap/backup/setup.rb +2 -2
- data/lib/imap/backup/text/sanitizer.rb +50 -0
- data/lib/imap/backup/version.rb +2 -2
- metadata +13 -13
- data/lib/cli_coverage.rb +0 -17
- data/lib/email/mboxrd/message.rb +0 -103
- data/lib/email/provider/apple_mail.rb +0 -11
- data/lib/email/provider/base.rb +0 -14
- data/lib/email/provider/fastmail.rb +0 -7
- data/lib/email/provider/gmail.rb +0 -7
- data/lib/email/provider/purelymail.rb +0 -11
- data/lib/email/provider/unknown.rb +0 -13
- data/lib/email/provider.rb +0 -32
- data/lib/retry_on_error.rb +0 -15
- data/lib/text/sanitizer.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1da04e15e0f6c66398320aff6a7ce638e50089db3b6470a3aed7c6a4b49f0a66
|
4
|
+
data.tar.gz: '0261943b048d8a97f334ca58f21010ae26799ff0afab23cd611cf4528ac841c7'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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"
|
@@ -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
|
@@ -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,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,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
|
data/lib/imap/backup/logger.rb
CHANGED
@@ -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
|
@@ -60,16 +60,13 @@ module Imap::Backup
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def selected?(folder_name)
|
63
|
-
|
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
|
-
|
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
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
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
|
data/lib/imap/backup/setup.rb
CHANGED
@@ -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
|
data/lib/imap/backup/version.rb
CHANGED
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.
|
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-
|
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
|
data/lib/email/mboxrd/message.rb
DELETED
@@ -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
|
data/lib/email/provider/base.rb
DELETED
@@ -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
|
data/lib/email/provider/gmail.rb
DELETED
@@ -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
|
data/lib/email/provider.rb
DELETED
@@ -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
|
data/lib/retry_on_error.rb
DELETED
@@ -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
|
data/lib/text/sanitizer.rb
DELETED
@@ -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
|