imap-backup 16.4.0 → 16.4.2
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/imap-backup.gemspec +1 -1
- data/lib/imap/backup/account/backup.rb +5 -4
- 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 +10 -1
- data/lib/imap/backup/account/folder_mapper.rb +11 -1
- data/lib/imap/backup/account/local_only_folder_deleter.rb +1 -0
- data/lib/imap/backup/account/locker.rb +7 -3
- data/lib/imap/backup/account/restore.rb +3 -0
- data/lib/imap/backup/account/serialized_folders.rb +15 -4
- data/lib/imap/backup/account.rb +39 -17
- 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 +15 -1
- data/lib/imap/backup/cli/transfer.rb +3 -0
- data/lib/imap/backup/cli/utils.rb +36 -9
- 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 +4 -1
- data/lib/imap/backup/serializer/appender.rb +17 -15
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +2 -2
- data/lib/imap/backup/serializer/directory_maker.rb +43 -0
- data/lib/imap/backup/serializer/files/path.rb +27 -0
- data/lib/imap/backup/serializer/files.rb +167 -0
- data/lib/imap/backup/serializer/imap.rb +15 -12
- data/lib/imap/backup/serializer/mbox.rb +7 -7
- data/lib/imap/backup/serializer/message.rb +1 -1
- data/lib/imap/backup/serializer/unused_name_finder.rb +12 -7
- data/lib/imap/backup/serializer.rb +47 -206
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +6 -0
- data/lib/imap/backup/uploader.rb +5 -1
- data/lib/imap/backup/version.rb +1 -1
- metadata +11 -10
- data/lib/imap/backup/account/folder_ensurer.rb +0 -32
- data/lib/imap/backup/serializer/directory.rb +0 -51
- data/lib/imap/backup/serializer/folder_maker.rb +0 -44
- data/lib/imap/backup/serializer/version2_migrator.rb +0 -123
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc471fce2bd514e0eee4cec922e8f368df0b2fb99f8b89b2d852e07799a2be77
|
|
4
|
+
data.tar.gz: fd563c2ee648d5e384a10cdfc79a7d1d5be128bdce5908711396d414c801173f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36088caab383a7ff2528d8ad83813f58918abda4d80a56b0f2b2f1f45d5fc840934d1bc31e1907de11cc665f5aecb5b12152bb8dee686fb656294eb55440897e
|
|
7
|
+
data.tar.gz: 389a82b7755342dd591fd0e2d45d760eb8350f697d46414d8d90776624bd0ee7263acfe163ab1e8dd42c869d12a544c2d5faa4656d4ef5c802f4991b9f33c1b4
|
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
|
+
```
|
data/imap-backup.gemspec
CHANGED
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |gem|
|
|
|
30
30
|
gem.add_dependency "rake"
|
|
31
31
|
gem.add_dependency "sys-proctable"
|
|
32
32
|
gem.add_dependency "thor", "~> 1.1"
|
|
33
|
-
gem.add_dependency "thunderbird", "0.
|
|
33
|
+
gem.add_dependency "thunderbird", "~> 0.6.0"
|
|
34
34
|
|
|
35
35
|
gem.metadata = {
|
|
36
36
|
"rubygems_mfa_required" => "true"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require "imap/backup/account/backup_folders"
|
|
2
2
|
require "imap/backup/account/folder_backup"
|
|
3
|
-
require "imap/backup/account/folder_ensurer"
|
|
4
3
|
require "imap/backup/account/local_only_folder_deleter"
|
|
5
4
|
require "imap/backup/account/locker"
|
|
6
5
|
|
|
@@ -11,6 +10,8 @@ module Imap::Backup
|
|
|
11
10
|
|
|
12
11
|
# Carries out the backup of the configured folders of the account
|
|
13
12
|
class Account::Backup
|
|
13
|
+
# @param account [Account] the account to back up
|
|
14
|
+
# @param refresh [Boolean] true to refresh folder metadata even if mirror is disabled
|
|
14
15
|
def initialize(account:, refresh: false)
|
|
15
16
|
@account = account
|
|
16
17
|
@refresh = refresh
|
|
@@ -23,7 +24,7 @@ module Imap::Backup
|
|
|
23
24
|
# start the connection so we get logging messages in the right order
|
|
24
25
|
account.client.login
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
ensure_directory
|
|
27
28
|
delete_local_only_folders if account.mirror_mode
|
|
28
29
|
|
|
29
30
|
if backup_folders.none?
|
|
@@ -51,8 +52,8 @@ module Imap::Backup
|
|
|
51
52
|
Account::LocalOnlyFolderDeleter.new(account: account).run
|
|
52
53
|
end
|
|
53
54
|
|
|
54
|
-
def
|
|
55
|
-
|
|
55
|
+
def ensure_directory
|
|
56
|
+
Serializer::DirectoryMaker.new(files_path: account.files_path).run
|
|
56
57
|
end
|
|
57
58
|
|
|
58
59
|
def locker
|
|
@@ -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
|
|
@@ -4,6 +4,7 @@ require "imap/backup/flag_refresher"
|
|
|
4
4
|
require "imap/backup/local_only_message_deleter"
|
|
5
5
|
require "imap/backup/logger"
|
|
6
6
|
require "imap/backup/serializer"
|
|
7
|
+
require "imap/backup/serializer/files/path"
|
|
7
8
|
|
|
8
9
|
module Imap; end
|
|
9
10
|
|
|
@@ -12,6 +13,9 @@ module Imap::Backup
|
|
|
12
13
|
|
|
13
14
|
# Implements backup for a single folder
|
|
14
15
|
class Account::FolderBackup
|
|
16
|
+
# @param account [Account] the account that owns the folder
|
|
17
|
+
# @param folder [Account::Folder] the online folder being backed up
|
|
18
|
+
# @param refresh [Boolean] true to refresh metadata regardless of mirror mode
|
|
15
19
|
def initialize(account:, folder:, refresh: false)
|
|
16
20
|
@account = account
|
|
17
21
|
@folder = folder
|
|
@@ -83,7 +87,12 @@ module Imap::Backup
|
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
def raw_serializer
|
|
86
|
-
@raw_serializer ||=
|
|
90
|
+
@raw_serializer ||= begin
|
|
91
|
+
path = Serializer::Files::Path.new(
|
|
92
|
+
base_path: account.local_path, folder_name: folder.name
|
|
93
|
+
)
|
|
94
|
+
Serializer.new(files_path: path)
|
|
95
|
+
end
|
|
87
96
|
end
|
|
88
97
|
end
|
|
89
98
|
end
|
|
@@ -3,6 +3,7 @@ require "pathname"
|
|
|
3
3
|
|
|
4
4
|
require "imap/backup/account/folder"
|
|
5
5
|
require "imap/backup/serializer"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
6
7
|
|
|
7
8
|
module Imap; end
|
|
8
9
|
|
|
@@ -11,6 +12,12 @@ module Imap::Backup
|
|
|
11
12
|
|
|
12
13
|
# Implements a folder enumerator for backed-up accounts
|
|
13
14
|
class Account::FolderMapper
|
|
15
|
+
# @param account [Account] the account whose local folders are being iterated
|
|
16
|
+
# @param destination [Account] the destination account
|
|
17
|
+
# @param destination_delimiter [String] the delimiter to use for destination folder names
|
|
18
|
+
# @param destination_prefix [String] a prefix applied to destination folder names
|
|
19
|
+
# @param source_delimiter [String] the delimiter used in the source account
|
|
20
|
+
# @param source_prefix [String] a prefix applied to source folder names
|
|
14
21
|
def initialize(
|
|
15
22
|
account:,
|
|
16
23
|
destination:,
|
|
@@ -38,7 +45,10 @@ module Imap::Backup
|
|
|
38
45
|
glob = File.join(source_local_path, "**", "*.imap")
|
|
39
46
|
Pathname.glob(glob) do |path|
|
|
40
47
|
name = source_folder_name(path)
|
|
41
|
-
|
|
48
|
+
path = Serializer::Files::Path.new(
|
|
49
|
+
base_path: source_local_path, folder_name: name
|
|
50
|
+
)
|
|
51
|
+
serializer = Serializer.new(files_path: path)
|
|
42
52
|
folder = destination_folder_for_path(name)
|
|
43
53
|
block.call(serializer, folder)
|
|
44
54
|
end
|
|
@@ -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
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require "imap/backup/account/folder_ensurer"
|
|
2
1
|
require "imap/backup/lockfile"
|
|
3
2
|
|
|
4
3
|
module Imap; end
|
|
@@ -9,6 +8,7 @@ module Imap::Backup
|
|
|
9
8
|
class Account::Locker
|
|
10
9
|
attr_reader :account
|
|
11
10
|
|
|
11
|
+
# @param account [Account] the account whose backup must be locked
|
|
12
12
|
def initialize(account:)
|
|
13
13
|
@account = account
|
|
14
14
|
end
|
|
@@ -24,10 +24,14 @@ module Imap::Backup
|
|
|
24
24
|
Logger.logger.info("Stale lockfile '#{account.lockfile_path}' found. Removing it.")
|
|
25
25
|
lockfile.remove
|
|
26
26
|
else
|
|
27
|
-
|
|
27
|
+
Serializer::DirectoryMaker.new(files_path: account.files_path).run
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
begin
|
|
31
|
+
lockfile.with_lock do
|
|
32
|
+
block.call
|
|
33
|
+
end
|
|
34
|
+
rescue Lockfile::ProcessStartTimeUnavailableError
|
|
31
35
|
block.call
|
|
32
36
|
end
|
|
33
37
|
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
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require "pathname"
|
|
2
2
|
|
|
3
3
|
require "imap/backup/account/folder"
|
|
4
|
-
require "imap/backup/account/folder_ensurer"
|
|
5
4
|
require "imap/backup/serializer"
|
|
5
|
+
require "imap/backup/serializer/directory_maker"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
6
7
|
|
|
7
8
|
module Imap; end
|
|
8
9
|
|
|
@@ -13,6 +14,7 @@ module Imap::Backup
|
|
|
13
14
|
class Account::SerializedFolders
|
|
14
15
|
include Enumerable
|
|
15
16
|
|
|
17
|
+
# @param account [Account] the account whose serialized folders are iterated
|
|
16
18
|
def initialize(account:)
|
|
17
19
|
@account = account
|
|
18
20
|
end
|
|
@@ -26,7 +28,10 @@ module Imap::Backup
|
|
|
26
28
|
|
|
27
29
|
glob.each do |path|
|
|
28
30
|
name = path.relative_path_from(base).to_s[0..-6]
|
|
29
|
-
|
|
31
|
+
files_path = Serializer::Files::Path.new(
|
|
32
|
+
base_path: account.local_path, folder_name: name
|
|
33
|
+
)
|
|
34
|
+
serializer = Serializer.new(files_path: files_path)
|
|
30
35
|
folder = Account::Folder.new(account.client, name)
|
|
31
36
|
block.call(serializer, folder)
|
|
32
37
|
end
|
|
@@ -40,7 +45,10 @@ module Imap::Backup
|
|
|
40
45
|
|
|
41
46
|
glob.each do |path|
|
|
42
47
|
name = path.relative_path_from(base).to_s[0..-6]
|
|
43
|
-
|
|
48
|
+
files_path = Serializer::Files::Path.new(
|
|
49
|
+
base_path: account.local_path, folder_name: name
|
|
50
|
+
)
|
|
51
|
+
serializer = Serializer.new(files_path: files_path)
|
|
44
52
|
block.call(serializer)
|
|
45
53
|
end
|
|
46
54
|
end
|
|
@@ -68,7 +76,10 @@ module Imap::Backup
|
|
|
68
76
|
|
|
69
77
|
def glob
|
|
70
78
|
@glob ||= begin
|
|
71
|
-
|
|
79
|
+
files_path = Serializer::Files::Path.new(
|
|
80
|
+
base_path: account.local_path, folder_name: nil
|
|
81
|
+
)
|
|
82
|
+
Serializer::DirectoryMaker.new(files_path: files_path).run
|
|
72
83
|
|
|
73
84
|
pattern = File.join(account.local_path, "**", "*.imap")
|
|
74
85
|
Pathname.glob(pattern)
|
data/lib/imap/backup/account.rb
CHANGED
|
@@ -4,7 +4,6 @@ require "imap/backup/account/client_factory"
|
|
|
4
4
|
|
|
5
5
|
module Imap; end
|
|
6
6
|
|
|
7
|
-
# rubocop:disable Metrics/ClassLength
|
|
8
7
|
module Imap::Backup
|
|
9
8
|
# Contains the attributes relating to an email account.
|
|
10
9
|
class Account
|
|
@@ -65,6 +64,25 @@ module Imap::Backup
|
|
|
65
64
|
# @return [String] one of "active" (the default), "archived", or "offline"
|
|
66
65
|
attr_reader :status
|
|
67
66
|
|
|
67
|
+
# @param options [Hash] Account attributes
|
|
68
|
+
# @option opts [String] :username the username of the account (usually the same as the email
|
|
69
|
+
# address)
|
|
70
|
+
# @option opts [String] :password the password of the account
|
|
71
|
+
# @option opts [String] :local_path the path where backups will be saved
|
|
72
|
+
# @option opts [Array<String>] :folders (nil) the list of folders that have been configured for
|
|
73
|
+
# the Account
|
|
74
|
+
# @option opts [Boolean] :folder_blacklist (false) whether the folders attribute is a blacklist
|
|
75
|
+
# @option opts [Boolean] :mirror_mode (false) whether to run in mirror mode
|
|
76
|
+
# @option opts [String] :server the address of the IMAP server
|
|
77
|
+
# @option opts [Hash] :connection_options (nil) additional connection options for the IMAP
|
|
78
|
+
# server
|
|
79
|
+
# @option opts [String] :download_strategy (nil) the name of the download strategy to adopt
|
|
80
|
+
# during backups
|
|
81
|
+
# @option opts [Integer] :multi_fetch_size (nil) the number of emails to fetch from the IMAP
|
|
82
|
+
# server at a time
|
|
83
|
+
# @option opts [Boolean] :reset_seen_flags_after_fetch (false) whether to reset seen flags after
|
|
84
|
+
# fetching messages
|
|
85
|
+
# @option opts [String] :status ("active") the status of the account
|
|
68
86
|
def initialize(options)
|
|
69
87
|
check_options!(options)
|
|
70
88
|
@username = options[:username]
|
|
@@ -131,19 +149,19 @@ module Imap::Backup
|
|
|
131
149
|
# @return [Hash] all Account data for serialization
|
|
132
150
|
def to_h
|
|
133
151
|
h = {
|
|
134
|
-
username:
|
|
135
|
-
password:
|
|
152
|
+
username: username,
|
|
153
|
+
password: password,
|
|
136
154
|
status: status
|
|
137
155
|
}
|
|
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] =
|
|
156
|
+
h[:local_path] = local_path if local_path
|
|
157
|
+
h[:folders] = folders if folders
|
|
158
|
+
h[:folder_blacklist] = true if folder_blacklist
|
|
159
|
+
h[:mirror_mode] = true if mirror_mode
|
|
160
|
+
h[:server] = server if server
|
|
161
|
+
h[:connection_options] = connection_options if connection_options
|
|
144
162
|
h[:multi_fetch_size] = multi_fetch_size
|
|
145
|
-
if
|
|
146
|
-
h[:reset_seen_flags_after_fetch] =
|
|
163
|
+
if reset_seen_flags_after_fetch
|
|
164
|
+
h[:reset_seen_flags_after_fetch] = reset_seen_flags_after_fetch
|
|
147
165
|
end
|
|
148
166
|
h
|
|
149
167
|
end
|
|
@@ -169,6 +187,11 @@ module Imap::Backup
|
|
|
169
187
|
update(:local_path, value)
|
|
170
188
|
end
|
|
171
189
|
|
|
190
|
+
# @return [Serializer::Files::Path] the base files path for the account
|
|
191
|
+
def files_path
|
|
192
|
+
Serializer::Files::Path.new(base_path: local_path, folder_name: nil)
|
|
193
|
+
end
|
|
194
|
+
|
|
172
195
|
# @raise [RuntimeError] if the local_path is not set
|
|
173
196
|
# @return [String] the path to the lockfile for the account
|
|
174
197
|
def lockfile_path
|
|
@@ -216,12 +239,11 @@ module Imap::Backup
|
|
|
216
239
|
def connection_options=(value)
|
|
217
240
|
# Ensure we've loaded the connection_options
|
|
218
241
|
connection_options
|
|
219
|
-
parsed =
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
end
|
|
242
|
+
parsed = if value == ""
|
|
243
|
+
nil
|
|
244
|
+
else
|
|
245
|
+
JSON.parse(value, symbolize_names: true)
|
|
246
|
+
end
|
|
225
247
|
update(:connection_options, parsed)
|
|
226
248
|
end
|
|
227
249
|
|
|
@@ -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
|