imap-backup 4.2.0 → 5.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/imap-backup.gemspec +2 -0
- 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/restore.rb +20 -4
- data/lib/imap/backup/cli.rb +55 -10
- 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/version.rb +2 -2
- data/spec/features/backup_spec.rb +13 -12
- 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/restore_spec.rb +7 -5
- data/spec/features/support/aruba.rb +73 -0
- data/spec/features/support/backup_directory.rb +0 -4
- data/spec/features/support/email_server.rb +1 -1
- data/spec/spec_helper.rb +2 -3
- 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 +57 -11
- data/lib/thunderbird/install.rb +0 -16
- data/lib/thunderbird/local_folder.rb +0 -65
- data/lib/thunderbird/profile.rb +0 -30
- data/lib/thunderbird/profiles.rb +0 -71
- data/lib/thunderbird/subdirectory.rb +0 -93
- data/lib/thunderbird/subdirectory_placeholder.rb +0 -21
- data/lib/thunderbird.rb +0 -14
- data/spec/gather_rspec_coverage.rb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 59babb556b2f47a07590debff15dcd5456f1e61454c3aba2e504610ce4322647
|
4
|
+
data.tar.gz: 89909077444746e44fe3955070df6b590942da0d44da58c9b08b282e694000d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea00bb55bb853cef73ec12171a4bdde9b0b5ef88a914065bbe307f48712db3329e8c8f1712cfa48b4a49102d20fb38cc465dc463c2d3ea9e0caba000a184766b
|
7
|
+
data.tar.gz: 24d368c209c11db4ae7f05a93b9cf4e0435b65eef0cffd7e58f8c669e634ca735aa3187b6a6506b7b10ba3bec961d3aa9d7387df40d4880f1bed73df7f24bbb9
|
data/imap-backup.gemspec
CHANGED
@@ -27,7 +27,9 @@ Gem::Specification.new do |gem|
|
|
27
27
|
gem.add_runtime_dependency "os"
|
28
28
|
gem.add_runtime_dependency "rake"
|
29
29
|
gem.add_runtime_dependency "thor", "~> 1.1"
|
30
|
+
gem.add_runtime_dependency "thunderbird", ">= 0.0.0"
|
30
31
|
|
32
|
+
gem.add_development_dependency "aruba", ">= 0.0.0"
|
31
33
|
gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
|
32
34
|
if RUBY_ENGINE == "jruby"
|
33
35
|
gem.add_development_dependency "pry-debugger-jruby"
|
@@ -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
|
@@ -3,16 +3,32 @@ module Imap::Backup
|
|
3
3
|
include Thor::Actions
|
4
4
|
include CLI::Helpers
|
5
5
|
|
6
|
+
attr_reader :email
|
6
7
|
attr_reader :account_names
|
7
8
|
|
8
|
-
def initialize(options)
|
9
|
-
|
10
|
-
@account_names =
|
9
|
+
def initialize(email = nil, options)
|
10
|
+
@email = email
|
11
|
+
@account_names =
|
12
|
+
if options.key?(:accounts)
|
13
|
+
options[:accounts].split(",")
|
14
|
+
end
|
11
15
|
end
|
12
16
|
|
13
17
|
no_commands do
|
14
18
|
def run
|
15
|
-
|
19
|
+
case
|
20
|
+
when email && !account_names
|
21
|
+
connection = connection(email)
|
22
|
+
connection.restore
|
23
|
+
when !email && !account_names
|
24
|
+
Logger.logger.info "Calling restore without an EMAIL parameter is deprecated"
|
25
|
+
each_connection([]) { |connection| connection.restore }
|
26
|
+
when email && account_names.any?
|
27
|
+
raise "Pass either an email or the --accounts option, not both"
|
28
|
+
when account_names.any?
|
29
|
+
Logger.logger.info "Calling restore with the --account option is deprected, please pass a single EMAIL argument"
|
30
|
+
each_connection(account_names) { |connection| connection.restore }
|
31
|
+
end
|
16
32
|
end
|
17
33
|
end
|
18
34
|
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,15 +58,62 @@ module Imap::Backup
|
|
57
58
|
Folders.new(symbolized(options)).run
|
58
59
|
end
|
59
60
|
|
60
|
-
desc "
|
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"
|
61
66
|
long_desc <<~DESC
|
62
|
-
|
63
|
-
|
64
|
-
|
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
|
+
|
109
|
+
desc "restore EMAIL", "Restores a single account"
|
110
|
+
long_desc <<~DESC
|
111
|
+
Restores all backed-up emails for the supplied account to
|
112
|
+
their original server.
|
65
113
|
DESC
|
66
114
|
accounts_option
|
67
|
-
def restore
|
68
|
-
Restore.new(symbolized(options)).run
|
115
|
+
def restore(email = nil)
|
116
|
+
Restore.new(email, symbolized(options)).run
|
69
117
|
end
|
70
118
|
|
71
119
|
desc "setup", "Configure imap-backup"
|
@@ -86,9 +134,6 @@ module Imap::Backup
|
|
86
134
|
Status.new(symbolized(options)).run
|
87
135
|
end
|
88
136
|
|
89
|
-
desc "local SUBCOMMAND [OPTIONS]", "View local info"
|
90
|
-
subcommand "local", Local
|
91
|
-
|
92
137
|
desc "utils SUBCOMMAND [OPTIONS]", "Various utilities"
|
93
138
|
subcommand "utils", Utils
|
94
139
|
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
|
data/lib/imap/backup/version.rb
CHANGED
@@ -1,28 +1,28 @@
|
|
1
1
|
require "features/helper"
|
2
2
|
|
3
|
-
RSpec.describe "backup", type: :
|
3
|
+
RSpec.describe "backup", type: :aruba, docker: true do
|
4
4
|
include_context "imap-backup connection"
|
5
5
|
include_context "message-fixtures"
|
6
6
|
|
7
|
+
let(:local_backup_path) { File.expand_path("~/backup") }
|
8
|
+
let(:backup_folders) { [{name: folder}] }
|
9
|
+
let(:folder) { "my-stuff" }
|
7
10
|
let(:messages_as_mbox) do
|
8
11
|
message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
|
9
12
|
end
|
10
|
-
|
11
|
-
let(:email1) { send_email folder, msg1 }
|
12
|
-
let(:email2) { send_email folder, msg2 }
|
13
|
+
|
13
14
|
let!(:pre) {}
|
14
15
|
let!(:setup) do
|
15
16
|
server_create_folder folder
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
send_email folder, msg1
|
18
|
+
send_email folder, msg2
|
19
|
+
create_config(accounts: [account.to_h])
|
20
|
+
|
21
|
+
run_command_and_stop("imap-backup backup")
|
19
22
|
end
|
20
23
|
|
21
24
|
after do
|
22
|
-
FileUtils.rm_rf local_backup_path
|
23
|
-
delete_emails folder
|
24
25
|
server_delete_folder folder
|
25
|
-
connection.disconnect
|
26
26
|
end
|
27
27
|
|
28
28
|
it "downloads messages" do
|
@@ -51,11 +51,10 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
51
51
|
|
52
52
|
context "when uid_validity does not match" do
|
53
53
|
let(:new_name) { "NEWNAME" }
|
54
|
-
let(:email3) { send_email folder, msg3 }
|
55
54
|
let(:original_folder_uid_validity) { server_uid_validity(folder) }
|
56
55
|
let!(:pre) do
|
57
56
|
server_create_folder folder
|
58
|
-
|
57
|
+
send_email folder, msg3
|
59
58
|
original_folder_uid_validity
|
60
59
|
connection.run_backup
|
61
60
|
connection.disconnect
|
@@ -78,6 +77,7 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
78
77
|
context "when a renamed local backup exists" do
|
79
78
|
let!(:pre) do
|
80
79
|
super()
|
80
|
+
create_directory local_backup_path
|
81
81
|
File.write(imap_path(renamed_folder), "existing imap")
|
82
82
|
File.write(mbox_path(renamed_folder), "existing mbox")
|
83
83
|
end
|
@@ -91,6 +91,7 @@ RSpec.describe "backup", type: :feature, docker: true do
|
|
91
91
|
|
92
92
|
context "when an unversioned .imap file is found" do
|
93
93
|
let!(:pre) do
|
94
|
+
create_directory local_backup_path
|
94
95
|
File.open(imap_path(folder), "w") { |f| f.write "old format imap" }
|
95
96
|
File.open(mbox_path(folder), "w") { |f| f.write "old format emails" }
|
96
97
|
end
|
@@ -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
|