imap-backup 15.0.2 → 15.1.3
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/docs/performance.md +5 -7
- data/lib/imap/backup/account/backup.rb +11 -5
- data/lib/imap/backup/account/folder.rb +7 -8
- data/lib/imap/backup/account/folder_backup.rb +8 -4
- data/lib/imap/backup/cli/backup.rb +3 -0
- data/lib/imap/backup/cli/single.rb +2 -2
- data/lib/imap/backup/client/default.rb +3 -0
- data/lib/imap/backup/downloader.rb +24 -4
- data/lib/imap/backup/version.rb +2 -2
- metadata +3 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a391a879278c1f286d5756ebfc3e77c39acda7e5ec9441776e30fb720251cee
|
4
|
+
data.tar.gz: bcd9c851be504e9c775a241a21e1ecc708baf4f05ff7fe42160e7468c10ca3f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 289206eb94864c60e82b35db46e9f5d7531f73692bd10c02d407e800c77aa00597a149cfc7d05f749abe8d8189745a4b9b5a184fd8df472ea13d646fe8065105
|
7
|
+
data.tar.gz: b38e2740e7862fc92175a684dfd93a3b176ed3b8e56ae2ee6438490577ab54002238ecdf4d6f1449d0c557ca5996eea1b81f6aabbb6aaaa47ad455796b431903
|
data/docs/performance.md
CHANGED
@@ -1,14 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# Performance
|
2
2
|
|
3
|
-
The two performance-related settings are "Download strategy",
|
4
|
-
which is a global setting,
|
5
|
-
and "Multi-fetch size", which is an Account-level setting.
|
3
|
+
The two performance-related settings are "Download strategy", which is a global setting, and "Multi-fetch size", which is an Account-level setting.
|
6
4
|
|
7
5
|
As with all performance tweaks, there are trade-offs.
|
8
6
|
|
9
7
|
# Overview
|
10
8
|
|
11
|
-
The defaults, which suit most machines and
|
9
|
+
The defaults, which suit most machines and play nice with servers are:
|
12
10
|
|
13
11
|
* Download strategy: "delay writing metadata",
|
14
12
|
* Multi-fetch size: 1.
|
@@ -18,7 +16,7 @@ a small virtual server or Raspberry Pi
|
|
18
16
|
to run your backups, you can change "Download strategy".
|
19
17
|
|
20
18
|
If your email provider supports it,
|
21
|
-
and you don't have tight
|
19
|
+
and you don't have tight memory limits,
|
22
20
|
increase "Multi-fetch size" for faster backups.
|
23
21
|
|
24
22
|
# Delay download writes
|
@@ -45,7 +43,7 @@ By default, during backup, each message is downloaded one-by-one.
|
|
45
43
|
Using this setting, you can download chunks of emails at a time,
|
46
44
|
potentially speeding up the process.
|
47
45
|
|
48
|
-
Using multi-fetch
|
46
|
+
Using multi-fetch mean that the backup process *will* use
|
49
47
|
more memory - equivalent to the size of the groups of messages
|
50
48
|
that are downloaded.
|
51
49
|
|
@@ -18,27 +18,33 @@ module Imap::Backup
|
|
18
18
|
# Runs the backup
|
19
19
|
# @return [void]
|
20
20
|
def run
|
21
|
-
Logger.logger.info "Running backup of account
|
21
|
+
Logger.logger.info "Running backup of account '#{account.username}'"
|
22
22
|
# start the connection so we get logging messages in the right order
|
23
23
|
account.client.login
|
24
24
|
|
25
|
-
|
26
|
-
Account::LocalOnlyFolderDeleter.new(account: account).run if account.mirror_mode
|
25
|
+
run_pre_backup_tasks
|
27
26
|
backup_folders = Account::BackupFolders.new(
|
28
27
|
client: account.client, account: account
|
29
|
-
)
|
28
|
+
).to_a
|
30
29
|
if backup_folders.none?
|
31
|
-
Logger.logger.warn "
|
30
|
+
Logger.logger.warn "No folders found to backup for account '#{account.username}'"
|
32
31
|
return
|
33
32
|
end
|
33
|
+
Logger.logger.debug "Starting backup of #{backup_folders.count} folders"
|
34
34
|
backup_folders.each do |folder|
|
35
35
|
Account::FolderBackup.new(account: account, folder: folder, refresh: refresh).run
|
36
36
|
end
|
37
|
+
Logger.logger.debug "Backup of account '#{account.username}' complete"
|
37
38
|
end
|
38
39
|
|
39
40
|
private
|
40
41
|
|
41
42
|
attr_reader :account
|
42
43
|
attr_reader :refresh
|
44
|
+
|
45
|
+
def run_pre_backup_tasks
|
46
|
+
Account::FolderEnsurer.new(account: account).run
|
47
|
+
Account::LocalOnlyFolderDeleter.new(account: account).run if account.mirror_mode
|
48
|
+
end
|
43
49
|
end
|
44
50
|
end
|
@@ -31,19 +31,15 @@ module Imap::Backup
|
|
31
31
|
|
32
32
|
# @raise any error that occurs more than 10 times
|
33
33
|
def exist?
|
34
|
-
|
35
|
-
previous_debug = Net::IMAP.debug
|
36
|
-
Imap::Backup::Logger.logger.level = ::Logger::Severity::UNKNOWN
|
37
|
-
Net::IMAP.debug = false
|
34
|
+
Logger.logger.debug "Checking whether folder '#{name}' exists"
|
38
35
|
retry_on_error(errors: EXAMINE_RETRY_CLASSES) do
|
39
36
|
examine
|
40
37
|
end
|
38
|
+
Logger.logger.debug "Folder '#{name}' exists"
|
41
39
|
true
|
42
40
|
rescue FolderNotFound
|
41
|
+
Logger.logger.debug "Folder '#{name}' does not exist"
|
43
42
|
false
|
44
|
-
ensure
|
45
|
-
Imap::Backup::Logger.logger.level = previous_level
|
46
|
-
Net::IMAP.debug = previous_debug
|
47
43
|
end
|
48
44
|
|
49
45
|
# Creates the folder on the server
|
@@ -69,8 +65,11 @@ module Imap::Backup
|
|
69
65
|
# @raise any error that occurs more than 10 times
|
70
66
|
# @return [Array<Integer>] the folders message UIDs
|
71
67
|
def uids
|
68
|
+
Logger.logger.debug "Fetching UIDs for folder '#{name}'"
|
72
69
|
examine
|
73
|
-
client.uid_search(["ALL"]).sort
|
70
|
+
result = client.uid_search(["ALL"]).sort
|
71
|
+
Logger.logger.debug "#{result.count} UIDs found for folder '#{name}'"
|
72
|
+
result
|
74
73
|
rescue FolderNotFound
|
75
74
|
[]
|
76
75
|
rescue NoMethodError
|
@@ -22,11 +22,11 @@ module Imap::Backup
|
|
22
22
|
# @raise [RuntimeError] if the configured download strategy is incorrect
|
23
23
|
# @return [void]
|
24
24
|
def run
|
25
|
+
Logger.logger.debug "Running backup for folder '#{folder.name}'"
|
26
|
+
|
25
27
|
folder_ok = folder_ok?
|
26
28
|
return if !folder_ok
|
27
29
|
|
28
|
-
Logger.logger.debug "[#{folder.name}] running backup"
|
29
|
-
|
30
30
|
serializer.apply_uid_validity(folder.uid_validity)
|
31
31
|
|
32
32
|
serializer.transaction do
|
@@ -36,6 +36,7 @@ module Imap::Backup
|
|
36
36
|
# After the transaction the serializer will have any appended messages
|
37
37
|
# so we can check differences between the server and the local backup
|
38
38
|
LocalOnlyMessageDeleter.new(folder, raw_serializer).run if account.mirror_mode
|
39
|
+
Logger.logger.debug "Backup for folder '#{folder.name}' complete"
|
39
40
|
end
|
40
41
|
|
41
42
|
private
|
@@ -46,10 +47,13 @@ module Imap::Backup
|
|
46
47
|
|
47
48
|
def folder_ok?
|
48
49
|
begin
|
49
|
-
|
50
|
+
if !folder.exist?
|
51
|
+
Logger.logger.info "Skipping backup for folder '#{folder.name}' as it does not exist"
|
52
|
+
return false
|
53
|
+
end
|
50
54
|
rescue Encoding::UndefinedConversionError
|
51
55
|
message = "Skipping backup for '#{folder.name}' " \
|
52
|
-
"as it is not UTF-7 encoded correctly"
|
56
|
+
"as it's name is not UTF-7 encoded correctly"
|
53
57
|
Logger.logger.info message
|
54
58
|
return false
|
55
59
|
end
|
@@ -24,6 +24,7 @@ module Imap::Backup
|
|
24
24
|
# @return [void]
|
25
25
|
no_commands do
|
26
26
|
def run
|
27
|
+
Logger.logger.debug "Loading configuration"
|
27
28
|
config = load_config(**options)
|
28
29
|
exit_code = nil
|
29
30
|
accounts = requested_accounts(config)
|
@@ -31,6 +32,7 @@ module Imap::Backup
|
|
31
32
|
Logger.logger.warn "No matching accounts found to backup"
|
32
33
|
return
|
33
34
|
end
|
35
|
+
Logger.logger.debug "Starting backup of #{accounts.count} accounts"
|
34
36
|
accounts.each do |account|
|
35
37
|
backup = Account::Backup.new(account: account, refresh: refresh)
|
36
38
|
backup.run
|
@@ -43,6 +45,7 @@ module Imap::Backup
|
|
43
45
|
Logger.logger.error message
|
44
46
|
next
|
45
47
|
end
|
48
|
+
Logger.logger.debug "Backup complete"
|
46
49
|
exit(exit_code) if exit_code
|
47
50
|
end
|
48
51
|
end
|
@@ -88,7 +88,7 @@ module Imap::Backup
|
|
88
88
|
"password-environment-variable",
|
89
89
|
type: :string,
|
90
90
|
desc: "an environment variable that is set to your password",
|
91
|
-
aliases: ["-
|
91
|
+
aliases: ["-w"]
|
92
92
|
)
|
93
93
|
method_option(
|
94
94
|
"password-file",
|
@@ -137,7 +137,7 @@ module Imap::Backup
|
|
137
137
|
"mirror",
|
138
138
|
type: :boolean,
|
139
139
|
desc: "if this option is given, " \
|
140
|
-
"emails that are
|
140
|
+
"existing backed-up emails that are no longer on the server " \
|
141
141
|
"will be removed from the local backup.",
|
142
142
|
aliases: ["-m"]
|
143
143
|
)
|
@@ -27,6 +27,7 @@ module Imap::Backup
|
|
27
27
|
# @return [Array<String>] the account folders
|
28
28
|
def list
|
29
29
|
root = provider_root
|
30
|
+
Logger.logger.debug "Listing all account folders"
|
30
31
|
mailbox_lists = imap.list(root, "*")
|
31
32
|
|
32
33
|
return [] if mailbox_lists.nil?
|
@@ -105,7 +106,9 @@ module Imap::Backup
|
|
105
106
|
# in the reference.
|
106
107
|
def provider_root
|
107
108
|
@provider_root ||= begin
|
109
|
+
Logger.logger.debug "Fetching provider root"
|
108
110
|
root_info = imap.list("", "")[0]
|
111
|
+
Logger.logger.debug "Provider root is '#{root_info.name}'"
|
109
112
|
root_info.name
|
110
113
|
end
|
111
114
|
end
|
@@ -19,7 +19,19 @@ module Imap::Backup
|
|
19
19
|
# Runs the downloader
|
20
20
|
# @return [void]
|
21
21
|
def run
|
22
|
-
|
22
|
+
debug("#{serializer_uids.count} already messages already downloaded")
|
23
|
+
debug("#{folder_uids.count} messages on server")
|
24
|
+
local_only_count = (serializer_uids - folder_uids).count
|
25
|
+
if local_only_count.positive?
|
26
|
+
debug("#{local_only_count} downloaded messages no longer on server")
|
27
|
+
end
|
28
|
+
|
29
|
+
if uids.none?
|
30
|
+
debug("no new messages on server — skipping")
|
31
|
+
return
|
32
|
+
end
|
33
|
+
|
34
|
+
info("#{uids.count} new messages")
|
23
35
|
|
24
36
|
uids.each_slice(multi_fetch_size).with_index do |block, i|
|
25
37
|
multifetch_failed = download_block(block, i)
|
@@ -62,8 +74,8 @@ module Imap::Backup
|
|
62
74
|
end
|
63
75
|
if uids_and_bodies.nil?
|
64
76
|
if multi_fetch_size > 1
|
65
|
-
|
66
|
-
debug("Multi fetch failed for UIDs #{
|
77
|
+
uid_list = block.join(", ")
|
78
|
+
debug("Multi fetch failed for UIDs #{uid_list}, switching to single fetches")
|
67
79
|
return true
|
68
80
|
else
|
69
81
|
debug("Fetch failed for UID #{block[0]} - skipping")
|
@@ -96,8 +108,16 @@ module Imap::Backup
|
|
96
108
|
error(e)
|
97
109
|
end
|
98
110
|
|
111
|
+
def folder_uids
|
112
|
+
@folder_uids ||= folder.uids
|
113
|
+
end
|
114
|
+
|
115
|
+
def serializer_uids
|
116
|
+
@serializer_uids ||= serializer.uids
|
117
|
+
end
|
118
|
+
|
99
119
|
def uids
|
100
|
-
@uids ||=
|
120
|
+
@uids ||= folder_uids - serializer_uids
|
101
121
|
end
|
102
122
|
|
103
123
|
def debug(message)
|
data/lib/imap/backup/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: imap-backup
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 15.
|
4
|
+
version: 15.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: highline
|
@@ -219,7 +218,6 @@ licenses:
|
|
219
218
|
- MIT
|
220
219
|
metadata:
|
221
220
|
rubygems_mfa_required: 'true'
|
222
|
-
post_install_message:
|
223
221
|
rdoc_options: []
|
224
222
|
require_paths:
|
225
223
|
- lib
|
@@ -234,8 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
234
232
|
- !ruby/object:Gem::Version
|
235
233
|
version: '0'
|
236
234
|
requirements: []
|
237
|
-
rubygems_version: 3.
|
238
|
-
signing_key:
|
235
|
+
rubygems_version: 3.6.7
|
239
236
|
specification_version: 4
|
240
237
|
summary: Backup GMail (or other IMAP) accounts to disk
|
241
238
|
test_files: []
|