imap-backup 4.0.2 → 4.0.3

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +9 -1
  3. data/docs/development.md +1 -2
  4. data/imap-backup.gemspec +1 -0
  5. data/lib/email/provider/apple_mail.rb +7 -0
  6. data/lib/email/provider/default.rb +12 -0
  7. data/lib/email/provider/fastmail.rb +7 -0
  8. data/lib/email/provider/gmail.rb +7 -0
  9. data/lib/email/provider.rb +16 -26
  10. data/lib/imap/backup/account/connection.rb +19 -29
  11. data/lib/imap/backup/account/folder.rb +9 -10
  12. data/lib/imap/backup/cli/helpers.rb +1 -0
  13. data/lib/imap/backup/cli/local.rb +4 -1
  14. data/lib/imap/backup/cli/utils.rb +16 -5
  15. data/lib/imap/backup/client/apple_mail.rb +11 -0
  16. data/lib/imap/backup/client/default.rb +51 -0
  17. data/lib/imap/backup/configuration/account.rb +3 -1
  18. data/lib/imap/backup/configuration/connection_tester.rb +1 -1
  19. data/lib/imap/backup/configuration/store.rb +10 -5
  20. data/lib/imap/backup/downloader.rb +3 -4
  21. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +12 -25
  22. data/lib/imap/backup/version.rb +1 -1
  23. data/lib/thunderbird/install.rb +16 -0
  24. data/lib/thunderbird/local_folder.rb +33 -65
  25. data/lib/thunderbird/profiles.rb +18 -10
  26. data/lib/thunderbird/subdirectory.rb +96 -0
  27. data/lib/thunderbird/{local_folder_placeholder.rb → subdirectory_placeholder.rb} +4 -4
  28. data/lib/thunderbird.rb +10 -2
  29. data/spec/features/restore_spec.rb +1 -1
  30. data/spec/features/support/email_server.rb +2 -2
  31. data/spec/unit/email/provider/apple_mail_spec.rb +7 -0
  32. data/spec/unit/email/provider/default_spec.rb +17 -0
  33. data/spec/unit/email/provider/fastmail_spec.rb +7 -0
  34. data/spec/unit/email/provider/gmail_spec.rb +7 -0
  35. data/spec/unit/email/provider_spec.rb +12 -25
  36. data/spec/unit/imap/backup/account/connection_spec.rb +26 -51
  37. data/spec/unit/imap/backup/account/folder_spec.rb +22 -22
  38. data/spec/unit/imap/backup/cli/utils_spec.rb +2 -2
  39. data/spec/unit/imap/backup/client/default_spec.rb +22 -0
  40. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +3 -3
  41. data/spec/unit/imap/backup/configuration/store_spec.rb +25 -12
  42. data/spec/unit/imap/backup/downloader_spec.rb +1 -2
  43. metadata +57 -26
  44. data/lib/thunderbird/mailbox.rb +0 -25
@@ -6,9 +6,9 @@ describe Imap::Backup::Account::Folder do
6
6
 
7
7
  subject { described_class.new(connection, FOLDER_NAME) }
8
8
 
9
- let(:imap) do
9
+ let(:client) do
10
10
  instance_double(
11
- Net::IMAP,
11
+ Imap::Backup::Client::Default,
12
12
  append: append_response,
13
13
  create: nil,
14
14
  examine: nil,
@@ -16,7 +16,7 @@ describe Imap::Backup::Account::Folder do
16
16
  )
17
17
  end
18
18
  let(:connection) do
19
- instance_double(Imap::Backup::Account::Connection, imap: imap)
19
+ instance_double(Imap::Backup::Account::Connection, client: client)
20
20
  end
21
21
  let(:missing_mailbox_data) do
22
22
  OpenStruct.new(text: "Unknown Mailbox: #{FOLDER_NAME}")
@@ -31,7 +31,7 @@ describe Imap::Backup::Account::Folder do
31
31
  describe "#uids" do
32
32
  let(:uids) { [5678, 123] }
33
33
 
34
- before { allow(imap).to receive(:uid_search) { uids } }
34
+ before { allow(client).to receive(:uid_search) { uids } }
35
35
 
36
36
  it "lists available messages" do
37
37
  expect(subject.uids).to eq(uids.reverse)
@@ -39,7 +39,7 @@ describe Imap::Backup::Account::Folder do
39
39
 
40
40
  context "with missing mailboxes" do
41
41
  before do
42
- allow(imap).to receive(:examine).
42
+ allow(client).to receive(:examine).
43
43
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
44
44
  end
