imap-backup 16.4.0 → 16.4.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/README.md +3 -92
- data/docs/development.md +8 -0
- data/docs/testing.md +93 -0
- data/lib/imap/backup/account/backup.rb +2 -0
- data/lib/imap/backup/account/backup_folders.rb +2 -0
- data/lib/imap/backup/account/client_factory.rb +1 -0
- data/lib/imap/backup/account/folder.rb +2 -0
- data/lib/imap/backup/account/folder_backup.rb +3 -0
- data/lib/imap/backup/account/folder_ensurer.rb +1 -0
- data/lib/imap/backup/account/folder_mapper.rb +6 -0
- data/lib/imap/backup/account/local_only_folder_deleter.rb +1 -0
- data/lib/imap/backup/account/locker.rb +6 -1
- data/lib/imap/backup/account/restore.rb +3 -0
- data/lib/imap/backup/account/serialized_folders.rb +1 -0
- data/lib/imap/backup/account.rb +34 -16
- data/lib/imap/backup/cli/backup.rb +4 -0
- data/lib/imap/backup/cli/local/check.rb +5 -0
- data/lib/imap/backup/cli/options.rb +1 -0
- data/lib/imap/backup/cli/restore.rb +10 -3
- data/lib/imap/backup/cli/setup.rb +3 -0
- data/lib/imap/backup/cli/single/backup.rb +20 -0
- data/lib/imap/backup/cli/stats.rb +10 -0
- data/lib/imap/backup/cli/transfer.rb +3 -0
- data/lib/imap/backup/client/automatic_login_wrapper.rb +1 -0
- data/lib/imap/backup/client/default.rb +1 -0
- data/lib/imap/backup/configuration.rb +1 -0
- data/lib/imap/backup/downloader.rb +4 -0
- data/lib/imap/backup/email/mboxrd/message.rb +1 -0
- data/lib/imap/backup/file_mode.rb +1 -0
- data/lib/imap/backup/flag_refresher.rb +2 -0
- data/lib/imap/backup/local_only_message_deleter.rb +2 -0
- data/lib/imap/backup/lockfile.rb +19 -3
- data/lib/imap/backup/logger.rb +1 -1
- data/lib/imap/backup/migrator.rb +3 -0
- data/lib/imap/backup/mirror/map.rb +2 -1
- data/lib/imap/backup/mirror.rb +3 -0
- data/lib/imap/backup/serializer/message.rb +1 -1
- data/lib/imap/backup/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3ffe8d1bd32587d59e12a514b005d68175244e2cb73a9d138dd3a5d273c785f
|
|
4
|
+
data.tar.gz: 62d6364641799f1ce20466affc34052f8614a2a2d5633f53a05b4cac31a4e0f3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f4fdd4254948ef5dd7fba17e3683aa13bf84a19a77ffd935bad3f830355d4bf0d14ff48c731be2542ce56332ed8b4903b1b7971a16e7e947f4406ba4473b4a44
|
|
7
|
+
data.tar.gz: 4ca3ca9bb9eda64a9b8b9a6cc0889e5815f446e2a798f762f1d55e228928e99988c186bdaad4dfd7d033cd21667de7d906ea3cbd1f11d947dc77d68f4bbd1090
|
data/README.md
CHANGED
|
@@ -25,102 +25,13 @@ The {file:CHANGELOG.md CHANGELOG} has a history of the changes to the program.
|
|
|
25
25
|
* Restartable - calculate start point based on already downloaded messages
|
|
26
26
|
* Standalone - do not rely on an email client or MTA
|
|
27
27
|
|
|
28
|
-
#
|
|
28
|
+
# Development
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
better `git blame` output:
|
|
32
|
-
|
|
33
|
-
```sh
|
|
34
|
-
git config --local blame.ignoreRevsFile .git-blame-ignore-revs
|
|
35
|
-
```
|
|
30
|
+
See the [development documentation](/docs/development.md).
|
|
36
31
|
|
|
37
32
|
# Testing
|
|
38
33
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
Specs under `specs/features` are integration specs.
|
|
42
|
-
Some of these specs run against two local IMAP servers
|
|
43
|
-
controlled by Podman (or Docker) Compose.
|
|
44
|
-
|
|
45
|
-
Start them before running the test suite
|
|
46
|
-
|
|
47
|
-
```sh
|
|
48
|
-
$ podman-compose -f dev/compose.yml up -d
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
or, with Docker
|
|
52
|
-
|
|
53
|
-
```sh
|
|
54
|
-
$ docker-compose -f dev/compose.yml up -d
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
Then, run all specs
|
|
58
|
-
|
|
59
|
-
```sh
|
|
60
|
-
$ rspec
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
To exclude container-based tests
|
|
64
|
-
|
|
65
|
-
```sh
|
|
66
|
-
$ rspec --tag ~container
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
To run **just** the feature specs
|
|
70
|
-
|
|
71
|
-
```sh
|
|
72
|
-
rspec spec/features/**/*_spec.rb
|
|
73
|
-
```
|
|
74
|
-
|
|
75
|
-
## Full Test Run
|
|
76
|
-
|
|
77
|
-
The full test run includes RSpec specs **and** Rubocop checks
|
|
78
|
-
|
|
79
|
-
```sh
|
|
80
|
-
rake
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
# Test Debugging
|
|
84
|
-
|
|
85
|
-
The feature specs are run 'out of process' via the Aruba gem.
|
|
86
|
-
In order to see debugging output from the process,
|
|
87
|
-
use `last_command_started.output`.
|
|
88
|
-
|
|
89
|
-
# Older Rubies
|
|
90
|
-
|
|
91
|
-
A Containerfile is available to allow testing with all available Ruby versions,
|
|
92
|
-
see the README in the `dev` directory.
|
|
93
|
-
|
|
94
|
-
# Performance Specs
|
|
95
|
-
|
|
96
|
-
```sh
|
|
97
|
-
PERFORMANCE=1 rspec --order=defined
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Beware: the performance spec (just backup for now) takes a very
|
|
101
|
-
long time to run, approximately 24 hours!
|
|
102
|
-
|
|
103
|
-
# Access Docker imap server
|
|
104
|
-
|
|
105
|
-
```ruby
|
|
106
|
-
require "net/imap"
|
|
107
|
-
require_relative "spec/features/support/30_email_server_helpers"
|
|
108
|
-
|
|
109
|
-
include EmailServerHelpers
|
|
110
|
-
|
|
111
|
-
test_connection = test_server_connection_parameters
|
|
112
|
-
|
|
113
|
-
test_imap = Net::IMAP.new(test_connection[:server], test_connection[:connection_options])
|
|
114
|
-
test_imap.login(test_connection[:username], test_connection[:password])
|
|
115
|
-
|
|
116
|
-
message = "From: #{test_connection[:username]}\nSubject: Some Subject\n\nHello!\n"
|
|
117
|
-
response = test_imap.append("INBOX", message, nil, nil)
|
|
118
|
-
|
|
119
|
-
test_imap.examine("INBOX")
|
|
120
|
-
uids = test_imap.uid_search(["ALL"]).sort
|
|
121
|
-
|
|
122
|
-
fetch_data_items = test_imap.uid_fetch(uids, ["BODY[]"])
|
|
123
|
-
```
|
|
34
|
+
See the [testing documentation](/docs/testing.md).
|
|
124
35
|
|
|
125
36
|
# Contributing
|
|
126
37
|
|
data/docs/development.md
ADDED
data/docs/testing.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
Specs under `spec/features` are integration specs.
|
|
4
|
+
Some of these specs run against two local IMAP servers
|
|
5
|
+
controlled by Podman (or Docker) Compose.
|
|
6
|
+
|
|
7
|
+
Start them before running the test suite
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
$ podman-compose -f dev/compose.yml up -d
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
or, with Docker
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
$ docker-compose -f dev/compose.yml up -d
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
# Invocations
|
|
20
|
+
|
|
21
|
+
Run all specs
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
$ rake spec
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run **just** the unit specs
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
$ rake spec_unit
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Run **just** the feature specs
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
$ rake spec_feature
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
To exclude the slow container-based tests
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
$ rake spec_non_container
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Full Test Run
|
|
46
|
+
|
|
47
|
+
The full test run includes RSpec specs **and** Rubocop checks
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
rake test
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
# Debugging
|
|
54
|
+
|
|
55
|
+
The feature specs are run 'out of process' via the Aruba gem.
|
|
56
|
+
In order to see debugging output from the process,
|
|
57
|
+
use `last_command_started.output`.
|
|
58
|
+
|
|
59
|
+
# Older Rubies
|
|
60
|
+
|
|
61
|
+
A Containerfile is available to allow testing with all available Ruby versions,
|
|
62
|
+
see the README in the `dev` directory.
|
|
63
|
+
|
|
64
|
+
# Performance Specs
|
|
65
|
+
|
|
66
|
+
```sh
|
|
67
|
+
PERFORMANCE=1 rspec --order=defined
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Beware: the performance spec (just backup for now) takes a very
|
|
71
|
+
long time to run, approximately 24 hours!
|
|
72
|
+
|
|
73
|
+
# Access Docker imap server
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
require "net/imap"
|
|
77
|
+
require_relative "spec/features/support/30_email_server_helpers"
|
|
78
|
+
|
|
79
|
+
include EmailServerHelpers
|
|
80
|
+
|
|
81
|
+
test_connection = test_server_connection_parameters
|
|
82
|
+
|
|
83
|
+
test_imap = Net::IMAP.new(test_connection[:server], test_connection[:connection_options])
|
|
84
|
+
test_imap.login(test_connection[:username], test_connection[:password])
|
|
85
|
+
|
|
86
|
+
message = "From: #{test_connection[:username]}\nSubject: Some Subject\n\nHello!\n"
|
|
87
|
+
response = test_imap.append("INBOX", message, nil, nil)
|
|
88
|
+
|
|
89
|
+
test_imap.examine("INBOX")
|
|
90
|
+
uids = test_imap.uid_search(["ALL"]).sort
|
|
91
|
+
|
|
92
|
+
fetch_data_items = test_imap.uid_fetch(uids, ["BODY[]"])
|
|
93
|
+
```
|
|
@@ -11,6 +11,8 @@ module Imap::Backup
|
|
|
11
11
|
|
|
12
12
|
# Carries out the backup of the configured folders of the account
|
|
13
13
|
class Account::Backup
|
|
14
|
+
# @param account [Account] the account to back up
|
|
15
|
+
# @param refresh [Boolean] true to refresh folder metadata even if mirror is disabled
|
|
14
16
|
def initialize(account:, refresh: false)
|
|
15
17
|
@account = account
|
|
16
18
|
@refresh = refresh
|
|
@@ -9,6 +9,8 @@ module Imap::Backup
|
|
|
9
9
|
class Account::BackupFolders
|
|
10
10
|
include Enumerable
|
|
11
11
|
|
|
12
|
+
# @param client [Client::AutomaticLoginWrapper] the IMAP client that lists folders
|
|
13
|
+
# @param account [Account] the account whose folders are being backed up
|
|
12
14
|
def initialize(client:, account:)
|
|
13
15
|
@client = client
|
|
14
16
|
@account = account
|
|
@@ -23,6 +23,8 @@ module Imap::Backup
|
|
|
23
23
|
# @return [String] the name of the folder
|
|
24
24
|
attr_reader :name
|
|
25
25
|
|
|
26
|
+
# @param client [Client::Default] the IMAP client used to talk to the server
|
|
27
|
+
# @param name [String] the UTF-7 encoded folder name
|
|
26
28
|
def initialize(client, name)
|
|
27
29
|
@client = client
|
|
28
30
|
@name = name
|
|
@@ -12,6 +12,9 @@ module Imap::Backup
|
|
|
12
12
|
|
|
13
13
|
# Implements backup for a single folder
|
|
14
14
|
class Account::FolderBackup
|
|
15
|
+
# @param account [Account] the account that owns the folder
|
|
16
|
+
# @param folder [Account::Folder] the online folder being backed up
|
|
17
|
+
# @param refresh [Boolean] true to refresh metadata regardless of mirror mode
|
|
15
18
|
def initialize(account:, folder:, refresh: false)
|
|
16
19
|
@account = account
|
|
17
20
|
@folder = folder
|
|
@@ -11,6 +11,12 @@ module Imap::Backup
|
|
|
11
11
|
|
|
12
12
|
# Implements a folder enumerator for backed-up accounts
|
|
13
13
|
class Account::FolderMapper
|
|
14
|
+
# @param account [Account] the account whose local folders are being iterated
|
|
15
|
+
# @param destination [Account] the destination account
|
|
16
|
+
# @param destination_delimiter [String] the delimiter to use for destination folder names
|
|
17
|
+
# @param destination_prefix [String] a prefix applied to destination folder names
|
|
18
|
+
# @param source_delimiter [String] the delimiter used in the source account
|
|
19
|
+
# @param source_prefix [String] a prefix applied to source folder names
|
|
14
20
|
def initialize(
|
|
15
21
|
account:,
|
|
16
22
|
destination:,
|
|
@@ -10,6 +10,7 @@ module Imap::Backup
|
|
|
10
10
|
# This is used in mirror mode, where local copies are only kept as long as they
|
|
11
11
|
# exist on the server.
|
|
12
12
|
class Account::LocalOnlyFolderDeleter
|
|
13
|
+
# @param account [Account] the account whose serialized folders are being pruned
|
|
13
14
|
def initialize(account:)
|
|
14
15
|
@account = account
|
|
15
16
|
end
|
|
@@ -9,6 +9,7 @@ module Imap::Backup
|
|
|
9
9
|
class Account::Locker
|
|
10
10
|
attr_reader :account
|
|
11
11
|
|
|
12
|
+
# @param account [Account] the account whose backup must be locked
|
|
12
13
|
def initialize(account:)
|
|
13
14
|
@account = account
|
|
14
15
|
end
|
|
@@ -27,7 +28,11 @@ module Imap::Backup
|
|
|
27
28
|
Account::FolderEnsurer.new(account: account).run
|
|
28
29
|
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
begin
|
|
32
|
+
lockfile.with_lock do
|
|
33
|
+
block.call
|
|
34
|
+
end
|
|
35
|
+
rescue Lockfile::ProcessStartTimeUnavailableError
|
|
31
36
|
block.call
|
|
32
37
|
end
|
|
33
38
|
end
|
|
@@ -9,6 +9,9 @@ module Imap::Backup
|
|
|
9
9
|
|
|
10
10
|
# Restores all backed up folders to the server
|
|
11
11
|
class Account::Restore
|
|
12
|
+
# @param account [Account] the account whose backups will be restored
|
|
13
|
+
# @param delimiter [String] the destination folder delimiter
|
|
14
|
+
# @param prefix [String] a prefix applied to restored folder names
|
|
12
15
|
def initialize(account:, delimiter: "/", prefix: "")
|
|
13
16
|
@account = account
|
|
14
17
|
@destination_delimiter = delimiter
|
data/lib/imap/backup/account.rb
CHANGED
|
@@ -65,6 +65,25 @@ module Imap::Backup
|
|
|
65
65
|
# @return [String] one of "active" (the default), "archived", or "offline"
|
|
66
66
|
attr_reader :status
|
|
67
67
|
|
|
68
|
+
# @param options [Hash] Account attributes
|
|
69
|
+
# @option opts [String] :username the username of the account (usually the same as the email
|
|
70
|
+
# address)
|
|
71
|
+
# @option opts [String] :password the password of the account
|
|
72
|
+
# @option opts [String] :local_path the path where backups will be saved
|
|
73
|
+
# @option opts [Array<String>] :folders (nil) the list of folders that have been configured for
|
|
74
|
+
# the Account
|
|
75
|
+
# @option opts [Boolean] :folder_blacklist (false) whether the folders attribute is a blacklist
|
|
76
|
+
# @option opts [Boolean] :mirror_mode (false) whether to run in mirror mode
|
|
77
|
+
# @option opts [String] :server the address of the IMAP server
|
|
78
|
+
# @option opts [Hash] :connection_options (nil) additional connection options for the IMAP
|
|
79
|
+
# server
|
|
80
|
+
# @option opts [String] :download_strategy (nil) the name of the download strategy to adopt
|
|
81
|
+
# during backups
|
|
82
|
+
# @option opts [Integer] :multi_fetch_size (nil) the number of emails to fetch from the IMAP
|
|
83
|
+
# server at a time
|
|
84
|
+
# @option opts [Boolean] :reset_seen_flags_after_fetch (false) whether to reset seen flags after
|
|
85
|
+
# fetching messages
|
|
86
|
+
# @option opts [String] :status ("active") the status of the account
|
|
68
87
|
def initialize(options)
|
|
69
88
|
check_options!(options)
|
|
70
89
|
@username = options[:username]
|
|
@@ -131,19 +150,19 @@ module Imap::Backup
|
|
|
131
150
|
# @return [Hash] all Account data for serialization
|
|
132
151
|
def to_h
|
|
133
152
|
h = {
|
|
134
|
-
username:
|
|
135
|
-
password:
|
|
153
|
+
username: username,
|
|
154
|
+
password: password,
|
|
136
155
|
status: status
|
|
137
156
|
}
|
|
138
|
-
h[:local_path] =
|
|
139
|
-
h[:folders] =
|
|
140
|
-
h[:folder_blacklist] = true if
|
|
141
|
-
h[:mirror_mode] = true if
|
|
142
|
-
h[:server] =
|
|
143
|
-
h[:connection_options] =
|
|
157
|
+
h[:local_path] = local_path if local_path
|
|
158
|
+
h[:folders] = folders if folders
|
|
159
|
+
h[:folder_blacklist] = true if folder_blacklist
|
|
160
|
+
h[:mirror_mode] = true if mirror_mode
|
|
161
|
+
h[:server] = server if server
|
|
162
|
+
h[:connection_options] = connection_options if connection_options
|
|
144
163
|
h[:multi_fetch_size] = multi_fetch_size
|
|
145
|
-
if
|
|
146
|
-
h[:reset_seen_flags_after_fetch] =
|
|
164
|
+
if reset_seen_flags_after_fetch
|
|
165
|
+
h[:reset_seen_flags_after_fetch] = reset_seen_flags_after_fetch
|
|
147
166
|
end
|
|
148
167
|
h
|
|
149
168
|
end
|
|
@@ -216,12 +235,11 @@ module Imap::Backup
|
|
|
216
235
|
def connection_options=(value)
|
|
217
236
|
# Ensure we've loaded the connection_options
|
|
218
237
|
connection_options
|
|
219
|
-
parsed =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
end
|
|
238
|
+
parsed = if value == ""
|
|
239
|
+
nil
|
|
240
|
+
else
|
|
241
|
+
JSON.parse(value, symbolize_names: true)
|
|
242
|
+
end
|
|
225
243
|
update(:connection_options, parsed)
|
|
226
244
|
end
|
|
227
245
|
|
|
@@ -15,6 +15,10 @@ module Imap::Backup
|
|
|
15
15
|
include Thor::Actions
|
|
16
16
|
include CLI::Helpers
|
|
17
17
|
|
|
18
|
+
# @param options [Hash] CLI options controlling output
|
|
19
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
20
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
21
|
+
# @option opts [Boolean] :refresh (false) whether to force refresh of folder metadata
|
|
18
22
|
def initialize(options)
|
|
19
23
|
super([])
|
|
20
24
|
@options = options
|
|
@@ -14,6 +14,11 @@ module Imap::Backup
|
|
|
14
14
|
class CLI::Local::Check
|
|
15
15
|
include CLI::Helpers
|
|
16
16
|
|
|
17
|
+
# @param options [Hash] CLI options controlling output
|
|
18
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
19
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
20
|
+
# @option opts [Boolean] :delete_corrupt (false) whether to delete corrupt folders
|
|
21
|
+
# @option opts [String] :format ("text") the output format, either "text" or "json"
|
|
17
22
|
def initialize(options)
|
|
18
23
|
@options = options
|
|
19
24
|
end
|
|
@@ -14,6 +14,13 @@ module Imap::Backup
|
|
|
14
14
|
include Thor::Actions
|
|
15
15
|
include CLI::Helpers
|
|
16
16
|
|
|
17
|
+
# @param email [String, nil] optional email address identifying the account to restore
|
|
18
|
+
# @param options [Hash] CLI options controlling output
|
|
19
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
20
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
21
|
+
# @option opts [Array<String>] :accounts (nil) the accounts to restore
|
|
22
|
+
# @option opts [String] :delimiter ("/") the destination folder delimiter
|
|
23
|
+
# @option opts [String] :prefix ("") a prefix applied to restored folder names
|
|
17
24
|
def initialize(email = nil, options)
|
|
18
25
|
super([])
|
|
19
26
|
@email = email
|
|
@@ -30,9 +37,6 @@ module Imap::Backup
|
|
|
30
37
|
when email && !options.key?(:accounts)
|
|
31
38
|
account = account(config, email)
|
|
32
39
|
restore(account, **restore_options)
|
|
33
|
-
when !email && !options.key?(:accounts)
|
|
34
|
-
Logger.logger.info "Calling restore without an EMAIL parameter is deprecated"
|
|
35
|
-
config.accounts.each { |a| restore(a) }
|
|
36
40
|
when email && options.key?(:accounts)
|
|
37
41
|
raise "Missing EMAIL parameter"
|
|
38
42
|
when !email && options.key?(:accounts)
|
|
@@ -41,6 +45,9 @@ module Imap::Backup
|
|
|
41
45
|
"please pass a single EMAIL parameter"
|
|
42
46
|
)
|
|
43
47
|
requested_accounts(config).each { |a| restore(a) }
|
|
48
|
+
else
|
|
49
|
+
Logger.logger.info "Calling restore without an EMAIL parameter is deprecated"
|
|
50
|
+
config.accounts.each { |a| restore(a) }
|
|
44
51
|
end
|
|
45
52
|
end
|
|
46
53
|
end
|
|
@@ -13,6 +13,9 @@ module Imap::Backup
|
|
|
13
13
|
include Thor::Actions
|
|
14
14
|
include CLI::Helpers
|
|
15
15
|
|
|
16
|
+
# @param options [Hash] CLI options controlling output
|
|
17
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
18
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
16
19
|
def initialize(options)
|
|
17
20
|
super([])
|
|
18
21
|
@options = options
|
|
@@ -12,6 +12,26 @@ module Imap::Backup
|
|
|
12
12
|
|
|
13
13
|
# Runs a backup without relying on existing configuration
|
|
14
14
|
class CLI::Single::Backup
|
|
15
|
+
# @param options [Hash] CLI options controlling output
|
|
16
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
17
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
18
|
+
# @option opts [String] :email the email address identifying the account to backup
|
|
19
|
+
# @option opts [String] :password (nil) the password for the account
|
|
20
|
+
# @option opts [String] :password_environment_variable (nil) the name of an environment variable
|
|
21
|
+
# containing the password
|
|
22
|
+
# @option opts [String] :password_file (nil) the path to a file containing the password
|
|
23
|
+
# @option opts [String] :server the IMAP server address
|
|
24
|
+
# @option opts [String] :download_strategy (nil) the download strategy to use, either "delay"
|
|
25
|
+
# or "direct"
|
|
26
|
+
# @option opts [Boolean] :folder_blacklist (false) whether to treat folder list as a blacklist
|
|
27
|
+
# @option opts [Array<String>] :folder ([]) the folders to backup
|
|
28
|
+
# @option opts [String] :path (nil) the local path to store the backup
|
|
29
|
+
# @option opts [Boolean] :mirror (false) whether to run in mirror mode
|
|
30
|
+
# @option opts [Integer] :multi_fetch_size (nil) the number of messages to fetch in a single
|
|
31
|
+
# operation
|
|
32
|
+
# @option opts [Boolean] :refresh (false) whether to force refresh of folder metadata
|
|
33
|
+
# @option opts [Boolean] :reset_seen_flags_after_fetch (false) whether to reset seen flags
|
|
34
|
+
# after fetching messages
|
|
15
35
|
def initialize(options)
|
|
16
36
|
@options = options
|
|
17
37
|
@password = nil
|
|
@@ -1,14 +1,24 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
|
|
1
3
|
require "imap/backup/account/backup_folders"
|
|
4
|
+
require "imap/backup/cli/helpers"
|
|
2
5
|
require "imap/backup/serializer"
|
|
3
6
|
|
|
4
7
|
module Imap; end
|
|
5
8
|
|
|
6
9
|
module Imap::Backup
|
|
10
|
+
class CLI < Thor; end
|
|
11
|
+
|
|
7
12
|
# Prints various statistics about an account and its backup
|
|
8
13
|
class CLI::Stats < Thor
|
|
9
14
|
include Thor::Actions
|
|
10
15
|
include CLI::Helpers
|
|
11
16
|
|
|
17
|
+
# @param email [String] the email address identifying the account to inspect
|
|
18
|
+
# @param options [Hash] CLI options controlling output
|
|
19
|
+
# @option opts [String] :config (nil) the path to the configuration file
|
|
20
|
+
# @option opts [String] :erb_configuration (nil) the path to the ERB configuration file
|
|
21
|
+
# @option opts [String] :format ("text") the output format, either "text" or "json"
|
|
12
22
|
def initialize(email, options)
|
|
13
23
|
super([])
|
|
14
24
|
@email = email
|
|
@@ -16,6 +16,9 @@ module Imap::Backup
|
|
|
16
16
|
# The possible values for the action parameter
|
|
17
17
|
ACTIONS = %i(copy migrate mirror).freeze
|
|
18
18
|
|
|
19
|
+
# @param action [Symbol] one of ACTIONS describing the transfer mode
|
|
20
|
+
# @param source_email [String] the email of the source account
|
|
21
|
+
# @param destination_email [String] the email of the destination account
|
|
19
22
|
def initialize(action, source_email, destination_email, options)
|
|
20
23
|
@action = action
|
|
21
24
|
@source_email = source_email
|
|
@@ -8,6 +8,10 @@ module Imap::Backup
|
|
|
8
8
|
# @private
|
|
9
9
|
class MultiFetchFailedError < StandardError; end
|
|
10
10
|
|
|
11
|
+
# @param folder [Account::Folder] the remote folder to download from
|
|
12
|
+
# @param serializer [Serializer] the local serializer that stores messages
|
|
13
|
+
# @param multi_fetch_size [Integer] how many messages to fetch per batch
|
|
14
|
+
# @param reset_seen_flags_after_fetch [Boolean] true to restore unseen flags after fetch
|
|
11
15
|
def initialize(folder, serializer, multi_fetch_size: 1, reset_seen_flags_after_fetch: false)
|
|
12
16
|
@folder = folder
|
|
13
17
|
@serializer = serializer
|
|
@@ -6,6 +6,8 @@ module Imap::Backup
|
|
|
6
6
|
# The number of messages to process at a time
|
|
7
7
|
CHUNK_SIZE = 100
|
|
8
8
|
|
|
9
|
+
# @param folder [Account::Folder] the remote folder whose flags are refreshed
|
|
10
|
+
# @param serializer [Serializer] the local serializer providing stored UIDs
|
|
9
11
|
def initialize(folder, serializer)
|
|
10
12
|
@folder = folder
|
|
11
13
|
@serializer = serializer
|
|
@@ -5,6 +5,8 @@ module Imap; end
|
|
|
5
5
|
module Imap::Backup
|
|
6
6
|
# Deletes locally backed-up emails that are no longer on the server
|
|
7
7
|
class LocalOnlyMessageDeleter
|
|
8
|
+
# @param folder [Account::Folder] the remote folder used to detect missing UIDs
|
|
9
|
+
# @param serializer [Serializer] the local serializer that may contain stale messages
|
|
8
10
|
def initialize(folder, serializer)
|
|
9
11
|
@folder = folder
|
|
10
12
|
@serializer = serializer
|
data/lib/imap/backup/lockfile.rb
CHANGED
|
@@ -7,6 +7,7 @@ module Imap::Backup
|
|
|
7
7
|
class Lockfile
|
|
8
8
|
# An error that is thrown if a lockfile already exists
|
|
9
9
|
class LockfileExistsError < StandardError; end
|
|
10
|
+
class ProcessStartTimeUnavailableError < StandardError; end
|
|
10
11
|
|
|
11
12
|
attr_reader :path
|
|
12
13
|
|
|
@@ -53,7 +54,9 @@ module Imap::Backup
|
|
|
53
54
|
|
|
54
55
|
return true if proc_table_entry.nil?
|
|
55
56
|
|
|
56
|
-
|
|
57
|
+
other_starttime = starttime(proc_table_entry)
|
|
58
|
+
|
|
59
|
+
other_starttime != starttime
|
|
57
60
|
end
|
|
58
61
|
|
|
59
62
|
private
|
|
@@ -62,9 +65,11 @@ module Imap::Backup
|
|
|
62
65
|
pid = Process.pid
|
|
63
66
|
proc_table_entry = Sys::ProcTable.ps(pid: pid)
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
if proc_table_entry.nil?
|
|
69
|
+
raise ProcessStartTimeUnavailableError, "Unable to get process info for PID #{pid}"
|
|
70
|
+
end
|
|
66
71
|
|
|
67
|
-
starttime = proc_table_entry
|
|
72
|
+
starttime = starttime(proc_table_entry)
|
|
68
73
|
|
|
69
74
|
data = {
|
|
70
75
|
pid: pid,
|
|
@@ -74,5 +79,16 @@ module Imap::Backup
|
|
|
74
79
|
json_data = JSON.generate(data)
|
|
75
80
|
File.write(path, json_data)
|
|
76
81
|
end
|
|
82
|
+
|
|
83
|
+
def starttime(proc_table_entry)
|
|
84
|
+
case
|
|
85
|
+
when proc_table_entry.respond_to?(:starttime)
|
|
86
|
+
proc_table_entry.starttime
|
|
87
|
+
when proc_table_entry.respond_to?(:start_tvsec)
|
|
88
|
+
proc_table_entry.start_tvsec
|
|
89
|
+
else
|
|
90
|
+
raise ProcessStartTimeUnavailableError, "Proctable entry structure unknown"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
77
93
|
end
|
|
78
94
|
end
|
data/lib/imap/backup/logger.rb
CHANGED
|
@@ -49,7 +49,7 @@ module Imap::Backup
|
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Wraps a block, filtering output to standard error,
|
|
52
|
-
#
|
|
52
|
+
# hiding passwords, and outputs the results to standard out
|
|
53
53
|
# @return [void]
|
|
54
54
|
def self.sanitize_stderr(&block)
|
|
55
55
|
sanitizer = Text::Sanitizer.new($stdout)
|
data/lib/imap/backup/migrator.rb
CHANGED
|
@@ -5,6 +5,9 @@ module Imap; end
|
|
|
5
5
|
module Imap::Backup
|
|
6
6
|
# Copies a folder of backed-up emails to an online folder
|
|
7
7
|
class Migrator
|
|
8
|
+
# @param serializer [Serializer] the local folder to migrate
|
|
9
|
+
# @param folder [Account::Folder] the destination folder on the server
|
|
10
|
+
# @param reset [Boolean] true to clear the destination before uploading
|
|
8
11
|
def initialize(serializer, folder, reset: false)
|
|
9
12
|
@folder = folder
|
|
10
13
|
@reset = reset
|
|
@@ -7,6 +7,8 @@ module Imap::Backup
|
|
|
7
7
|
|
|
8
8
|
# Keeps track of the mapping between source and destination UIDs
|
|
9
9
|
class Mirror::Map
|
|
10
|
+
# @param pathname [String] the path to the on-disk UID map
|
|
11
|
+
# @param destination [String] the destination account identifier
|
|
10
12
|
def initialize(pathname:, destination:)
|
|
11
13
|
@pathname = pathname
|
|
12
14
|
@destination = destination
|
|
@@ -18,7 +20,6 @@ module Imap::Backup
|
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
# @return [Boolean] whether the supplied values match the existing
|
|
21
|
-
# UID validity values
|
|
22
23
|
def check_uid_validities(source:, destination:)
|
|
23
24
|
store
|
|
24
25
|
return false if source != source_uid_validity
|
data/lib/imap/backup/mirror.rb
CHANGED
|
@@ -5,6 +5,9 @@ module Imap; end
|
|
|
5
5
|
module Imap::Backup
|
|
6
6
|
# Synchronises a folder between a source and destination
|
|
7
7
|
class Mirror
|
|
8
|
+
# @param serializer [Serializer] the source of backed-up messages
|
|
9
|
+
# @param folder [Account::Folder] the destination folder to mirror into
|
|
10
|
+
# @param reset [Boolean] true to delete destination-only messages first
|
|
8
11
|
def initialize(serializer, folder, reset: false)
|
|
9
12
|
@serializer = serializer
|
|
10
13
|
@folder = folder
|
|
@@ -12,7 +12,7 @@ module Imap::Backup
|
|
|
12
12
|
# @return [Array[Symbol]] the message's flags
|
|
13
13
|
attr_accessor :flags
|
|
14
14
|
# @return [Integer] the length of the message (as stored on disk)
|
|
15
|
-
|
|
15
|
+
attr_accessor :length
|
|
16
16
|
# @return [Integer] the start of the message inside the mailbox file
|
|
17
17
|
attr_reader :offset
|
|
18
18
|
# @return [Integer] the message's UID
|
data/lib/imap/backup/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: imap-backup
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 16.4.
|
|
4
|
+
version: 16.4.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Joe Yates
|
|
@@ -175,8 +175,10 @@ files:
|
|
|
175
175
|
- README.md
|
|
176
176
|
- bin/imap-backup
|
|
177
177
|
- docs/delimiters-and-prefixes.md
|
|
178
|
+
- docs/development.md
|
|
178
179
|
- docs/documentation.md
|
|
179
180
|
- docs/performance.md
|
|
181
|
+
- docs/testing.md
|
|
180
182
|
- imap-backup.gemspec
|
|
181
183
|
- lib/imap/backup/account.rb
|
|
182
184
|
- lib/imap/backup/account/backup.rb
|
|
@@ -277,7 +279,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
277
279
|
- !ruby/object:Gem::Version
|
|
278
280
|
version: '0'
|
|
279
281
|
requirements: []
|
|
280
|
-
rubygems_version:
|
|
282
|
+
rubygems_version: 3.6.7
|
|
281
283
|
specification_version: 4
|
|
282
284
|
summary: Backup GMail (or other IMAP) accounts to disk
|
|
283
285
|
test_files: []
|