imap-backup 2.0.0.rc1 → 2.0.0.rc2
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 +5 -5
- data/.travis.yml +3 -5
- data/docs/docker-imap.md +18 -0
- data/imap-backup.gemspec +1 -0
- data/lib/email/mboxrd/message.rb +2 -0
- data/lib/imap/backup/account/folder.rb +1 -1
- data/lib/imap/backup/configuration/store.rb +1 -1
- data/lib/imap/backup/version.rb +1 -1
- data/spec/fixtures/connection.yml +3 -2
- data/spec/unit/email/mboxrd/message_spec.rb +34 -2
- data/spec/unit/{account → imap/backup/account}/connection_spec.rb +122 -13
- data/spec/unit/imap/backup/account/folder_spec.rb +168 -0
- data/spec/unit/{configuration → imap/backup/configuration}/account_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/asker_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/connection_tester_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/folder_chooser_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/list_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/setup_spec.rb +0 -0
- data/spec/unit/{configuration → imap/backup/configuration}/store_spec.rb +1 -0
- data/spec/unit/{downloader_spec.rb → imap/backup/downloader_spec.rb} +0 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +164 -0
- data/spec/unit/{serializer → imap/backup/serializer}/mbox_store_spec.rb +0 -0
- data/spec/unit/imap/backup/uploader_spec.rb +40 -0
- data/spec/unit/{utils_spec.rb → imap/backup/utils_spec.rb} +0 -0
- metadata +33 -30
- data/spec/unit/account/folder_spec.rb +0 -75
- data/spec/unit/serializer/mbox_spec.rb +0 -82
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d5590ab1d097eb650e1af98d2b913fc6f2b95287
|
4
|
+
data.tar.gz: 7ff2e0eb4c6dde13300166e9e18b512c5e01c6fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f372713523f16c4e281c7443c3f8f5bca116c392b93e3bea2df1f2ea823675f1330ef44d5b5f50ba6f21a991d972a1acc39483a36784fb77f0608b484cd24979
|
7
|
+
data.tar.gz: 99bfc3b3a4c2366a76bf0f94e019a37b564b8e06b54213746fa520e51af8d89cb2795e3dcc2eafb69fae0b59d2c4060bf505859591507719bae553d66cf8f04f
|
data/.travis.yml
CHANGED
data/docs/docker-imap.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Access Docker imap server
|
2
|
+
|
3
|
+
```ruby
|
4
|
+
require "net/imap"
|
5
|
+
|
6
|
+
imap = Net::IMAP.new("localhost", {port: 8993, ssl: {verify_mode: 0}})
|
7
|
+
username = "address@example.org"
|
8
|
+
imap.login(username, "pass")
|
9
|
+
|
10
|
+
message = "From: #{username}\nSubject: Some Subject\n\nHello!\n"
|
11
|
+
response = imap.append("INBOX", message, nil, nil)
|
12
|
+
|
13
|
+
imap.examine("INBOX")
|
14
|
+
uids = imap.uid_search(["ALL"]).sort
|
15
|
+
|
16
|
+
REQUESTED_ATTRIBUTES = ["RFC822", "FLAGS", "INTERNALDATE"].freeze
|
17
|
+
fetch_data_items = imap.uid_fetch(uids, REQUESTED_ATTRIBUTES)
|
18
|
+
```
|
data/imap-backup.gemspec
CHANGED
@@ -13,6 +13,7 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
14
14
|
gem.test_files = gem.files.grep(%r{^spec/})
|
15
15
|
gem.require_paths = ["lib"]
|
16
|
+
gem.required_ruby_version = [">= 2.2.0"]
|
16
17
|
gem.version = Imap::Backup::VERSION
|
17
18
|
|
18
19
|
gem.post_install_message = <<-MESSAGE.gsub(/^\s{4}/m, "")
|
data/lib/email/mboxrd/message.rb
CHANGED
data/lib/imap/backup/version.rb
CHANGED
@@ -30,6 +30,21 @@ describe Email::Mboxrd::Message do
|
|
30
30
|
let(:cloned_message_body) do
|
31
31
|
"Foo\nBar\nFrom at the beginning of the line\n>>From quoted"
|
32
32
|
end
|
33
|
+
let(:msg_good) do
|
34
|
+
%Q|Delivered-To: you@example.com
|
35
|
+
From: Foo <foo@example.com>
|
36
|
+
To: FirstName LastName <you@example.com>
|
37
|
+
Date: #{date.rfc822}
|
38
|
+
Subject: Re: no subject|
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:msg_bad_date) do
|
42
|
+
%Q|Delivered-To: you@example.com
|
43
|
+
From: Foo <foo@example.com>
|
44
|
+
To: FirstName LastName <you@example.com>
|
45
|
+
Date: Mon,5 May 2014 08:97:99 GMT
|
46
|
+
Subject: Re: no subject|
|
47
|
+
end
|
33
48
|
|
34
49
|
subject { described_class.new(message_body) }
|
35
50
|
|
@@ -90,14 +105,15 @@ describe Email::Mboxrd::Message do
|
|
90
105
|
allow(Mail).to receive(:new).and_call_original
|
91
106
|
end
|
92
107
|
|
93
|
-
context "when original message 'from' is
|
108
|
+
context "when original message 'from' is missing" do
|
94
109
|
let(:message_body) { msg_no_from }
|
110
|
+
|
95
111
|
it "'from' is empty string" do
|
96
112
|
expect(subject.to_serialized).to start_with("From \n")
|
97
113
|
end
|
98
114
|
end
|
99
115
|
|
100
|
-
context "when original message 'from' is a string but not
|
116
|
+
context "when original message 'from' is a string but not well-formed address" do
|
101
117
|
let(:message_body) { msg_bad_from }
|
102
118
|
|
103
119
|
it "'from' is empty string" do
|
@@ -122,4 +138,20 @@ describe Email::Mboxrd::Message do
|
|
122
138
|
end
|
123
139
|
end
|
124
140
|
end
|
141
|
+
|
142
|
+
context "#date" do
|
143
|
+
let(:message_body) { msg_good }
|
144
|
+
|
145
|
+
it "returns the date" do
|
146
|
+
expect(subject.date).to eq(date)
|
147
|
+
end
|
148
|
+
|
149
|
+
context "with incorrect minutes and seconds" do
|
150
|
+
let(:message_body) { msg_bad_date }
|
151
|
+
|
152
|
+
it "returns nil" do
|
153
|
+
expect(subject.date).to be_nil
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
125
157
|
end
|
@@ -28,9 +28,21 @@ describe Imap::Backup::Account::Connection do
|
|
28
28
|
instance_double(Net::IMAP::MailboxList, name: root_name)
|
29
29
|
end
|
30
30
|
let(:root_name) { "foo" }
|
31
|
+
let(:serializer) do
|
32
|
+
instance_double(
|
33
|
+
Imap::Backup::Serializer::Mbox,
|
34
|
+
folder: serialized_folder,
|
35
|
+
force_uid_validity: nil,
|
36
|
+
set_uid_validity: new_uid_validity,
|
37
|
+
uids: [local_uid]
|
38
|
+
)
|
39
|
+
end
|
40
|
+
let(:serialized_folder) { nil }
|
41
|
+
let(:new_uid_validity) { nil }
|
42
|
+
let(:local_uid) { "local_uid" }
|
31
43
|
|
32
44
|
before do
|
33
|
-
allow(Net::IMAP).to receive(:new)
|
45
|
+
allow(Net::IMAP).to receive(:new) { imap }
|
34
46
|
allow(imap).to receive(:list).with("", "") { [root_info] }
|
35
47
|
allow(imap).to receive(:list).with(root_name, "*") { imap_folders }
|
36
48
|
allow(Imap::Backup::Utils).to receive(:make_folder)
|
@@ -89,10 +101,6 @@ describe Imap::Backup::Account::Connection do
|
|
89
101
|
let(:folder) do
|
90
102
|
instance_double(Imap::Backup::Account::Folder, uids: [remote_uid])
|
91
103
|
end
|
92
|
-
let(:local_uid) { "local_uid" }
|
93
|
-
let(:serializer) do
|
94
|
-
instance_double(Imap::Backup::Serializer::Mbox, uids: [local_uid])
|
95
|
-
end
|
96
104
|
let(:remote_uid) { "remote_uid" }
|
97
105
|
|
98
106
|
before do
|
@@ -122,12 +130,6 @@ describe Imap::Backup::Account::Connection do
|
|
122
130
|
)
|
123
131
|
end
|
124
132
|
let(:uid_validity) { 123 }
|
125
|
-
let(:serializer) do
|
126
|
-
instance_double(
|
127
|
-
Imap::Backup::Serializer::Mbox,
|
128
|
-
set_uid_validity: nil
|
129
|
-
)
|
130
|
-
end
|
131
133
|
let(:downloader) { instance_double(Imap::Backup::Downloader, run: nil) }
|
132
134
|
|
133
135
|
before do
|
@@ -193,13 +195,120 @@ describe Imap::Backup::Account::Connection do
|
|
193
195
|
end
|
194
196
|
end
|
195
197
|
|
198
|
+
context "#restore" do
|
199
|
+
let(:folder) do
|
200
|
+
instance_double(
|
201
|
+
Imap::Backup::Account::Folder,
|
202
|
+
create: nil,
|
203
|
+
exist?: exists,
|
204
|
+
name: "my_folder",
|
205
|
+
uid_validity: uid_validity
|
206
|
+
)
|
207
|
+
end
|
208
|
+
let(:exists) { true }
|
209
|
+
let(:uid_validity) { 123 }
|
210
|
+
let(:serialized_folder) { "old name" }
|
211
|
+
let(:uploader) do
|
212
|
+
instance_double(Imap::Backup::Uploader, run: false)
|
213
|
+
end
|
214
|
+
let(:updated_uploader) do
|
215
|
+
instance_double(Imap::Backup::Uploader, run: false)
|
216
|
+
end
|
217
|
+
let(:updated_folder) do
|
218
|
+
instance_double(
|
219
|
+
Imap::Backup::Account::Folder,
|
220
|
+
create: nil,
|
221
|
+
uid_validity: "new uid validity"
|
222
|
+
)
|
223
|
+
end
|
224
|
+
let(:updated_serializer) do
|
225
|
+
instance_double(
|
226
|
+
Imap::Backup::Serializer::Mbox, force_uid_validity: nil
|
227
|
+
)
|
228
|
+
end
|
229
|
+
|
230
|
+
before do
|
231
|
+
allow(Imap::Backup::Account::Folder).to receive(:new).
|
232
|
+
with(subject, "my_folder") { folder }
|
233
|
+
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
234
|
+
with(anything, "my_folder") { serializer }
|
235
|
+
allow(Imap::Backup::Account::Folder).to receive(:new).
|
236
|
+
with(subject, "new name") { updated_folder }
|
237
|
+
allow(Imap::Backup::Serializer::Mbox).to receive(:new).
|
238
|
+
with(anything, "new name") { updated_serializer }
|
239
|
+
allow(Imap::Backup::Uploader).to receive(:new).
|
240
|
+
with(folder, serializer) { uploader }
|
241
|
+
allow(Imap::Backup::Uploader).to receive(:new).
|
242
|
+
with(updated_folder, updated_serializer) { updated_uploader }
|
243
|
+
allow(Pathname).to receive(:glob).
|
244
|
+
and_yield(Pathname.new(File.join(local_path, "my_folder.imap")))
|
245
|
+
subject.restore
|
246
|
+
end
|
247
|
+
|
248
|
+
it "sets local uid validity" do
|
249
|
+
expect(serializer).to have_received(:set_uid_validity).with(uid_validity)
|
250
|
+
end
|
251
|
+
|
252
|
+
context "when folders exist" do
|
253
|
+
context "when the local folder is renamed" do
|
254
|
+
let(:new_uid_validity) { "new name" }
|
255
|
+
|
256
|
+
it "creates the new folder" do
|
257
|
+
expect(updated_folder).to have_received(:create)
|
258
|
+
end
|
259
|
+
|
260
|
+
it "sets the renamed folder's uid validity" do
|
261
|
+
expect(updated_serializer).
|
262
|
+
to have_received(:force_uid_validity).with("new uid validity")
|
263
|
+
end
|
264
|
+
|
265
|
+
it "creates the uploader with updated folder and serializer" do
|
266
|
+
expect(updated_uploader).to have_received(:run)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
context "when the local folder is not renamed" do
|
271
|
+
it "runs the uploader" do
|
272
|
+
expect(uploader).to have_received(:run)
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "when folders don't exist" do
|
278
|
+
let(:exists) { false }
|
279
|
+
|
280
|
+
it "creates the folder" do
|
281
|
+
expect(folder).to have_received(:create)
|
282
|
+
end
|
283
|
+
|
284
|
+
it "sets local uid validity" do
|
285
|
+
end
|
286
|
+
|
287
|
+
it "runs the uploader" do
|
288
|
+
expect(uploader).to have_received(:run)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
context "#reconnect" do
|
294
|
+
before { subject.reconnect }
|
295
|
+
|
296
|
+
it "disconnects from the server" do
|
297
|
+
expect(imap).to have_received(:disconnect)
|
298
|
+
end
|
299
|
+
|
300
|
+
it "causes reconnection on future access" do
|
301
|
+
allow(Net::IMAP).to receive(:new) { imap }
|
302
|
+
subject.imap
|
303
|
+
expect(Net::IMAP).to have_received(:new).twice
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
196
307
|
context "#disconnect" do
|
197
308
|
before { subject.disconnect }
|
198
309
|
|
199
310
|
it "disconnects from the server" do
|
200
311
|
expect(imap).to have_received(:disconnect)
|
201
312
|
end
|
202
|
-
|
203
|
-
include_examples "connects to IMAP"
|
204
313
|
end
|
205
314
|
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Imap::Backup::Account::Folder do
|
4
|
+
let(:imap) do
|
5
|
+
instance_double(
|
6
|
+
Net::IMAP,
|
7
|
+
append: append_response,
|
8
|
+
create: nil,
|
9
|
+
examine: nil,
|
10
|
+
responses: responses
|
11
|
+
)
|
12
|
+
end
|
13
|
+
let(:connection) { double("Imap::Backup::Account::Connection", imap: imap) }
|
14
|
+
let(:missing_mailbox_data) do
|
15
|
+
double("Data", text: "Unknown Mailbox: my_folder")
|
16
|
+
end
|
17
|
+
let(:missing_mailbox_response) do
|
18
|
+
double("Response", data: missing_mailbox_data)
|
19
|
+
end
|
20
|
+
let(:missing_mailbox_error) do
|
21
|
+
Net::IMAP::NoResponseError.new(missing_mailbox_response)
|
22
|
+
end
|
23
|
+
let(:responses) { [] }
|
24
|
+
let(:append_response) { nil }
|
25
|
+
|
26
|
+
subject { described_class.new(connection, "my_folder") }
|
27
|
+
|
28
|
+
context "#uids" do
|
29
|
+
let(:uids) { [5678, 123] }
|
30
|
+
|
31
|
+
before { allow(imap).to receive(:uid_search).and_return(uids) }
|
32
|
+
|
33
|
+
it "lists available messages" do
|
34
|
+
expect(subject.uids).to eq(uids.reverse)
|
35
|
+
end
|
36
|
+
|
37
|
+
context "with missing mailboxes" do
|
38
|
+
before do
|
39
|
+
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns an empty array" do
|
43
|
+
expect(subject.uids).to eq([])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "#fetch" do
|
49
|
+
let(:message_body) { double("the body", force_encoding: nil) }
|
50
|
+
let(:attributes) { {"RFC822" => message_body, "other" => "xxx"} }
|
51
|
+
let(:fetch_data_item) do
|
52
|
+
instance_double(Net::IMAP::FetchData, attr: attributes)
|
53
|
+
end
|
54
|
+
|
55
|
+
before { allow(imap).to receive(:uid_fetch) { [fetch_data_item] } }
|
56
|
+
|
57
|
+
it "returns the message" do
|
58
|
+
expect(subject.fetch(123)).to eq(attributes)
|
59
|
+
end
|
60
|
+
|
61
|
+
context "if the server responds with nothing" do
|
62
|
+
before { allow(imap).to receive(:uid_fetch) { nil } }
|
63
|
+
|
64
|
+
it "is nil" do
|
65
|
+
expect(subject.fetch(123)).to be_nil
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "if the mailbox doesn't exist" do
|
70
|
+
before do
|
71
|
+
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "is nil" do
|
75
|
+
expect(subject.fetch(123)).to be_nil
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it "sets the encoding on the message" do
|
80
|
+
subject.fetch(123)
|
81
|
+
|
82
|
+
expect(message_body).to have_received(:force_encoding).with("utf-8")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "#folder" do
|
87
|
+
it "is the name" do
|
88
|
+
expect(subject.folder).to eq("my_folder")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "#exist?" do
|
93
|
+
context "when the folder exists" do
|
94
|
+
it "is true" do
|
95
|
+
expect(subject.exist?).to be_truthy
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context "when the folder doesn't exist" do
|
100
|
+
before do
|
101
|
+
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "is false" do
|
105
|
+
expect(subject.exist?).to be_falsey
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "#create" do
|
111
|
+
context "when the folder exists" do
|
112
|
+
before { subject.create }
|
113
|
+
|
114
|
+
it "is does not create the folder" do
|
115
|
+
expect(imap).to_not have_received(:create)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
context "when the folder doesn't exist" do
|
120
|
+
before do
|
121
|
+
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
122
|
+
subject.create
|
123
|
+
end
|
124
|
+
|
125
|
+
it "is does not create the folder" do
|
126
|
+
expect(imap).to have_received(:create)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context "#uid_validity" do
|
132
|
+
let(:responses) { {"UIDVALIDITY" => ["x", "uid validity"]} }
|
133
|
+
|
134
|
+
it "is returned" do
|
135
|
+
expect(subject.uid_validity).to eq("uid validity")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
context "#append" do
|
140
|
+
let(:message) do
|
141
|
+
instance_double(
|
142
|
+
Email::Mboxrd::Message,
|
143
|
+
imap_body: "imap body",
|
144
|
+
date: Time.now
|
145
|
+
)
|
146
|
+
end
|
147
|
+
let(:append_response) { "response" }
|
148
|
+
let(:result) { subject.append(message) }
|
149
|
+
|
150
|
+
before do
|
151
|
+
allow(append_response).
|
152
|
+
to receive_message_chain("data.code.data") { "1 2" }
|
153
|
+
result
|
154
|
+
end
|
155
|
+
|
156
|
+
it "appends the message" do
|
157
|
+
expect(imap).to have_received(:append)
|
158
|
+
end
|
159
|
+
|
160
|
+
it "returns the new uid" do
|
161
|
+
expect(result).to eq(2)
|
162
|
+
end
|
163
|
+
|
164
|
+
it "set the new uid validity" do
|
165
|
+
expect(subject.uid_validity).to eq(1)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -16,6 +16,7 @@ describe Imap::Backup::Configuration::Store do
|
|
16
16
|
"Imap::Backup::Configuration::Store::CONFIGURATION_DIRECTORY", directory
|
17
17
|
)
|
18
18
|
allow(File).to receive(:directory?).with(directory) { directory_exists }
|
19
|
+
allow(File).to receive(:exist?).and_call_original
|
19
20
|
allow(File).to receive(:exist?).with(file_path) { file_exists }
|
20
21
|
allow(Imap::Backup::Utils).
|
21
22
|
to receive(:stat).with(directory).and_return(0o700)
|
File without changes
|
@@ -0,0 +1,164 @@
|
|
1
|
+
describe Imap::Backup::Serializer::Mbox do
|
2
|
+
let(:base_path) { "/base/path" }
|
3
|
+
let(:store) do
|
4
|
+
instance_double(
|
5
|
+
Imap::Backup::Serializer::MboxStore,
|
6
|
+
add: nil,
|
7
|
+
rename: nil,
|
8
|
+
uids: nil,
|
9
|
+
uid_validity: existing_uid_validity,
|
10
|
+
"uid_validity=": nil
|
11
|
+
)
|
12
|
+
end
|
13
|
+
let(:imap_folder) { "folder" }
|
14
|
+
let(:permissions) { 0o700 }
|
15
|
+
let(:dir_exists) { true }
|
16
|
+
let(:existing_uid_validity) { nil }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(Imap::Backup::Utils).to receive(:make_folder)
|
20
|
+
allow(Imap::Backup::Utils).to receive(:mode) { permissions }
|
21
|
+
allow(Imap::Backup::Utils).to receive(:check_permissions) { true }
|
22
|
+
allow(File).to receive(:directory?) { dir_exists }
|
23
|
+
end
|
24
|
+
|
25
|
+
subject { described_class.new(base_path, imap_folder) }
|
26
|
+
|
27
|
+
before do
|
28
|
+
allow(FileUtils).to receive(:chmod)
|
29
|
+
allow(Imap::Backup::Serializer::MboxStore).to receive(:new) { store }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "containing directory" do
|
33
|
+
before { subject.uids }
|
34
|
+
|
35
|
+
context "when the IMAP folder has multiple elements" do
|
36
|
+
let(:imap_folder) { "folder/path" }
|
37
|
+
|
38
|
+
context "when the containing directory is missing" do
|
39
|
+
let(:dir_exists) { false }
|
40
|
+
|
41
|
+
it "is created" do
|
42
|
+
expect(Imap::Backup::Utils).to have_received(:make_folder).
|
43
|
+
with(base_path, File.dirname(imap_folder), 0o700)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when the containing directory permissons are incorrect" do
|
49
|
+
let(:permissions) { 0o777 }
|
50
|
+
|
51
|
+
it "corrects them" do
|
52
|
+
path = File.expand_path(File.join(base_path, File.dirname(imap_folder)))
|
53
|
+
expect(FileUtils).to have_received(:chmod).with(0o700, path)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "when the containing directory permissons are correct" do
|
58
|
+
it "does nothing" do
|
59
|
+
expect(FileUtils).to_not have_received(:chmod)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context "when the containing directory exists" do
|
64
|
+
it "is not created" do
|
65
|
+
expect(Imap::Backup::Utils).to_not have_received(:make_folder).
|
66
|
+
with(base_path, File.dirname(imap_folder), 0o700)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context "#uids" do
|
72
|
+
it "calls the store" do
|
73
|
+
subject.uids
|
74
|
+
|
75
|
+
expect(store).to have_received(:uids)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "#save" do
|
80
|
+
it "calls the store" do
|
81
|
+
subject.save("foo", "bar")
|
82
|
+
|
83
|
+
expect(store).to have_received(:add)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "#set_uid_validity" do
|
88
|
+
let(:result) { subject.set_uid_validity("aaa") }
|
89
|
+
|
90
|
+
context "when the existing uid validity is unset" do
|
91
|
+
let!(:result) { super() }
|
92
|
+
|
93
|
+
it "sets uid validity" do
|
94
|
+
expect(store).to have_received(:uid_validity=).with("aaa")
|
95
|
+
end
|
96
|
+
|
97
|
+
it "does not rename the store" do
|
98
|
+
expect(store).to_not have_received(:rename)
|
99
|
+
end
|
100
|
+
|
101
|
+
it "returns nil" do
|
102
|
+
expect(result).to be_nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when the uid validity is unchanged" do
|
107
|
+
let!(:result) { super() }
|
108
|
+
let(:existing_uid_validity) { "aaa" }
|
109
|
+
|
110
|
+
it "does not set uid validity" do
|
111
|
+
expect(store).to_not have_received(:uid_validity=)
|
112
|
+
end
|
113
|
+
|
114
|
+
it "does not rename the store" do
|
115
|
+
expect(store).to_not have_received(:rename)
|
116
|
+
end
|
117
|
+
|
118
|
+
it "returns nil" do
|
119
|
+
expect(result).to be_nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context "when the uid validity is changed" do
|
124
|
+
let(:existing_uid_validity) { "bbb" }
|
125
|
+
let(:existing_store) do
|
126
|
+
instance_double(Imap::Backup::Serializer::MboxStore)
|
127
|
+
end
|
128
|
+
let(:exists) { false }
|
129
|
+
|
130
|
+
before do
|
131
|
+
allow(Imap::Backup::Serializer::MboxStore).
|
132
|
+
to receive(:new).with(anything, /bbb/) { existing_store }
|
133
|
+
allow(existing_store).to receive(:exist?).and_return(exists, false)
|
134
|
+
result
|
135
|
+
end
|
136
|
+
|
137
|
+
it "sets uid validity" do
|
138
|
+
expect(store).to have_received(:uid_validity=).with("aaa")
|
139
|
+
end
|
140
|
+
|
141
|
+
context "when adding the uid validity does not cause a name clash" do
|
142
|
+
it "renames the store, adding the existing uid validity" do
|
143
|
+
expect(store).to have_received(:rename).with("folder.bbb")
|
144
|
+
end
|
145
|
+
|
146
|
+
it "returns the new name" do
|
147
|
+
expect(result).to eq("folder.bbb")
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "when adding the uid validity causes a name clash" do
|
152
|
+
let(:exists) { true }
|
153
|
+
|
154
|
+
it "renames the store, adding the existing uid validity and a digit" do
|
155
|
+
expect(store).to have_received(:rename).with("folder.bbb.1")
|
156
|
+
end
|
157
|
+
|
158
|
+
it "returns the new name" do
|
159
|
+
expect(result).to eq("folder.bbb.1")
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
File without changes
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Imap::Backup::Uploader do
|
4
|
+
subject { described_class.new(folder, serializer) }
|
5
|
+
|
6
|
+
let(:folder) do
|
7
|
+
instance_double(Imap::Backup::Account::Folder, uids: [2, 3], append: 99)
|
8
|
+
end
|
9
|
+
let(:serializer) do
|
10
|
+
instance_double(
|
11
|
+
Imap::Backup::Serializer::Mbox,
|
12
|
+
uids: [1, 2],
|
13
|
+
update_uid: nil
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#run" do
|
18
|
+
before do
|
19
|
+
allow(serializer).to receive(:load).with(1) { "missing message" }
|
20
|
+
allow(serializer).to receive(:load).with(2) { "existing message" }
|
21
|
+
subject.run
|
22
|
+
end
|
23
|
+
|
24
|
+
context "with messages that are missing" do
|
25
|
+
it "restores them" do
|
26
|
+
expect(folder).to have_received(:append).with("missing message")
|
27
|
+
end
|
28
|
+
|
29
|
+
it "updates the local message id" do
|
30
|
+
expect(serializer).to have_received(:update_uid).with(1, 99)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context "with messages that are present on server" do
|
35
|
+
it "does nothing" do
|
36
|
+
expect(folder).to_not have_received(:append).with("existing message")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
File without changes
|
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: 2.0.0.
|
4
|
+
version: 2.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joe Yates
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- Rakefile
|
142
142
|
- bin/imap-backup
|
143
143
|
- docker-compose.yml
|
144
|
+
- docs/docker-imap.md
|
144
145
|
- imap-backup.gemspec
|
145
146
|
- lib/email/mboxrd/message.rb
|
146
147
|
- lib/email/provider.rb
|
@@ -175,21 +176,22 @@ files:
|
|
175
176
|
- spec/support/higline_test_helpers.rb
|
176
177
|
- spec/support/shared_examples/account_flagging.rb
|
177
178
|
- spec/support/silence_logging.rb
|
178
|
-
- spec/unit/account/connection_spec.rb
|
179
|
-
- spec/unit/account/folder_spec.rb
|
180
|
-
- spec/unit/configuration/account_spec.rb
|
181
|
-
- spec/unit/configuration/asker_spec.rb
|
182
|
-
- spec/unit/configuration/connection_tester_spec.rb
|
183
|
-
- spec/unit/configuration/folder_chooser_spec.rb
|
184
|
-
- spec/unit/configuration/list_spec.rb
|
185
|
-
- spec/unit/configuration/setup_spec.rb
|
186
|
-
- spec/unit/configuration/store_spec.rb
|
187
|
-
- spec/unit/downloader_spec.rb
|
188
179
|
- spec/unit/email/mboxrd/message_spec.rb
|
189
180
|
- spec/unit/email/provider_spec.rb
|
190
|
-
- spec/unit/
|
191
|
-
- spec/unit/
|
192
|
-
- spec/unit/
|
181
|
+
- spec/unit/imap/backup/account/connection_spec.rb
|
182
|
+
- spec/unit/imap/backup/account/folder_spec.rb
|
183
|
+
- spec/unit/imap/backup/configuration/account_spec.rb
|
184
|
+
- spec/unit/imap/backup/configuration/asker_spec.rb
|
185
|
+
- spec/unit/imap/backup/configuration/connection_tester_spec.rb
|
186
|
+
- spec/unit/imap/backup/configuration/folder_chooser_spec.rb
|
187
|
+
- spec/unit/imap/backup/configuration/list_spec.rb
|
188
|
+
- spec/unit/imap/backup/configuration/setup_spec.rb
|
189
|
+
- spec/unit/imap/backup/configuration/store_spec.rb
|
190
|
+
- spec/unit/imap/backup/downloader_spec.rb
|
191
|
+
- spec/unit/imap/backup/serializer/mbox_spec.rb
|
192
|
+
- spec/unit/imap/backup/serializer/mbox_store_spec.rb
|
193
|
+
- spec/unit/imap/backup/uploader_spec.rb
|
194
|
+
- spec/unit/imap/backup/utils_spec.rb
|
193
195
|
- tmp/.gitkeep
|
194
196
|
homepage: https://github.com/joeyates/imap-backup
|
195
197
|
licenses: []
|
@@ -206,7 +208,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
206
208
|
requirements:
|
207
209
|
- - ">="
|
208
210
|
- !ruby/object:Gem::Version
|
209
|
-
version:
|
211
|
+
version: 2.2.0
|
210
212
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
211
213
|
requirements:
|
212
214
|
- - ">"
|
@@ -214,7 +216,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
214
216
|
version: 1.3.1
|
215
217
|
requirements: []
|
216
218
|
rubyforge_project:
|
217
|
-
rubygems_version: 2.
|
219
|
+
rubygems_version: 2.5.2.3
|
218
220
|
signing_key:
|
219
221
|
specification_version: 4
|
220
222
|
summary: Backup GMail (or other IMAP) accounts to disk
|
@@ -233,18 +235,19 @@ test_files:
|
|
233
235
|
- spec/support/higline_test_helpers.rb
|
234
236
|
- spec/support/shared_examples/account_flagging.rb
|
235
237
|
- spec/support/silence_logging.rb
|
236
|
-
- spec/unit/account/connection_spec.rb
|
237
|
-
- spec/unit/account/folder_spec.rb
|
238
|
-
- spec/unit/configuration/account_spec.rb
|
239
|
-
- spec/unit/configuration/asker_spec.rb
|
240
|
-
- spec/unit/configuration/connection_tester_spec.rb
|
241
|
-
- spec/unit/configuration/folder_chooser_spec.rb
|
242
|
-
- spec/unit/configuration/list_spec.rb
|
243
|
-
- spec/unit/configuration/setup_spec.rb
|
244
|
-
- spec/unit/configuration/store_spec.rb
|
245
|
-
- spec/unit/downloader_spec.rb
|
246
238
|
- spec/unit/email/mboxrd/message_spec.rb
|
247
239
|
- spec/unit/email/provider_spec.rb
|
248
|
-
- spec/unit/
|
249
|
-
- spec/unit/
|
250
|
-
- spec/unit/
|
240
|
+
- spec/unit/imap/backup/account/connection_spec.rb
|
241
|
+
- spec/unit/imap/backup/account/folder_spec.rb
|
242
|
+
- spec/unit/imap/backup/configuration/account_spec.rb
|
243
|
+
- spec/unit/imap/backup/configuration/asker_spec.rb
|
244
|
+
- spec/unit/imap/backup/configuration/connection_tester_spec.rb
|
245
|
+
- spec/unit/imap/backup/configuration/folder_chooser_spec.rb
|
246
|
+
- spec/unit/imap/backup/configuration/list_spec.rb
|
247
|
+
- spec/unit/imap/backup/configuration/setup_spec.rb
|
248
|
+
- spec/unit/imap/backup/configuration/store_spec.rb
|
249
|
+
- spec/unit/imap/backup/downloader_spec.rb
|
250
|
+
- spec/unit/imap/backup/serializer/mbox_spec.rb
|
251
|
+
- spec/unit/imap/backup/serializer/mbox_store_spec.rb
|
252
|
+
- spec/unit/imap/backup/uploader_spec.rb
|
253
|
+
- spec/unit/imap/backup/utils_spec.rb
|
@@ -1,75 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Imap::Backup::Account::Folder do
|
4
|
-
let(:imap) { double("Net::IMAP", examine: nil) }
|
5
|
-
let(:connection) { double("Imap::Backup::Account::Connection", imap: imap) }
|
6
|
-
let(:missing_mailbox_data) do
|
7
|
-
double("Data", text: "Unknown Mailbox: my_folder")
|
8
|
-
end
|
9
|
-
let(:missing_mailbox_response) do
|
10
|
-
double("Response", data: missing_mailbox_data)
|
11
|
-
end
|
12
|
-
let(:missing_mailbox_error) do
|
13
|
-
Net::IMAP::NoResponseError.new(missing_mailbox_response)
|
14
|
-
end
|
15
|
-
|
16
|
-
subject { described_class.new(connection, "my_folder") }
|
17
|
-
|
18
|
-
context "#uids" do
|
19
|
-
let(:uids) { [5678, 123] }
|
20
|
-
|
21
|
-
before { allow(imap).to receive(:uid_search).and_return(uids) }
|
22
|
-
|
23
|
-
it "lists available messages" do
|
24
|
-
expect(subject.uids).to eq(uids.reverse)
|
25
|
-
end
|
26
|
-
|
27
|
-
context "with missing mailboxes" do
|
28
|
-
before do
|
29
|
-
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
30
|
-
end
|
31
|
-
|
32
|
-
it "returns an empty array" do
|
33
|
-
expect(subject.uids).to eq([])
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
context "#fetch" do
|
39
|
-
let(:message_body) { double("the body", force_encoding: nil) }
|
40
|
-
let(:attributes) { {"RFC822" => message_body, "other" => "xxx"} }
|
41
|
-
let(:fetch_data_item) do
|
42
|
-
instance_double(Net::IMAP::FetchData, attr: attributes)
|
43
|
-
end
|
44
|
-
|
45
|
-
before { allow(imap).to receive(:uid_fetch) { [fetch_data_item] } }
|
46
|
-
|
47
|
-
it "returns the message" do
|
48
|
-
expect(subject.fetch(123)).to eq(attributes)
|
49
|
-
end
|
50
|
-
|
51
|
-
context "if the server responds with nothing" do
|
52
|
-
before { allow(imap).to receive(:uid_fetch) { nil } }
|
53
|
-
|
54
|
-
it "is nil" do
|
55
|
-
expect(subject.fetch(123)).to be_nil
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "if the mailbox doesn't exist" do
|
60
|
-
before do
|
61
|
-
allow(imap).to receive(:examine).and_raise(missing_mailbox_error)
|
62
|
-
end
|
63
|
-
|
64
|
-
it "is nil" do
|
65
|
-
expect(subject.fetch(123)).to be_nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
it "sets the encoding on the message" do
|
70
|
-
subject.fetch(123)
|
71
|
-
|
72
|
-
expect(message_body).to have_received(:force_encoding).with("utf-8")
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,82 +0,0 @@
|
|
1
|
-
describe Imap::Backup::Serializer::Mbox do
|
2
|
-
let(:base_path) { "/base/path" }
|
3
|
-
let(:store) do
|
4
|
-
instance_double(
|
5
|
-
Imap::Backup::Serializer::MboxStore,
|
6
|
-
add: nil,
|
7
|
-
uids: nil
|
8
|
-
)
|
9
|
-
end
|
10
|
-
let(:imap_folder) { "folder" }
|
11
|
-
let(:permissions) { 0o700 }
|
12
|
-
let(:dir_exists) { true }
|
13
|
-
|
14
|
-
before do
|
15
|
-
allow(Imap::Backup::Utils).to receive(:make_folder)
|
16
|
-
allow(Imap::Backup::Utils).to receive(:mode) { permissions }
|
17
|
-
allow(Imap::Backup::Utils).to receive(:check_permissions) { true }
|
18
|
-
allow(File).to receive(:directory?) { dir_exists }
|
19
|
-
end
|
20
|
-
|
21
|
-
subject { described_class.new(base_path, imap_folder) }
|
22
|
-
|
23
|
-
before do
|
24
|
-
allow(FileUtils).to receive(:chmod)
|
25
|
-
allow(Imap::Backup::Serializer::MboxStore).to receive(:new) { store }
|
26
|
-
end
|
27
|
-
|
28
|
-
context "containing directory" do
|
29
|
-
before { subject.uids }
|
30
|
-
|
31
|
-
context "when the IMAP folder has multiple elements" do
|
32
|
-
let(:imap_folder) { "folder/path" }
|
33
|
-
|
34
|
-
context "when the containing directory is missing" do
|
35
|
-
let(:dir_exists) { false }
|
36
|
-
|
37
|
-
it "is created" do
|
38
|
-
expect(Imap::Backup::Utils).to have_received(:make_folder).
|
39
|
-
with(base_path, File.dirname(imap_folder), 0o700)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
context "when the containing directory permissons are incorrect" do
|
45
|
-
let(:permissions) { 0o777 }
|
46
|
-
|
47
|
-
it "corrects them" do
|
48
|
-
path = File.expand_path(File.join(base_path, File.dirname(imap_folder)))
|
49
|
-
expect(FileUtils).to have_received(:chmod).with(0o700, path)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
context "when the containing directory permissons are correct" do
|
54
|
-
it "does nothing" do
|
55
|
-
expect(FileUtils).to_not have_received(:chmod)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
context "when the containing directory exists" do
|
60
|
-
it "is not created" do
|
61
|
-
expect(Imap::Backup::Utils).to_not have_received(:make_folder).
|
62
|
-
with(base_path, File.dirname(imap_folder), 0o700)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
context "#uids" do
|
68
|
-
it "calls the store" do
|
69
|
-
subject.uids
|
70
|
-
|
71
|
-
expect(store).to have_received(:uids)
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
context "#save" do
|
76
|
-
it "calls the store" do
|
77
|
-
subject.save("foo", "bar")
|
78
|
-
|
79
|
-
expect(store).to have_received(:add)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|