imap-backup 4.0.7 → 4.1.1
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 +5 -2
- data/lib/imap/backup/account/connection.rb +53 -33
- data/lib/imap/backup/account/folder.rb +22 -2
- data/lib/imap/backup/account.rb +4 -0
- data/lib/imap/backup/cli/accounts.rb +43 -0
- data/lib/imap/backup/cli/folders.rb +3 -1
- data/lib/imap/backup/cli/helpers.rb +8 -9
- data/lib/imap/backup/cli/local.rb +4 -2
- data/lib/imap/backup/cli/setup.rb +1 -1
- data/lib/imap/backup/cli/utils.rb +3 -2
- data/lib/imap/backup/{configuration/store.rb → configuration.rb} +16 -3
- data/lib/imap/backup/downloader.rb +26 -12
- data/lib/imap/backup/logger.rb +42 -0
- data/lib/imap/backup/sanitizer.rb +42 -0
- data/lib/imap/backup/serializer/mbox_store.rb +2 -2
- data/lib/imap/backup/{configuration → setup}/account.rb +29 -19
- data/lib/imap/backup/{configuration → setup}/asker.rb +5 -5
- data/lib/imap/backup/setup/connection_tester.rb +26 -0
- data/lib/imap/backup/{configuration → setup}/folder_chooser.rb +18 -8
- data/lib/imap/backup/setup/helpers.rb +15 -0
- data/lib/imap/backup/{configuration/setup.rb → setup.rb} +23 -17
- data/lib/imap/backup/uploader.rb +2 -2
- data/lib/imap/backup/version.rb +2 -2
- data/lib/imap/backup.rb +7 -33
- data/lib/retry_on_error.rb +1 -1
- data/spec/features/backup_spec.rb +1 -0
- data/spec/features/support/email_server.rb +5 -2
- data/spec/support/higline_test_helpers.rb +1 -1
- data/spec/support/silence_logging.rb +1 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +14 -9
- data/spec/unit/imap/backup/cli/accounts_spec.rb +47 -0
- data/spec/unit/imap/backup/cli/local_spec.rb +7 -3
- data/spec/unit/imap/backup/cli/utils_spec.rb +15 -5
- data/spec/unit/imap/backup/{configuration/store_spec.rb → configuration_spec.rb} +2 -2
- data/spec/unit/imap/backup/downloader_spec.rb +1 -1
- data/spec/unit/imap/backup/logger_spec.rb +48 -0
- data/spec/unit/imap/backup/{configuration → setup}/account_spec.rb +31 -24
- data/spec/unit/imap/backup/{configuration → setup}/asker_spec.rb +2 -2
- data/spec/unit/imap/backup/{configuration → setup}/connection_tester_spec.rb +10 -10
- data/spec/unit/imap/backup/{configuration → setup}/folder_chooser_spec.rb +8 -8
- data/spec/unit/imap/backup/{configuration/setup_spec.rb → setup_spec.rb} +48 -40
- metadata +49 -46
- data/lib/imap/backup/configuration/connection_tester.rb +0 -14
- data/lib/imap/backup/configuration/list.rb +0 -53
- data/spec/unit/imap/backup/configuration/list_spec.rb +0 -96
- data/spec/unit/imap/backup_spec.rb +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d6302ae17c5b96de2f99c6683c4a6403edbf80d2afc51cbe0f471918251cfaef
|
4
|
+
data.tar.gz: 349c0b68911a751e670176a91dfc98d4359a06a9cb8acae7c2cc5d5efa028f25
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 444432d3bf689fa12de1226cb84e9f8975a300f2c94cc2529254e1e3680fba6d7b0cfddfc3fff9001a866dd5d84d1afabc0e952e894ec5ae1a91557ea5538472
|
7
|
+
data.tar.gz: f4d156790a84308e08ee3d4348741e6d55b26f07fece75473a80f4fc0b7c0265c1bb62230cee178bf4c00941ff0de91a323ade1c0e0783a72a970c738c8823f0
|
data/bin/imap-backup
CHANGED
@@ -2,7 +2,10 @@
|
|
2
2
|
|
3
3
|
$LOAD_PATH.unshift(File.expand_path("../lib/", __dir__))
|
4
4
|
require "imap/backup/cli"
|
5
|
+
require "imap/backup/logger"
|
5
6
|
|
6
|
-
Imap::Backup::
|
7
|
+
Imap::Backup::Logger.setup_logging
|
7
8
|
|
8
|
-
Imap::Backup::
|
9
|
+
Imap::Backup::Logger.sanitize_stderr do
|
10
|
+
Imap::Backup::CLI.start(ARGV)
|
11
|
+
end
|
@@ -15,44 +15,63 @@ module Imap::Backup
|
|
15
15
|
|
16
16
|
def initialize(account)
|
17
17
|
@account = account
|
18
|
-
|
18
|
+
reset
|
19
19
|
create_account_folder
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
# TODO: Make this private once the 'folders' command
|
23
|
+
# has been removed.
|
24
|
+
def folder_names
|
25
|
+
@folder_names ||=
|
24
26
|
begin
|
25
|
-
|
27
|
+
folder_names = client.list
|
26
28
|
|
27
|
-
if
|
29
|
+
if folder_names.empty?
|
28
30
|
message = "Unable to get folder list for account #{account.username}"
|
29
|
-
Imap::Backup.logger.info message
|
31
|
+
Imap::Backup::Logger.logger.info message
|
30
32
|
raise message
|
31
33
|
end
|
32
34
|
|
33
|
-
|
35
|
+
folder_names
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def backup_folders
|
40
|
+
@backup_folders ||=
|
41
|
+
begin
|
42
|
+
names =
|
43
|
+
if account.folders&.any?
|
44
|
+
account.folders.map { |af| af[:name] }
|
45
|
+
else
|
46
|
+
folder_names
|
47
|
+
end
|
48
|
+
|
49
|
+
names.map do |name|
|
50
|
+
Account::Folder.new(self, name)
|
51
|
+
end
|
34
52
|
end
|
35
53
|
end
|
36
54
|
|
37
55
|
def status
|
38
|
-
backup_folders.map do |
|
39
|
-
|
40
|
-
|
41
|
-
{name: backup_folder[:name], local: s.uids, remote: f.uids}
|
56
|
+
backup_folders.map do |folder|
|
57
|
+
s = Serializer::Mbox.new(account.local_path, folder.name)
|
58
|
+
{name: folder.name, local: s.uids, remote: folder.uids}
|
42
59
|
end
|
43
60
|
end
|
44
61
|
|
45
62
|
def run_backup
|
46
|
-
Imap::Backup.logger.debug "Running backup of account: #{account.username}"
|
63
|
+
Imap::Backup::Logger.logger.debug "Running backup of account: #{account.username}"
|
47
64
|
# start the connection so we get logging messages in the right order
|
48
65
|
client
|
49
66
|
each_folder do |folder, serializer|
|
50
67
|
next if !folder.exist?
|
51
68
|
|
52
|
-
Imap::Backup.logger.debug "[#{folder.name}] running backup"
|
69
|
+
Imap::Backup::Logger.logger.debug "[#{folder.name}] running backup"
|
53
70
|
serializer.apply_uid_validity(folder.uid_validity)
|
54
71
|
begin
|
55
|
-
Downloader.new(
|
72
|
+
Downloader.new(
|
73
|
+
folder, serializer, block_size: config.download_block_size
|
74
|
+
).run
|
56
75
|
rescue Net::IMAP::ByeResponseError
|
57
76
|
reconnect
|
58
77
|
retry
|
@@ -81,18 +100,27 @@ module Imap::Backup
|
|
81
100
|
|
82
101
|
def disconnect
|
83
102
|
client.disconnect if @client
|
103
|
+
reset
|
84
104
|
end
|
85
105
|
|
86
106
|
def reconnect
|
87
107
|
disconnect
|
108
|
+
end
|
109
|
+
|
110
|
+
def reset
|
111
|
+
@backup_folders = nil
|
88
112
|
@client = nil
|
113
|
+
@config = nil
|
114
|
+
@folder_names = nil
|
115
|
+
@provider = nil
|
116
|
+
@server = nil
|
89
117
|
end
|
90
118
|
|
91
119
|
def client
|
92
120
|
@client ||=
|
93
121
|
retry_on_error(errors: LOGIN_RETRY_CLASSES) do
|
94
122
|
options = provider_options
|
95
|
-
Imap::Backup.logger.debug(
|
123
|
+
Imap::Backup::Logger.logger.debug(
|
96
124
|
"Creating IMAP instance: #{server}, options: #{options.inspect}"
|
97
125
|
)
|
98
126
|
client =
|
@@ -101,9 +129,9 @@ module Imap::Backup
|
|
101
129
|
else
|
102
130
|
Client::Default.new(server, options)
|
103
131
|
end
|
104
|
-
Imap::Backup.logger.debug "Logging in: #{account.username}/#{masked_password}"
|
132
|
+
Imap::Backup::Logger.logger.debug "Logging in: #{account.username}/#{masked_password}"
|
105
133
|
client.login(account.username, account.password)
|
106
|
-
Imap::Backup.logger.debug "Login complete"
|
134
|
+
Imap::Backup::Logger.logger.debug "Login complete"
|
107
135
|
client
|
108
136
|
end
|
109
137
|
end
|
@@ -115,9 +143,8 @@ module Imap::Backup
|
|
115
143
|
private
|
116
144
|
|
117
145
|
def each_folder
|
118
|
-
backup_folders.each do |
|
119
|
-
|
120
|
-
serializer = Serializer::Mbox.new(account.local_path, backup_folder[:name])
|
146
|
+
backup_folders.each do |folder|
|
147
|
+
serializer = Serializer::Mbox.new(account.local_path, folder.name)
|
121
148
|
yield folder, serializer
|
122
149
|
end
|
123
150
|
end
|
@@ -125,13 +152,13 @@ module Imap::Backup
|
|
125
152
|
def restore_folder(serializer, folder)
|
126
153
|
existing_uids = folder.uids
|
127
154
|
if existing_uids.any?
|
128
|
-
Imap::Backup.logger.debug(
|
155
|
+
Imap::Backup::Logger.logger.debug(
|
129
156
|
"There's already a '#{folder.name}' folder with emails"
|
130
157
|
)
|
131
158
|
new_name = serializer.apply_uid_validity(folder.uid_validity)
|
132
159
|
old_name = serializer.folder
|
133
160
|
if new_name
|
134
|
-
Imap::Backup.logger.debug(
|
161
|
+
Imap::Backup::Logger.logger.debug(
|
135
162
|
"Backup '#{old_name}' renamed and restored to '#{new_name}'"
|
136
163
|
)
|
137
164
|
new_serializer = Serializer::Mbox.new(account.local_path, new_name)
|
@@ -161,17 +188,6 @@ module Imap::Backup
|
|
161
188
|
account.password.gsub(/./, "x")
|
162
189
|
end
|
163
190
|
|
164
|
-
def backup_folders
|
165
|
-
@backup_folders ||=
|
166
|
-
begin
|
167
|
-
if account.folders&.any?
|
168
|
-
account.folders
|
169
|
-
else
|
170
|
-
folders.map { |name| {name: name} }
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
191
|
def provider
|
176
192
|
@provider ||= Email::Provider.for_address(account.username)
|
177
193
|
end
|
@@ -179,5 +195,9 @@ module Imap::Backup
|
|
179
195
|
def provider_options
|
180
196
|
provider.options.merge(account.connection_options || {})
|
181
197
|
end
|
198
|
+
|
199
|
+
def config
|
200
|
+
@config ||= Configuration.new
|
201
|
+
end
|
182
202
|
end
|
183
203
|
end
|
@@ -64,7 +64,7 @@ module Imap::Backup
|
|
64
64
|
in `search_internal` in stdlib net/imap.rb.
|
65
65
|
This is caused by `@responses["SEARCH"] being unset/undefined
|
66
66
|
MESSAGE
|
67
|
-
Imap::Backup.logger.warn message
|
67
|
+
Imap::Backup::Logger.logger.warn message
|
68
68
|
[]
|
69
69
|
end
|
70
70
|
|
@@ -84,6 +84,26 @@ module Imap::Backup
|
|
84
84
|
nil
|
85
85
|
end
|
86
86
|
|
87
|
+
def fetch_multi(uids)
|
88
|
+
examine
|
89
|
+
fetch_data_items =
|
90
|
+
retry_on_error(errors: UID_FETCH_RETRY_CLASSES) do
|
91
|
+
client.uid_fetch(uids, [BODY_ATTRIBUTE])
|
92
|
+
end
|
93
|
+
return nil if fetch_data_items.nil?
|
94
|
+
|
95
|
+
fetch_data_items.map do |item|
|
96
|
+
attributes = item.attr
|
97
|
+
|
98
|
+
{
|
99
|
+
uid: attributes["UID"],
|
100
|
+
body: attributes[BODY_ATTRIBUTE]
|
101
|
+
}
|
102
|
+
end
|
103
|
+
rescue FolderNotFound
|
104
|
+
nil
|
105
|
+
end
|
106
|
+
|
87
107
|
def append(message)
|
88
108
|
body = message.imap_body
|
89
109
|
date = message.date&.to_time
|
@@ -96,7 +116,7 @@ module Imap::Backup
|
|
96
116
|
def examine
|
97
117
|
client.examine(utf7_encoded_name)
|
98
118
|
rescue Net::IMAP::NoResponseError
|
99
|
-
Imap::Backup.logger.warn "Folder '#{name}' does not exist on server"
|
119
|
+
Imap::Backup::Logger.logger.warn "Folder '#{name}' does not exist on server"
|
100
120
|
raise FolderNotFound, "Folder '#{name}' does not exist on server"
|
101
121
|
end
|
102
122
|
|
data/lib/imap/backup/account.rb
CHANGED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI; end
|
3
|
+
|
4
|
+
class CLI::Accounts
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :required_accounts
|
8
|
+
|
9
|
+
def initialize(required_accounts = [])
|
10
|
+
@required_accounts = required_accounts
|
11
|
+
end
|
12
|
+
|
13
|
+
def each(&block)
|
14
|
+
return enum_for(:each) if !block
|
15
|
+
|
16
|
+
accounts.each(&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def accounts
|
22
|
+
@accounts ||=
|
23
|
+
if required_accounts.empty?
|
24
|
+
config.accounts
|
25
|
+
else
|
26
|
+
config.accounts.select do |account|
|
27
|
+
required_accounts.include?(account.username)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def config
|
33
|
+
@config ||= begin
|
34
|
+
exists = Configuration.exist?
|
35
|
+
if !exists
|
36
|
+
path = Configuration.default_pathname
|
37
|
+
raise ConfigurationNotFound, "Configuration file '#{path}' not found"
|
38
|
+
end
|
39
|
+
Configuration.new
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -14,7 +14,9 @@ module Imap::Backup
|
|
14
14
|
def run
|
15
15
|
each_connection(account_names) do |connection|
|
16
16
|
puts connection.username
|
17
|
-
|
17
|
+
# TODO: Make folder_names private once this command
|
18
|
+
# has been removed.
|
19
|
+
folders = connection.folder_names
|
18
20
|
if folders.nil?
|
19
21
|
warn "Unable to list account folders"
|
20
22
|
return false
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require "imap/backup"
|
2
|
+
require "imap/backup/cli/accounts"
|
2
3
|
|
3
4
|
module Imap::Backup::CLI::Helpers
|
4
5
|
def symbolized(options)
|
@@ -6,8 +7,8 @@ module Imap::Backup::CLI::Helpers
|
|
6
7
|
end
|
7
8
|
|
8
9
|
def account(email)
|
9
|
-
|
10
|
-
account =
|
10
|
+
accounts = Imap::Backup::CLI::Accounts.new
|
11
|
+
account = accounts.find { |a| a.username == email }
|
11
12
|
raise "#{email} is not a configured account" if !account
|
12
13
|
|
13
14
|
account
|
@@ -20,14 +21,12 @@ module Imap::Backup::CLI::Helpers
|
|
20
21
|
end
|
21
22
|
|
22
23
|
def each_connection(names)
|
23
|
-
|
24
|
-
connections = Imap::Backup::Configuration::List.new(names)
|
25
|
-
rescue Imap::Backup::ConfigurationNotFound
|
26
|
-
raise "imap-backup is not configured. Run `imap-backup setup`"
|
27
|
-
end
|
24
|
+
accounts = Imap::Backup::CLI::Accounts.new(names)
|
28
25
|
|
29
|
-
|
30
|
-
yield connection
|
26
|
+
accounts.each do |account|
|
27
|
+
yield account.connection
|
31
28
|
end
|
29
|
+
rescue Imap::Backup::ConfigurationNotFound
|
30
|
+
raise "imap-backup is not configured. Run `imap-backup setup`"
|
32
31
|
end
|
33
32
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "imap/backup/cli/accounts"
|
2
|
+
|
1
3
|
module Imap::Backup
|
2
4
|
class CLI::Local < Thor
|
3
5
|
include Thor::Actions
|
@@ -5,8 +7,8 @@ module Imap::Backup
|
|
5
7
|
|
6
8
|
desc "accounts", "List locally backed-up accounts"
|
7
9
|
def accounts
|
8
|
-
|
9
|
-
|
10
|
+
accounts = CLI::Accounts.new
|
11
|
+
accounts.each { |a| Kernel.puts a.username }
|
10
12
|
end
|
11
13
|
|
12
14
|
desc "folders EMAIL", "List account folders"
|
@@ -11,9 +11,10 @@ module Imap::Backup
|
|
11
11
|
def ignore_history(email)
|
12
12
|
connection = connection(email)
|
13
13
|
|
14
|
-
connection.
|
14
|
+
connection.backup_folders.each do |folder|
|
15
15
|
next if !folder.exist?
|
16
16
|
|
17
|
+
serializer = Serializer::Mbox.new(connection.account.local_path, folder.name)
|
17
18
|
do_ignore_folder_history(folder, serializer)
|
18
19
|
end
|
19
20
|
end
|
@@ -63,7 +64,7 @@ module Imap::Backup
|
|
63
64
|
no_commands do
|
64
65
|
def do_ignore_folder_history(folder, serializer)
|
65
66
|
uids = folder.uids - serializer.uids
|
66
|
-
Imap::Backup.logger.info "Folder '#{folder.name}' - #{uids.length} messages"
|
67
|
+
Imap::Backup::Logger.logger.info "Folder '#{folder.name}' - #{uids.length} messages"
|
67
68
|
|
68
69
|
serializer.apply_uid_validity(folder.uid_validity)
|
69
70
|
|
@@ -4,10 +4,9 @@ require "os"
|
|
4
4
|
require "imap/backup/account"
|
5
5
|
|
6
6
|
module Imap::Backup
|
7
|
-
|
8
|
-
|
9
|
-
class Configuration::Store
|
7
|
+
class Configuration
|
10
8
|
CONFIGURATION_DIRECTORY = File.expand_path("~/.imap-backup")
|
9
|
+
DEFAULT_DOWNLOAD_BLOCK_SIZE = 1
|
11
10
|
VERSION = "2.0"
|
12
11
|
|
13
12
|
attr_reader :pathname
|
@@ -22,6 +21,7 @@ module Imap::Backup
|
|
22
21
|
|
23
22
|
def initialize(pathname = self.class.default_pathname)
|
24
23
|
@pathname = pathname
|
24
|
+
@saved_debug = nil
|
25
25
|
@debug = nil
|
26
26
|
end
|
27
27
|
|
@@ -42,6 +42,7 @@ module Imap::Backup
|
|
42
42
|
}
|
43
43
|
File.open(pathname, "w") { |f| f.write(JSON.pretty_generate(save_data)) }
|
44
44
|
FileUtils.chmod(0o600, pathname) if !windows?
|
45
|
+
@data = nil
|
45
46
|
end
|
46
47
|
|
47
48
|
def accounts
|
@@ -51,8 +52,19 @@ module Imap::Backup
|
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
55
|
+
def download_block_size
|
56
|
+
size = ENV["DOWNLOAD_BLOCK_SIZE"].to_i
|
57
|
+
if size > 0
|
58
|
+
size
|
59
|
+
else
|
60
|
+
DEFAULT_DOWNLOAD_BLOCK_SIZE
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
54
64
|
def modified?
|
55
65
|
ensure_loaded!
|
66
|
+
return true if @saved_debug != @debug
|
67
|
+
|
56
68
|
accounts.any? { |a| a.modified? || a.marked_for_deletion? }
|
57
69
|
end
|
58
70
|
|
@@ -73,6 +85,7 @@ module Imap::Backup
|
|
73
85
|
|
74
86
|
data
|
75
87
|
@debug = data.key?(:debug) ? data[:debug] == true : false
|
88
|
+
@saved_debug = @debug
|
76
89
|
true
|
77
90
|
end
|
78
91
|
|
@@ -2,27 +2,41 @@ module Imap::Backup
|
|
2
2
|
class Downloader
|
3
3
|
attr_reader :folder
|
4
4
|
attr_reader :serializer
|
5
|
+
attr_reader :block_size
|
5
6
|
|
6
|
-
def initialize(folder, serializer)
|
7
|
+
def initialize(folder, serializer, block_size: 1)
|
7
8
|
@folder = folder
|
8
9
|
@serializer = serializer
|
10
|
+
@block_size = block_size
|
9
11
|
end
|
10
12
|
|
11
13
|
def run
|
12
14
|
uids = folder.uids - serializer.uids
|
13
15
|
count = uids.count
|
14
|
-
Imap::Backup.logger.debug "[#{folder.name}] #{count} new messages"
|
15
|
-
uids.
|
16
|
-
|
17
|
-
|
18
|
-
if
|
19
|
-
|
20
|
-
|
16
|
+
Imap::Backup::Logger.logger.debug "[#{folder.name}] #{count} new messages"
|
17
|
+
uids.each_slice(block_size).with_index do |block, i|
|
18
|
+
offset = i * block_size + 1
|
19
|
+
uids_and_bodies = folder.fetch_multi(block)
|
20
|
+
if uids_and_bodies.nil?
|
21
|
+
if block_size > 1
|
22
|
+
Imap::Backup::Logger.logger.debug("[#{folder.name}] Multi fetch failed for UIDs #{block.join(", ")}, switching to single fetches")
|
23
|
+
@block_size = 1
|
24
|
+
redo
|
25
|
+
else
|
26
|
+
Imap::Backup::Logger.logger.debug("[#{folder.name}] Fetch failed for UID #{block[0]} - skipping")
|
27
|
+
next
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
uids_and_bodies.each.with_index do |uid_and_body, j|
|
32
|
+
uid = uid_and_body[:uid]
|
33
|
+
body = uid_and_body[:body]
|
34
|
+
Imap::Backup::Logger.logger.debug(
|
35
|
+
"[#{folder.name}] uid: #{uid} (#{offset +j}/#{count}) - " \
|
36
|
+
"#{body.size} bytes"
|
37
|
+
)
|
38
|
+
serializer.save(uid, body)
|
21
39
|
end
|
22
|
-
Imap::Backup.logger.debug(
|
23
|
-
"#{log_prefix} #{body.size} bytes"
|
24
|
-
)
|
25
|
-
serializer.save(uid, body)
|
26
40
|
end
|
27
41
|
end
|
28
42
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "logger"
|
2
|
+
require "singleton"
|
3
|
+
|
4
|
+
require "imap/backup/configuration"
|
5
|
+
require "imap/backup/sanitizer"
|
6
|
+
|
7
|
+
module Imap::Backup
|
8
|
+
class Logger
|
9
|
+
include Singleton
|
10
|
+
|
11
|
+
def self.logger
|
12
|
+
Logger.instance.logger
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.setup_logging(config = Configuration.new)
|
16
|
+
logger.level =
|
17
|
+
if config.debug?
|
18
|
+
::Logger::Severity::DEBUG
|
19
|
+
else
|
20
|
+
::Logger::Severity::ERROR
|
21
|
+
end
|
22
|
+
Net::IMAP.debug = config.debug?
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.sanitize_stderr
|
26
|
+
sanitizer = Sanitizer.new($stdout)
|
27
|
+
previous_stderr = $stderr
|
28
|
+
$stderr = sanitizer
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
sanitizer.flush
|
32
|
+
$stderr = previous_stderr
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :logger
|
36
|
+
|
37
|
+
def initialize
|
38
|
+
@logger = ::Logger.new($stdout)
|
39
|
+
$stdout.sync = true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class Sanitizer
|
3
|
+
attr_reader :output
|
4
|
+
|
5
|
+
def initialize(output)
|
6
|
+
@output = output
|
7
|
+
@current = ""
|
8
|
+
end
|
9
|
+
|
10
|
+
def write(*args)
|
11
|
+
output.write(*args)
|
12
|
+
end
|
13
|
+
|
14
|
+
def print(*args)
|
15
|
+
@current << args.join
|
16
|
+
loop do
|
17
|
+
line, newline, rest = @current.partition("\n")
|
18
|
+
break if newline != "\n"
|
19
|
+
clean = sanitize(line)
|
20
|
+
output.puts clean
|
21
|
+
@current = rest
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def flush
|
26
|
+
return if @current == ""
|
27
|
+
|
28
|
+
clean = sanitize(@current)
|
29
|
+
output.puts clean
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def sanitize(t)
|
35
|
+
# Hide password in Net::IMAP debug output
|
36
|
+
t.gsub(
|
37
|
+
/\A(C: RUBY\d+ LOGIN \S+) \S+/,
|
38
|
+
"\\1 [PASSWORD REDACTED]"
|
39
|
+
)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -46,7 +46,7 @@ module Imap::Backup
|
|
46
46
|
|
47
47
|
uid = uid.to_i
|
48
48
|
if uids.include?(uid)
|
49
|
-
Imap::Backup.logger.debug(
|
49
|
+
Imap::Backup::Logger.logger.debug(
|
50
50
|
"[#{folder}] message #{uid} already downloaded - skipping"
|
51
51
|
)
|
52
52
|
return
|
@@ -65,7 +65,7 @@ module Imap::Backup
|
|
65
65
|
#{body}. #{e}:
|
66
66
|
#{e.backtrace.join("\n")}"
|
67
67
|
ERROR
|
68
|
-
Imap::Backup.logger.warn message
|
68
|
+
Imap::Backup::Logger.logger.warn message
|
69
69
|
ensure
|
70
70
|
mbox&.close
|
71
71
|
end
|