imap-backup 4.1.2 → 5.0.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/imap-backup.gemspec +1 -0
- data/lib/email/mboxrd/message.rb +6 -2
- data/lib/imap/backup/account/connection.rb +4 -4
- data/lib/imap/backup/account/folder.rb +8 -0
- data/lib/imap/backup/cli/folders.rb +1 -1
- data/lib/imap/backup/cli/helpers.rb +4 -1
- data/lib/imap/backup/cli/local.rb +1 -1
- data/lib/imap/backup/cli/migrate.rb +114 -0
- data/lib/imap/backup/cli/remote.rb +15 -0
- data/lib/imap/backup/cli.rb +50 -4
- data/lib/imap/backup/client/default.rb +2 -2
- data/lib/imap/backup/downloader.rb +23 -9
- data/lib/imap/backup/migrator.rb +51 -0
- data/lib/imap/backup/sanitizer.rb +7 -4
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +10 -1
- data/lib/imap/backup/version.rb +3 -3
- data/lib/thunderbird/subdirectory.rb +3 -6
- data/spec/features/configuration/minimal_configuration.rb +15 -0
- data/spec/features/configuration/missing_configuration.rb +14 -0
- data/spec/features/folders_spec.rb +36 -0
- data/spec/features/local/list_accounts_spec.rb +12 -0
- data/spec/features/local/list_emails_spec.rb +21 -0
- data/spec/features/local/list_folders_spec.rb +21 -0
- data/spec/features/local/show_an_email_spec.rb +34 -0
- data/spec/features/migrate_spec.rb +35 -0
- data/spec/features/remote/list_account_folders_spec.rb +16 -0
- data/spec/features/support/aruba.rb +69 -0
- data/spec/features/support/email_server.rb +1 -0
- data/spec/unit/imap/backup/account/connection_spec.rb +6 -8
- data/spec/unit/imap/backup/account/folder_spec.rb +26 -5
- data/spec/unit/imap/backup/cli/helpers_spec.rb +87 -0
- data/spec/unit/imap/backup/migrator_spec.rb +58 -0
- metadata +77 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 16911b1cea1983b844c3bbf7f74c03ff7436990fb5aa4d5b49fa18f03ae2e229
|
4
|
+
data.tar.gz: 265f63995e70ba3f68b98ec72d3a12b3e95100f7e6b320026b18bcc156888a92
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a541a7830ddab458f2366bdad4924f3e6cbc3ba858651839e6dcd1a530acbbeb01e2c59a44331af73a2d77f606f2d6b39eacd2b5a705d78ec20e93d2c2f2527
|
7
|
+
data.tar.gz: 7b60170e3d29e9882f3bcbf13b04daa162434944d59311a5a03a4b1c1022a7a9501b292c3eb929d6e07039c47cc433316a338e062ced4e8a6b7ea424e6c0678b
|
data/imap-backup.gemspec
CHANGED
@@ -28,6 +28,7 @@ Gem::Specification.new do |gem|
|
|
28
28
|
gem.add_runtime_dependency "rake"
|
29
29
|
gem.add_runtime_dependency "thor", "~> 1.1"
|
30
30
|
|
31
|
+
gem.add_development_dependency "aruba", ">= 0.0.0"
|
31
32
|
gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
|
32
33
|
if RUBY_ENGINE == "jruby"
|
33
34
|
gem.add_development_dependency "pry-debugger-jruby"
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -10,7 +10,7 @@ module Email::Mboxrd
|
|
10
10
|
|
11
11
|
attr_reader :supplied_body
|
12
12
|
|
13
|
-
def self.
|
13
|
+
def self.clean_serialized(serialized)
|
14
14
|
cleaned = serialized.gsub(/^>(>*From)/, "\\1")
|
15
15
|
# Serialized messages in this format *should* start with a line
|
16
16
|
# From xxx yy zz
|
@@ -19,7 +19,11 @@ module Email::Mboxrd
|
|
19
19
|
cleaned = cleaned.sub(/^From .*[\r\n]*/, "")
|
20
20
|
end
|
21
21
|
# rubocop:enable Style/IfUnlessModifier
|
22
|
-
|
22
|
+
cleaned
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.from_serialized(serialized)
|
26
|
+
new(clean_serialized(serialized))
|
23
27
|
end
|
24
28
|
|
25
29
|
def initialize(supplied_body)
|
@@ -16,11 +16,8 @@ module Imap::Backup
|
|
16
16
|
def initialize(account)
|
17
17
|
@account = account
|
18
18
|
reset
|
19
|
-
create_account_folder
|
20
19
|
end
|
21
20
|
|
22
|
-
# TODO: Make this private once the 'folders' command
|
23
|
-
# has been removed.
|
24
21
|
def folder_names
|
25
22
|
@folder_names ||=
|
26
23
|
begin
|
@@ -53,6 +50,7 @@ module Imap::Backup
|
|
53
50
|
end
|
54
51
|
|
55
52
|
def status
|
53
|
+
ensure_account_folder
|
56
54
|
backup_folders.map do |folder|
|
57
55
|
s = Serializer::Mbox.new(account.local_path, folder.name)
|
58
56
|
{name: folder.name, local: s.uids, remote: folder.uids}
|
@@ -63,6 +61,7 @@ module Imap::Backup
|
|
63
61
|
Imap::Backup::Logger.logger.debug "Running backup of account: #{account.username}"
|
64
62
|
# start the connection so we get logging messages in the right order
|
65
63
|
client
|
64
|
+
ensure_account_folder
|
66
65
|
each_folder do |folder, serializer|
|
67
66
|
next if !folder.exist?
|
68
67
|
|
@@ -82,6 +81,7 @@ module Imap::Backup
|
|
82
81
|
def local_folders
|
83
82
|
return enum_for(:local_folders) if !block_given?
|
84
83
|
|
84
|
+
ensure_account_folder
|
85
85
|
glob = File.join(account.local_path, "**", "*.imap")
|
86
86
|
base = Pathname.new(account.local_path)
|
87
87
|
Pathname.glob(glob) do |path|
|
@@ -176,7 +176,7 @@ module Imap::Backup
|
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
-
def
|
179
|
+
def ensure_account_folder
|
180
180
|
Utils.make_folder(
|
181
181
|
File.dirname(account.local_path),
|
182
182
|
File.basename(account.local_path),
|
@@ -111,6 +111,14 @@ module Imap::Backup
|
|
111
111
|
extract_uid(response)
|
112
112
|
end
|
113
113
|
|
114
|
+
def clear
|
115
|
+
existing = uids
|
116
|
+
# Use read-write access, via `select`
|
117
|
+
client.select(utf7_encoded_name)
|
118
|
+
client.uid_store(existing, "+FLAGS", [:Deleted])
|
119
|
+
client.expunge
|
120
|
+
end
|
121
|
+
|
114
122
|
private
|
115
123
|
|
116
124
|
def examine
|
@@ -13,7 +13,7 @@ module Imap::Backup
|
|
13
13
|
no_commands do
|
14
14
|
def run
|
15
15
|
each_connection(account_names) do |connection|
|
16
|
-
puts connection.username
|
16
|
+
puts connection.account.username
|
17
17
|
# TODO: Make folder_names private once this command
|
18
18
|
# has been removed.
|
19
19
|
folders = connection.folder_names
|
@@ -3,7 +3,10 @@ require "imap/backup/cli/accounts"
|
|
3
3
|
|
4
4
|
module Imap::Backup::CLI::Helpers
|
5
5
|
def symbolized(options)
|
6
|
-
options.each.with_object({})
|
6
|
+
options.each.with_object({}) do |(k, v), acc|
|
7
|
+
key = k.gsub("-", "_").intern
|
8
|
+
acc[key] = v
|
9
|
+
end
|
7
10
|
end
|
8
11
|
|
9
12
|
def account(email)
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require "imap/backup/migrator"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class CLI::Migrate < Thor
|
5
|
+
include Thor::Actions
|
6
|
+
|
7
|
+
attr_reader :destination_email
|
8
|
+
attr_reader :destination_prefix
|
9
|
+
attr_reader :reset
|
10
|
+
attr_reader :source_email
|
11
|
+
attr_reader :source_prefix
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
source_email,
|
15
|
+
destination_email,
|
16
|
+
destination_prefix: "",
|
17
|
+
reset: false,
|
18
|
+
source_prefix: ""
|
19
|
+
)
|
20
|
+
super([])
|
21
|
+
@destination_email = destination_email
|
22
|
+
@destination_prefix = destination_prefix
|
23
|
+
@reset = reset
|
24
|
+
@source_email = source_email
|
25
|
+
@source_prefix = source_prefix
|
26
|
+
end
|
27
|
+
|
28
|
+
no_commands do
|
29
|
+
def run
|
30
|
+
check_accounts!
|
31
|
+
folders.each do |serializer, folder|
|
32
|
+
Migrator.new(serializer, folder, reset: reset).run
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_accounts!
|
37
|
+
if destination_email == source_email
|
38
|
+
raise "Source and destination accounts cannot be the same!"
|
39
|
+
end
|
40
|
+
|
41
|
+
if !destination_account
|
42
|
+
raise "Account #{destination_email} does not exist"
|
43
|
+
end
|
44
|
+
|
45
|
+
if !source_account
|
46
|
+
raise "Account #{source_email} does not exist"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def config
|
51
|
+
Configuration.new
|
52
|
+
end
|
53
|
+
|
54
|
+
def destination_account
|
55
|
+
config.accounts.find { |a| a.username == destination_email }
|
56
|
+
end
|
57
|
+
|
58
|
+
def folders
|
59
|
+
return enum_for(:folders) if !block_given?
|
60
|
+
|
61
|
+
glob = File.join(source_local_path, "**", "*.imap")
|
62
|
+
Pathname.glob(glob) do |path|
|
63
|
+
name = source_folder_name(path)
|
64
|
+
serializer = Serializer::Mbox.new(source_local_path, name)
|
65
|
+
folder = folder_for(name)
|
66
|
+
yield serializer, folder
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def folder_for(source_folder)
|
71
|
+
no_source_prefix =
|
72
|
+
if source_prefix != "" &&
|
73
|
+
source_folder.start_with?(source_prefix)
|
74
|
+
source_folder.delete_prefix(source_prefix)
|
75
|
+
else
|
76
|
+
source_folder.to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
with_destination_prefix =
|
80
|
+
if destination_prefix &&
|
81
|
+
destination_prefix != ""
|
82
|
+
destination_prefix + no_source_prefix
|
83
|
+
else
|
84
|
+
no_source_prefix
|
85
|
+
end
|
86
|
+
|
87
|
+
Account::Folder.new(
|
88
|
+
destination_account.connection,
|
89
|
+
with_destination_prefix
|
90
|
+
)
|
91
|
+
end
|
92
|
+
|
93
|
+
def source_local_path
|
94
|
+
source_account.local_path
|
95
|
+
end
|
96
|
+
|
97
|
+
def source_account
|
98
|
+
config.accounts.find { |a| a.username == source_email }
|
99
|
+
end
|
100
|
+
|
101
|
+
def source_folder_name(imap_pathname)
|
102
|
+
base = Pathname.new(source_local_path)
|
103
|
+
imap_name = imap_pathname.relative_path_from(base).to_s
|
104
|
+
dir = File.dirname(imap_name)
|
105
|
+
stripped = File.basename(imap_name, ".imap")
|
106
|
+
if dir == "."
|
107
|
+
stripped
|
108
|
+
else
|
109
|
+
File.join(dir, stripped)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Remote < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
desc "folders EMAIL", "List account folders"
|
7
|
+
def folders(email)
|
8
|
+
connection = connection(email)
|
9
|
+
|
10
|
+
connection.folder_names.each do |name|
|
11
|
+
Kernel.puts %("#{name}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/imap/backup/cli.rb
CHANGED
@@ -9,6 +9,7 @@ module Imap::Backup
|
|
9
9
|
autoload :Backup, "imap/backup/cli/backup"
|
10
10
|
autoload :Folders, "imap/backup/cli/folders"
|
11
11
|
autoload :Local, "imap/backup/cli/local"
|
12
|
+
autoload :Migrate, "imap/backup/cli/migrate"
|
12
13
|
autoload :Remote, "imap/backup/cli/remote"
|
13
14
|
autoload :Restore, "imap/backup/cli/restore"
|
14
15
|
autoload :Setup, "imap/backup/cli/setup"
|
@@ -27,7 +28,7 @@ module Imap::Backup
|
|
27
28
|
method_option(
|
28
29
|
"accounts",
|
29
30
|
type: :string,
|
30
|
-
|
31
|
+
desc: "a comma-separated list of accounts (defaults to all configured accounts)",
|
31
32
|
aliases: ["-a"]
|
32
33
|
)
|
33
34
|
end
|
@@ -57,6 +58,54 @@ module Imap::Backup
|
|
57
58
|
Folders.new(symbolized(options)).run
|
58
59
|
end
|
59
60
|
|
61
|
+
desc "local SUBCOMMAND [OPTIONS]", "View local info"
|
62
|
+
subcommand "local", Local
|
63
|
+
|
64
|
+
desc "migrate SOURCE_EMAIL DESTINATION_EMAIL [OPTIONS]",
|
65
|
+
"[Experimental] Uploads backed-up emails from account SOURCE_EMAIL to account DESTINATION_EMAIL"
|
66
|
+
long_desc <<~DESC
|
67
|
+
All emails which have been backed up for the "source account" (SOURCE_EMAIL) are
|
68
|
+
uploaded to the "destination account" (DESTINATION_EMAIL).
|
69
|
+
|
70
|
+
When one or other account has namespaces (i.e. prefixes like "INBOX."),
|
71
|
+
use the `--source-prefix=` and/or `--destination-prefix=` options.
|
72
|
+
|
73
|
+
Usually, you should migrate to an account with empty folders.
|
74
|
+
|
75
|
+
Before migrating each folder, `imap-backup` checks if the destination
|
76
|
+
folder is empty.
|
77
|
+
|
78
|
+
If it finds a non-empty destination folder, it halts with an error.
|
79
|
+
|
80
|
+
If you are sure that these destination emails can be deleted,
|
81
|
+
use the `--reset` option. In this case, all existing emails are
|
82
|
+
deleted before uploading the migrated emails.
|
83
|
+
DESC
|
84
|
+
method_option(
|
85
|
+
"destination-prefix",
|
86
|
+
type: :string,
|
87
|
+
desc: "the prefix (namespace) to add to destination folder names",
|
88
|
+
aliases: ["-d"]
|
89
|
+
)
|
90
|
+
method_option(
|
91
|
+
"reset",
|
92
|
+
type: :boolean,
|
93
|
+
desc: "DANGER! This option deletes all messages from destination folders before uploading",
|
94
|
+
aliases: ["-r"]
|
95
|
+
)
|
96
|
+
method_option(
|
97
|
+
"source-prefix",
|
98
|
+
type: :string,
|
99
|
+
desc: "the prefix (namespace) to strip from source folder names",
|
100
|
+
aliases: ["-s"]
|
101
|
+
)
|
102
|
+
def migrate(source_email, destination_email)
|
103
|
+
Migrate.new(source_email, destination_email, symbolized(options)).run
|
104
|
+
end
|
105
|
+
|
106
|
+
desc "remote SUBCOMMAND [OPTIONS]", "View info about online accounts"
|
107
|
+
subcommand "remote", Remote
|
108
|
+
|
60
109
|
desc "restore [OPTIONS]", "This command is deprecated, use `imap-backup restore ACCOUNT`"
|
61
110
|
long_desc <<~DESC
|
62
111
|
By default, restores all local emails to their respective servers.
|
@@ -86,9 +135,6 @@ module Imap::Backup
|
|
86
135
|
Status.new(symbolized(options)).run
|
87
136
|
end
|
88
137
|
|
89
|
-
desc "local SUBCOMMAND [OPTIONS]", "View local info"
|
90
|
-
subcommand "local", Local
|
91
|
-
|
92
138
|
desc "utils SUBCOMMAND [OPTIONS]", "Various utilities"
|
93
139
|
subcommand "utils", Utils
|
94
140
|
end
|
@@ -7,8 +7,8 @@ module Imap::Backup
|
|
7
7
|
class Client::Default
|
8
8
|
extend Forwardable
|
9
9
|
def_delegators :imap, *%i(
|
10
|
-
append authenticate create disconnect examine
|
11
|
-
login responses uid_fetch uid_search
|
10
|
+
append authenticate create disconnect examine expunge
|
11
|
+
login responses select uid_fetch uid_search uid_store
|
12
12
|
)
|
13
13
|
|
14
14
|
attr_reader :args
|
@@ -13,31 +13,45 @@ module Imap::Backup
|
|
13
13
|
def run
|
14
14
|
uids = folder.uids - serializer.uids
|
15
15
|
count = uids.count
|
16
|
-
|
16
|
+
debug "#{count} new messages"
|
17
17
|
uids.each_slice(block_size).with_index do |block, i|
|
18
|
-
offset = i * block_size + 1
|
19
18
|
uids_and_bodies = folder.fetch_multi(block)
|
20
19
|
if uids_and_bodies.nil?
|
21
20
|
if block_size > 1
|
22
|
-
|
21
|
+
debug("Multi fetch failed for UIDs #{block.join(", ")}, switching to single fetches")
|
23
22
|
@block_size = 1
|
24
23
|
redo
|
25
24
|
else
|
26
|
-
|
25
|
+
debug("Fetch failed for UID #{block[0]} - skipping")
|
27
26
|
next
|
28
27
|
end
|
29
28
|
end
|
30
29
|
|
30
|
+
offset = i * block_size + 1
|
31
31
|
uids_and_bodies.each.with_index do |uid_and_body, j|
|
32
32
|
uid = uid_and_body[:uid]
|
33
33
|
body = uid_and_body[:body]
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
case
|
35
|
+
when !body
|
36
|
+
info("Fetch returned empty body - skipping")
|
37
|
+
when !uid
|
38
|
+
info("Fetch returned empty UID - skipping")
|
39
|
+
else
|
40
|
+
debug("uid: #{uid} (#{offset + j}/#{count}) - #{body.size} bytes")
|
41
|
+
serializer.save(uid, body)
|
42
|
+
end
|
39
43
|
end
|
40
44
|
end
|
41
45
|
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def info(message)
|
50
|
+
Imap::Backup::Logger.logger.info("[#{folder.name}] #{message}")
|
51
|
+
end
|
52
|
+
|
53
|
+
def debug(message)
|
54
|
+
Imap::Backup::Logger.logger.debug("[#{folder.name}] #{message}")
|
55
|
+
end
|
42
56
|
end
|
43
57
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class Migrator
|
3
|
+
attr_reader :folder
|
4
|
+
attr_reader :reset
|
5
|
+
attr_reader :serializer
|
6
|
+
|
7
|
+
def initialize(serializer, folder, reset: false)
|
8
|
+
@folder = folder
|
9
|
+
@reset = reset
|
10
|
+
@serializer = serializer
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
count = serializer.uids.count
|
15
|
+
folder.create
|
16
|
+
ensure_destination_empty!
|
17
|
+
|
18
|
+
Imap::Backup::Logger.logger.debug "[#{folder.name}] #{count} to migrate"
|
19
|
+
serializer.each_message(serializer.uids).with_index do |(uid, message), i|
|
20
|
+
next if message.nil?
|
21
|
+
|
22
|
+
log_prefix = "[#{folder.name}] uid: #{uid} (#{i + 1}/#{count}) -"
|
23
|
+
Imap::Backup::Logger.logger.debug(
|
24
|
+
"#{log_prefix} #{message.supplied_body.size} bytes"
|
25
|
+
)
|
26
|
+
|
27
|
+
folder.append(message)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def ensure_destination_empty!
|
34
|
+
if reset
|
35
|
+
folder.clear
|
36
|
+
else
|
37
|
+
fail_if_destination_not_empty!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def fail_if_destination_not_empty!
|
42
|
+
return if folder.uids.empty?
|
43
|
+
|
44
|
+
raise <<~ERROR
|
45
|
+
The destination folder '#{folder.name}' is not empty.
|
46
|
+
Pass the --reset flag if you want to clear existing emails from destination
|
47
|
+
folders before uploading.
|
48
|
+
ERROR
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,16 +1,19 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module Imap::Backup
|
2
4
|
class Sanitizer
|
5
|
+
extend Forwardable
|
6
|
+
|
3
7
|
attr_reader :output
|
4
8
|
|
9
|
+
delegate puts: :output
|
10
|
+
delegate write: :output
|
11
|
+
|
5
12
|
def initialize(output)
|
6
13
|
@output = output
|
7
14
|
@current = ""
|
8
15
|
end
|
9
16
|
|
10
|
-
def write(*args)
|
11
|
-
output.write(*args)
|
12
|
-
end
|
13
|
-
|
14
17
|
def print(*args)
|
15
18
|
@current << args.join
|
16
19
|
loop do
|
@@ -40,7 +40,16 @@ module Imap::Backup
|
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
|
-
|
43
|
+
File.open(local_folder.full_path, "w") do |f|
|
44
|
+
enumerator = Serializer::MboxEnumerator.new(serializer.mbox_pathname)
|
45
|
+
enumerator.each.with_index do |raw, i|
|
46
|
+
clean = Email::Mboxrd::Message.clean_serialized(raw)
|
47
|
+
timestamp = Time.now.strftime("%a %b %d %H:%M:%S %Y")
|
48
|
+
thunderbird_fom_line = "From - #{timestamp}"
|
49
|
+
output = "#{thunderbird_fom_line}\n#{clean}\n"
|
50
|
+
f.write output
|
51
|
+
end
|
52
|
+
end
|
44
53
|
|
45
54
|
true
|
46
55
|
end
|
data/lib/imap/backup/version.rb
CHANGED
@@ -77,17 +77,14 @@ class Thunderbird::Subdirectory
|
|
77
77
|
|
78
78
|
def check
|
79
79
|
case
|
80
|
-
when placeholder.exists? && !exists?
|
81
|
-
Kernel.puts "Can't set up folder '#{folder_path}': '#{placeholder.path}' exists, but '#{full_path}' is missing"
|
82
|
-
false
|
83
80
|
when exists? && !placeholder.exists?
|
84
|
-
Kernel.puts "Can't set up folder '#{
|
81
|
+
Kernel.puts "Can't set up folder '#{full_path}': '#{full_path}' exists, but '#{placeholder.path}' is missing"
|
85
82
|
false
|
86
83
|
when placeholder.exists? && !placeholder.regular?
|
87
|
-
Kernel.puts "Can't set up folder '#{
|
84
|
+
Kernel.puts "Can't set up folder '#{full_path}': '#{placeholder.path}' exists, but it is not a regular file"
|
88
85
|
false
|
89
86
|
when exists? && !directory?
|
90
|
-
Kernel.puts "Can't set up folder '#{
|
87
|
+
Kernel.puts "Can't set up folder '#{full_path}': '#{full_path}' exists, but it is not a directory"
|
91
88
|
false
|
92
89
|
else
|
93
90
|
true
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Running commands", type: :aruba do
|
4
|
+
before do
|
5
|
+
create_config(accounts: [])
|
6
|
+
run_command("imap-backup local accounts")
|
7
|
+
last_command_started.stop
|
8
|
+
end
|
9
|
+
|
10
|
+
context "when no accounts are configured" do
|
11
|
+
it "succeeds" do
|
12
|
+
expect(last_command_started).to have_exit_status(0)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Running commands", type: :aruba do
|
4
|
+
before do
|
5
|
+
run_command("imap-backup")
|
6
|
+
last_command_started.stop
|
7
|
+
end
|
8
|
+
|
9
|
+
context "when the configuration file is missing" do
|
10
|
+
it "fails" do
|
11
|
+
expect(last_command_started).not_to have_exit_status(0)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
require "imap/backup/cli/folders"
|
3
|
+
|
4
|
+
RSpec.describe "folders", type: :feature, docker: true do
|
5
|
+
include_context "imap-backup connection"
|
6
|
+
|
7
|
+
let(:options) do
|
8
|
+
{accounts: "address@example.org"}
|
9
|
+
end
|
10
|
+
let(:folder) { "my-stuff" }
|
11
|
+
let(:output) { StringIO.new }
|
12
|
+
|
13
|
+
before do
|
14
|
+
allow(Imap::Backup::CLI::Accounts).to receive(:new) { [account] }
|
15
|
+
server_create_folder folder
|
16
|
+
end
|
17
|
+
|
18
|
+
around do |example|
|
19
|
+
stdout = $stdout
|
20
|
+
$stdout = output
|
21
|
+
example.run
|
22
|
+
$stdout = stdout
|
23
|
+
end
|
24
|
+
|
25
|
+
after do
|
26
|
+
FileUtils.rm_rf local_backup_path
|
27
|
+
server_delete_folder folder
|
28
|
+
connection.disconnect
|
29
|
+
end
|
30
|
+
|
31
|
+
it "lists account folders" do
|
32
|
+
Imap::Backup::CLI::Folders.new(options).run
|
33
|
+
|
34
|
+
expect(output.string).to match(/^\tmy-stuff\n/)
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Listing accounts", type: :aruba do
|
4
|
+
before do
|
5
|
+
create_config(accounts: [{"username": "me@example.com"}])
|
6
|
+
run_command_and_stop("imap-backup local accounts")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "lists accounts" do
|
10
|
+
expect(last_command_started).to have_output("me@example.com")
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Listing emails", type: :aruba do
|
4
|
+
let(:email) { "me@example.com" }
|
5
|
+
let(:account) do
|
6
|
+
{
|
7
|
+
username: email,
|
8
|
+
local_path: File.join(config_path, email.gsub("@", "_"))
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
create_config(accounts: [account])
|
14
|
+
store_email(email: email, folder: "my_folder", subject: "Ciao")
|
15
|
+
run_command_and_stop("imap-backup local list #{email} my_folder")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "lists emails" do
|
19
|
+
expect(last_command_started).to have_output(/1: Ciao/)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Listing account folders", type: :aruba do
|
4
|
+
let(:email) { "me@example.com" }
|
5
|
+
let(:account) do
|
6
|
+
{
|
7
|
+
username: email,
|
8
|
+
local_path: File.join(config_path, email.gsub("@", "_"))
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
create_config(accounts: [account])
|
14
|
+
store_email(email: email, folder: "my_folder", body: "Hi")
|
15
|
+
run_command_and_stop("imap-backup local folders #{email}")
|
16
|
+
end
|
17
|
+
|
18
|
+
it "lists folders that have been backed up" do
|
19
|
+
expect(last_command_started).to have_output(%q("my_folder"))
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Show an email", type: :aruba do
|
4
|
+
let(:email) { "me@example.com" }
|
5
|
+
let(:account) do
|
6
|
+
{
|
7
|
+
username: email,
|
8
|
+
local_path: File.join(config_path, email.gsub("@", "_"))
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
before do
|
13
|
+
create_config(accounts: [account])
|
14
|
+
store_email(
|
15
|
+
email: email,
|
16
|
+
folder: "my_folder",
|
17
|
+
uid: 99,
|
18
|
+
from: "me@example.com",
|
19
|
+
subject: "Hello",
|
20
|
+
body: "How're things?"
|
21
|
+
)
|
22
|
+
run_command_and_stop("imap-backup local show #{email} my_folder 99")
|
23
|
+
end
|
24
|
+
|
25
|
+
it "shows the email" do
|
26
|
+
expected = <<~BODY
|
27
|
+
From: me@example.com
|
28
|
+
Subject: Hello
|
29
|
+
|
30
|
+
How're things?
|
31
|
+
BODY
|
32
|
+
expect(last_command_started).to have_output(expected)
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "Migration", type: :aruba, docker: true do
|
4
|
+
let(:email) { "me@example.com" }
|
5
|
+
let(:folder) { "my_folder" }
|
6
|
+
let(:source_account) do
|
7
|
+
{
|
8
|
+
username: email,
|
9
|
+
local_path: File.join(config_path, email.gsub("@", "_"))
|
10
|
+
}
|
11
|
+
end
|
12
|
+
let(:destination_account) { fixture("connection") }
|
13
|
+
|
14
|
+
before do
|
15
|
+
create_config(accounts: [source_account, destination_account])
|
16
|
+
store_email(email: email, folder: folder, subject: "Ciao")
|
17
|
+
run_command_and_stop("imap-backup migrate #{email} #{destination_account[:username]}")
|
18
|
+
end
|
19
|
+
|
20
|
+
after do
|
21
|
+
delete_emails(folder)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "copies email to the destination account" do
|
25
|
+
messages = server_messages(folder)
|
26
|
+
expected = <<~MESSAGE.gsub("\n", "\r\n")
|
27
|
+
From: sender@example.com
|
28
|
+
Subject: Ciao
|
29
|
+
|
30
|
+
body
|
31
|
+
|
32
|
+
MESSAGE
|
33
|
+
expect(messages[0]["BODY[]"]).to eq(expected)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "features/helper"
|
2
|
+
|
3
|
+
RSpec.describe "List account folders", type: :aruba do
|
4
|
+
let(:account) do
|
5
|
+
fixture("connection")
|
6
|
+
end
|
7
|
+
|
8
|
+
before do
|
9
|
+
create_config(accounts: [account])
|
10
|
+
run_command_and_stop("imap-backup remote folders #{account[:username]}")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "lists folders" do
|
14
|
+
expect(last_command_started).to have_output(/"INBOX"/)
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "aruba/rspec"
|
2
|
+
|
3
|
+
Aruba.configure do |config|
|
4
|
+
config.home_directory = File.expand_path("./tmp/home")
|
5
|
+
end
|
6
|
+
|
7
|
+
module ConfigurationHelpers
|
8
|
+
def config_path
|
9
|
+
File.expand_path("~/.imap-backup")
|
10
|
+
end
|
11
|
+
|
12
|
+
def create_config(accounts:, debug: false)
|
13
|
+
pathname = File.join(config_path, "config.json")
|
14
|
+
save_data = {
|
15
|
+
version: Imap::Backup::Configuration::VERSION,
|
16
|
+
accounts: accounts,
|
17
|
+
debug: debug
|
18
|
+
}
|
19
|
+
FileUtils.mkdir_p config_path
|
20
|
+
File.open(pathname, "w") { |f| f.write(JSON.pretty_generate(save_data)) }
|
21
|
+
FileUtils.chmod(0o600, pathname)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module StoreHelpers
|
26
|
+
def store_email(
|
27
|
+
email:, folder:,
|
28
|
+
uid: 1,
|
29
|
+
from: "sender@example.com",
|
30
|
+
subject: "The Subject",
|
31
|
+
body: "body"
|
32
|
+
)
|
33
|
+
account = config.accounts.find { |a| a.username == email }
|
34
|
+
raise "Account not found" if !account
|
35
|
+
FileUtils.mkdir_p account.local_path
|
36
|
+
store = Imap::Backup::Serializer::MboxStore.new(account.local_path, folder)
|
37
|
+
store.uid_validity = "42" if !store.uid_validity
|
38
|
+
serialized = to_serialized(from: from, subject: subject, body: body)
|
39
|
+
store.add(uid, serialized)
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_serialized(from:, subject:, body:)
|
43
|
+
body_and_headers = <<~BODY
|
44
|
+
From: #{from}
|
45
|
+
Subject: #{subject}
|
46
|
+
|
47
|
+
#{body}
|
48
|
+
BODY
|
49
|
+
end
|
50
|
+
|
51
|
+
def config
|
52
|
+
Imap::Backup::Configuration.new(
|
53
|
+
File.expand_path("~/.imap-backup/config.json")
|
54
|
+
)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
RSpec.configure do |config|
|
59
|
+
config.include ConfigurationHelpers, type: :aruba
|
60
|
+
config.include StoreHelpers, type: :aruba
|
61
|
+
|
62
|
+
config.before(:suite) do
|
63
|
+
FileUtils.rm_rf "./tmp/home"
|
64
|
+
end
|
65
|
+
|
66
|
+
config.after do
|
67
|
+
FileUtils.rm_rf "./tmp/home"
|
68
|
+
end
|
69
|
+
end
|
@@ -61,14 +61,6 @@ describe Imap::Backup::Account::Connection do
|
|
61
61
|
end
|
62
62
|
end
|
63
63
|
|
64
|
-
describe "#initialize" do
|
65
|
-
it "creates the path" do
|
66
|
-
expect(Imap::Backup::Utils).to receive(:make_folder)
|
67
|
-
|
68
|
-
subject
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
64
|
describe "#client" do
|
73
65
|
let(:result) { subject.client }
|
74
66
|
|
@@ -127,6 +119,12 @@ describe Imap::Backup::Account::Connection do
|
|
127
119
|
allow(Imap::Backup::Serializer::Mbox).to receive(:new) { serializer }
|
128
120
|
end
|
129
121
|
|
122
|
+
it "creates the path" do
|
123
|
+
expect(Imap::Backup::Utils).to receive(:make_folder)
|
124
|
+
|
125
|
+
subject.status
|
126
|
+
end
|
127
|
+
|
130
128
|
it "returns the names of folders" do
|
131
129
|
expect(subject.status[0][:name]).to eq(IMAP_FOLDER)
|
132
130
|
end
|
@@ -12,7 +12,10 @@ describe Imap::Backup::Account::Folder do
|
|
12
12
|
append: append_response,
|
13
13
|
create: nil,
|
14
14
|
examine: nil,
|
15
|
-
|
15
|
+
expunge: nil,
|
16
|
+
responses: responses,
|
17
|
+
select: nil,
|
18
|
+
uid_store: nil
|
16
19
|
)
|
17
20
|
end
|
18
21
|
let(:connection) do
|
@@ -27,12 +30,11 @@ describe Imap::Backup::Account::Folder do
|
|
27
30
|
end
|
28
31
|
let(:responses) { [] }
|
29
32
|
let(:append_response) { nil }
|
33
|
+
let(:uids) { [5678, 123] }
|
30
34
|
|
31
|
-
|
32
|
-
let(:uids) { [5678, 123] }
|
33
|
-
|
34
|
-
before { allow(client).to receive(:uid_search) { uids } }
|
35
|
+
before { allow(client).to receive(:uid_search) { uids } }
|
35
36
|
|
37
|
+
describe "#uids" do
|
36
38
|
it "lists available messages" do
|
37
39
|
expect(subject.uids).to eq(uids.reverse)
|
38
40
|
end
|
@@ -233,6 +235,25 @@ describe Imap::Backup::Account::Folder do
|
|
233
235
|
expect(subject.uid_validity).to eq(1)
|
234
236
|
end
|
235
237
|
end
|
238
|
+
|
239
|
+
describe "#clear" do
|
240
|
+
before do
|
241
|
+
subject.clear
|
242
|
+
end
|
243
|
+
|
244
|
+
it "uses select to have read-write access" do
|
245
|
+
expect(client).to have_received(:select)
|
246
|
+
end
|
247
|
+
|
248
|
+
it "marks all emails as deleted" do
|
249
|
+
expect(client).
|
250
|
+
to have_received(:uid_store).with(uids.sort, "+FLAGS", [:Deleted])
|
251
|
+
end
|
252
|
+
|
253
|
+
it "deletes marked emails" do
|
254
|
+
expect(client).to have_received(:expunge)
|
255
|
+
end
|
256
|
+
end
|
236
257
|
end
|
237
258
|
|
238
259
|
# rubocop:enable RSpec/PredicateMatcher
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "imap/backup/cli/helpers"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class WithHelpers
|
5
|
+
include CLI::Helpers
|
6
|
+
end
|
7
|
+
|
8
|
+
RSpec.describe CLI::Helpers do
|
9
|
+
subject { WithHelpers.new }
|
10
|
+
|
11
|
+
let(:accounts) { instance_double(CLI::Accounts) }
|
12
|
+
let(:email) { "email@example.com" }
|
13
|
+
let(:account1) { instance_double(Account, username: email, connection: "c1") }
|
14
|
+
let(:account2) { instance_double(Account, username: "foo", connection: "c2") }
|
15
|
+
let(:items) { [account1, account2] }
|
16
|
+
|
17
|
+
before do
|
18
|
+
allow(CLI::Accounts).to receive(:new) { accounts }
|
19
|
+
allow(accounts).to receive(:each).
|
20
|
+
and_yield(account1).
|
21
|
+
and_yield(account2)
|
22
|
+
allow(accounts).to receive(:find) do |&block|
|
23
|
+
items.find { |a| block.call(a) }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe ".symbolized" do
|
28
|
+
let(:arguments) { {"foo" => 1, "bar" => 2} }
|
29
|
+
let(:result) { subject.symbolized(arguments) }
|
30
|
+
|
31
|
+
it "converts string keys to symbols" do
|
32
|
+
expect(result.keys).to eq([:foo, :bar])
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when keys have hyphens" do
|
36
|
+
let(:arguments) { {"some-option"=> 3} }
|
37
|
+
|
38
|
+
it "replaces them with underscores" do
|
39
|
+
expect(result.keys).to eq([:some_option])
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe ".account" do
|
45
|
+
it "returns any account with a matching username" do
|
46
|
+
expect(subject.account(email)).to eq(account1)
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when no match is found" do
|
50
|
+
let(:items) { [account2] }
|
51
|
+
it "fails" do
|
52
|
+
expect do
|
53
|
+
subject.account(email)
|
54
|
+
end.to raise_error(RuntimeError, /not a configured account/)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe ".connection" do
|
60
|
+
it "returns the connection for any account with a matching username" do
|
61
|
+
result = subject.connection(email)
|
62
|
+
expect(result).to be_a(Account::Connection)
|
63
|
+
expect(result.account).to eq(account1)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe ".each_connection" do
|
68
|
+
it "yields each connection" do
|
69
|
+
expect { |b| subject.each_connection([email, "foo"], &b) }.
|
70
|
+
to yield_successive_args("c1", "c2")
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when there is no configuration" do
|
74
|
+
before do
|
75
|
+
allow(accounts).to receive(:each).
|
76
|
+
and_raise(Imap::Backup::ConfigurationNotFound)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "fails" do
|
80
|
+
expect do
|
81
|
+
subject.each_connection([email])
|
82
|
+
end.to raise_error(RuntimeError, /not configured/)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "imap/backup/migrator"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
RSpec.describe Migrator do
|
5
|
+
subject { described_class.new(serializer, folder, reset: reset) }
|
6
|
+
|
7
|
+
let(:serializer) { instance_double(Serializer::MboxStore, uids: [1]) }
|
8
|
+
let(:folder) do
|
9
|
+
instance_double(
|
10
|
+
Account::Folder,
|
11
|
+
append: nil, clear: nil, create: nil, name: "name", uids: folder_uids
|
12
|
+
)
|
13
|
+
end
|
14
|
+
let(:folder_uids) { [] }
|
15
|
+
let(:reset) { false }
|
16
|
+
let(:messages) { [[1, message]] }
|
17
|
+
let(:message) { instance_double(Email::Mboxrd::Message, supplied_body: body) }
|
18
|
+
let(:body) { "body" }
|
19
|
+
|
20
|
+
before do
|
21
|
+
allow(serializer).to receive(:each_message) do
|
22
|
+
messages.enum_for(:each)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
it "creates the folder" do
|
27
|
+
subject.run
|
28
|
+
|
29
|
+
expect(folder).to have_received(:create)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "uploads messages" do
|
33
|
+
subject.run
|
34
|
+
|
35
|
+
expect(folder).to have_received(:append).with(message)
|
36
|
+
end
|
37
|
+
|
38
|
+
context "when the folder is not empty" do
|
39
|
+
let(:folder_uids) { [99] }
|
40
|
+
|
41
|
+
it "fails" do
|
42
|
+
expect do
|
43
|
+
subject.run
|
44
|
+
end.to raise_error(RuntimeError, /The destination folder 'name' is not empty/)
|
45
|
+
end
|
46
|
+
|
47
|
+
context "when `reset` is true" do
|
48
|
+
let(:reset) { true }
|
49
|
+
|
50
|
+
it "clears the folder" do
|
51
|
+
subject.run
|
52
|
+
|
53
|
+
expect(folder).to have_received(:clear)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
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:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-02-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: aruba
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.0.0
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.0.0
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: codeclimate-test-reporter
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -196,6 +210,8 @@ files:
|
|
196
210
|
- lib/imap/backup/cli/folders.rb
|
197
211
|
- lib/imap/backup/cli/helpers.rb
|
198
212
|
- lib/imap/backup/cli/local.rb
|
213
|
+
- lib/imap/backup/cli/migrate.rb
|
214
|
+
- lib/imap/backup/cli/remote.rb
|
199
215
|
- lib/imap/backup/cli/restore.rb
|
200
216
|
- lib/imap/backup/cli/setup.rb
|
201
217
|
- lib/imap/backup/cli/status.rb
|
@@ -205,6 +221,7 @@ files:
|
|
205
221
|
- lib/imap/backup/configuration.rb
|
206
222
|
- lib/imap/backup/downloader.rb
|
207
223
|
- lib/imap/backup/logger.rb
|
224
|
+
- lib/imap/backup/migrator.rb
|
208
225
|
- lib/imap/backup/sanitizer.rb
|
209
226
|
- lib/imap/backup/serializer.rb
|
210
227
|
- lib/imap/backup/serializer/mbox.rb
|
@@ -229,9 +246,19 @@ files:
|
|
229
246
|
- lib/thunderbird/subdirectory.rb
|
230
247
|
- lib/thunderbird/subdirectory_placeholder.rb
|
231
248
|
- spec/features/backup_spec.rb
|
249
|
+
- spec/features/configuration/minimal_configuration.rb
|
250
|
+
- spec/features/configuration/missing_configuration.rb
|
251
|
+
- spec/features/folders_spec.rb
|
232
252
|
- spec/features/helper.rb
|
253
|
+
- spec/features/local/list_accounts_spec.rb
|
254
|
+
- spec/features/local/list_emails_spec.rb
|
255
|
+
- spec/features/local/list_folders_spec.rb
|
256
|
+
- spec/features/local/show_an_email_spec.rb
|
257
|
+
- spec/features/migrate_spec.rb
|
258
|
+
- spec/features/remote/list_account_folders_spec.rb
|
233
259
|
- spec/features/restore_spec.rb
|
234
260
|
- spec/features/status_spec.rb
|
261
|
+
- spec/features/support/aruba.rb
|
235
262
|
- spec/features/support/backup_directory.rb
|
236
263
|
- spec/features/support/email_server.rb
|
237
264
|
- spec/features/support/shared/connection_context.rb
|
@@ -251,12 +278,14 @@ files:
|
|
251
278
|
- spec/unit/imap/backup/account/connection_spec.rb
|
252
279
|
- spec/unit/imap/backup/account/folder_spec.rb
|
253
280
|
- spec/unit/imap/backup/cli/accounts_spec.rb
|
281
|
+
- spec/unit/imap/backup/cli/helpers_spec.rb
|
254
282
|
- spec/unit/imap/backup/cli/local_spec.rb
|
255
283
|
- spec/unit/imap/backup/cli/utils_spec.rb
|
256
284
|
- spec/unit/imap/backup/client/default_spec.rb
|
257
285
|
- spec/unit/imap/backup/configuration_spec.rb
|
258
286
|
- spec/unit/imap/backup/downloader_spec.rb
|
259
287
|
- spec/unit/imap/backup/logger_spec.rb
|
288
|
+
- spec/unit/imap/backup/migrator_spec.rb
|
260
289
|
- spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
|
261
290
|
- spec/unit/imap/backup/serializer/mbox_spec.rb
|
262
291
|
- spec/unit/imap/backup/serializer/mbox_store_spec.rb
|
@@ -271,7 +300,7 @@ homepage: https://github.com/joeyates/imap-backup
|
|
271
300
|
licenses:
|
272
301
|
- MIT
|
273
302
|
metadata: {}
|
274
|
-
post_install_message:
|
303
|
+
post_install_message:
|
275
304
|
rdoc_options: []
|
276
305
|
require_paths:
|
277
306
|
- lib
|
@@ -287,46 +316,58 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
287
316
|
version: '0'
|
288
317
|
requirements: []
|
289
318
|
rubygems_version: 3.1.6
|
290
|
-
signing_key:
|
319
|
+
signing_key:
|
291
320
|
specification_version: 4
|
292
321
|
summary: Backup GMail (or other IMAP) accounts to disk
|
293
322
|
test_files:
|
294
|
-
- spec/
|
295
|
-
- spec/
|
296
|
-
- spec/
|
297
|
-
- spec/
|
298
|
-
- spec/
|
299
|
-
- spec/
|
300
|
-
- spec/
|
301
|
-
- spec/features/backup_spec.rb
|
302
|
-
- spec/support/higline_test_helpers.rb
|
303
|
-
- spec/support/fixtures.rb
|
304
|
-
- spec/support/silence_logging.rb
|
305
|
-
- spec/gather_rspec_coverage.rb
|
306
|
-
- spec/unit/email/provider/gmail_spec.rb
|
307
|
-
- spec/unit/email/provider/fastmail_spec.rb
|
308
|
-
- spec/unit/email/provider/base_spec.rb
|
309
|
-
- spec/unit/email/provider/apple_mail_spec.rb
|
310
|
-
- spec/unit/email/mboxrd/message_spec.rb
|
311
|
-
- spec/unit/email/provider_spec.rb
|
312
|
-
- spec/unit/imap/backup/cli/utils_spec.rb
|
313
|
-
- spec/unit/imap/backup/cli/accounts_spec.rb
|
314
|
-
- spec/unit/imap/backup/cli/local_spec.rb
|
323
|
+
- spec/spec_helper.rb
|
324
|
+
- spec/unit/imap/backup/serializer/mbox_spec.rb
|
325
|
+
- spec/unit/imap/backup/serializer/mbox_store_spec.rb
|
326
|
+
- spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
|
327
|
+
- spec/unit/imap/backup/setup_spec.rb
|
328
|
+
- spec/unit/imap/backup/logger_spec.rb
|
329
|
+
- spec/unit/imap/backup/configuration_spec.rb
|
315
330
|
- spec/unit/imap/backup/setup/account_spec.rb
|
316
331
|
- spec/unit/imap/backup/setup/connection_tester_spec.rb
|
317
|
-
- spec/unit/imap/backup/setup/folder_chooser_spec.rb
|
318
332
|
- spec/unit/imap/backup/setup/asker_spec.rb
|
319
|
-
- spec/unit/imap/backup/
|
320
|
-
- spec/unit/imap/backup/
|
321
|
-
- spec/unit/imap/backup/
|
322
|
-
- spec/unit/imap/backup/
|
323
|
-
- spec/unit/imap/backup/
|
324
|
-
- spec/unit/imap/backup/serializer/mbox_store_spec.rb
|
325
|
-
- spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
|
326
|
-
- spec/unit/imap/backup/serializer/mbox_spec.rb
|
333
|
+
- spec/unit/imap/backup/setup/folder_chooser_spec.rb
|
334
|
+
- spec/unit/imap/backup/cli/helpers_spec.rb
|
335
|
+
- spec/unit/imap/backup/cli/accounts_spec.rb
|
336
|
+
- spec/unit/imap/backup/cli/utils_spec.rb
|
337
|
+
- spec/unit/imap/backup/cli/local_spec.rb
|
327
338
|
- spec/unit/imap/backup/downloader_spec.rb
|
339
|
+
- spec/unit/imap/backup/migrator_spec.rb
|
340
|
+
- spec/unit/imap/backup/uploader_spec.rb
|
328
341
|
- spec/unit/imap/backup/account/folder_spec.rb
|
329
342
|
- spec/unit/imap/backup/account/connection_spec.rb
|
343
|
+
- spec/unit/imap/backup/utils_spec.rb
|
330
344
|
- spec/unit/imap/backup/client/default_spec.rb
|
331
|
-
- spec/
|
345
|
+
- spec/unit/email/mboxrd/message_spec.rb
|
346
|
+
- spec/unit/email/provider/gmail_spec.rb
|
347
|
+
- spec/unit/email/provider/apple_mail_spec.rb
|
348
|
+
- spec/unit/email/provider/fastmail_spec.rb
|
349
|
+
- spec/unit/email/provider/base_spec.rb
|
350
|
+
- spec/unit/email/provider_spec.rb
|
351
|
+
- spec/features/folders_spec.rb
|
352
|
+
- spec/features/configuration/missing_configuration.rb
|
353
|
+
- spec/features/configuration/minimal_configuration.rb
|
354
|
+
- spec/features/backup_spec.rb
|
355
|
+
- spec/features/helper.rb
|
356
|
+
- spec/features/restore_spec.rb
|
357
|
+
- spec/features/local/list_emails_spec.rb
|
358
|
+
- spec/features/local/list_accounts_spec.rb
|
359
|
+
- spec/features/local/show_an_email_spec.rb
|
360
|
+
- spec/features/local/list_folders_spec.rb
|
361
|
+
- spec/features/status_spec.rb
|
362
|
+
- spec/features/support/email_server.rb
|
363
|
+
- spec/features/support/backup_directory.rb
|
364
|
+
- spec/features/support/aruba.rb
|
365
|
+
- spec/features/support/shared/connection_context.rb
|
366
|
+
- spec/features/support/shared/message_fixtures.rb
|
367
|
+
- spec/features/migrate_spec.rb
|
368
|
+
- spec/features/remote/list_account_folders_spec.rb
|
369
|
+
- spec/support/fixtures.rb
|
370
|
+
- spec/support/silence_logging.rb
|
371
|
+
- spec/support/higline_test_helpers.rb
|
332
372
|
- spec/fixtures/connection.yml
|
373
|
+
- spec/gather_rspec_coverage.rb
|