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,7 +0,0 @@
1
- describe Email::Provider::AppleMail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.mail.me.com")
5
- end
6
- end
7
- end
@@ -1,11 +0,0 @@
1
- describe Email::Provider::Base do
2
- describe "#options" do
3
- it "returns options" do
4
- expect(subject.options).to be_a(Hash)
5
- end
6
-
7
- it "forces TLSv1_2" do
8
- expect(subject.options[:ssl][:ssl_version]).to eq(:TLSv1_2)
9
- end
10
- end
11
- end
@@ -1,7 +0,0 @@
1
- describe Email::Provider::Fastmail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.fastmail.com")
5
- end
6
- end
7
- end
@@ -1,7 +0,0 @@
1
- describe Email::Provider::GMail do
2
- describe "#host" do
3
- it "returns host" do
4
- expect(subject.host).to eq("imap.gmail.com")
5
- end
6
- end
7
- end
@@ -1,27 +0,0 @@
1
- describe Email::Provider do
2
- describe ".for_address" do
3
- context "with known providers" do
4
- [
5
- ["fastmail.com", "Fastmail .com", Email::Provider::Fastmail],
6
- ["fastmail.fm", "Fastmail .fm", Email::Provider::Fastmail],
7
- ["gmail.com", "GMail", Email::Provider::GMail],
8
- ["icloud.com", "Apple Mail icloud.com", Email::Provider::AppleMail],
9
- ["mac.com", "Apple Mail mac.com", Email::Provider::AppleMail],
10
- ["me.com", "Apple Mail me.com", Email::Provider::AppleMail]
11
- ].each do |domain, name, klass|
12
- it "recognizes #{name} addresses" do
13
- address = "foo@#{domain}"
14
- expect(described_class.for_address(address)).to be_a(klass)
15
- end
16
- end
17
- end
18
-
19
- context "with unknown providers" do
20
- it "returns the Unknown provider" do
21
- result = described_class.for_address("foo@unknown.com")
22
-
23
- expect(result).to be_a(Email::Provider::Unknown)
24
- end
25
- end
26
- end
27
- end
@@ -1,433 +0,0 @@
1
- require "ostruct"
2
-
3
- describe Imap::Backup::Account::Connection do
4
- BACKUP_FOLDER = "backup_folder".freeze
5
- FOLDER_CONFIG = {name: BACKUP_FOLDER}.freeze
6
- FOLDER_NAME = "my_folder".freeze
7
- GMAIL_IMAP_SERVER = "imap.gmail.com".freeze
8
- IMAP_FOLDER = "imap_folder".freeze
9
- LOCAL_PATH = "local_path".freeze
10
- LOCAL_UID = "local_uid".freeze
11
- PASSWORD = "secret".freeze
12
- ROOT_NAME = "foo".freeze
13
- SERVER = "imap.example.com".freeze
14
- USERNAME = "username@example.com".freeze
15
-
16
- subject { described_class.new(account) }
17
-
18
- let(:client) do
19
- instance_double(
20
- Imap::Backup::Client::Default, authenticate: nil, login: nil, disconnect: nil
21
- )
22
- end
23
- let(:imap_folders) { [] }
24
- let(:account) do
25
- instance_double(
26
- Imap::Backup::Account,
27
- username: username,
28
- password: PASSWORD,
29
- local_path: LOCAL_PATH,
30
- folders: config_folders,
31
- multi_fetch_size: multi_fetch_size,
32
- server: server,
33
- connection_options: nil
34
- )
35
- end
36
- let(:username) { USERNAME }
37
- let(:config_folders) { [FOLDER_CONFIG] }
38
- let(:multi_fetch_size) { 1 }
39
- let(:root_info) do
40
- instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
41
- end
42
- let(:serializer) do
43
- instance_double(
44
- Imap::Backup::Serializer,
45
- folder: serialized_folder,
46
- force_uid_validity: nil,
47
- apply_uid_validity: new_uid_validity,
48
- uids: [LOCAL_UID]
49
- )
50
- end
51
- let(:serialized_folder) { nil }
52
- let(:server) { SERVER }
53
- let(:new_uid_validity) { nil }
54
-
55
- before do
56
- allow(Imap::Backup::Client::Default).to receive(:new) { client }
57
- allow(client).to receive(:list) { imap_folders }
58
- allow(Imap::Backup::Utils).to receive(:make_folder)
59
- end
60
-
61
- shared_examples "connects to IMAP" do
62
- it "logs in to the imap server" do
63
- expect(client).to have_received(:login)
64
- end
65
- end
66
-
67
- describe "#client" do
68
- let(:result) { subject.client }
69
-
70
- it "returns the IMAP connection" do
71
- expect(result).to eq(client)
72
- end
73
-
74
- it "uses the password" do
75
- result
76
-
77
- expect(client).to have_received(:login).with(USERNAME, PASSWORD)
78
- end
79
-
80
- context "when the first login attempt fails" do
81
- before do
82
- outcomes = [-> { raise EOFError }, -> { true }]
83
- allow(client).to receive(:login) { outcomes.shift.call }
84
- end
85
-
86
- it "retries" do
87
- subject.client
88
-
89
- expect(client).to have_received(:login).twice
90
- end
91
- end
92
-
93
- context "when the provider is Apple" do
94
- let(:username) { "user@mac.com" }
95
- let(:apple_client) do
96
- instance_double(
97
- Imap::Backup::Client::AppleMail, login: nil
98
- )
99
- end
100
-
101
- before do
102
- allow(Imap::Backup::Client::AppleMail).to receive(:new) { apple_client }
103
- end
104
-
105
- it "returns the Apple client" do
106
- expect(result).to eq(apple_client)
107
- end
108
- end
109
-
110
- context "when run" do
111
- before { subject.client }
112
-
113
- include_examples "connects to IMAP"
114
- end
115
- end
116
-
117
- describe "#folder_names" do
118
- let(:imap_folders) do
119
- [IMAP_FOLDER]
120
- end
121
-
122
- it "returns the list of folders" do
123
- expect(subject.folder_names).to eq([IMAP_FOLDER])
124
- end
125
- end
126
-
127
- describe "#status" do
128
- let(:folder) do
129
- instance_double(
130
- Imap::Backup::Account::Folder,
131
- uids: [remote_uid],
132
- name: IMAP_FOLDER
133
- )
134
- end
135
- let(:remote_uid) { "remote_uid" }
136
-
137
- before do
138
- allow(Imap::Backup::Account::Folder).to receive(:new) { folder }
139
- allow(Imap::Backup::Serializer).to receive(:new) { serializer }
140
- end
141
-
142
- it "creates the path" do
143
- expect(Imap::Backup::Utils).to receive(:make_folder)
144
-
145
- subject.status
146
- end
147
-
148
- it "returns the names of folders" do
149
- expect(subject.status[0][:name]).to eq(IMAP_FOLDER)
150
- end
151
-
152
- it "returns local message uids" do
153
- expect(subject.status[0][:local]).to eq([LOCAL_UID])
154
- end
155
-
156
- it "retrieves the available uids" do
157
- expect(subject.status[0][:remote]).to eq([remote_uid])
158
- end
159
- end
160
-
161
- describe "#run_backup" do
162
- let(:folder) do
163
- instance_double(
164
- Imap::Backup::Account::Folder,
165
- name: IMAP_FOLDER,
166
- exist?: exists,
167
- uid_validity: uid_validity
168
- )
169
- end
170
- let(:exists) { true }
171
- let(:uid_validity) { 123 }
172
- let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
173
- let(:multi_fetch_size) { 10 }
174
-
175
- before do
176
- allow(Imap::Backup::Downloader).
177
- to receive(:new).with(folder, serializer, anything) { downloader }
178
- allow(Imap::Backup::Account::Folder).to receive(:new).
179
- with(subject, BACKUP_FOLDER) { folder }
180
- allow(Imap::Backup::Serializer).to receive(:new).
181
- with(LOCAL_PATH, IMAP_FOLDER) { serializer }
182
- end
183
-
184
- it "passes the multi_fetch_size" do
185
- subject.run_backup
186
-
187
- expect(Imap::Backup::Downloader).to have_received(:new).
188
- with(anything, anything, {multi_fetch_size: 10})
189
- end
190
-
191
- context "with supplied config_folders" do
192
- it "runs the downloader" do
193
- expect(downloader).to receive(:run)
194
-
195
- subject.run_backup
196
- end
197
-
198
- context "when a folder does not exist" do
199
- let(:exists) { false }
200
-
201
- it "does not run the downloader" do
202
- expect(downloader).to_not receive(:run)
203
-
204
- subject.run_backup
205
- end
206
- end
207
- end
208
-
209
- context "without supplied config_folders" do
210
- let(:imap_folders) { [ROOT_NAME] }
211
-
212
- before do
213
- allow(Imap::Backup::Account::Folder).to receive(:new).
214
- with(subject, ROOT_NAME) { folder }
215
- allow(Imap::Backup::Serializer).to receive(:new).
216
- with(LOCAL_PATH, ROOT_NAME) { serializer }
217
- end
218
-
219
- context "when supplied config_folders is nil" do
220
- let(:config_folders) { nil }
221
-
222
- it "runs the downloader for each folder" do
223
- expect(downloader).to receive(:run).exactly(:once)
224
-
225
- subject.run_backup
226
- end
227
- end
228
-
229
- context "when supplied config_folders is an empty list" do
230
- let(:config_folders) { [] }
231
-
232
- it "runs the downloader for each folder" do
233
- expect(downloader).to receive(:run).exactly(:once)
234
-
235
- subject.run_backup
236
- end
237
- end
238
-
239
- context "when the imap server doesn't return folders" do
240
- let(:config_folders) { nil }
241
- let(:imap_folders) { [] }
242
-
243
- it "fails" do
244
- expect do
245
- subject.run_backup
246
- end.to raise_error(RuntimeError, /Unable to get folder list/)
247
- end
248
- end
249
- end
250
-
251
- context "when the IMAP session expires" do
252
- before do
253
- data = OpenStruct.new(data: "Session expired")
254
- response = OpenStruct.new(data: data)
255
- outcomes = [
256
- -> { raise Net::IMAP::ByeResponseError, response },
257
- -> { nil }
258
- ]
259
- allow(downloader).to receive(:run) { outcomes.shift.call }
260
- end
261
-
262
- it "reconnects" do
263
- expect(downloader).to receive(:run).exactly(:twice)
264
-
265
- subject.run_backup
266
- end
267
- end
268
-
269
- context "when run" do
270
- before { subject.run_backup }
271
-
272
- include_examples "connects to IMAP"
273
- end
274
- end
275
-
276
- describe "#restore" do
277
- let(:folder) do
278
- instance_double(
279
- Imap::Backup::Account::Folder,
280
- create: nil,
281
- uids: uids,
282
- name: IMAP_FOLDER,
283
- uid_validity: uid_validity
284
- )
285
- end
286
- let(:uids) { [99] }
287
- let(:uid_validity) { 123 }
288
- let(:serialized_folder) { "old name" }
289
- let(:uploader) do
290
- instance_double(Imap::Backup::Uploader, run: false)
291
- end
292
- let(:updated_uploader) do
293
- instance_double(Imap::Backup::Uploader, run: false)
294
- end
295
- let(:updated_folder) do
296
- instance_double(
297
- Imap::Backup::Account::Folder,
298
- create: nil,
299
- uid_validity: "new uid validity"
300
- )
301
- end
302
- let(:updated_serializer) do
303
- instance_double(
304
- Imap::Backup::Serializer, force_uid_validity: nil
305
- )
306
- end
307
-
308
- before do
309
- allow(Imap::Backup::Account::Folder).to receive(:new).
310
- with(subject, FOLDER_NAME) { folder }
311
- allow(Imap::Backup::Serializer).to receive(:new).
312
- with(anything, FOLDER_NAME) { serializer }
313
- allow(Imap::Backup::Account::Folder).to receive(:new).
314
- with(subject, "new name") { updated_folder }
315
- allow(Imap::Backup::Serializer).to receive(:new).
316
- with(anything, "new name") { updated_serializer }
317
- allow(Imap::Backup::Uploader).to receive(:new).
318
- with(folder, serializer) { uploader }
319
- allow(Imap::Backup::Uploader).to receive(:new).
320
- with(updated_folder, updated_serializer) { updated_uploader }
321
- allow(Pathname).to receive(:glob).
322
- and_yield(Pathname.new(File.join(LOCAL_PATH, "#{FOLDER_NAME}.imap")))
323
- end
324
-
325
- it "sets local uid validity" do
326
- expect(serializer).to receive(:apply_uid_validity).with(uid_validity)
327
-
328
- subject.restore
329
- end
330
-
331
- context "when folders exist with contents" do
332
- context "when the local folder is renamed" do
333
- let(:new_uid_validity) { "new name" }
334
-
335
- it "creates the new folder" do
336
- expect(updated_folder).to receive(:create)
337
-
338
- subject.restore
339
- end
340
-
341
- it "sets the renamed folder's uid validity" do
342
- expect(updated_serializer).
343
- to receive(:force_uid_validity).with("new uid validity")
344
-
345
- subject.restore
346
- end
347
-
348
- it "creates the uploader with updated folder and serializer" do
349
- expect(updated_uploader).to receive(:run)
350
-
351
- subject.restore
352
- end
353
- end
354
-
355
- context "when the local folder is not renamed" do
356
- it "runs the uploader" do
357
- expect(uploader).to receive(:run)
358
-
359
- subject.restore
360
- end
361
- end
362
- end
363
-
364
- context "when folders don't exist or are empty" do
365
- let(:uids) { [] }
366
-
367
- it "creates the folder" do
368
- expect(folder).to receive(:create)
369
-
370
- subject.restore
371
- end
372
-
373
- it "forces local uid validity" do
374
- expect(serializer).to receive(:force_uid_validity).with(uid_validity)
375
-
376
- subject.restore
377
- end
378
-
379
- it "runs the uploader" do
380
- expect(uploader).to receive(:run)
381
-
382
- subject.restore
383
- end
384
- end
385
- end
386
-
387
- describe "#reconnect" do
388
- context "when the IMAP connection has been used" do
389
- before { subject.client }
390
-
391
- it "disconnects from the server" do
392
- expect(client).to receive(:disconnect)
393
-
394
- subject.reconnect
395
- end
396
- end
397
-
398
- context "when the IMAP connection has not been used" do
399
- it "does not disconnect from the server" do
400
- expect(client).to_not receive(:disconnect)
401
-
402
- subject.reconnect
403
- end
404
- end
405
-
406
- it "causes reconnection on future access" do
407
- expect(Imap::Backup::Client::Default).to receive(:new)
408
-
409
- subject.reconnect
410
- subject.client
411
- end
412
- end
413
-
414
- describe "#disconnect" do
415
- context "when the IMAP connection has been used" do
416
- it "disconnects from the server" do
417
- subject.client
418
-
419
- expect(client).to receive(:disconnect)
420
-
421
- subject.disconnect
422
- end
423
- end
424
-
425
- context "when the IMAP connection has not been used" do
426
- it "does not disconnect from the server" do
427
- expect(client).to_not receive(:disconnect)
428
-
429
- subject.disconnect
430
- end
431
- end
432
- end
433
- end