45
45
 
@@ -54,7 +54,7 @@ describe Imap::Backup::Account::Folder do
54
54
  end
55
55
 
56
56
  before do
57
- allow(imap).to receive(:examine).
57
+ allow(client).to receive(:examine).
58
58
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
59
59
  end
60
60
 
@@ -66,19 +66,19 @@ describe Imap::Backup::Account::Folder do
66
66
 
67
67
  describe "#fetch" do
68
68
  let(:message_body) { instance_double(String, force_encoding: nil) }
69
- let(:attributes) { {"RFC822" => message_body, "other" => "xxx"} }
69
+ let(:attributes) { {"BODY[]" => message_body, "other" => "xxx"} }
70
70
  let(:fetch_data_item) do
71
71
  instance_double(Net::IMAP::FetchData, attr: attributes)
72
72
  end
73
73
 
74
- before { allow(imap).to receive(:uid_fetch) { [fetch_data_item] } }
74
+ before { allow(client).to receive(:uid_fetch) { [fetch_data_item] } }
75
75
 
76
76
  it "returns the message" do
77
- expect(subject.fetch(123)).to eq(attributes)
77
+ expect(subject.fetch(123)).to eq(message_body)
78
78
  end
79
79
 
80
80
  context "when the server responds with nothing" do
81
- before { allow(imap).to receive(:uid_fetch) { nil } }
81
+ before { allow(client).to receive(:uid_fetch) { nil } }
82
82
 
83
83
  it "is nil" do
84
84
  expect(subject.fetch(123)).to be_nil
@@ -87,7 +87,7 @@ describe Imap::Backup::Account::Folder do
87
87
 
88
88
  context "when the mailbox doesn't exist" do
89
89
  before do
90
- allow(imap).to receive(:examine).
90
+ allow(client).to receive(:examine).
91
91
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
92
92
  end
93
93
 
@@ -96,7 +96,7 @@ describe Imap::Backup::Account::Folder do
96
96
  end
97
97
  end
98
98
 
99
- context "when the response doesn't have RFC822" do
99
+ context "when the response doesn't include 'BODY[]'" do
100
100
  let(:attributes) { {} }
101
101
 
102
102
  it "is nil" do
@@ -107,13 +107,13 @@ describe Imap::Backup::Account::Folder do
107
107
  context "when the first fetch_uid attempts fail" do
108
108
  before do
109
109
  outcomes = [-> { raise EOFError }, -> { [fetch_data_item] }]
110
- allow(imap).to receive(:uid_fetch) { outcomes.shift.call }
110
+ allow(client).to receive(:uid_fetch) { outcomes.shift.call }
111
111
  end
112
112
 
113
113
  it "retries" do
114
114
  subject.fetch(123)
115
115
 
116
- expect(imap).to have_received(:uid_fetch).twice
116
+ expect(client).to have_received(:uid_fetch).twice
117
117
  end
118
118
 
119
119
  it "succeeds" do
@@ -137,7 +137,7 @@ describe Imap::Backup::Account::Folder do
137
137
 
138
138
  context "when the folder doesn't exist" do
139
139
  before do
140
- allow(imap).to receive(:examine).
140
+ allow(client).to receive(:examine).
141
141
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
142
142
  end
143
143
 
@@ -150,7 +150,7 @@ describe Imap::Backup::Account::Folder do
150
150
  describe "#create" do
151
151
  context "when the folder exists" do
152
152
  it "is does not create the folder" do
153
- expect(imap).to_not receive(:create)
153
+ expect(client).to_not receive(:create)
154
154
 
155
155
  subject.create
156
156
  end
@@ -158,18 +158,18 @@ describe Imap::Backup::Account::Folder do
158
158
 
159
159
  context "when the folder doesn't exist" do
160
160
  before do
161
- allow(imap).to receive(:examine).
161
+ allow(client).to receive(:examine).
162
162
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
163
163
  end
164
164
 
165
165
  it "creates the folder" do
166
- expect(imap).to receive(:create)
166
+ expect(client).to receive(:create)
167
167
 
168
168
  subject.create
169
169
  end
170
170
 
171
171
  it "encodes the folder name" do
172
- expect(imap).to receive(:create).with(ENCODED_FOLDER_NAME)
172
+ expect(client).to receive(:create).with(ENCODED_FOLDER_NAME)
173
173
 
174
174
  subject.create
175
175
  end
@@ -185,7 +185,7 @@ describe Imap::Backup::Account::Folder do
185
185
 
