imap-backup 14.4.4 → 14.5.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/README.md +67 -137
- data/docs/documentation.md +19 -0
- data/imap-backup.gemspec +1 -1
- data/lib/imap/backup/account/backup.rb +2 -0
- data/lib/imap/backup/account/backup_folders.rb +7 -1
- data/lib/imap/backup/account/client_factory.rb +1 -0
- data/lib/imap/backup/account/folder.rb +26 -0
- data/lib/imap/backup/account/folder_backup.rb +4 -1
- data/lib/imap/backup/account/folder_ensurer.rb +3 -0
- data/lib/imap/backup/account/local_only_folder_deleter.rb +3 -1
- data/lib/imap/backup/account/restore.rb +2 -0
- data/lib/imap/backup/account/serialized_folders.rb +31 -1
- data/lib/imap/backup/account.rb +34 -18
- data/lib/imap/backup/cli/backup.rb +3 -0
- data/lib/imap/backup/cli/folder_enumerator.rb +6 -0
- data/lib/imap/backup/cli/helpers.rb +13 -0
- data/lib/imap/backup/cli/local/check.rb +3 -0
- data/lib/imap/backup/cli/local.rb +14 -1
- data/lib/imap/backup/cli/remote.rb +7 -0
- data/lib/imap/backup/cli/restore.rb +4 -0
- data/lib/imap/backup/cli/setup.rb +3 -0
- data/lib/imap/backup/cli/single/backup.rb +3 -0
- data/lib/imap/backup/cli/single.rb +4 -0
- data/lib/imap/backup/cli/stats.rb +3 -0
- data/lib/imap/backup/cli/transfer.rb +8 -0
- data/lib/imap/backup/cli/utils.rb +7 -1
- data/lib/imap/backup/cli.rb +8 -0
- data/lib/imap/backup/client/apple_mail.rb +2 -0
- data/lib/imap/backup/client/automatic_login_wrapper.rb +9 -1
- data/lib/imap/backup/client/default.rb +15 -4
- data/lib/imap/backup/configuration.rb +13 -0
- data/lib/imap/backup/configuration_not_found.rb +1 -0
- data/lib/imap/backup/downloader.rb +4 -0
- data/lib/imap/backup/email/mboxrd/message.rb +14 -0
- data/lib/imap/backup/email/provider/apple_mail.rb +2 -0
- data/lib/imap/backup/email/provider/base.rb +3 -3
- data/lib/imap/backup/email/provider/fastmail.rb +2 -0
- data/lib/imap/backup/email/provider/gmail.rb +2 -0
- data/lib/imap/backup/email/provider/purelymail.rb +2 -0
- data/lib/imap/backup/email/provider/unknown.rb +2 -6
- data/lib/imap/backup/email/provider.rb +5 -0
- data/lib/imap/backup/file_mode.rb +2 -0
- data/lib/imap/backup/flag_refresher.rb +4 -0
- data/lib/imap/backup/local_only_message_deleter.rb +2 -0
- data/lib/imap/backup/logger.rb +18 -0
- data/lib/imap/backup/migrator.rb +3 -0
- data/lib/imap/backup/mirror/map.rb +21 -0
- data/lib/imap/backup/mirror.rb +8 -0
- data/lib/imap/backup/naming.rb +10 -1
- data/lib/imap/backup/retry_on_error.rb +9 -0
- data/lib/imap/backup/serializer/appender.rb +9 -0
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +4 -0
- data/lib/imap/backup/serializer/folder_maker.rb +1 -0
- data/lib/imap/backup/serializer/imap.rb +38 -2
- data/lib/imap/backup/serializer/integrity_checker.rb +1 -0
- data/lib/imap/backup/serializer/mbox.rb +26 -0
- data/lib/imap/backup/serializer/message.rb +13 -0
- data/lib/imap/backup/serializer/message_enumerator.rb +10 -2
- data/lib/imap/backup/serializer/permission_checker.rb +6 -0
- data/lib/imap/backup/serializer/transaction.rb +18 -0
- data/lib/imap/backup/serializer/unused_name_finder.rb +4 -0
- data/lib/imap/backup/serializer/version2_migrator.rb +4 -0
- data/lib/imap/backup/serializer.rb +56 -2
- data/lib/imap/backup/setup/account/header.rb +6 -0
- data/lib/imap/backup/setup/account.rb +6 -0
- data/lib/imap/backup/setup/asker.rb +16 -0
- data/lib/imap/backup/setup/backup_path.rb +6 -0
- data/lib/imap/backup/setup/connection_tester.rb +5 -0
- data/lib/imap/backup/setup/email_changer.rb +6 -0
- data/lib/imap/backup/setup/folder_chooser.rb +4 -0
- data/lib/imap/backup/setup/global_options/download_strategy_chooser.rb +4 -0
- data/lib/imap/backup/setup/global_options.rb +4 -0
- data/lib/imap/backup/setup/helpers.rb +3 -0
- data/lib/imap/backup/setup.rb +5 -0
- data/lib/imap/backup/text/sanitizer.rb +9 -0
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +8 -1
- data/lib/imap/backup/uploader.rb +5 -0
- data/lib/imap/backup/version.rb +7 -2
- metadata +11 -13
- data/docs/api.md +0 -20
- data/docs/development.md +0 -110
- data/docs/migrate-server-keep-address.md +0 -47
data/lib/imap/backup/account.rb
CHANGED
@@ -14,10 +14,9 @@ module Imap::Backup
|
|
14
14
|
# The username of the account (usually the same as the email address)
|
15
15
|
# @return [String]
|
16
16
|
attr_reader :username
|
17
|
-
#
|
17
|
+
# @return [String] password of the Account
|
18
18
|
attr_reader :password
|
19
|
-
#
|
20
|
-
# @return [String]
|
19
|
+
# @return [String] the path where backups will be saved
|
21
20
|
attr_reader :local_path
|
22
21
|
# @overload folders
|
23
22
|
# The list of folders that have been configured for the Account
|
@@ -38,13 +37,11 @@ module Imap::Backup
|
|
38
37
|
attr_reader :folder_blacklist
|
39
38
|
# Should all emails be backed up progressively, or should emails
|
40
39
|
# which are deleted from the server be deleted locally?
|
40
|
+
# @return [Boolean]
|
41
41
|
attr_reader :mirror_mode
|
42
42
|
# The address of the IMAP server
|
43
43
|
# @return [String]
|
44
44
|
attr_reader :server
|
45
|
-
# Extra options to be passed to the IMAP server when connecting
|
46
|
-
# @return [Hash, void]
|
47
|
-
attr_reader :connection_options
|
48
45
|
# The name of the download strategy to adopt during backups
|
49
46
|
# @return [String]
|
50
47
|
attr_accessor :download_strategy
|
@@ -61,18 +58,17 @@ module Imap::Backup
|
|
61
58
|
# mark messages as '\Seen' when accessed).
|
62
59
|
# @return [Boolean]
|
63
60
|
attr_reader :reset_seen_flags_after_fetch
|
64
|
-
# Tracks changes to the Account's attributes
|
65
|
-
attr_reader :changes
|
66
61
|
|
67
62
|
def initialize(options)
|
68
63
|
@username = options[:username]
|
69
64
|
@password = options[:password]
|
70
65
|
@local_path = options[:local_path]
|
71
66
|
@folders = options[:folders]
|
72
|
-
@folder_blacklist = options[:folder_blacklist]
|
73
|
-
@mirror_mode = options[:mirror_mode]
|
67
|
+
@folder_blacklist = options[:folder_blacklist] || false
|
68
|
+
@mirror_mode = options[:mirror_mode] || false
|
74
69
|
@server = options[:server]
|
75
|
-
@connection_options =
|
70
|
+
@connection_options = nil
|
71
|
+
@supplied_connection_options = options[:connection_options]
|
76
72
|
@download_strategy = options[:download_strategy]
|
77
73
|
@multi_fetch_size_orignal = options[:multi_fetch_size]
|
78
74
|
@reset_seen_flags_after_fetch = options[:reset_seen_flags_after_fetch]
|
@@ -123,6 +119,7 @@ module Imap::Backup
|
|
123
119
|
end
|
124
120
|
|
125
121
|
# Resets the store of changes, indicating that the current state is the saved state
|
122
|
+
# @return [void]
|
126
123
|
def clear_changes
|
127
124
|
@changes = {}
|
128
125
|
end
|
@@ -134,16 +131,12 @@ module Imap::Backup
|
|
134
131
|
@marked_for_deletion = true
|
135
132
|
end
|
136
133
|
|
137
|
-
#
|
138
|
-
#
|
139
|
-
# @return [Boolean]
|
134
|
+
# @return [Boolean] whether the account has been flagged for deletion during setup
|
140
135
|
def marked_for_deletion?
|
141
136
|
@marked_for_deletion
|
142
137
|
end
|
143
138
|
|
144
|
-
#
|
145
|
-
#
|
146
|
-
# @return [Hash]
|
139
|
+
# @return [Hash] all Account data for serialization
|
147
140
|
def to_h
|
148
141
|
h = {username: @username, password: @password}
|
149
142
|
h[:local_path] = @local_path if @local_path
|
@@ -151,7 +144,7 @@ module Imap::Backup
|
|
151
144
|
h[:folder_blacklist] = true if @folder_blacklist
|
152
145
|
h[:mirror_mode] = true if @mirror_mode
|
153
146
|
h[:server] = @server if @server
|
154
|
-
h[:connection_options] = @connection_options if
|
147
|
+
h[:connection_options] = @connection_options if connection_options
|
155
148
|
h[:multi_fetch_size] = multi_fetch_size
|
156
149
|
if @reset_seen_flags_after_fetch
|
157
150
|
h[:reset_seen_flags_after_fetch] = @reset_seen_flags_after_fetch
|
@@ -180,25 +173,45 @@ module Imap::Backup
|
|
180
173
|
update(:local_path, value)
|
181
174
|
end
|
182
175
|
|
176
|
+
# @raise [RuntimeError] if the supplied value is not an Array
|
177
|
+
# @return [void]
|
183
178
|
def folders=(value)
|
184
179
|
raise "folders must be an Array" if !value.is_a?(Array)
|
185
180
|
|
186
181
|
update(:folders, value)
|
187
182
|
end
|
188
183
|
|
184
|
+
# @return [void]
|
189
185
|
def folder_blacklist=(value)
|
190
186
|
update(:folder_blacklist, value)
|
191
187
|
end
|
192
188
|
|
189
|
+
# @return [void]
|
193
190
|
def mirror_mode=(value)
|
194
191
|
update(:mirror_mode, value)
|
195
192
|
end
|
196
193
|
|
194
|
+
# @return [void]
|
197
195
|
def server=(value)
|
198
196
|
update(:server, value)
|
199
197
|
end
|
200
198
|
|
199
|
+
# Extra options to be passed to the IMAP server when connecting
|
200
|
+
# @return [Hash, void]
|
201
|
+
def connection_options
|
202
|
+
@connection_options ||=
|
203
|
+
case @supplied_connection_options
|
204
|
+
when String
|
205
|
+
JSON.parse(@supplied_connection_options, symbolize_names: true)
|
206
|
+
else
|
207
|
+
@supplied_connection_options
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# @return [void]
|
201
212
|
def connection_options=(value)
|
213
|
+
# Ensure we've loaded the connection_options
|
214
|
+
connection_options
|
202
215
|
parsed =
|
203
216
|
if value == ""
|
204
217
|
nil
|
@@ -232,12 +245,15 @@ module Imap::Backup
|
|
232
245
|
update(:multi_fetch_size, parsed)
|
233
246
|
end
|
234
247
|
|
248
|
+
# @return [void]
|
235
249
|
def reset_seen_flags_after_fetch=(value)
|
236
250
|
update(:reset_seen_flags_after_fetch, value)
|
237
251
|
end
|
238
252
|
|
239
253
|
private
|
240
254
|
|
255
|
+
attr_reader :changes
|
256
|
+
|
241
257
|
def update(field, value)
|
242
258
|
key = :"@#{field}"
|
243
259
|
if changes[field]
|
@@ -10,6 +10,7 @@ module Imap; end
|
|
10
10
|
module Imap::Backup
|
11
11
|
class CLI < Thor; end
|
12
12
|
|
13
|
+
# Runs backups of configured accounts
|
13
14
|
class CLI::Backup < Thor
|
14
15
|
include Thor::Actions
|
15
16
|
include CLI::Helpers
|
@@ -19,6 +20,8 @@ module Imap::Backup
|
|
19
20
|
@options = options
|
20
21
|
end
|
21
22
|
|
23
|
+
# @!method run
|
24
|
+
# @return [void]
|
22
25
|
no_commands do
|
23
26
|
def run
|
24
27
|
config = load_config(**options)
|
@@ -9,6 +9,7 @@ module Imap; end
|
|
9
9
|
module Imap::Backup
|
10
10
|
class CLI < Thor; end
|
11
11
|
|
12
|
+
# Implements a folder enumerator for backed-up accounts
|
12
13
|
class CLI::FolderEnumerator
|
13
14
|
def initialize(
|
14
15
|
destination:,
|
@@ -26,6 +27,11 @@ module Imap::Backup
|
|
26
27
|
@source_prefix = source_prefix
|
27
28
|
end
|
28
29
|
|
30
|
+
# Enumerates backed-up folders
|
31
|
+
# When called without a block, returns an Enumerator
|
32
|
+
# @yieldparam serializer [Serializer] the folder's serializer
|
33
|
+
# @yieldparam folder [Account::Folder] the online folder
|
34
|
+
# @return [Enumerator, void]
|
29
35
|
def each
|
30
36
|
return enum_for(:each) if !block_given?
|
31
37
|
|
@@ -8,6 +8,7 @@ module Imap; end
|
|
8
8
|
module Imap::Backup
|
9
9
|
class CLI < Thor; end
|
10
10
|
|
11
|
+
# Provides helper methods for CLI classes
|
11
12
|
module CLI::Helpers
|
12
13
|
def self.included(base)
|
13
14
|
base.class_eval do
|
@@ -73,6 +74,10 @@ module Imap::Backup
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
77
|
+
# Processes command-line parameters
|
78
|
+
# @return [Hash] the supplied command-line parameters with
|
79
|
+
# with hyphens in keys replaced by underscores
|
80
|
+
# and the keys converted to Symbols
|
76
81
|
def options
|
77
82
|
@symbolized_options ||= # rubocop:disable Naming/MemoizedInstanceVariableName
|
78
83
|
begin
|
@@ -89,6 +94,9 @@ module Imap::Backup
|
|
89
94
|
end
|
90
95
|
end
|
91
96
|
|
97
|
+
# Loads the application configuration
|
98
|
+
# @raise [ConfigurationNotFound] if the configuration file does not exist
|
99
|
+
# @return [Configuration]
|
92
100
|
def load_config(**options)
|
93
101
|
path = options[:config]
|
94
102
|
require_exists = options.key?(:require_exists) ? options[:require_exists] : true
|
@@ -102,6 +110,8 @@ module Imap::Backup
|
|
102
110
|
Configuration.new(path: path)
|
103
111
|
end
|
104
112
|
|
113
|
+
# @raise [RuntimeError] if the account does not exist
|
114
|
+
# @return [Account] the Account information for the email address
|
105
115
|
def account(config, email)
|
106
116
|
account = config.accounts.find { |a| a.username == email }
|
107
117
|
raise "#{email} is not a configured account" if !account
|
@@ -109,6 +119,9 @@ module Imap::Backup
|
|
109
119
|
account
|
110
120
|
end
|
111
121
|
|
122
|
+
# @return [Array<Account>] If email addresses have been specified
|
123
|
+
# returns the Account configurations for them.
|
124
|
+
# If non have been specified, returns all account configurations
|
112
125
|
def requested_accounts(config)
|
113
126
|
emails = (options[:accounts] || "").split(",")
|
114
127
|
if emails.any?
|
@@ -10,6 +10,7 @@ module Imap::Backup
|
|
10
10
|
class CLI; end
|
11
11
|
class CLI::Local < Thor; end
|
12
12
|
|
13
|
+
# Runs integrity check on local backups
|
13
14
|
class CLI::Local::Check
|
14
15
|
include CLI::Helpers
|
15
16
|
|
@@ -17,6 +18,8 @@ module Imap::Backup
|
|
17
18
|
@options = options
|
18
19
|
end
|
19
20
|
|
21
|
+
# Runs the check
|
22
|
+
# @return [void]
|
20
23
|
def run
|
21
24
|
results = requested_accounts(config).map do |account|
|
22
25
|
serialized_folders = Account::SerializedFolders.new(account: account)
|
@@ -10,6 +10,7 @@ module Imap; end
|
|
10
10
|
module Imap::Backup
|
11
11
|
class CLI < Thor; end
|
12
12
|
|
13
|
+
# Implements the CLI functions relating to local storage
|
13
14
|
class CLI::Local < Thor
|
14
15
|
include Thor::Actions
|
15
16
|
include CLI::Helpers
|
@@ -19,6 +20,8 @@ module Imap::Backup
|
|
19
20
|
format_option
|
20
21
|
quiet_option
|
21
22
|
verbose_option
|
23
|
+
# Lists configured accounts
|
24
|
+
# @return [void]
|
22
25
|
def accounts
|
23
26
|
names = config.accounts.map(&:username)
|
24
27
|
case options[:format]
|
@@ -44,6 +47,8 @@ module Imap::Backup
|
|
44
47
|
format_option
|
45
48
|
quiet_option
|
46
49
|
verbose_option
|
50
|
+
# Runs integrity checks on backups
|
51
|
+
# @return [void]
|
47
52
|
def check
|
48
53
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
49
54
|
Check.new(non_logging_options).run
|
@@ -54,6 +59,8 @@ module Imap::Backup
|
|
54
59
|
format_option
|
55
60
|
quiet_option
|
56
61
|
verbose_option
|
62
|
+
# Lists backed-up folders for an account
|
63
|
+
# @return [void]
|
57
64
|
def folders(email)
|
58
65
|
account = account(config, email)
|
59
66
|
|
@@ -63,7 +70,7 @@ module Imap::Backup
|
|
63
70
|
list = serialized_folders.map { |_s, f| {name: f.name} }
|
64
71
|
Kernel.puts list.to_json
|
65
72
|
else
|
66
|
-
serialized_folders.
|
73
|
+
serialized_folders.each_value do |f|
|
67
74
|
Kernel.puts %("#{f.name}")
|
68
75
|
end
|
69
76
|
end
|
@@ -74,6 +81,9 @@ module Imap::Backup
|
|
74
81
|
format_option
|
75
82
|
quiet_option
|
76
83
|
verbose_option
|
84
|
+
# Lists backed-up emails for an account folder
|
85
|
+
# @raise [RuntimeError] if the folder does not exist
|
86
|
+
# @return [void]
|
77
87
|
def list(email, folder_name)
|
78
88
|
account = account(config, email)
|
79
89
|
|
@@ -101,6 +111,9 @@ module Imap::Backup
|
|
101
111
|
format_option
|
102
112
|
quiet_option
|
103
113
|
verbose_option
|
114
|
+
# Shows the content of one or more backed-up email messages
|
115
|
+
# @raise [RuntimeError] if the folder does not exist
|
116
|
+
# @return [void]
|
104
117
|
def show(email, folder_name, uids)
|
105
118
|
account = account(config, email)
|
106
119
|
|
@@ -8,6 +8,7 @@ module Imap; end
|
|
8
8
|
module Imap::Backup
|
9
9
|
class CLI < Thor; end
|
10
10
|
|
11
|
+
# Implements the CLI functions relating to configured online accounts
|
11
12
|
class CLI::Remote < Thor
|
12
13
|
include Thor::Actions
|
13
14
|
include CLI::Helpers
|
@@ -17,6 +18,8 @@ module Imap::Backup
|
|
17
18
|
format_option
|
18
19
|
quiet_option
|
19
20
|
verbose_option
|
21
|
+
# Prints an account's folders
|
22
|
+
# @return [void]
|
20
23
|
def folders(email)
|
21
24
|
Imap::Backup::Logger.setup_logging options
|
22
25
|
folder_names = folder_names(email)
|
@@ -36,6 +39,8 @@ module Imap::Backup
|
|
36
39
|
format_option
|
37
40
|
quiet_option
|
38
41
|
verbose_option
|
42
|
+
# Prints an account's IMAP capabilities
|
43
|
+
# @return [void]
|
39
44
|
def capabilities(email)
|
40
45
|
Imap::Backup::Logger.setup_logging options
|
41
46
|
config = load_config(**options)
|
@@ -55,6 +60,8 @@ module Imap::Backup
|
|
55
60
|
format_option
|
56
61
|
quiet_option
|
57
62
|
verbose_option
|
63
|
+
# Prints an account's IMAP namespaces
|
64
|
+
# @return [void]
|
58
65
|
def namespaces(email)
|
59
66
|
Imap::Backup::Logger.setup_logging options
|
60
67
|
config = load_config(**options)
|
@@ -8,6 +8,7 @@ module Imap; end
|
|
8
8
|
module Imap::Backup
|
9
9
|
class CLI < Thor; end
|
10
10
|
|
11
|
+
# Restores backups for one or more accounts
|
11
12
|
class CLI::Restore < Thor
|
12
13
|
include Thor::Actions
|
13
14
|
include CLI::Helpers
|
@@ -18,6 +19,9 @@ module Imap::Backup
|
|
18
19
|
@options = options
|
19
20
|
end
|
20
21
|
|
22
|
+
# @!method run
|
23
|
+
# @raise [RuntimeError] if no email is specified
|
24
|
+
# @return [void]
|
21
25
|
no_commands do
|
22
26
|
def run
|
23
27
|
config = load_config(**options)
|
@@ -8,6 +8,7 @@ module Imap; end
|
|
8
8
|
module Imap::Backup
|
9
9
|
class CLI < Thor; end
|
10
10
|
|
11
|
+
# Runs the menu-driven setup program
|
11
12
|
class CLI::Setup < Thor
|
12
13
|
include Thor::Actions
|
13
14
|
include CLI::Helpers
|
@@ -17,6 +18,8 @@ module Imap::Backup
|
|
17
18
|
@options = options
|
18
19
|
end
|
19
20
|
|
21
|
+
# @!method run
|
22
|
+
# @return [void]
|
20
23
|
no_commands do
|
21
24
|
def run
|
22
25
|
config = load_config(**options, require_exists: false)
|
@@ -10,12 +10,15 @@ module Imap::Backup
|
|
10
10
|
class CLI < Thor; end
|
11
11
|
class CLI::Single < Thor; end
|
12
12
|
|
13
|
+
# Runs a backup without relying on existing configuration
|
13
14
|
class CLI::Single::Backup
|
14
15
|
def initialize(options)
|
15
16
|
@options = options
|
16
17
|
@password = nil
|
17
18
|
end
|
18
19
|
|
20
|
+
# Runs the backup
|
21
|
+
# @return [void]
|
19
22
|
def run
|
20
23
|
process_options!
|
21
24
|
account = Account.new(
|
@@ -9,6 +9,8 @@ module Imap; end
|
|
9
9
|
module Imap::Backup
|
10
10
|
class CLI < Thor; end
|
11
11
|
|
12
|
+
# Processes parameters to run a backup via command-line parameters
|
13
|
+
# (without using a configuration file)
|
12
14
|
class CLI::Single < Thor
|
13
15
|
include CLI::Helpers
|
14
16
|
|
@@ -170,6 +172,8 @@ module Imap::Backup
|
|
170
172
|
)
|
171
173
|
quiet_option
|
172
174
|
verbose_option
|
175
|
+
# Launches the backup procedure
|
176
|
+
# @return [void]
|
173
177
|
def backup
|
174
178
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
175
179
|
direct = Backup.new(non_logging_options)
|
@@ -4,6 +4,7 @@ require "imap/backup/serializer"
|
|
4
4
|
module Imap; end
|
5
5
|
|
6
6
|
module Imap::Backup
|
7
|
+
# Prints various statistics about an account and its backup
|
7
8
|
class CLI::Stats < Thor
|
8
9
|
include Thor::Actions
|
9
10
|
include CLI::Helpers
|
@@ -14,6 +15,8 @@ module Imap::Backup
|
|
14
15
|
@options = options
|
15
16
|
end
|
16
17
|
|
18
|
+
# @!method run
|
19
|
+
# @return [void]
|
17
20
|
no_commands do
|
18
21
|
def run
|
19
22
|
case options[:format]
|
@@ -8,10 +8,12 @@ require "imap/backup/mirror"
|
|
8
8
|
module Imap; end
|
9
9
|
|
10
10
|
module Imap::Backup
|
11
|
+
# Implements migration and mirroring
|
11
12
|
class CLI::Transfer < Thor
|
12
13
|
include Thor::Actions
|
13
14
|
include CLI::Helpers
|
14
15
|
|
16
|
+
# The possible vaues for the action parameter
|
15
17
|
ACTIONS = %i(migrate mirror).freeze
|
16
18
|
|
17
19
|
def initialize(action, source_email, destination_email, options)
|
@@ -29,6 +31,12 @@ module Imap::Backup
|
|
29
31
|
@source_prefix = nil
|
30
32
|
end
|
31
33
|
|
34
|
+
# @!method run
|
35
|
+
# @raise [RuntimeError] if the indicated action is unknown,
|
36
|
+
# or the source and destination accounts are the same,
|
37
|
+
# or either of the accounts is not configured,
|
38
|
+
# or incompatible namespace/delimter parameters have been supplied
|
39
|
+
# @return [void]
|
32
40
|
no_commands do
|
33
41
|
def run
|
34
42
|
raise "Unknown action '#{action}'" if !ACTIONS.include?(action)
|
@@ -13,6 +13,7 @@ module Imap; end
|
|
13
13
|
module Imap::Backup
|
14
14
|
class CLI < Thor; end
|
15
15
|
|
16
|
+
# Implements the CLI utility functions
|
16
17
|
class CLI::Utils < Thor
|
17
18
|
include Thor::Actions
|
18
19
|
include CLI::Helpers
|
@@ -21,6 +22,9 @@ module Imap::Backup
|
|
21
22
|
config_option
|
22
23
|
quiet_option
|
23
24
|
verbose_option
|
25
|
+
# Creates fake downloaded emails so that only the account's future emails
|
26
|
+
# will really get backed up
|
27
|
+
# @return [void]
|
24
28
|
def ignore_history(email)
|
25
29
|
Logger.setup_logging options
|
26
30
|
config = load_config(**options)
|
@@ -59,6 +63,8 @@ module Imap::Backup
|
|
59
63
|
banner: "the name of the Thunderbird profile to copy emails to",
|
60
64
|
aliases: ["-p"]
|
61
65
|
)
|
66
|
+
# Exports the account's emails to Thunderbird
|
67
|
+
# @return [void]
|
62
68
|
def export_to_thunderbird(email)
|
63
69
|
Imap::Backup::Logger.setup_logging options
|
64
70
|
force = options.key?(:force) ? options[:force] : false
|
@@ -78,7 +84,7 @@ module Imap::Backup
|
|
78
84
|
|
79
85
|
raise "No serialized folders were found for account '#{email}'" if serialized_folders.none?
|
80
86
|
|
81
|
-
serialized_folders.
|
87
|
+
serialized_folders.each_key do |serializer|
|
82
88
|
Thunderbird::MailboxExporter.new(
|
83
89
|
email, serializer, profile, force: force
|
84
90
|
).run
|
data/lib/imap/backup/cli.rb
CHANGED
@@ -48,6 +48,7 @@ module Imap::Backup
|
|
48
48
|
|
49
49
|
# Overrides {https://www.rubydoc.info/gems/thor/Thor%2FBase%2FClassMethods:start Thor's method}
|
50
50
|
# to handle '--version' and rearrange parameters if 'help' is passed
|
51
|
+
# @return [void]
|
51
52
|
def self.start(args)
|
52
53
|
if args.include?("--version")
|
53
54
|
new.version
|
@@ -87,6 +88,7 @@ module Imap::Backup
|
|
87
88
|
refresh_option
|
88
89
|
verbose_option
|
89
90
|
# Runs account backups
|
91
|
+
# @return [void]
|
90
92
|
def backup
|
91
93
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
92
94
|
Backup.new(non_logging_options).run
|
@@ -148,6 +150,7 @@ module Imap::Backup
|
|
148
150
|
aliases: ["-s"]
|
149
151
|
)
|
150
152
|
# Migrates emails from one account to another
|
153
|
+
# @return [void]
|
151
154
|
def migrate(source_email, destination_email)
|
152
155
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
153
156
|
Transfer.new(:migrate, source_email, destination_email, non_logging_options).run
|
@@ -208,6 +211,7 @@ module Imap::Backup
|
|
208
211
|
aliases: ["-s"]
|
209
212
|
)
|
210
213
|
# Keeps one email account in line with another
|
214
|
+
# @return [void]
|
211
215
|
def mirror(source_email, destination_email)
|
212
216
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
213
217
|
Transfer.new(:mirror, source_email, destination_email, non_logging_options).run
|
@@ -226,6 +230,7 @@ module Imap::Backup
|
|
226
230
|
quiet_option
|
227
231
|
verbose_option
|
228
232
|
# Restores backed up meails to an account
|
233
|
+
# @return [void]
|
229
234
|
def restore(email = nil)
|
230
235
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
231
236
|
Restore.new(email, non_logging_options).run
|
@@ -240,6 +245,7 @@ module Imap::Backup
|
|
240
245
|
quiet_option
|
241
246
|
verbose_option
|
242
247
|
# Runs the menu-driven setup program
|
248
|
+
# @return [void]
|
243
249
|
def setup
|
244
250
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
245
251
|
CLI::Setup.new(non_logging_options).run
|
@@ -264,6 +270,7 @@ module Imap::Backup
|
|
264
270
|
quiet_option
|
265
271
|
verbose_option
|
266
272
|
# Prints various statistics about a configured account
|
273
|
+
# @return [void]
|
267
274
|
def stats(email)
|
268
275
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
269
276
|
Stats.new(email, non_logging_options).run
|
@@ -274,6 +281,7 @@ module Imap::Backup
|
|
274
281
|
|
275
282
|
desc "version", "Print the imap-backup version"
|
276
283
|
# Prints the program version
|
284
|
+
# @return [void]
|
277
285
|
def version
|
278
286
|
Kernel.puts "imap-backup #{Imap::Backup::VERSION}"
|
279
287
|
end
|
@@ -3,9 +3,11 @@ require "imap/backup/client/default"
|
|
3
3
|
module Imap; end
|
4
4
|
|
5
5
|
module Imap::Backup
|
6
|
+
# Overrides default IMAP client behaviour for Apple Mail accounts
|
6
7
|
class Client::AppleMail < Client::Default
|
7
8
|
# With Apple Mails's IMAP, passing "/" to list
|
8
9
|
# results in an empty list
|
10
|
+
# @return [String] the value to use when requesting the list of account folders
|
9
11
|
def provider_root
|
10
12
|
""
|
11
13
|
end
|
@@ -5,19 +5,24 @@ module Imap; end
|
|
5
5
|
module Imap::Backup
|
6
6
|
module Client; end
|
7
7
|
|
8
|
+
# Transparently wraps a client instance, while delaying login until it becomes necessary
|
8
9
|
class Client::AutomaticLoginWrapper
|
9
10
|
include RetryOnError
|
10
11
|
|
12
|
+
# @private
|
11
13
|
LOGIN_RETRY_CLASSES = [::EOFError, ::Errno::ECONNRESET, ::SocketError].freeze
|
12
14
|
|
15
|
+
# @return [Client]
|
13
16
|
attr_reader :client
|
14
|
-
attr_reader :login_called
|
15
17
|
|
16
18
|
def initialize(client:)
|
17
19
|
@client = client
|
18
20
|
@login_called = false
|
19
21
|
end
|
20
22
|
|
23
|
+
# Proxies calls to the client.
|
24
|
+
# Before the first call does login
|
25
|
+
# @return the return value of the client method called
|
21
26
|
def method_missing(method_name, *arguments, &block)
|
22
27
|
if login_called
|
23
28
|
client.send(method_name, *arguments, &block)
|
@@ -27,12 +32,15 @@ module Imap::Backup
|
|
27
32
|
end
|
28
33
|
end
|
29
34
|
|
35
|
+
# @return [Boolean] whether the client responds to the method
|
30
36
|
def respond_to_missing?(method_name, _include_private = false)
|
31
37
|
client.respond_to?(method_name)
|
32
38
|
end
|
33
39
|
|
34
40
|
private
|
35
41
|
|
42
|
+
attr_reader :login_called
|
43
|
+
|
36
44
|
def do_first_login
|
37
45
|
retry_on_error(errors: LOGIN_RETRY_CLASSES) do
|
38
46
|
client.login
|
@@ -8,6 +8,8 @@ module Imap; end
|
|
8
8
|
module Imap::Backup
|
9
9
|
module Client; end
|
10
10
|
|
11
|
+
# Wraps a Net::IMAP instance
|
12
|
+
# Tracks the latest folder selection in order to avoid repeated calls
|
11
13
|
class Client::Default
|
12
14
|
extend Forwardable
|
13
15
|
def_delegators :imap, *%i(
|
@@ -22,6 +24,7 @@ module Imap::Backup
|
|
22
24
|
@state = nil
|
23
25
|
end
|
24
26
|
|
27
|
+
# @return [Array<String>] the account folders
|
25
28
|
def list
|
26
29
|
root = provider_root
|
27
30
|
mailbox_lists = imap.list(root, "*")
|
@@ -31,36 +34,44 @@ module Imap::Backup
|
|
31
34
|
mailbox_lists.map { |ml| extract_name(ml) }
|
32
35
|
end
|
33
36
|
|
37
|
+
# Logs in to the account on the IMAP server
|
38
|
+
# @return [void]
|
34
39
|
def login
|
35
40
|
Logger.logger.debug "Logging in: #{account.username}/#{masked_password}"
|
36
41
|
imap.login(account.username, account.password)
|
37
42
|
Logger.logger.debug "Login complete"
|
38
43
|
end
|
39
44
|
|
45
|
+
# Logs out and back in to the server
|
46
|
+
# @return [void]
|
40
47
|
def reconnect
|
41
48
|
disconnect
|
42
49
|
login
|
43
50
|
end
|
44
51
|
|
52
|
+
# @return [String] the account username
|
45
53
|
def username
|
46
54
|
account.username
|
47
55
|
end
|
48
56
|
|
49
|
-
#
|
50
|
-
|
57
|
+
# Disconects from the server
|
58
|
+
# @return [void]
|
51
59
|
def disconnect
|
52
60
|
imap.disconnect
|
53
61
|
self.state = nil
|
54
62
|
end
|
55
63
|
|
64
|
+
# Prepares read-only access to a folder
|
65
|
+
# @return [void]
|
56
66
|
def examine(mailbox)
|
57
67
|
return if state == [:examine, mailbox]
|
58
68
|
|
59
|
-
|
69
|
+
imap.examine(mailbox)
|
60
70
|
self.state = [:examine, mailbox]
|
61
|
-
result
|
62
71
|
end
|
63
72
|
|
73
|
+
# Prepares read-write access to a folder
|
74
|
+
# @return [void]
|
64
75
|
def select(mailbox)
|
65
76
|
return if state == [:select, mailbox]
|
66
77
|
|