imap-backup 11.0.0 → 11.1.0.rc1
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/lib/email/mboxrd/message.rb +3 -1
- data/lib/imap/backup/account/backup.rb +7 -61
- data/lib/imap/backup/account/backup_folders.rb +2 -0
- data/lib/imap/backup/account/folder_backup.rb +85 -0
- data/lib/imap/backup/cli/backup.rb +6 -1
- data/lib/imap/backup/cli/local/check.rb +62 -0
- data/lib/imap/backup/cli/local.rb +5 -39
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +1 -10
- data/lib/imap/backup/serializer/imap.rb +1 -0
- data/lib/imap/backup/serializer/mbox.rb +5 -9
- data/lib/imap/backup/serializer.rb +0 -3
- data/lib/imap/backup/version.rb +2 -2
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fce713a5edcf2115c1f8636cfe7b6521aae0403a9bb79dfaf05b484363dbb7ad
|
4
|
+
data.tar.gz: 68544128dba3ade90077683c14d454596a514368cb40a46094934ec94220a606
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f5b467457df7840d8cf69cc4ace9758cfc2b38242acdb3f4c75a9dd0327c996995aa0986f649725c57e8b058ad0be4d386d9760720a20d505c3154e2a82aa59c
|
7
|
+
data.tar.gz: 3dd8a7f189e0d313e487b63fd64d7c57a647d384360e4128f2787cb7673f1db35a05ffb4853603e7bafbe17537a1b309a43a825180a910e45abc202630283eb9
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -1,10 +1,7 @@
|
|
1
|
+
require "imap/backup/account/backup_folders"
|
2
|
+
require "imap/backup/account/folder_backup"
|
1
3
|
require "imap/backup/account/folder_ensurer"
|
2
4
|
require "imap/backup/account/local_only_folder_deleter"
|
3
|
-
require "imap/backup/account/serialized_folders"
|
4
|
-
require "imap/backup/serializer/delayed_metadata_serializer"
|
5
|
-
require "imap/backup/downloader"
|
6
|
-
require "imap/backup/flag_refresher"
|
7
|
-
require "imap/backup/local_only_message_deleter"
|
8
5
|
|
9
6
|
module Imap; end
|
10
7
|
|
@@ -27,66 +24,15 @@ module Imap::Backup
|
|
27
24
|
|
28
25
|
Account::FolderEnsurer.new(account: account).run
|
29
26
|
Account::LocalOnlyFolderDeleter.new(account: account).run if account.mirror_mode
|
30
|
-
each_folder do |folder, serializer|
|
31
|
-
begin
|
32
|
-
next if !folder.exist?
|
33
|
-
rescue Encoding::UndefinedConversionError
|
34
|
-
message = "Skipping backup for '#{folder.name}' " \
|
35
|
-
"as it is not UTF-7 encoded correctly"
|
36
|
-
Logger.logger.info message
|
37
|
-
next
|
38
|
-
end
|
39
|
-
|
40
|
-
Logger.logger.debug "[#{folder.name}] running backup"
|
41
|
-
|
42
|
-
serializer.apply_uid_validity(folder.uid_validity)
|
43
|
-
|
44
|
-
download_serializer =
|
45
|
-
case account.download_strategy
|
46
|
-
when "direct"
|
47
|
-
serializer
|
48
|
-
when "delay_metadata"
|
49
|
-
Serializer::DelayedMetadataSerializer.new(serializer: serializer)
|
50
|
-
else
|
51
|
-
raise "Unknown download strategy '#{account.download_strategy}'"
|
52
|
-
end
|
53
|
-
|
54
|
-
downloader = Downloader.new(
|
55
|
-
folder,
|
56
|
-
download_serializer,
|
57
|
-
multi_fetch_size: account.multi_fetch_size,
|
58
|
-
reset_seen_flags_after_fetch: account.reset_seen_flags_after_fetch
|
59
|
-
)
|
60
|
-
# rubocop:disable Lint/RescueException
|
61
|
-
download_serializer.transaction do
|
62
|
-
downloader.run
|
63
|
-
rescue Exception => e
|
64
|
-
message = <<~ERROR
|
65
|
-
#{self.class} error #{e}
|
66
|
-
#{e.backtrace.join("\n")}
|
67
|
-
ERROR
|
68
|
-
Logger.logger.error message
|
69
|
-
download_serializer.rollback
|
70
|
-
raise e
|
71
|
-
end
|
72
|
-
# rubocop:enable Lint/RescueException
|
73
|
-
if account.mirror_mode
|
74
|
-
Logger.logger.info "Mirror mode - Deleting messages only present locally"
|
75
|
-
LocalOnlyMessageDeleter.new(folder, serializer).run
|
76
|
-
end
|
77
|
-
FlagRefresher.new(folder, serializer).run if account.mirror_mode || refresh
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
private
|
82
|
-
|
83
|
-
def each_folder
|
84
27
|
backup_folders = Account::BackupFolders.new(
|
85
28
|
client: account.client, account: account
|
86
29
|
)
|
30
|
+
if backup_folders.none?
|
31
|
+
Logger.logger.warn "Account #{account.username}: No folders found to backup"
|
32
|
+
return
|
33
|
+
end
|
87
34
|
backup_folders.each do |folder|
|
88
|
-
|
89
|
-
yield folder, serializer
|
35
|
+
Account::FolderBackup.new(account: account, folder: folder, refresh: refresh).run
|
90
36
|
end
|
91
37
|
end
|
92
38
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require "imap/backup/serializer/delayed_metadata_serializer"
|
2
|
+
require "imap/backup/downloader"
|
3
|
+
require "imap/backup/flag_refresher"
|
4
|
+
require "imap/backup/local_only_message_deleter"
|
5
|
+
|
6
|
+
module Imap; end
|
7
|
+
|
8
|
+
module Imap::Backup
|
9
|
+
class Account; end
|
10
|
+
|
11
|
+
class Account::FolderBackup
|
12
|
+
attr_reader :account
|
13
|
+
attr_reader :folder
|
14
|
+
attr_reader :refresh
|
15
|
+
|
16
|
+
def initialize(account:, folder:, refresh: false)
|
17
|
+
@account = account
|
18
|
+
@folder = folder
|
19
|
+
@refresh = refresh
|
20
|
+
end
|
21
|
+
|
22
|
+
def run
|
23
|
+
folder_ok = folder_ok?
|
24
|
+
return if !folder_ok
|
25
|
+
|
26
|
+
Logger.logger.debug "[#{folder.name}] running backup"
|
27
|
+
|
28
|
+
serializer.apply_uid_validity(folder.uid_validity)
|
29
|
+
|
30
|
+
download_serializer.transaction do
|
31
|
+
downloader.run
|
32
|
+
end
|
33
|
+
|
34
|
+
clean_up
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def folder_ok?
|
40
|
+
begin
|
41
|
+
return false if !folder.exist?
|
42
|
+
rescue Encoding::UndefinedConversionError
|
43
|
+
message = "Skipping backup for '#{folder.name}' " \
|
44
|
+
"as it is not UTF-7 encoded correctly"
|
45
|
+
Logger.logger.info message
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
|
49
|
+
true
|
50
|
+
end
|
51
|
+
|
52
|
+
def clean_up
|
53
|
+
if account.mirror_mode
|
54
|
+
Logger.logger.info "Mirror mode - Deleting messages only present locally"
|
55
|
+
LocalOnlyMessageDeleter.new(folder, serializer).run
|
56
|
+
end
|
57
|
+
FlagRefresher.new(folder, serializer).run if account.mirror_mode || refresh
|
58
|
+
end
|
59
|
+
|
60
|
+
def downloader
|
61
|
+
@downloader ||= Downloader.new(
|
62
|
+
folder,
|
63
|
+
download_serializer,
|
64
|
+
multi_fetch_size: account.multi_fetch_size,
|
65
|
+
reset_seen_flags_after_fetch: account.reset_seen_flags_after_fetch
|
66
|
+
)
|
67
|
+
end
|
68
|
+
|
69
|
+
def download_serializer
|
70
|
+
@download_serializer ||=
|
71
|
+
case account.download_strategy
|
72
|
+
when "direct"
|
73
|
+
serializer
|
74
|
+
when "delay_metadata"
|
75
|
+
Serializer::DelayedMetadataSerializer.new(serializer: serializer)
|
76
|
+
else
|
77
|
+
raise "Unknown download strategy '#{account.download_strategy}'"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def serializer
|
82
|
+
@serializer ||= Serializer.new(account.local_path, folder.name)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -19,7 +19,12 @@ module Imap::Backup
|
|
19
19
|
def run
|
20
20
|
config = load_config(**options)
|
21
21
|
exit_code = nil
|
22
|
-
requested_accounts(config)
|
22
|
+
accounts = requested_accounts(config)
|
23
|
+
if accounts.none?
|
24
|
+
Logger.logger.warn "No matching accounts found to backup"
|
25
|
+
return
|
26
|
+
end
|
27
|
+
accounts.each do |account|
|
23
28
|
backup = Account::Backup.new(account: account, refresh: refresh)
|
24
29
|
backup.run
|
25
30
|
rescue StandardError => e
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Imap; end
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class CLI; end
|
5
|
+
class CLI::Local < Thor; end
|
6
|
+
|
7
|
+
class CLI::Local::Check
|
8
|
+
include CLI::Helpers
|
9
|
+
|
10
|
+
attr_reader :options
|
11
|
+
|
12
|
+
def initialize(options)
|
13
|
+
@options = options
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
results = requested_accounts(config).map do |account|
|
18
|
+
serialized_folders = Account::SerializedFolders.new(account: account)
|
19
|
+
folder_results = serialized_folders.map do |serializer, _folder|
|
20
|
+
serializer.check_integrity!
|
21
|
+
{name: serializer.folder, result: "OK"}
|
22
|
+
rescue Serializer::FolderIntegrityError => e
|
23
|
+
message = e.to_s
|
24
|
+
if options[:delete_corrupt]
|
25
|
+
serializer.delete
|
26
|
+
message << " and has been deleted"
|
27
|
+
end
|
28
|
+
|
29
|
+
{
|
30
|
+
name: serializer.folder,
|
31
|
+
result: message
|
32
|
+
}
|
33
|
+
end
|
34
|
+
{account: account.username, folders: folder_results}
|
35
|
+
end
|
36
|
+
|
37
|
+
case options[:format]
|
38
|
+
when "json"
|
39
|
+
print_check_results_as_json(results)
|
40
|
+
else
|
41
|
+
print_check_results_as_text(results)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_check_results_as_json(results)
|
46
|
+
Kernel.puts results.to_json
|
47
|
+
end
|
48
|
+
|
49
|
+
def print_check_results_as_text(results)
|
50
|
+
results.each do |account_results|
|
51
|
+
Kernel.puts "Account: #{account_results[:account]}"
|
52
|
+
account_results[:folders].each do |folder_results|
|
53
|
+
Kernel.puts "\t#{folder_results[:name]}: #{folder_results[:result]}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def config
|
59
|
+
@config ||= load_config(**options)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require "imap/backup/account/serialized_folders"
|
2
|
+
require "imap/backup/cli/local/check"
|
3
|
+
|
1
4
|
module Imap; end
|
2
5
|
|
3
6
|
module Imap::Backup
|
@@ -38,32 +41,8 @@ module Imap::Backup
|
|
38
41
|
quiet_option
|
39
42
|
verbose_option
|
40
43
|
def check
|
41
|
-
|
42
|
-
|
43
|
-
folder_results = serialized_folders.map do |serializer, _folder|
|
44
|
-
serializer.check_integrity!
|
45
|
-
{name: serializer.folder, result: "OK"}
|
46
|
-
rescue Serializer::FolderIntegrityError => e
|
47
|
-
message = e.to_s
|
48
|
-
if options[:delete_corrupt]
|
49
|
-
serializer.delete
|
50
|
-
message << " and has been deleted"
|
51
|
-
end
|
52
|
-
|
53
|
-
{
|
54
|
-
name: serializer.folder,
|
55
|
-
result: message
|
56
|
-
}
|
57
|
-
end
|
58
|
-
{account: account.username, folders: folder_results}
|
59
|
-
end
|
60
|
-
|
61
|
-
case options[:format]
|
62
|
-
when "json"
|
63
|
-
print_check_results_as_json(results)
|
64
|
-
else
|
65
|
-
print_check_results_as_text(results)
|
66
|
-
end
|
44
|
+
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
45
|
+
Check.new(non_logging_options).run
|
67
46
|
end
|
68
47
|
|
69
48
|
desc "folders EMAIL", "List backed up folders"
|
@@ -138,19 +117,6 @@ module Imap::Backup
|
|
138
117
|
end
|
139
118
|
|
140
119
|
no_commands do
|
141
|
-
def print_check_results_as_json(results)
|
142
|
-
Kernel.puts results.to_json
|
143
|
-
end
|
144
|
-
|
145
|
-
def print_check_results_as_text(results)
|
146
|
-
results.each do |account_results|
|
147
|
-
Kernel.puts "Account: #{account_results[:account]}"
|
148
|
-
account_results[:folders].each do |folder_results|
|
149
|
-
Kernel.puts "\t#{folder_results[:name]}: #{folder_results[:result]}"
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
120
|
def list_emails_as_json(serializer)
|
155
121
|
emails = serializer.each_message.map do |message|
|
156
122
|
{
|
@@ -20,23 +20,13 @@ module Imap::Backup
|
|
20
20
|
def transaction(&block)
|
21
21
|
tsx.fail_in_transaction!(:transaction, message: "nested transactions are not supported")
|
22
22
|
|
23
|
-
# rubocop:disable Lint/RescueException
|
24
23
|
tsx.begin({metadata: []}) do
|
25
24
|
mbox.transaction do
|
26
25
|
block.call
|
27
26
|
|
28
27
|
commit
|
29
|
-
rescue Exception => e
|
30
|
-
message = <<~ERROR
|
31
|
-
#{self.class} error #{e}
|
32
|
-
#{e.backtrace.join("\n")}
|
33
|
-
ERROR
|
34
|
-
Logger.logger.error message
|
35
|
-
mbox.rollback
|
36
|
-
raise e
|
37
28
|
end
|
38
29
|
end
|
39
|
-
# rubocop:enable Lint/RescueException
|
40
30
|
end
|
41
31
|
|
42
32
|
def append(uid, message, flags)
|
@@ -56,6 +46,7 @@ module Imap::Backup
|
|
56
46
|
imap.append m[:uid], m[:length], flags: m[:flags]
|
57
47
|
end
|
58
48
|
rescue Exception => e
|
49
|
+
Logger.logger.error "#{self.class} handling #{e.class}"
|
59
50
|
imap.rollback
|
60
51
|
raise e
|
61
52
|
end
|
@@ -15,26 +15,22 @@ module Imap::Backup
|
|
15
15
|
def transaction(&block)
|
16
16
|
tsx.fail_in_transaction!(:transaction, message: "nested transactions are not supported")
|
17
17
|
|
18
|
-
# rubocop:disable Lint/RescueException
|
19
18
|
tsx.begin({savepoint: {length: length}}) do
|
20
19
|
block.call
|
21
|
-
rescue
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
Logger.logger.error message
|
20
|
+
rescue StandardError => e
|
21
|
+
rollback
|
22
|
+
raise e
|
23
|
+
rescue SignalException => e
|
24
|
+
Logger.logger.error "#{self.class} handling #{e.class}"
|
27
25
|
rollback
|
28
26
|
raise e
|
29
27
|
end
|
30
|
-
# rubocop:enable Lint/RescueException
|
31
28
|
end
|
32
29
|
|
33
30
|
def rollback
|
34
31
|
tsx.fail_outside_transaction!(:rollback)
|
35
32
|
|
36
33
|
rewind(tsx.data[:savepoint][:length])
|
37
|
-
tsx.clear
|
38
34
|
end
|
39
35
|
|
40
36
|
def valid?
|
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: 11.0.
|
4
|
+
version: 11.1.0.rc1
|
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-
|
11
|
+
date: 2023-08-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -236,6 +236,7 @@ files:
|
|
236
236
|
- lib/imap/backup/account/backup_folders.rb
|
237
237
|
- lib/imap/backup/account/client_factory.rb
|
238
238
|
- lib/imap/backup/account/folder.rb
|
239
|
+
- lib/imap/backup/account/folder_backup.rb
|
239
240
|
- lib/imap/backup/account/folder_ensurer.rb
|
240
241
|
- lib/imap/backup/account/local_only_folder_deleter.rb
|
241
242
|
- lib/imap/backup/account/restore.rb
|
@@ -245,6 +246,7 @@ files:
|
|
245
246
|
- lib/imap/backup/cli/folder_enumerator.rb
|
246
247
|
- lib/imap/backup/cli/helpers.rb
|
247
248
|
- lib/imap/backup/cli/local.rb
|
249
|
+
- lib/imap/backup/cli/local/check.rb
|
248
250
|
- lib/imap/backup/cli/migrate.rb
|
249
251
|
- lib/imap/backup/cli/mirror.rb
|
250
252
|
- lib/imap/backup/cli/remote.rb
|
@@ -310,9 +312,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
310
312
|
version: '2.6'
|
311
313
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
312
314
|
requirements:
|
313
|
-
- - "
|
315
|
+
- - ">"
|
314
316
|
- !ruby/object:Gem::Version
|
315
|
-
version:
|
317
|
+
version: 1.3.1
|
316
318
|
requirements: []
|
317
319
|
rubygems_version: 3.3.7
|
318
320
|
signing_key:
|