186
186
  context "when the folder doesn't exist" do
187
187
  before do
188
- allow(imap).to receive(:examine).
188
+ allow(client).to receive(:examine).
189
189
  with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
190
190
  end
191
191
 
@@ -211,13 +211,13 @@ describe Imap::Backup::Account::Folder do
211
211
  end
212
212
 
213
213
  it "appends the message" do
214
- expect(imap).to receive(:append)
214
+ expect(client).to receive(:append)
215
215
 
216
216
  subject.append(message)
217
217
  end
218
218
 
219
219
  it "sets the date and time" do
220
- expect(imap).to receive(:append).
220
+ expect(client).to receive(:append).
221
221
  with(anything, anything, anything, message_date)
222
222
 
223
223
  subject.append(message)
@@ -16,13 +16,13 @@ describe Imap::Backup::CLI::Utils do
16
16
  exist?: true,
17
17
  name: "name",
18
18
  uid_validity: "uid_validity",
19
- uids: ["123", "456"]
19
+ uids: %w(123 456)
20
20
  )
21
21
  end
22
22
  let(:serializer) do
23
23
  instance_double(
24
24
  Imap::Backup::Serializer::Mbox,
25
- uids: ["123", "789"],
25
+ uids: %w(123 789),
26
26
  apply_uid_validity: nil,
27
27
  save: nil
28
28
  )
@@ -0,0 +1,22 @@
1
+ describe Imap::Backup::Client::Default do
2
+ subject { described_class.new("server", {}) }
3
+
4
+ let(:imap) { instance_double(Net::IMAP, list: imap_folders) }
5
+ let(:imap_folders) { [] }
6
+
7
+ before do
8
+ allow(Net::IMAP).to receive(:new) { imap }
9
+ end
10
+
11
+ describe "#list" do
12
+ context "with non-ASCII folder names" do
13
+ let(:imap_folders) do
14
+ [instance_double(Net::IMAP::MailboxList, name: "Gel&APY-scht")]
15
+ end
16
+
17
+ it "converts them to UTF-8" do
18
+ expect(subject.list).to eq(["Gelöscht"])
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,7 +1,7 @@
1
1
  describe Imap::Backup::Configuration::ConnectionTester do
2
2
  describe ".test" do
3
3
  let(:connection) do
4
- instance_double(Imap::Backup::Account::Connection, imap: nil)
4
+ instance_double(Imap::Backup::Account::Connection, client: nil)
5
5
  end
6
6
 
7
7
  before do
@@ -10,7 +10,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
10
10
 
11
11
  describe "call" do
12
12
  it "tries to connect" do
13
- expect(connection).to receive(:imap)
13
+ expect(connection).to receive(:client)
14
14
 
15
15
  subject.test("foo")
16
16
  end
@@ -24,7 +24,7 @@ describe Imap::Backup::Configuration::ConnectionTester do
24
24
 
25
25
  describe "failure" do
26
26
  before do
27
- allow(connection).to receive(:imap).and_raise(error)
27
+ allow(connection).to receive(:client).and_raise(error)
28
28
  end
29
29
 
30
30
  context "with no connection" do
@@ -1,4 +1,5 @@
1
1
  require "json"
2
+ require "os"
2
3
 
3
4
  # rubocop:disable RSpec/PredicateMatcher
4
5
 
@@ -189,10 +190,16 @@ describe Imap::Backup::Configuration::Store do
189
190
  end
190
191
 
191
192
  context "when file permissions are too open" do
192
- it "sets them to 0600" do
193
- expect(FileUtils).to receive(:chmod).with(0o600, file_path)
193
+ context "when on UNIX" do
194
+ before do
195
+ allow(OS).to receive(:windows?) { false }
196
+ end
194
197
 
195
- subject.save
198
+ it "sets them to 0600" do
199
+ expect(FileUtils).to receive(:chmod).with(0o600, file_path)
200
+
201
+ subject.save
202
+ end
196
203
  end
197
204
  end
198
205
 
@@ -206,18 +213,24 @@ describe Imap::Backup::Configuration::Store do
206
213
  end
207
214
  end
208
215
 
209
- context "when the config file permissions are too lax" do
210
- let(:file_exists) { true }
211
-
216
+ context "when on UNIX" do
212
217
  before do
213
- allow(Imap::Backup::Utils).to receive(:check_permissions).
214
- with(file_path, 0o600).and_raise("Error")
218
+ allow(OS).to receive(:windows?) { false }
215
219
  end
216
220
 
