imap-backup 2.0.0.rc1 → 2.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|