imap-backup 12.0.0 → 12.1.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 +14 -14
- data/docs/development.md +19 -19
- data/lib/imap/backup/cli/folder_enumerator.rb +3 -3
- data/lib/imap/backup/cli/transfer.rb +163 -0
- data/lib/imap/backup/cli.rb +38 -10
- data/lib/imap/backup/version.rb +1 -1
- data/lib/retry_on_error.rb +2 -1
- metadata +3 -4
- data/lib/imap/backup/cli/migrate.rb +0 -87
- data/lib/imap/backup/cli/mirror.rb +0 -97
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ed7a9fd869f7b1ea66f5dc0816a4ce35d77c150bcf8170e2d778e893fda43e9d
|
|
4
|
+
data.tar.gz: e75c535f849eb326906c1965a1e86507676209cf41cae5b98ad62e894912d9b8
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7ced0838fae828c771e831441fc3caf71b0465f4da630c3580e1ff842c687e51ad92d7e1913e7f8d48e9bb2c2db2eeee60eea3e2dcb464fda2b51ffc9252d8ea
|
|
7
|
+
data.tar.gz: d751a6fb0e2eebc107f539cb80609d353bb0c07126fb09e5001bea69476ca9eaa5f1ffce7e6bab27f7efdc149be213997d20c741cc2b60bf3ccf6a3fd0bd810c
|
data/README.md
CHANGED
|
@@ -113,7 +113,7 @@ These are activated via two settings:
|
|
|
113
113
|
As with all performance tweaks, there are trade-offs.
|
|
114
114
|
If you are using a small virtual server or Raspberry Pi
|
|
115
115
|
to run your backups, you will probably want to leave
|
|
116
|
-
the
|
|
116
|
+
the default settings.
|
|
117
117
|
If, on the other hand, you are using a computer with a
|
|
118
118
|
fair bit of RAM, and you are dealing with a *lot* of email,
|
|
119
119
|
then changing these settings may be worthwhile.
|
|
@@ -122,16 +122,17 @@ then changing these settings may be worthwhile.
|
|
|
122
122
|
|
|
123
123
|
This setting affects all account backups.
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
from scratch.
|
|
125
|
+
By default, `imap-backup` uses the "delay metadata" strategy.
|
|
126
|
+
As messages are being backed-up, the message *text*
|
|
127
|
+
is written to disk, while the related metadata is stored in memory.
|
|
129
128
|
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
While this uses a little more memory, it avoids rewiting a growing JSON
|
|
130
|
+
file for every message, speeding things up and reducing disk wear.
|
|
132
131
|
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
The alternative strategy, called "direct", writes everything to disk
|
|
133
|
+
as it is received. This method is slower, but has the advantage
|
|
134
|
+
of using slightly less memory, which may be important on very
|
|
135
|
+
resource-limited systems, like Raspberry Pis.
|
|
135
136
|
|
|
136
137
|
## Multi-fetch Size
|
|
137
138
|
|
|
@@ -140,12 +141,11 @@ By default, during backup, each message is downloaded one-by-one.
|
|
|
140
141
|
Using this setting, you can download chunks of emails at a time,
|
|
141
142
|
potentially speeding up the process.
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
of messages downloaded at a time.
|
|
144
|
+
Using multi-fetch *will* mean that the backup process will use
|
|
145
|
+
more memory - equivalent to the size of the groups of messages
|
|
146
|
+
that are downloaded.
|
|
147
147
|
|
|
148
|
-
This behaviour may also exceed limits on your email provider,
|
|
148
|
+
This behaviour may also exceed the rate limits on your email provider,
|
|
149
149
|
so it's best to check before cranking it up!
|
|
150
150
|
|
|
151
151
|
# Troubleshooting
|
data/docs/development.md
CHANGED
|
@@ -6,25 +6,26 @@
|
|
|
6
6
|
|
|
7
7
|
# Development
|
|
8
8
|
|
|
9
|
-
A
|
|
10
|
-
|
|
9
|
+
A Dockerfile is available to allow testing with all available Ruby versions,
|
|
10
|
+
see the `dev/container` directory.
|
|
11
11
|
|
|
12
12
|
# Testing
|
|
13
13
|
|
|
14
14
|
## Feature Specs
|
|
15
15
|
|
|
16
|
-
Specs under `specs/features` are integration specs run against
|
|
17
|
-
controlled by Docker Compose.
|
|
18
|
-
|
|
16
|
+
Specs under `specs/features` are integration specs run against
|
|
17
|
+
two local IMAP servers controlled by Docker Compose.
|
|
18
|
+
|
|
19
|
+
Start them before running the test suite
|
|
19
20
|
|
|
20
21
|
```sh
|
|
21
|
-
$ docker-compose up -d
|
|
22
|
+
$ docker-compose -f dev/docker-compose.yml up -d
|
|
22
23
|
```
|
|
23
24
|
|
|
24
25
|
or, with Podman
|
|
25
26
|
|
|
26
27
|
```sh
|
|
27
|
-
$ podman-compose -f docker-compose.yml up -d
|
|
28
|
+
$ podman-compose -f dev/docker-compose.yml up -d
|
|
28
29
|
```
|
|
29
30
|
|
|
30
31
|
```sh
|
|
@@ -62,24 +63,23 @@ use `last_command_started.output`.
|
|
|
62
63
|
|
|
63
64
|
```ruby
|
|
64
65
|
require "net/imap"
|
|
66
|
+
require_relative "spec/features/support/30_email_server_helpers"
|
|
65
67
|
|
|
66
|
-
|
|
67
|
-
username = "address@example.com"
|
|
68
|
-
imap.login(username, "pass")
|
|
68
|
+
include EmailServerHelpers
|
|
69
69
|
|
|
70
|
-
|
|
71
|
-
response = imap.append("INBOX", message, nil, nil)
|
|
70
|
+
test_connection = test_server_connection_parameters
|
|
72
71
|
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
test_imap = Net::IMAP.new(test_connection[:server], test_connection[:connection_options])
|
|
73
|
+
test_imap.login(test_connection[:username], test_connection[:password])
|
|
75
74
|
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
message = "From: #{test_connection[:username]}\nSubject: Some Subject\n\nHello!\n"
|
|
76
|
+
response = test_imap.append("INBOX", message, nil, nil)
|
|
78
77
|
|
|
79
|
-
|
|
78
|
+
test_imap.examine("INBOX")
|
|
79
|
+
uids = test_imap.uid_search(["ALL"]).sort
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
fetch_data_items = test_imap.uid_fetch(uids, ["BODY[]"])
|
|
82
|
+
```
|
|
83
83
|
|
|
84
84
|
# Contributing
|
|
85
85
|
|
|
@@ -30,7 +30,7 @@ module Imap::Backup
|
|
|
30
30
|
Pathname.glob(glob) do |path|
|
|
31
31
|
name = source_folder_name(path)
|
|
32
32
|
serializer = Serializer.new(source_local_path, name)
|
|
33
|
-
folder =
|
|
33
|
+
folder = destination_folder_for_path(name)
|
|
34
34
|
yield serializer, folder
|
|
35
35
|
end
|
|
36
36
|
end
|
|
@@ -55,8 +55,8 @@ module Imap::Backup
|
|
|
55
55
|
end
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
parts = name.split(
|
|
58
|
+
def destination_folder_for_path(name)
|
|
59
|
+
parts = name.split("/")
|
|
60
60
|
no_source_prefix =
|
|
61
61
|
if source_prefix_clipped != "" && parts.first == source_prefix_clipped
|
|
62
62
|
parts[1..]
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
require "imap/backup/cli/backup"
|
|
2
|
+
require "imap/backup/cli/helpers"
|
|
3
|
+
require "imap/backup/cli/folder_enumerator"
|
|
4
|
+
require "imap/backup/migrator"
|
|
5
|
+
require "imap/backup/mirror"
|
|
6
|
+
|
|
7
|
+
module Imap; end
|
|
8
|
+
|
|
9
|
+
module Imap::Backup
|
|
10
|
+
class CLI::Transfer < Thor
|
|
11
|
+
include Thor::Actions
|
|
12
|
+
include CLI::Helpers
|
|
13
|
+
|
|
14
|
+
ACTIONS = %i(migrate mirror).freeze
|
|
15
|
+
|
|
16
|
+
attr_reader :action
|
|
17
|
+
attr_accessor :automatic_namespaces
|
|
18
|
+
attr_accessor :config_path
|
|
19
|
+
attr_accessor :destination_delimiter
|
|
20
|
+
attr_reader :destination_email
|
|
21
|
+
attr_accessor :destination_prefix
|
|
22
|
+
attr_reader :options
|
|
23
|
+
attr_accessor :reset
|
|
24
|
+
attr_accessor :source_delimiter
|
|
25
|
+
attr_reader :source_email
|
|
26
|
+
attr_accessor :source_prefix
|
|
27
|
+
|
|
28
|
+
def initialize(action, source_email, destination_email, options)
|
|
29
|
+
super([])
|
|
30
|
+
@action = action
|
|
31
|
+
@source_email = source_email
|
|
32
|
+
@destination_email = destination_email
|
|
33
|
+
@options = options
|
|
34
|
+
@automatic_namespaces = nil
|
|
35
|
+
@config_path = nil
|
|
36
|
+
@destination_delimiter = nil
|
|
37
|
+
@destination_prefix = nil
|
|
38
|
+
@reset = nil
|
|
39
|
+
@source_delimiter = nil
|
|
40
|
+
@source_prefix = nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
no_commands do
|
|
44
|
+
def run
|
|
45
|
+
raise "Unknown action '#{action}'" if !ACTIONS.include?(action)
|
|
46
|
+
|
|
47
|
+
process_options!
|
|
48
|
+
prepare_mirror if action == :mirror
|
|
49
|
+
|
|
50
|
+
folders.each do |serializer, folder|
|
|
51
|
+
case action
|
|
52
|
+
when :migrate
|
|
53
|
+
Migrator.new(serializer, folder, reset: reset).run
|
|
54
|
+
when :mirror
|
|
55
|
+
Mirror.new(serializer, folder).run
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def process_options!
|
|
61
|
+
self.automatic_namespaces = options[:automatic_namespaces] || false
|
|
62
|
+
self.config_path = options[:config]
|
|
63
|
+
self.destination_delimiter = options[:destination_delimiter]
|
|
64
|
+
self.destination_prefix = options[:destination_prefix]
|
|
65
|
+
self.source_delimiter = options[:source_delimiter]
|
|
66
|
+
self.source_prefix = options[:source_prefix]
|
|
67
|
+
self.reset = options[:reset] || false
|
|
68
|
+
check_accounts!
|
|
69
|
+
choose_prefixes_and_delimiters!
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def check_accounts!
|
|
73
|
+
if destination_email == source_email
|
|
74
|
+
raise "Source and destination accounts cannot be the same!"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
raise "Account '#{destination_email}' does not exist" if !destination_account
|
|
78
|
+
|
|
79
|
+
raise "Account '#{source_email}' does not exist" if !source_account
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def choose_prefixes_and_delimiters!
|
|
83
|
+
if automatic_namespaces
|
|
84
|
+
ensure_no_prefix_or_delimiter_parameters!
|
|
85
|
+
query_servers_for_settings
|
|
86
|
+
else
|
|
87
|
+
add_prefix_and_delimiter_defaults
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def ensure_no_prefix_or_delimiter_parameters!
|
|
92
|
+
if destination_delimiter
|
|
93
|
+
raise "--automatic-namespaces is incompatible with --destination-delimiter"
|
|
94
|
+
end
|
|
95
|
+
if destination_prefix
|
|
96
|
+
raise "--automatic-namespaces is incompatible with --destination-prefix"
|
|
97
|
+
end
|
|
98
|
+
raise "--automatic-namespaces is incompatible with --source-delimiter" if source_delimiter
|
|
99
|
+
raise "--automatic-namespaces is incompatible with --source-prefix" if source_prefix
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def query_servers_for_settings
|
|
103
|
+
self.destination_prefix, self.destination_delimiter = account_settings(destination_account)
|
|
104
|
+
self.source_prefix, self.source_delimiter = account_settings(source_account)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def account_settings(account)
|
|
108
|
+
namespaces = account.client.namespace
|
|
109
|
+
personal = namespaces.personal.first
|
|
110
|
+
[personal.prefix, personal.delim]
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def add_prefix_and_delimiter_defaults
|
|
114
|
+
self.destination_delimiter ||= "/"
|
|
115
|
+
self.destination_prefix ||= ""
|
|
116
|
+
self.source_delimiter ||= "/"
|
|
117
|
+
self.source_prefix ||= ""
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def prepare_mirror
|
|
121
|
+
warn_if_source_account_is_not_in_mirror_mode
|
|
122
|
+
|
|
123
|
+
CLI::Backup.new(config: config_path, accounts: source_email).run
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def warn_if_source_account_is_not_in_mirror_mode
|
|
127
|
+
return if source_account.mirror_mode
|
|
128
|
+
|
|
129
|
+
message =
|
|
130
|
+
"The account '#{source_account.username}' " \
|
|
131
|
+
"is not set up to make mirror backups"
|
|
132
|
+
Logger.logger.warn message
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def config
|
|
136
|
+
@config ||= load_config(config: config_path)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def enumerator_options
|
|
140
|
+
{
|
|
141
|
+
destination: destination_account,
|
|
142
|
+
destination_delimiter: destination_delimiter,
|
|
143
|
+
destination_prefix: destination_prefix,
|
|
144
|
+
source: source_account,
|
|
145
|
+
source_delimiter: source_delimiter,
|
|
146
|
+
source_prefix: source_prefix
|
|
147
|
+
}
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def folders
|
|
151
|
+
CLI::FolderEnumerator.new(**enumerator_options)
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def destination_account
|
|
155
|
+
config.accounts.find { |a| a.username == destination_email }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def source_account
|
|
159
|
+
config.accounts.find { |a| a.username == source_email }
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
data/lib/imap/backup/cli.rb
CHANGED
|
@@ -10,18 +10,38 @@ module Imap::Backup
|
|
|
10
10
|
autoload :Backup, "imap/backup/cli/backup"
|
|
11
11
|
autoload :Folders, "imap/backup/cli/folders"
|
|
12
12
|
autoload :Local, "imap/backup/cli/local"
|
|
13
|
-
autoload :Migrate, "imap/backup/cli/migrate"
|
|
14
|
-
autoload :Mirror, "imap/backup/cli/mirror"
|
|
15
13
|
autoload :Remote, "imap/backup/cli/remote"
|
|
16
14
|
autoload :Restore, "imap/backup/cli/restore"
|
|
17
15
|
autoload :Setup, "imap/backup/cli/setup"
|
|
18
16
|
autoload :Stats, "imap/backup/cli/stats"
|
|
17
|
+
autoload :Transfer, "imap/backup/cli/transfer"
|
|
19
18
|
autoload :Utils, "imap/backup/cli/utils"
|
|
20
19
|
|
|
21
20
|
include Helpers
|
|
22
21
|
|
|
23
22
|
VERSION_ARGUMENTS = %w(-v --version).freeze
|
|
24
23
|
|
|
24
|
+
NAMESPACE_CONFIGURATION_DESCRIPTION = <<~DESC.freeze
|
|
25
|
+
Some IMAP servers use namespaces (i.e. prefixes like "INBOX"),
|
|
26
|
+
while others, while others concatenate the names of subfolders
|
|
27
|
+
with a charater ("delimiter") other than "/".
|
|
28
|
+
|
|
29
|
+
In these cases there are two choices.
|
|
30
|
+
|
|
31
|
+
You can use the `--automatic-namespaces` option.
|
|
32
|
+
This wil query the source and detination servers for their
|
|
33
|
+
namespace configuration and will adapt paths accordingly.
|
|
34
|
+
This option requires that both the source and destination
|
|
35
|
+
servers are available and work with the provided parameters
|
|
36
|
+
and authentication.
|
|
37
|
+
|
|
38
|
+
If automatic configuration does not work as desired, there are the
|
|
39
|
+
`--source-prefix=`, `--source-delimiter=`,
|
|
40
|
+
`--destination-prefix=` and `--destination-delimiter=` parameters.
|
|
41
|
+
To check what values you should use, check the output of the
|
|
42
|
+
`imap-backup remote namespaces EMAIL` command.
|
|
43
|
+
DESC
|
|
44
|
+
|
|
25
45
|
default_task :backup
|
|
26
46
|
|
|
27
47
|
def self.start(*args)
|
|
@@ -83,19 +103,22 @@ module Imap::Backup
|
|
|
83
103
|
All emails which have been backed up for the "source account" (SOURCE_EMAIL) are
|
|
84
104
|
uploaded to the "destination account" (DESTINATION_EMAIL).
|
|
85
105
|
|
|
86
|
-
|
|
87
|
-
use the `--source-prefix=` and/or `--destination-prefix=` options.
|
|
106
|
+
Some configuration may be necessary, as follows:
|
|
88
107
|
|
|
89
|
-
|
|
90
|
-
use the `--source-delimiter=` and/or `--destination-delimiter=` options.
|
|
108
|
+
#{NAMESPACE_CONFIGURATION_DESCRIPTION}
|
|
91
109
|
|
|
92
|
-
|
|
110
|
+
Finally, if you want to delete existing emails in destination folders,
|
|
93
111
|
use the `--reset` option. In this case, all existing emails are
|
|
94
112
|
deleted before uploading the migrated emails.
|
|
95
113
|
DESC
|
|
96
114
|
config_option
|
|
97
115
|
quiet_option
|
|
98
116
|
verbose_option
|
|
117
|
+
method_option(
|
|
118
|
+
"automatic-namespaces",
|
|
119
|
+
type: :boolean,
|
|
120
|
+
desc: "automatically choose delimiters and prefixes"
|
|
121
|
+
)
|
|
99
122
|
method_option(
|
|
100
123
|
"destination-delimiter",
|
|
101
124
|
type: :string,
|
|
@@ -126,7 +149,7 @@ module Imap::Backup
|
|
|
126
149
|
)
|
|
127
150
|
def migrate(source_email, destination_email)
|
|
128
151
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
|
129
|
-
|
|
152
|
+
Transfer.new(:migrate, source_email, destination_email, non_logging_options).run
|
|
130
153
|
end
|
|
131
154
|
|
|
132
155
|
desc(
|
|
@@ -140,7 +163,7 @@ module Imap::Backup
|
|
|
140
163
|
If a folder list is configured for the SOURCE_EMAIL account,
|
|
141
164
|
only the folders indicated by the setting are copied.
|
|
142
165
|
|
|
143
|
-
First, runs the download of the SOURCE_EMAIL account.
|
|
166
|
+
First, it runs the download of the SOURCE_EMAIL account.
|
|
144
167
|
If the SOURCE_EMAIL account is **not** configured to be in 'mirror' mode,
|
|
145
168
|
a warning is printed.
|
|
146
169
|
|
|
@@ -152,6 +175,11 @@ module Imap::Backup
|
|
|
152
175
|
config_option
|
|
153
176
|
quiet_option
|
|
154
177
|
verbose_option
|
|
178
|
+
method_option(
|
|
179
|
+
"automatic-namespaces",
|
|
180
|
+
type: :boolean,
|
|
181
|
+
desc: "automatically choose delimiters and prefixes"
|
|
182
|
+
)
|
|
155
183
|
method_option(
|
|
156
184
|
"destination-delimiter",
|
|
157
185
|
type: :string,
|
|
@@ -176,7 +204,7 @@ module Imap::Backup
|
|
|
176
204
|
)
|
|
177
205
|
def mirror(source_email, destination_email)
|
|
178
206
|
non_logging_options = Imap::Backup::Logger.setup_logging(options)
|
|
179
|
-
|
|
207
|
+
Transfer.new(:mirror, source_email, destination_email, non_logging_options).run
|
|
180
208
|
end
|
|
181
209
|
|
|
182
210
|
desc "remote SUBCOMMAND [OPTIONS]", "View info about online accounts"
|
data/lib/imap/backup/version.rb
CHANGED
data/lib/retry_on_error.rb
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
module RetryOnError
|
|
2
|
-
def retry_on_error(errors:, limit: 10)
|
|
2
|
+
def retry_on_error(errors:, limit: 10, on_error: nil)
|
|
3
3
|
tries ||= 1
|
|
4
4
|
yield
|
|
5
5
|
rescue *errors => e
|
|
6
6
|
if tries < limit
|
|
7
7
|
message = "#{e}, attempt #{tries} of #{limit}"
|
|
8
8
|
Imap::Backup::Logger.logger.debug message
|
|
9
|
+
on_error&.call
|
|
9
10
|
tries += 1
|
|
10
11
|
retry
|
|
11
12
|
end
|
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: 12.
|
|
4
|
+
version: 12.1.0
|
|
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-09-
|
|
11
|
+
date: 2023-09-25 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: highline
|
|
@@ -247,12 +247,11 @@ files:
|
|
|
247
247
|
- lib/imap/backup/cli/helpers.rb
|
|
248
248
|
- lib/imap/backup/cli/local.rb
|
|
249
249
|
- lib/imap/backup/cli/local/check.rb
|
|
250
|
-
- lib/imap/backup/cli/migrate.rb
|
|
251
|
-
- lib/imap/backup/cli/mirror.rb
|
|
252
250
|
- lib/imap/backup/cli/remote.rb
|
|
253
251
|
- lib/imap/backup/cli/restore.rb
|
|
254
252
|
- lib/imap/backup/cli/setup.rb
|
|
255
253
|
- lib/imap/backup/cli/stats.rb
|
|
254
|
+
- lib/imap/backup/cli/transfer.rb
|
|
256
255
|
- lib/imap/backup/cli/utils.rb
|
|
257
256
|
- lib/imap/backup/client/apple_mail.rb
|
|
258
257
|
- lib/imap/backup/client/automatic_login_wrapper.rb
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
require "imap/backup/cli/folder_enumerator"
|
|
2
|
-
require "imap/backup/migrator"
|
|
3
|
-
|
|
4
|
-
module Imap; end
|
|
5
|
-
|
|
6
|
-
module Imap::Backup
|
|
7
|
-
class CLI::Migrate < Thor
|
|
8
|
-
include Thor::Actions
|
|
9
|
-
include CLI::Helpers
|
|
10
|
-
|
|
11
|
-
attr_reader :destination_delimiter
|
|
12
|
-
attr_reader :destination_email
|
|
13
|
-
attr_reader :destination_prefix
|
|
14
|
-
attr_reader :config_path
|
|
15
|
-
attr_reader :reset
|
|
16
|
-
attr_reader :source_delimiter
|
|
17
|
-
attr_reader :source_email
|
|
18
|
-
attr_reader :source_prefix
|
|
19
|
-
|
|
20
|
-
def initialize(
|
|
21
|
-
source_email,
|
|
22
|
-
destination_email,
|
|
23
|
-
config: nil,
|
|
24
|
-
destination_delimiter: "/",
|
|
25
|
-
destination_prefix: "",
|
|
26
|
-
reset: false,
|
|
27
|
-
source_delimiter: "/",
|
|
28
|
-
source_prefix: ""
|
|
29
|
-
)
|
|
30
|
-
super([])
|
|
31
|
-
@destination_delimiter = destination_delimiter
|
|
32
|
-
@destination_email = destination_email
|
|
33
|
-
@destination_prefix = destination_prefix
|
|
34
|
-
@config_path = config
|
|
35
|
-
@reset = reset
|
|
36
|
-
@source_delimiter = source_delimiter
|
|
37
|
-
@source_email = source_email
|
|
38
|
-
@source_prefix = source_prefix
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
no_commands do
|
|
42
|
-
def run
|
|
43
|
-
check_accounts!
|
|
44
|
-
folders.each do |serializer, folder|
|
|
45
|
-
Migrator.new(serializer, folder, reset: reset).run
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
def check_accounts!
|
|
50
|
-
if destination_email == source_email
|
|
51
|
-
raise "Source and destination accounts cannot be the same!"
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
raise "Account '#{destination_email}' does not exist" if !destination_account
|
|
55
|
-
|
|
56
|
-
raise "Account '#{source_email}' does not exist" if !source_account
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def config
|
|
60
|
-
@config ||= load_config(config: config_path)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def enumerator_options
|
|
64
|
-
{
|
|
65
|
-
destination: destination_account,
|
|
66
|
-
destination_delimiter: destination_delimiter,
|
|
67
|
-
destination_prefix: destination_prefix,
|
|
68
|
-
source: source_account,
|
|
69
|
-
source_delimiter: source_delimiter,
|
|
70
|
-
source_prefix: source_prefix
|
|
71
|
-
}
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def folders
|
|
75
|
-
CLI::FolderEnumerator.new(**enumerator_options)
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
def destination_account
|
|
79
|
-
config.accounts.find { |a| a.username == destination_email }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
def source_account
|
|
83
|
-
config.accounts.find { |a| a.username == source_email }
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
require "imap/backup/cli/folder_enumerator"
|
|
2
|
-
require "imap/backup/mirror"
|
|
3
|
-
|
|
4
|
-
module Imap; end
|
|
5
|
-
|
|
6
|
-
module Imap::Backup
|
|
7
|
-
class CLI::Mirror < Thor
|
|
8
|
-
include Thor::Actions
|
|
9
|
-
include CLI::Helpers
|
|
10
|
-
|
|
11
|
-
attr_reader :destination_delimiter
|
|
12
|
-
attr_reader :destination_email
|
|
13
|
-
attr_reader :destination_prefix
|
|
14
|
-
attr_reader :config_path
|
|
15
|
-
attr_reader :source_delimiter
|
|
16
|
-
attr_reader :source_email
|
|
17
|
-
attr_reader :source_prefix
|
|
18
|
-
|
|
19
|
-
def initialize(
|
|
20
|
-
source_email,
|
|
21
|
-
destination_email,
|
|
22
|
-
config: nil,
|
|
23
|
-
destination_delimiter: "/",
|
|
24
|
-
destination_prefix: "",
|
|
25
|
-
source_delimiter: "/",
|
|
26
|
-
source_prefix: ""
|
|
27
|
-
)
|
|
28
|
-
super([])
|
|
29
|
-
@destination_delimiter = destination_delimiter
|
|
30
|
-
@destination_email = destination_email
|
|
31
|
-
@destination_prefix = destination_prefix
|
|
32
|
-
@config_path = config
|
|
33
|
-
@source_delimiter = source_delimiter
|
|
34
|
-
@source_email = source_email
|
|
35
|
-
@source_prefix = source_prefix
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
no_commands do
|
|
39
|
-
def run
|
|
40
|
-
check_accounts!
|
|
41
|
-
warn_if_source_account_is_not_in_mirror_mode
|
|
42
|
-
|
|
43
|
-
CLI::Backup.new(config: config_path, accounts: source_email).run
|
|
44
|
-
|
|
45
|
-
folders.each do |serializer, folder|
|
|
46
|
-
Mirror.new(serializer, folder).run
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
def check_accounts!
|
|
51
|
-
if destination_email == source_email
|
|
52
|
-
raise "Source and destination accounts cannot be the same!"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
raise "Account '#{destination_email}' does not exist" if !destination_account
|
|
56
|
-
|
|
57
|
-
raise "Account '#{source_email}' does not exist" if !source_account
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def warn_if_source_account_is_not_in_mirror_mode
|
|
61
|
-
return if source_account.mirror_mode
|
|
62
|
-
|
|
63
|
-
message =
|
|
64
|
-
"The account '#{source_account.username}' " \
|
|
65
|
-
"is not set up to make mirror backups"
|
|
66
|
-
Logger.logger.warn message
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def config
|
|
70
|
-
@config = load_config(config: config_path)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def enumerator_options
|
|
74
|
-
{
|
|
75
|
-
destination: destination_account,
|
|
76
|
-
destination_delimiter: destination_delimiter,
|
|
77
|
-
destination_prefix: destination_prefix,
|
|
78
|
-
source: source_account,
|
|
79
|
-
source_delimiter: source_delimiter,
|
|
80
|
-
source_prefix: source_prefix
|
|
81
|
-
}
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
def folders
|
|
85
|
-
CLI::FolderEnumerator.new(**enumerator_options)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def destination_account
|
|
89
|
-
config.accounts.find { |a| a.username == destination_email }
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def source_account
|
|
93
|
-
config.accounts.find { |a| a.username == source_email }
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|