217
- it "fails" do
218
- expect do
219
- subject.save
220
- end.to raise_error(RuntimeError, "Error")
221
+ context "when the config file permissions are too lax" do
222
+ let(:file_exists) { true }
223
+
224
+ before do
225
+ allow(Imap::Backup::Utils).to receive(:check_permissions).
226
+ with(file_path, 0o600).and_raise("Error")
227
+ end
228
+
229
+ it "fails" do
230
+ expect do
231
+ subject.save
232
+ end.to raise_error(RuntimeError, "Error")
233
+ end
221
234
  end
222
235
  end
223
236
  end
@@ -3,11 +3,10 @@ describe Imap::Backup::Downloader do
3
3
  subject { described_class.new(folder, serializer) }
4
4
 
5
5
  let(:body) { "blah" }
6
- let(:message) { {"RFC822" => body} }
7
6
  let(:folder) do
8
7
  instance_double(
9
8
  Imap::Backup::Account::Folder,
10
- fetch: message,
9
+ fetch: body,
11
10
  name: "folder",
12
11
  uids: folder_uids
13
12
  )
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.0.2
4
+ version: 4.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-13 00:00:00.000000000 Z
11
+ date: 2021-12-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: os
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rake
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -167,6 +181,10 @@ files:
167
181
  - imap-backup.gemspec
168
182
  - lib/email/mboxrd/message.rb
169
183
  - lib/email/provider.rb
184
+ - lib/email/provider/apple_mail.rb
185
+ - lib/email/provider/default.rb
186
+ - lib/email/provider/fastmail.rb
187
+ - lib/email/provider/gmail.rb
170
188
  - lib/imap/backup.rb
171
189
  - lib/imap/backup/account/connection.rb
172
190
  - lib/imap/backup/account/folder.rb
@@ -179,6 +197,8 @@ files:
179
197
  - lib/imap/backup/cli/setup.rb
180
198
  - lib/imap/backup/cli/status.rb
181
199
  - lib/imap/backup/cli/utils.rb
200
+ - lib/imap/backup/client/apple_mail.rb
201
+ - lib/imap/backup/client/default.rb
182
202
  - lib/imap/backup/configuration/account.rb
183
203
  - lib/imap/backup/configuration/asker.rb
184
204
  - lib/imap/backup/configuration/connection_tester.rb
@@ -197,11 +217,12 @@ files:
197
217
  - lib/imap/backup/version.rb
198
218
  - lib/retry_on_error.rb
199
219
  - lib/thunderbird.rb
220
+ - lib/thunderbird/install.rb
200
221
  - lib/thunderbird/local_folder.rb
201
- - lib/thunderbird/local_folder_placeholder.rb
202
- - lib/thunderbird/mailbox.rb
203
222
  - lib/thunderbird/profile.rb
204
223
  - lib/thunderbird/profiles.rb
224
+ - lib/thunderbird/subdirectory.rb
225
+ - lib/thunderbird/subdirectory_placeholder.rb
205
226
  - spec/features/backup_spec.rb
206
227
  - spec/features/helper.rb
207
228
  - spec/features/restore_spec.rb
@@ -217,11 +238,16 @@ files:
217
238
  - spec/support/shared_examples/account_flagging.rb
218
239
  - spec/support/silence_logging.rb
219
240
  - spec/unit/email/mboxrd/message_spec.rb
241
+ - spec/unit/email/provider/apple_mail_spec.rb
242
+ - spec/unit/email/provider/default_spec.rb
243
+ - spec/unit/email/provider/fastmail_spec.rb
244
+ - spec/unit/email/provider/gmail_spec.rb
220
245
  - spec/unit/email/provider_spec.rb
221
246
  - spec/unit/imap/backup/account/connection_spec.rb
222
247
  - spec/unit/imap/backup/account/folder_spec.rb
223
248
  - spec/unit/imap/backup/cli/local_spec.rb
224
249
  - spec/unit/imap/backup/cli/utils_spec.rb
250
+ - spec/unit/imap/backup/client/default_spec.rb
225
251
  - spec/unit/imap/backup/configuration/account_spec.rb
226
252
  - spec/unit/imap/backup/configuration/asker_spec.rb
227
253
  - spec/unit/imap/backup/configuration/connection_tester_spec.rb
@@ -255,42 +281,47 @@ required_rubygems_version: !ruby/object:Gem::Requirement
255
281
  - !ruby/object:Gem::Version
256
282
  version: '0'
257
283
  requirements: []
