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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e4037b31fc56dcf88f312506ff2814b1c3fb516ae13cf9dbb4c8a772928b5fa
4
- data.tar.gz: 0465b9bf0b7a6a9dadc5c6c35b1a4dd65db058ab1ed37158d9fedf2dc093f777
3
+ metadata.gz: a62efa6b4c2e63180629e4a9770cf6b636eb48fa1aaad0177d7bab975ce3f251
4
+ data.tar.gz: ab0dee2784879df462e8fa1d622436b4a23f332dc731dd34ce353451a7164ce9
5
5
  SHA512:
6
- metadata.gz: 459620b79228707291d0b3be5037830e7100da42ecc920a1b5e19797fd1cc9aa9bc8fa667f5a5a7f89cc5d2647f47df5ae7e384a575c62a9bf4a13c25c70dd4b
7
- data.tar.gz: 8ac9d67819df8f957ffbc6a93be59aa401bdec704d75fa637c186012d2f15e23c3efa5c68ca8dc8918c97de3d90623ee5627d3cb8107615ee108e76588afd054
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
- @backup_folders = options[:folders]
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
- @folders = imap.list(root, "*")
33
- if @folders.nil?
34
- Imap::Backup.logger.warn(
35
- "Unable to get folder list for account #{username}"
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
- @folders
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 |folder|
44
- f = Account::Folder.new(self, folder[:name])
45
- s = Serializer::Mbox.new(local_path, folder[:name])
46
- {name: folder[:name], local: s.uids, remote: f.uids}
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 |folder_info|
118
- folder = Account::Folder.new(self, folder_info[:name])
119
- serializer = Serializer::Mbox.new(local_path, folder_info[:name])
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(name)
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(name, body, nil, date)
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(name)
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 folders.nil?
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
- folders.each do |folder|
48
- name = folder.name
49
- mark = selected?(name) ? "+" : "-"
50
- menu.choice("#{mark} #{name}") do
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
- backup_folders = account[:folders]
58
- return false if backup_folders.nil?
56
+ config_folders = account[:folders]
57
+ return false if config_folders.nil?
59
58
 
60
- backup_folders.find { |f| f[:name] == folder_name }
59
+ config_folders.find { |f| f[:name] == folder_name }
61
60
  end
62
61
 
63
62
  def remove_missing
64
63
  removed = []
65
- backup_folders = []
64
+ config_folders = []
66
65
  account[:folders].each do |f|
67
- found = folders.find { |folder| folder.name == f[:name] }
66
+ found = imap_folders.find { |folder| folder == f[:name] }
68
67
  if found
69
- backup_folders << f
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] = backup_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 folders
105
- @folders ||= connection.folders
103
+ def imap_folders
104
+ @imap_folders ||= connection.folders
106
105
  end
107
106
 
108
107
  def highline
@@ -2,7 +2,7 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 3
5
- MINOR = 0
5
+ MINOR = 1
6
6
  REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
@@ -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: backup_folders,
26
+ folders: config_folders,
27
27
  server: server
28
28
  }
29
29
  end
30
- let(:backup_folders) { [FOLDER_CONFIG] }
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(imap_folders)
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 backup_folders" do
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 backup_folders" do
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 backup_folders is nil" do
232
- let(:backup_folders) { nil }
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 backup_folders is an empty list" do
242
- let(:backup_folders) { [] }
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(:backup_folders) { nil }
261
+ let(:config_folders) { nil }
253
262
  let(:imap_folders) { nil }
254
263
 
255
- it "does not fail" do
256
- expect { subject.run_backup }.to_not raise_error
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
- subject { described_class.new(connection, "my_folder") }
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: my_folder")
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).and_raise(missing_mailbox_error)
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).and_raise(no_method_error)
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).and_raise(missing_mailbox_error)
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("my_folder")
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).and_raise(missing_mailbox_error)
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).and_raise(missing_mailbox_error)
144
+ allow(imap).to receive(:examine).
145
+ with(ENCODED_FOLDER_NAME).and_raise(missing_mailbox_error)
138
146
  end
139
147
 
140
- it "is does not create the folder" do
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).and_raise(missing_mailbox_error)
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: remote_folders
9
+ Imap::Backup::Account::Connection, folders: connection_folders
10
10
  )
11
11
  end
12
12
  let(:account) { {folders: []} }
13
- let(:remote_folders) { [] }
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(:remote_folders) do
41
- # this one is already backed up:
42
- folder1 = instance_double(
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(:remote_folders) do
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(:remote_folders) { nil }
109
+ let(:connection_folders) { nil }
116
110
 
117
111
  before do
118
112
  allow(Imap::Backup::Configuration::Setup.highline).
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates