imap-backup 6.0.0.rc2 → 6.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.
Files changed (108) hide show
  1. checksums.yaml +4 -4
  2. data/imap-backup.gemspec +5 -1
  3. data/lib/cli_coverage.rb +11 -11
  4. data/lib/email/provider/base.rb +2 -0
  5. data/lib/email/provider/unknown.rb +2 -0
  6. data/lib/email/provider.rb +2 -0
  7. data/lib/imap/backup/account/connection/backup_folders.rb +27 -0
  8. data/lib/imap/backup/account/connection/client_factory.rb +54 -0
  9. data/lib/imap/backup/account/connection/folder_names.rb +26 -0
  10. data/lib/imap/backup/account/connection.rb +11 -95
  11. data/lib/imap/backup/account/folder.rb +9 -6
  12. data/lib/imap/backup/account.rb +3 -5
  13. data/lib/imap/backup/cli/backup.rb +1 -3
  14. data/lib/imap/backup/cli/helpers.rb +24 -22
  15. data/lib/imap/backup/cli/local.rb +20 -13
  16. data/lib/imap/backup/cli/migrate.rb +4 -10
  17. data/lib/imap/backup/cli/restore.rb +8 -7
  18. data/lib/imap/backup/cli/setup.rb +10 -8
  19. data/lib/imap/backup/cli/stats.rb +78 -0
  20. data/lib/imap/backup/cli/status.rb +2 -2
  21. data/lib/imap/backup/cli/utils.rb +4 -6
  22. data/lib/imap/backup/cli.rb +24 -3
  23. data/lib/imap/backup/configuration.rb +9 -11
  24. data/lib/imap/backup/downloader.rb +48 -30
  25. data/lib/imap/backup/migrator.rb +5 -5
  26. data/lib/imap/backup/sanitizer.rb +3 -2
  27. data/lib/imap/backup/serializer/appender.rb +49 -0
  28. data/lib/imap/backup/serializer/message_enumerator.rb +29 -0
  29. data/lib/imap/backup/serializer/unused_name_finder.rb +27 -0
  30. data/lib/imap/backup/serializer.rb +24 -78
  31. data/lib/imap/backup/setup/account/header.rb +75 -0
  32. data/lib/imap/backup/setup/account.rb +14 -91
  33. data/lib/imap/backup/setup/asker.rb +4 -15
  34. data/lib/imap/backup/setup/backup_path.rb +41 -0
  35. data/lib/imap/backup/setup/email.rb +45 -0
  36. data/lib/imap/backup/setup/folder_chooser.rb +3 -3
  37. data/lib/imap/backup/setup/helpers.rb +1 -1
  38. data/lib/imap/backup/setup.rb +5 -4
  39. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +39 -20
  40. data/lib/imap/backup/uploader.rb +46 -8
  41. data/lib/imap/backup/utils.rb +1 -1
  42. data/lib/imap/backup/version.rb +1 -1
  43. data/lib/imap/backup.rb +0 -1
  44. metadata +31 -134
  45. data/spec/features/backup_spec.rb +0 -100
  46. data/spec/features/configuration/minimal_configuration.rb +0 -15
  47. data/spec/features/configuration/missing_configuration.rb +0 -14
  48. data/spec/features/folders_spec.rb +0 -36
  49. data/spec/features/helper.rb +0 -2
  50. data/spec/features/local/list_accounts_spec.rb +0 -12
  51. data/spec/features/local/list_emails_spec.rb +0 -21
  52. data/spec/features/local/list_folders_spec.rb +0 -21
  53. data/spec/features/local/show_an_email_spec.rb +0 -34
  54. data/spec/features/migrate_spec.rb +0 -35
  55. data/spec/features/remote/list_account_folders_spec.rb +0 -16
  56. data/spec/features/restore_spec.rb +0 -162
  57. data/spec/features/status_spec.rb +0 -43
  58. data/spec/features/support/aruba.rb +0 -78
  59. data/spec/features/support/backup_directory.rb +0 -43
  60. data/spec/features/support/email_server.rb +0 -110
  61. data/spec/features/support/shared/connection_context.rb +0 -14
  62. data/spec/features/support/shared/message_fixtures.rb +0 -16
  63. data/spec/fixtures/connection.yml +0 -7
  64. data/spec/spec_helper.rb +0 -15
  65. data/spec/support/fixtures.rb +0 -11
  66. data/spec/support/higline_test_helpers.rb +0 -8
  67. data/spec/support/silence_logging.rb +0 -7
  68. data/spec/unit/email/mboxrd/message_spec.rb +0 -177
  69. data/spec/unit/email/provider/apple_mail_spec.rb +0 -7
  70. data/spec/unit/email/provider/base_spec.rb +0 -11
  71. data/spec/unit/email/provider/fastmail_spec.rb +0 -7
  72. data/spec/unit/email/provider/gmail_spec.rb +0 -7
  73. data/spec/unit/email/provider_spec.rb +0 -27
  74. data/spec/unit/imap/backup/account/connection_spec.rb +0 -433
  75. data/spec/unit/imap/backup/account/folder_spec.rb +0 -261
  76. data/spec/unit/imap/backup/account_spec.rb +0 -246
  77. data/spec/unit/imap/backup/cli/accounts_spec.rb +0 -58
  78. data/spec/unit/imap/backup/cli/backup_spec.rb +0 -19
  79. data/spec/unit/imap/backup/cli/folders_spec.rb +0 -39
  80. data/spec/unit/imap/backup/cli/helpers_spec.rb +0 -87
  81. data/spec/unit/imap/backup/cli/local_spec.rb +0 -100
  82. data/spec/unit/imap/backup/cli/migrate_spec.rb +0 -80
  83. data/spec/unit/imap/backup/cli/restore_spec.rb +0 -67
  84. data/spec/unit/imap/backup/cli/setup_spec.rb +0 -17
  85. data/spec/unit/imap/backup/cli/utils_spec.rb +0 -125
  86. data/spec/unit/imap/backup/cli_spec.rb +0 -93
  87. data/spec/unit/imap/backup/client/apple_mail_spec.rb +0 -9
  88. data/spec/unit/imap/backup/client/default_spec.rb +0 -22
  89. data/spec/unit/imap/backup/configuration_spec.rb +0 -238
  90. data/spec/unit/imap/backup/downloader_spec.rb +0 -96
  91. data/spec/unit/imap/backup/logger_spec.rb +0 -48
  92. data/spec/unit/imap/backup/migrator_spec.rb +0 -58
  93. data/spec/unit/imap/backup/sanitizer_spec.rb +0 -42
  94. data/spec/unit/imap/backup/serializer/directory_spec.rb +0 -37
  95. data/spec/unit/imap/backup/serializer/imap_spec.rb +0 -218
  96. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +0 -45
  97. data/spec/unit/imap/backup/serializer/mbox_spec.rb +0 -101
  98. data/spec/unit/imap/backup/serializer_spec.rb +0 -296
  99. data/spec/unit/imap/backup/setup/account_spec.rb +0 -461
  100. data/spec/unit/imap/backup/setup/asker_spec.rb +0 -137
  101. data/spec/unit/imap/backup/setup/connection_tester_spec.rb +0 -51
  102. data/spec/unit/imap/backup/setup/folder_chooser_spec.rb +0 -146
  103. data/spec/unit/imap/backup/setup/helpers_spec.rb +0 -15
  104. data/spec/unit/imap/backup/setup_spec.rb +0 -301
  105. data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +0 -116
  106. data/spec/unit/imap/backup/uploader_spec.rb +0 -54
  107. data/spec/unit/imap/backup/utils_spec.rb +0 -92
  108. data/spec/unit/retry_on_error_spec.rb +0 -34
@@ -1,238 +0,0 @@
1
- require "json"
2
- require "os"
3
-
4
- # rubocop:disable RSpec/PredicateMatcher
5
-
6
- describe Imap::Backup::Configuration do
7
- let(:directory) { "/base/path" }
8
- let(:file_path) { File.join(directory, "/config.json") }
9
- let(:file_exists) { true }
10
- let(:directory_exists) { true }
11
- let(:debug) { true }
12
- let(:configuration) { data.to_json }
13
- let(:data) { {debug: debug, accounts: accounts.map(&:to_h)} }
14
- let(:accounts) { [account1, account2] }
15
- let(:account1) { Imap::Backup::Account.new({username: "username1"}) }
16
- let(:account2) { Imap::Backup::Account.new({username: "username2"}) }
17
-
18
- before do
19
- stub_const(
20
- "Imap::Backup::Configuration::CONFIGURATION_DIRECTORY", directory
21
- )
22
- allow(File).to receive(:directory?).with(directory) { directory_exists }
23
- allow(File).to receive(:exist?).and_call_original
24
- allow(File).to receive(:exist?).with(file_path) { file_exists }
25
- allow(Imap::Backup::Utils).
26
- to receive(:stat).with(directory) { 0o700 }
27
- allow(Imap::Backup::Utils).
28
- to receive(:stat).with(file_path) { 0o600 }
29
- allow(Imap::Backup::Utils).to receive(:check_permissions) { nil }
30
- allow(File).to receive(:read).with(file_path) { configuration }
31
- end
32
-
33
- describe ".exist?" do
34
- [true, false].each do |exists|
35
- state = exists ? "exists" : "doesn't exist"
36
- context "when the file #{state}" do
37
- let(:file_exists) { exists }
38
-
39
- it "returns #{exists}" do
40
- expect(described_class.exist?).to eq(file_exists)
41
- end
42
- end
43
- end
44
- end
45
-
46
- describe "#path" do
47
- it "is the directory containing the configuration file" do
48
- expect(subject.path).to eq(directory)
49
- end
50
- end
51
-
52
- describe "#modified?" do
53
- context "with modified accounts" do
54
- before { subject.accounts[0].username = "changed" }
55
-
56
- it "is true" do
57
- expect(subject.modified?).to be_truthy
58
- end
59
- end
60
-
61
- context "with accounts flagged 'delete'" do
62
- before { subject.accounts[0].mark_for_deletion }
63
-
64
- it "is true" do
65
- expect(subject.modified?).to be_truthy
66
- end
67
- end
68
-
69
- context "without accounts flagged 'modified'" do
70
- it "is false" do
71
- expect(subject.modified?).to be_falsey
72
- end
73
- end
74
- end
75
-
76
- describe "#debug?" do
77
- context "when the debug flag is true" do
78
- it "is true" do
79
- expect(subject.debug?).to be_truthy
80
- end
81
- end
82
-
83
- context "when the debug flag is false" do
84
- let(:debug) { false }
85
-
86
- it "is false" do
87
- expect(subject.debug?).to be_falsey
88
- end
89
- end
90
-
91
- context "when the debug flag is missing" do
92
- let(:data) { {accounts: accounts} }
93
-
94
- it "is false" do
95
- expect(subject.debug?).to be_falsey
96
- end
97
- end
98
-
99
- context "when the debug flag is neither true nor false" do
100
- let(:debug) { "hi" }
101
-
102
- it "is false" do
103
- expect(subject.debug?).to be_falsey
104
- end
105
- end
106
- end
107
-
108
- describe "#debug=" do
109
- before { subject.debug = debug }
110
-
111
- context "when the supplied value is true" do
112
- it "sets the flag to true" do
113
- expect(subject.debug?).to be_truthy
114
- end
115
- end
116
-
117
- context "when the supplied value is false" do
118
- let(:debug) { false }
119
-
120
- it "sets the flag to false" do
121
- expect(subject.debug?).to be_falsey
122
- end
123
- end
124
-
125
- context "when the supplied value is neither true nor false" do
126
- let(:debug) { "ciao" }
127
-
128
- it "sets the flag to false" do
129
- expect(subject.debug?).to be_falsey
130
- end
131
- end
132
- end
133
-
134
- describe "#save" do
135
- subject { described_class.new }
136
-
137
- let(:directory_exists) { false }
138
- let(:file) { instance_double(File, write: nil) }
139
-
140
- before do
141
- allow(FileUtils).to receive(:mkdir)
142
- allow(FileUtils).to receive(:chmod)
143
- allow(File).to receive(:open).with(file_path, "w").and_yield(file)
144
- allow(JSON).to receive(:pretty_generate) { "JSON output" }
145
- end
146
-
147
- it "creates the config directory" do
148
- expect(FileUtils).to receive(:mkdir).with(directory)
149
-
150
- subject.save
151
- end
152
-
153
- it "saves the configuration" do
154
- expect(file).to receive(:write).with("JSON output")
155
-
156
- subject.save
157
- end
158
-
159
- it "uses the Account#to_h method" do
160
- allow(subject.accounts[0]).to receive(:to_h) { "Account1" }
161
- allow(subject.accounts[1]).to receive(:to_h) { "Account2" }
162
-
163
- expect(JSON).to receive(:pretty_generate).
164
- with(hash_including({accounts: ["Account1", "Account2"]}))
165
-
166
- subject.save
167
- end
168
-
169
- context "when accounts are to be deleted" do
170
- let(:accounts) do
171
- [
172
- {name: "keep_me"},
173
- {name: "delete_me", delete: true}
174
- ]
175
- end
176
-
177
- before do
178
- allow(subject.accounts[0]).to receive(:to_h) { "Account1" }
179
- allow(subject.accounts[1]).to receive(:to_h) { "Account2" }
180
- subject.accounts[0].mark_for_deletion
181
- end
182
-
183
- it "does not save them" do
184
- expect(JSON).to receive(:pretty_generate).
185
- with(hash_including({accounts: ["Account2"]}))
186
-
187
- subject.save
188
- end
189
- end
190
-
191
- context "when file permissions are too open" do
192
- context "when on UNIX" do
193
- before do
194
- allow(OS).to receive(:windows?) { false }
195
- end
196
-
197
- it "sets them to 0600" do
198
- expect(FileUtils).to receive(:chmod).with(0o600, file_path)
199
-
200
- subject.save
201
- end
202
- end
203
- end
204
-
205
- context "when the configuration file is missing" do
206
- let(:file_exists) { false }
207
-
208
- it "doesn't fail" do
209
- expect do
210
- subject.save
211
- end.to_not raise_error
212
- end
213
- end
214
-
215
- context "when on UNIX" do
216
- before do
217
- allow(OS).to receive(:windows?) { false }
218
- end
219
-
220
- context "when the config file permissions are too lax" do
221
- let(:file_exists) { true }
222
-
223
- before do
224
- allow(Imap::Backup::Utils).to receive(:check_permissions).
225
- with(file_path, 0o600).and_raise("Error")
226
- end
227
-
228
- it "fails" do
229
- expect do
230
- subject.save
231
- end.to raise_error(RuntimeError, "Error")
232
- end
233
- end
234
- end
235
- end
236
- end
237
-
238
- # rubocop:enable RSpec/PredicateMatcher
@@ -1,96 +0,0 @@
1
- describe Imap::Backup::Downloader do
2
- describe "#run" do
3
- subject { described_class.new(folder, serializer, **options) }
4
-
5
- let(:body) { "blah" }
6
- let(:folder) do
7
- instance_double(
8
- Imap::Backup::Account::Folder,
9
- fetch_multi: [{uid: "111", body: body}],
10
- name: "folder",
11
- uids: remote_uids
12
- )
13
- end
14
- let(:remote_uids) { %w(111 222 333) }
15
- let(:serializer) do
16
- instance_double(Imap::Backup::Serializer, append: nil, uids: local_uids)
17
- end
18
- let(:local_uids) { ["222"] }
19
- let(:options) { {} }
20
-
21
- context "with fetched messages" do
22
- specify "are saved" do
23
- expect(serializer).to receive(:append).with("111", body)
24
-
25
- subject.run
26
- end
27
- end
28
-
29
- context "with messages which are already present" do
30
- specify "are skipped" do
31
- expect(serializer).to_not receive(:append).with("222", anything)
32
-
33
- subject.run
34
- end
35
- end
36
-
37
- context "with failed fetches" do
38
- specify "are skipped" do
39
- allow(folder).to receive(:fetch_multi) { nil }
40
- expect(serializer).to_not receive(:append)
41
-
42
- subject.run
43
- end
44
- end
45
-
46
- context "when the block size is greater than one" do
47
- let(:remote_uids) { %w(111 999) }
48
- let(:local_uids) { [] }
49
- let(:options) { {multi_fetch_size: 2} }
50
-
51
- context "when the first fetch fails" do
52
- before do
53
- allow(folder).to receive(:fetch_multi).with(["111", "999"]) { nil }
54
- allow(folder).to receive(:fetch_multi).with(["111"]).
55
- and_return([{uid: "111", body: body}]).
56
- and_return([{uid: "999", body: body}])
57
-
58
- subject.run
59
- end
60
-
61
- it "retries fetching messages singly" do
62
- expect(serializer).to have_received(:append).with("111", body)
63
- expect(serializer).to have_received(:append).with("999", body)
64
- end
65
- end
66
- end
67
-
68
- context "when no body is returned by the fetch" do
69
- let(:remote_uids) { %w(111) }
70
-
71
- before do
72
- allow(folder).to receive(:fetch_multi).with(["111"]) { [{uid: "111", body: nil}] }
73
-
74
- subject.run
75
- end
76
-
77
- it "skips the append" do
78
- expect(serializer).to_not have_received(:append)
79
- end
80
- end
81
-
82
- context "when the UID is not returned by the fetch" do
83
- let(:remote_uids) { %w(111) }
84
-
85
- before do
86
- allow(folder).to receive(:fetch_multi).with(["111"]) { [{uid: nil, body: body}] }
87
-
88
- subject.run
89
- end
90
-
91
- it "skips the append" do
92
- expect(serializer).to_not have_received(:append)
93
- end
94
- end
95
- end
96
- end
@@ -1,48 +0,0 @@
1
- require "net/imap"
2
-
3
- module Imap::Backup
4
- describe Logger do
5
- describe ".setup_logging" do
6
- let(:config) { instance_double(Configuration, debug?: debug) }
7
-
8
- around do |example|
9
- logger_previous = described_class.logger.level
10
- net_imap_previous = Net::IMAP.debug
11
- described_class.logger.level = 42
12
- Net::IMAP.debug = 42
13
- example.run
14
- Net::IMAP.debug = net_imap_previous
15
- described_class.logger.level = logger_previous
16
- end
17
-
18
- before do
19
- allow(Configuration).to receive(:new) { config }
20
- described_class.setup_logging
21
- end
22
-
23
- context "when config.debug?" do
24
- let(:debug) { true }
25
-
26
- it "sets logger level to debug" do
27
- expect(described_class.logger.level).to eq(::Logger::Severity::DEBUG)
28
- end
29
-
30
- it "sets the Net::IMAP debug flag" do
31
- expect(Net::IMAP.debug).to be_a(TrueClass)
32
- end
33
- end
34
-
35
- context "when not config.debug?" do
36
- let(:debug) { false }
37
-
38
- it "sets logger level to error" do
39
- expect(described_class.logger.level).to eq(::Logger::Severity::ERROR)
40
- end
41
-
42
- it "doesn't set the Net::IMAP debug flag" do
43
- expect(Net::IMAP.debug).to be_a(FalseClass)
44
- end
45
- end
46
- end
47
- end
48
- end
@@ -1,58 +0,0 @@
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, 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
@@ -1,42 +0,0 @@
1
- module Imap::Backup
2
- describe Sanitizer do
3
- require "stringio"
4
-
5
- subject { described_class.new(output) }
6
-
7
- let(:output) { StringIO.new }
8
-
9
- describe "#puts" do
10
- it "delegates to output" do
11
- subject.puts("x")
12
-
13
- expect(output.string).to eq("x\n")
14
- end
15
- end
16
-
17
- describe "#write" do
18
- it "delegates to output" do
19
- subject.write("x")
20
-
21
- expect(output.string).to eq("x")
22
- end
23
- end
24
-
25
- describe "#print" do
26
- it "removes passwords from complete lines of text" do
27
- subject.print("C: RUBY99 LOGIN xx) secret!!!!\netc")
28
-
29
- expect(output.string).to eq("C: RUBY99 LOGIN xx) [PASSWORD REDACTED]\n")
30
- end
31
- end
32
-
33
- describe "#flush" do
34
- it "sanitizes remaining text" do
35
- subject.print("before\nC: RUBY99 LOGIN xx) secret!!!!")
36
- subject.flush
37
-
38
- expect(output.string).to eq("before\nC: RUBY99 LOGIN xx) [PASSWORD REDACTED]\n")
39
- end
40
- end
41
- end
42
- end
@@ -1,37 +0,0 @@
1
- module Imap::Backup
2
- describe Serializer::Directory do
3
- subject { described_class.new("path", "relative") }
4
-
5
- let(:windows) { false }
6
-
7
- before do
8
- allow(File).to receive(:directory?) { false }
9
- allow(Utils).to receive(:make_folder)
10
- allow(OS).to receive(:windows?) { windows }
11
- allow(Utils).to receive(:mode) { 0o600 }
12
- allow(FileUtils).to receive(:chmod)
13
-
14
- subject.ensure_exists
15
- end
16
-
17
- describe "#ensure_exists" do
18
- context "when the directory doesn't exist" do
19
- it "makes the directory" do
20
- expect(Utils).to have_received(:make_folder)
21
- end
22
- end
23
-
24
- it "sets permissions" do
25
- expect(FileUtils).to have_received(:chmod)
26
- end
27
-
28
- context "when on Windows" do
29
- let(:windows) { true }
30
-
31
- it "doesn't set permissions" do
32
- expect(FileUtils).to_not have_received(:chmod)
33
- end
34
- end
35
- end
36
- end
37
- end