imap-backup 3.0.0 → 3.1.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.
- checksums.yaml +4 -4
- data/lib/imap/backup/account/connection.rb +28 -20
- data/lib/imap/backup/account/folder.rb +10 -5
- data/lib/imap/backup/configuration/folder_chooser.rb +14 -15
- data/lib/imap/backup/version.rb +1 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +25 -14
- data/spec/unit/imap/backup/account/folder_spec.rb +25 -10
- data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +7 -13
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a62efa6b4c2e63180629e4a9770cf6b636eb48fa1aaad0177d7bab975ce3f251
|
4
|
+
data.tar.gz: ab0dee2784879df462e8fa1d622436b4a23f332dc731dd34ce353451a7164ce9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ffe7443b8450980b05b170e8b4ed2d8cdaa093b17a60bf72c6c87c07e30752de1010aa444da3d4aea1b4348cbdd7b4fff0f2793e100f66de592796a8fa9885b6
|
7
|
+
data.tar.gz: 5779c9d8bc98070cba06a68c571b849cdb33fb994954f6f3cf320642d1d49023ab9db5c6f713db26c413703caf046745589a61e5d4cde2e2c6bce751e7d6f3a1
|
@@ -18,7 +18,7 @@ module Imap::Backup
|
|
18
18
|
@username = options[:username]
|
19
19
|
@password = options[:password]
|
20
20
|
@local_path = options[:local_path]
|
21
|
-
@
|
21
|
+
@config_folders = options[:folders]
|
22
22
|
@server = options[:server]
|
23
23
|
@connection_options = options[:connection_options] || {}
|
24
24
|
@folders = nil
|
@@ -29,21 +29,24 @@ module Imap::Backup
|
|
29
29
|
@folders ||=
|
30
30
|
begin
|
31
31
|
root = provider_root
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
32
|
+
mailbox_lists = imap.list(root, "*")
|
33
|
+
|
34
|
+
if mailbox_lists.nil?
|
35
|
+
message = "Unable to get folder list for account #{username}"
|
36
|
+
Imap::Backup.logger.info message
|
37
|
+
raise message
|
37
38
|
end
|
38
|
-
|
39
|
+
|
40
|
+
utf7_encoded = mailbox_lists.map(&:name)
|
41
|
+
utf7_encoded.map { |n| Net::IMAP.decode_utf7(n) }
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
def status
|
43
|
-
backup_folders.map do |
|
44
|
-
f = Account::Folder.new(self,
|
45
|
-
s = Serializer::Mbox.new(local_path,
|
46
|
-
{name:
|
46
|
+
backup_folders.map do |backup_folder|
|
47
|
+
f = Account::Folder.new(self, backup_folder[:name])
|
48
|
+
s = Serializer::Mbox.new(local_path, backup_folder[:name])
|
49
|
+
{name: backup_folder[:name], local: s.uids, remote: f.uids}
|
47
50
|
end
|
48
51
|
end
|
49
52
|
|
@@ -105,18 +108,12 @@ module Imap::Backup
|
|
105
108
|
@server = provider.host
|
106
109
|
end
|
107
110
|
|
108
|
-
def backup_folders
|
109
|
-
return @backup_folders if @backup_folders && !@backup_folders.empty?
|
110
|
-
|
111
|
-
(folders || []).map { |f| {name: f.name} }
|
112
|
-
end
|
113
|
-
|
114
111
|
private
|
115
112
|
|
116
113
|
def each_folder
|
117
|
-
backup_folders.each do |
|
118
|
-
folder = Account::Folder.new(self,
|
119
|
-
serializer = Serializer::Mbox.new(local_path,
|
114
|
+
backup_folders.each do |backup_folder|
|
115
|
+
folder = Account::Folder.new(self, backup_folder[:name])
|
116
|
+
serializer = Serializer::Mbox.new(local_path, backup_folder[:name])
|
120
117
|
yield folder, serializer
|
121
118
|
end
|
122
119
|
end
|
@@ -175,6 +172,17 @@ module Imap::Backup
|
|
175
172
|
end
|
176
173
|
end
|
177
174
|
|
175
|
+
def backup_folders
|
176
|
+
@backup_folders ||=
|
177
|
+
begin
|
178
|
+
if @config_folders && @config_folders.any?
|
179
|
+
@config_folders
|
180
|
+
else
|
181
|
+
folders.map { |name| {name: name} }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
178
186
|
def provider
|
179
187
|
@provider ||= Email::Provider.for_address(username)
|
180
188
|
end
|
@@ -36,7 +36,7 @@ module Imap::Backup
|
|
36
36
|
def create
|
37
37
|
return if exist?
|
38
38
|
|
39
|
-
imap.create(
|
39
|
+
imap.create(utf7_encoded_name)
|
40
40
|
end
|
41
41
|
|
42
42
|
def uid_validity
|
@@ -81,22 +81,27 @@ module Imap::Backup
|
|
81
81
|
def append(message)
|
82
82
|
body = message.imap_body
|
83
83
|
date = message.date&.to_time
|
84
|
-
response = imap.append(
|
84
|
+
response = imap.append(utf7_encoded_name, body, nil, date)
|
85
85
|
extract_uid(response)
|
86
86
|
end
|
87
87
|
|
88
88
|
private
|
89
89
|
|
90
90
|
def examine
|
91
|
-
imap.examine(
|
91
|
+
imap.examine(utf7_encoded_name)
|
92
92
|
rescue Net::IMAP::NoResponseError
|
93
|
-
Imap::Backup.logger.warn "Folder '#{name}' does not exist"
|
94
|
-
raise FolderNotFound, "Folder '#{name}' does not exist"
|
93
|
+
Imap::Backup.logger.warn "Folder '#{name}' does not exist on server"
|
94
|
+
raise FolderNotFound, "Folder '#{name}' does not exist on server"
|
95
95
|
end
|
96
96
|
|
97
97
|
def extract_uid(response)
|
98
98
|
@uid_validity, uid = response.data.code.data.split(" ").map(&:to_i)
|
99
99
|
uid
|
100
100
|
end
|
101
|
+
|
102
|
+
def utf7_encoded_name
|
103
|
+
@utf7_encoded_name ||=
|
104
|
+
Net::IMAP.encode_utf7(name).force_encoding("ASCII-8BIT")
|
105
|
+
end
|
101
106
|
end
|
102
107
|
end
|
@@ -15,7 +15,7 @@ module Imap::Backup
|
|
15
15
|
return
|
16
16
|
end
|
17
17
|
|
18
|
-
if
|
18
|
+
if imap_folders.nil?
|
19
19
|
Imap::Backup.logger.warn "Unable to get folder list"
|
20
20
|
highline.ask "Press a key "
|
21
21
|
return
|
@@ -44,29 +44,28 @@ module Imap::Backup
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def add_folders(menu)
|
47
|
-
|
48
|
-
|
49
|
-
mark
|
50
|
-
|
51
|
-
toggle_selection name
|
47
|
+
imap_folders.each do |folder|
|
48
|
+
mark = selected?(folder) ? "+" : "-"
|
49
|
+
menu.choice("#{mark} #{folder}") do
|
50
|
+
toggle_selection folder
|
52
51
|
end
|
53
52
|
end
|
54
53
|
end
|
55
54
|
|
56
55
|
def selected?(folder_name)
|
57
|
-
|
58
|
-
return false if
|
56
|
+
config_folders = account[:folders]
|
57
|
+
return false if config_folders.nil?
|
59
58
|
|
60
|
-
|
59
|
+
config_folders.find { |f| f[:name] == folder_name }
|
61
60
|
end
|
62
61
|
|
63
62
|
def remove_missing
|
64
63
|
removed = []
|
65
|
-
|
64
|
+
config_folders = []
|
66
65
|
account[:folders].each do |f|
|
67
|
-
found =
|
66
|
+
found = imap_folders.find { |folder| folder == f[:name] }
|
68
67
|
if found
|
69
|
-
|
68
|
+
config_folders << f
|
70
69
|
else
|
71
70
|
removed << f[:name]
|
72
71
|
end
|
@@ -74,7 +73,7 @@ module Imap::Backup
|
|
74
73
|
|
75
74
|
return if removed.empty?
|
76
75
|
|
77
|
-
account[:folders] =
|
76
|
+
account[:folders] = config_folders
|
78
77
|
account[:modified] = true
|
79
78
|
|
80
79
|
Kernel.puts <<~MESSAGE
|
@@ -101,8 +100,8 @@ module Imap::Backup
|
|
101
100
|
nil
|
102
101
|
end
|
103
102
|
|
104
|
-
def
|
105
|
-
@
|
103
|
+
def imap_folders
|
104
|
+
@imap_folders ||= connection.folders
|
106
105
|
end
|
107
106
|
|
108
107
|
def highline
|
data/lib/imap/backup/version.rb
CHANGED
@@ -23,11 +23,11 @@ describe Imap::Backup::Account::Connection do
|
|
23
23
|
username: USERNAME,
|
24
24
|
password: PASSWORD,
|
25
25
|
local_path: LOCAL_PATH,
|
26
|
-
folders:
|
26
|
+
folders: config_folders,
|
27
27
|
server: server
|
28
28
|
}
|
29
29
|
end
|
30
|
-
let(:
|
30
|
+
let(:config_folders) { [FOLDER_CONFIG] }
|
31
31
|
let(:root_info) do
|
32
32
|
instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)
|
33
33
|
end
|
@@ -62,7 +62,6 @@ describe Imap::Backup::Account::Connection do
|
|
62
62
|
[:username, USERNAME],
|
63
63
|
[:password, PASSWORD],
|
64
64
|
[:local_path, LOCAL_PATH],
|
65
|
-
[:backup_folders, [FOLDER_CONFIG]],
|
66
65
|
[:server, SERVER]
|
67
66
|
].each do |attr, expected|
|
68
67
|
it "expects #{attr}" do
|
@@ -144,11 +143,21 @@ describe Imap::Backup::Account::Connection do
|
|
144
143
|
|
145
144
|
describe "#folders" do
|
146
145
|
let(:imap_folders) do
|
147
|
-
[instance_double(Net::IMAP::MailboxList)]
|
146
|
+
[instance_double(Net::IMAP::MailboxList, name: BACKUP_FOLDER)]
|
148
147
|
end
|
149
148
|
|
150
149
|
it "returns the list of folders" do
|
151
|
-
expect(subject.folders).to eq(
|
150
|
+
expect(subject.folders).to eq([BACKUP_FOLDER])
|
151
|
+
end
|
152
|
+
|
153
|
+
context "with non-ASCII folder names" do
|
154
|
+
let(:imap_folders) do
|
155
|
+
[instance_double(Net::IMAP::MailboxList, name: "Gel&APY-scht")]
|
156
|
+
end
|
157
|
+
|
158
|
+
it "converts them to UTF-8" do
|
159
|
+
expect(subject.folders).to eq(["Gelöscht"])
|
160
|
+
end
|
152
161
|
end
|
153
162
|
end
|
154
163
|
|
@@ -198,7 +207,7 @@ describe Imap::Backup::Account::Connection do
|
|
198
207
|
with(LOCAL_PATH, BACKUP_FOLDER) { serializer }
|
199
208
|
end
|
200
209
|
|
201
|
-
context "with supplied
|
210
|
+
context "with supplied config_folders" do
|
202
211
|
it "runs the downloader" do
|
203
212
|
expect(downloader).to receive(:run)
|
204
213
|
|
@@ -216,7 +225,7 @@ describe Imap::Backup::Account::Connection do
|
|
216
225
|
end
|
217
226
|
end
|
218
227
|
|
219
|
-
context "without supplied
|
228
|
+
context "without supplied config_folders" do
|
220
229
|
let(:imap_folders) do
|
221
230
|
[instance_double(Net::IMAP::MailboxList, name: ROOT_NAME)]
|
222
231
|
end
|
@@ -228,8 +237,8 @@ describe Imap::Backup::Account::Connection do
|
|
228
237
|
with(LOCAL_PATH, ROOT_NAME) { serializer }
|
229
238
|
end
|
230
239
|
|
231
|
-
context "when supplied
|
232
|
-
let(:
|
240
|
+
context "when supplied config_folders is nil" do
|
241
|
+
let(:config_folders) { nil }
|
233
242
|
|
234
243
|
it "runs the downloader for each folder" do
|
235
244
|
expect(downloader).to receive(:run).exactly(:once)
|
@@ -238,8 +247,8 @@ describe Imap::Backup::Account::Connection do
|
|
238
247
|
end
|
239
248
|
end
|
240
249
|
|
241
|
-
context "when supplied
|
242
|
-
let(:
|
250
|
+
context "when supplied config_folders is an empty list" do
|
251
|
+
let(:config_folders) { [] }
|
243
252
|
|
244
253
|
it "runs the downloader for each folder" do
|
245
254
|
expect(downloader).to receive(:run).exactly(:once)
|
@@ -249,11 +258,13 @@ describe Imap::Backup::Account::Connection do
|
|
249
258
|
end
|
250
259
|
|
251
260
|
context "when the imap server doesn't return folders" do
|
252
|
-
let(:
|
261
|
+
let(:config_folders) { nil }
|
253
262
|
let(:imap_folders) { nil }
|
254
263
|
|
255
|
-
it "
|
256
|
-
expect
|
264
|
+
it "fails" do
|
265
|
+
expect do
|
266
|
+
subject.run_backup
|
267
|
+
end.to raise_error(RuntimeError, /Unable to get folder list/)
|
257
268
|
end
|
258
269
|
end
|
259
270
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# rubocop:disable RSpec/PredicateMatcher
|
2
2
|
|
3
3
|
describe Imap::Backup::Account::Folder do
|
4
|
-
|
4
|
+
FOLDER_NAME = "Gelöscht"
|
5
|
+
ENCODED_FOLDER_NAME = "Gel&APY-scht"
|
6
|
+
|
7
|
+
subject { described_class.new(connection, FOLDER_NAME) }
|
5
8
|
|
6
9
|
let(:imap) do
|
7
10
|
instance_double(
|
@@ -16,7 +19,7 @@ describe Imap::Backup::Account::Folder do
|
|
16
19
|
instance_double(Imap::Backup::Account::Connection, imap: imap)
|
17
20
|
end
|
18
21
|
let(:missing_mailbox_data) do
|
19
|
-
OpenStruct.new(text: "Unknown Mailbox:
|
22
|
+
OpenStruct.new(text: "Unknown Mailbox: #{FOLDER_NAME}")
|
20
23
|
end
|
21
24
|
let(:missing_mailbox_response) { OpenStruct.new(data: missing_mailbox_data) }
|
22
25
|
let(:missing_mailbox_error) do
|
@@ -36,7 +39,8 @@ describe Imap::Backup::Account::Folder do
|
|
36
39
|
|
37
40
|
context "with missing mailboxes" do
|
38
41
|
before do
|
39
|
-
allow(imap).to receive(:examine).
|
42
|
+
allow(imap).to receive(:examine).
|
43
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
40
44
|
end
|
41
45
|
|
42
46
|
it "returns an empty array" do
|
@@ -50,7 +54,8 @@ describe Imap::Backup::Account::Folder do
|
|
50
54
|
end
|
51
55
|
|
52
56
|
before do
|
53
|
-
allow(imap).to receive(:examine).
|
57
|
+
allow(imap).to receive(:examine).
|
58
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
54
59
|
end
|
55
60
|
|
56
61
|
it "returns an empty array" do
|
@@ -82,7 +87,8 @@ describe Imap::Backup::Account::Folder do
|
|
82
87
|
|
83
88
|
context "when the mailbox doesn't exist" do
|
84
89
|
before do
|
85
|
-
allow(imap).to receive(:examine).
|
90
|
+
allow(imap).to receive(:examine).
|
91
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
86
92
|
end
|
87
93
|
|
88
94
|
it "is nil" do
|
@@ -101,7 +107,7 @@ describe Imap::Backup::Account::Folder do
|
|
101
107
|
|
102
108
|
describe "#folder" do
|
103
109
|
it "is the name" do
|
104
|
-
expect(subject.folder).to eq(
|
110
|
+
expect(subject.folder).to eq(FOLDER_NAME)
|
105
111
|
end
|
106
112
|
end
|
107
113
|
|
@@ -114,7 +120,8 @@ describe Imap::Backup::Account::Folder do
|
|
114
120
|
|
115
121
|
context "when the folder doesn't exist" do
|
116
122
|
before do
|
117
|
-
allow(imap).to receive(:examine).
|
123
|
+
allow(imap).to receive(:examine).
|
124
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
118
125
|
end
|
119
126
|
|
120
127
|
it "is false" do
|
@@ -134,14 +141,21 @@ describe Imap::Backup::Account::Folder do
|
|
134
141
|
|
135
142
|
context "when the folder doesn't exist" do
|
136
143
|
before do
|
137
|
-
allow(imap).to receive(:examine).
|
144
|
+
allow(imap).to receive(:examine).
|
145
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
138
146
|
end
|
139
147
|
|
140
|
-
it "
|
148
|
+
it "creates the folder" do
|
141
149
|
expect(imap).to receive(:create)
|
142
150
|
|
143
151
|
subject.create
|
144
152
|
end
|
153
|
+
|
154
|
+
it "encodes the folder name" do
|
155
|
+
expect(imap).to receive(:create).with(ENCODED_FOLDER_NAME)
|
156
|
+
|
157
|
+
subject.create
|
158
|
+
end
|
145
159
|
end
|
146
160
|
end
|
147
161
|
|
@@ -154,7 +168,8 @@ describe Imap::Backup::Account::Folder do
|
|
154
168
|
|
155
169
|
context "when the folder doesn't exist" do
|
156
170
|
before do
|
157
|
-
allow(imap).to receive(:examine).
|
171
|
+
allow(imap).to receive(:examine).
|
172
|
+
with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
|
158
173
|
end
|
159
174
|
|
160
175
|
it "raises an error" do
|
@@ -6,11 +6,11 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
6
6
|
|
7
7
|
let(:connection) do
|
8
8
|
instance_double(
|
9
|
-
Imap::Backup::Account::Connection, folders:
|
9
|
+
Imap::Backup::Account::Connection, folders: connection_folders
|
10
10
|
)
|
11
11
|
end
|
12
12
|
let(:account) { {folders: []} }
|
13
|
-
let(:
|
13
|
+
let(:connection_folders) { [] }
|
14
14
|
let!(:highline_streams) { prepare_highline }
|
15
15
|
let(:input) { highline_streams[0] }
|
16
16
|
let(:output) { highline_streams[1] }
|
@@ -37,15 +37,9 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
37
37
|
|
38
38
|
describe "folder listing" do
|
39
39
|
let(:account) { {folders: [{name: "my_folder"}]} }
|
40
|
-
let(:
|
41
|
-
#
|
42
|
-
|
43
|
-
Imap::Backup::Account::Folder, name: "my_folder"
|
44
|
-
)
|
45
|
-
folder2 = instance_double(
|
46
|
-
Imap::Backup::Account::Folder, name: "another_folder"
|
47
|
-
)
|
48
|
-
[folder1, folder2]
|
40
|
+
let(:connection_folders) do
|
41
|
+
# N.B. my_folder is already backed up
|
42
|
+
%w(my_folder another_folder)
|
49
43
|
end
|
50
44
|
|
51
45
|
describe "display" do
|
@@ -93,7 +87,7 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
93
87
|
let(:account) do
|
94
88
|
{folders: [{name: "on_server"}, {name: "not_on_server"}]}
|
95
89
|
end
|
96
|
-
let(:
|
90
|
+
let(:connection_folders) do
|
97
91
|
[
|
98
92
|
instance_double(Imap::Backup::Account::Folder, name: "on_server")
|
99
93
|
]
|
@@ -112,7 +106,7 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
112
106
|
end
|
113
107
|
|
114
108
|
context "when folders are not available" do
|
115
|
-
let(:
|
109
|
+
let(:connection_folders) { nil }
|
116
110
|
|
117
111
|
before do
|
118
112
|
allow(Imap::Backup::Configuration::Setup.highline).
|