258
- rubygems_version: 3.1.2
284
+ rubygems_version: 3.1.4
259
285
  signing_key:
260
286
  specification_version: 4
261
287
  summary: Backup GMail (or other IMAP) accounts to disk
262
288
  test_files:
263
289
  - spec/spec_helper.rb
264
- - spec/fixtures/connection.yml
265
- - spec/unit/email/provider_spec.rb
266
- - spec/unit/email/mboxrd/message_spec.rb
267
290
  - spec/unit/imap/backup_spec.rb
268
- - spec/unit/imap/backup/cli/utils_spec.rb
269
- - spec/unit/imap/backup/cli/local_spec.rb
270
- - spec/unit/imap/backup/utils_spec.rb
271
- - spec/unit/imap/backup/downloader_spec.rb
272
- - spec/unit/imap/backup/configuration/store_spec.rb
291
+ - spec/unit/imap/backup/configuration/account_spec.rb
292
+ - spec/unit/imap/backup/configuration/list_spec.rb
273
293
  - spec/unit/imap/backup/configuration/connection_tester_spec.rb
274
- - spec/unit/imap/backup/configuration/asker_spec.rb
275
294
  - spec/unit/imap/backup/configuration/setup_spec.rb
295
+ - spec/unit/imap/backup/configuration/asker_spec.rb
276
296
  - spec/unit/imap/backup/configuration/folder_chooser_spec.rb
277
- - spec/unit/imap/backup/configuration/account_spec.rb
278
- - spec/unit/imap/backup/configuration/list_spec.rb
279
- - spec/unit/imap/backup/account/folder_spec.rb
280
- - spec/unit/imap/backup/account/connection_spec.rb
297
+ - spec/unit/imap/backup/configuration/store_spec.rb
281
298
  - spec/unit/imap/backup/serializer/mbox_spec.rb
282
299
  - spec/unit/imap/backup/serializer/mbox_store_spec.rb
283
300
  - spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb
301
+ - spec/unit/imap/backup/cli/utils_spec.rb
302
+ - spec/unit/imap/backup/cli/local_spec.rb
303
+ - spec/unit/imap/backup/downloader_spec.rb
284
304
  - spec/unit/imap/backup/uploader_spec.rb
285
- - spec/support/fixtures.rb
286
- - spec/support/shared_examples/account_flagging.rb
287
- - spec/support/higline_test_helpers.rb
288
- - spec/support/silence_logging.rb
289
- - spec/gather_rspec_coverage.rb
305
+ - spec/unit/imap/backup/account/folder_spec.rb
306
+ - spec/unit/imap/backup/account/connection_spec.rb
307
+ - spec/unit/imap/backup/utils_spec.rb
308
+ - spec/unit/imap/backup/client/default_spec.rb
309
+ - spec/unit/email/mboxrd/message_spec.rb
310
+ - spec/unit/email/provider/gmail_spec.rb
311
+ - spec/unit/email/provider/default_spec.rb
312
+ - spec/unit/email/provider/apple_mail_spec.rb
313
+ - spec/unit/email/provider/fastmail_spec.rb
314
+ - spec/unit/email/provider_spec.rb
290
315
  - spec/features/backup_spec.rb
291
316
  - spec/features/helper.rb
317
+ - spec/features/restore_spec.rb
318
+ - spec/features/support/email_server.rb
292
319
  - spec/features/support/backup_directory.rb
293
- - spec/features/support/shared/message_fixtures.rb
294
320
  - spec/features/support/shared/connection_context.rb
295
- - spec/features/support/email_server.rb
296
- - spec/features/restore_spec.rb
321
+ - spec/features/support/shared/message_fixtures.rb
322
+ - spec/support/fixtures.rb
323
+ - spec/support/silence_logging.rb
324
+ - spec/support/higline_test_helpers.rb
325
+ - spec/support/shared_examples/account_flagging.rb
326
+ - spec/fixtures/connection.yml
327
+ - spec/gather_rspec_coverage.rb
@@ -1,25 +0,0 @@
1
- class Thunderbird::Mailbox
2
- attr_reader :local_folder
3
- attr_reader :name
4
-
5
- def initialize(local_folder, name)
6
- @local_folder = local_folder
7
- @name = name
8
- end
9
-
10
- def path
11
- File.join(local_folder.full_path, name)
12
- end
13
-
14
- def exists?
15
- File.exists?(path)
16
- end
17
-
18
- def msf_path
19
- path + ".msf"
20
- end
21
-
22
- def msf_exists?
23
- File.exists?(msf_path)
24
- end
25
- end