imap-backup 1.0.9 → 1.0.10
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 +7 -0
- data/.travis.yml +1 -0
- data/Rakefile +0 -5
- data/imap-backup.gemspec +5 -5
- data/lib/email/mboxrd/message.rb +1 -1
- data/lib/imap/backup.rb +3 -1
- data/lib/imap/backup/account/connection.rb +9 -7
- data/lib/imap/backup/account/folder.rb +6 -4
- data/lib/imap/backup/configuration/account.rb +9 -7
- data/lib/imap/backup/configuration/asker.rb +7 -5
- data/lib/imap/backup/configuration/connection_tester.rb +5 -3
- data/lib/imap/backup/configuration/folder_chooser.rb +10 -8
- data/lib/imap/backup/configuration/list.rb +8 -6
- data/lib/imap/backup/configuration/setup.rb +7 -5
- data/lib/imap/backup/configuration/store.rb +7 -5
- data/lib/imap/backup/serializer/base.rb +2 -1
- data/lib/imap/backup/serializer/directory.rb +5 -3
- data/lib/imap/backup/serializer/mbox.rb +10 -5
- data/lib/imap/backup/utils.rb +23 -30
- data/lib/imap/backup/version.rb +7 -7
- data/spec/spec_helper.rb +10 -27
- data/spec/support/higline_test_helpers.rb +8 -0
- data/spec/support/silence_logging.rb +7 -0
- data/spec/unit/account/connection_spec.rb +1 -1
- data/spec/unit/account/folder_spec.rb +33 -52
- data/spec/unit/configuration/asker_spec.rb +46 -5
- data/spec/unit/configuration/folder_chooser_spec.rb +62 -102
- data/spec/unit/configuration/list_spec.rb +40 -48
- data/spec/unit/configuration/setup_spec.rb +41 -62
- data/spec/unit/configuration/store_spec.rb +107 -99
- data/spec/unit/downloader_spec.rb +8 -8
- data/spec/unit/email/mboxrd/message_spec.rb +9 -19
- data/spec/unit/serializer/base_spec.rb +7 -5
- data/spec/unit/serializer/directory_spec.rb +30 -38
- data/spec/unit/serializer/mbox_spec.rb +64 -58
- data/spec/unit/utils_spec.rb +67 -41
- metadata +107 -141
data/lib/imap/backup/utils.rb
CHANGED
@@ -1,40 +1,33 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'fileutils'
|
3
3
|
|
4
|
-
module Imap
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.stat(filename)
|
16
|
-
return nil unless File.exist?(filename)
|
17
|
-
|
18
|
-
stat = File.stat(filename)
|
19
|
-
stat.mode & 0777
|
4
|
+
module Imap::Backup
|
5
|
+
module Utils
|
6
|
+
def self.check_permissions(filename, limit)
|
7
|
+
actual = stat(filename)
|
8
|
+
return nil if actual.nil?
|
9
|
+
mask = ~limit & 0777
|
10
|
+
if actual & mask != 0
|
11
|
+
raise format("Permissions on '%s' should be 0%o, not 0%o", filename, limit, actual)
|
20
12
|
end
|
13
|
+
end
|
21
14
|
|
22
|
-
|
23
|
-
|
24
|
-
return if parts.size == 0
|
25
|
-
full_path = File.join(base_path, path)
|
26
|
-
FileUtils.mkdir_p full_path
|
27
|
-
path = base_path
|
28
|
-
parts.each do |part|
|
29
|
-
path = File.join(path, part)
|
30
|
-
FileUtils.chmod permissions, path
|
31
|
-
end
|
32
|
-
end
|
15
|
+
def self.stat(filename)
|
16
|
+
return nil unless File.exist?(filename)
|
33
17
|
|
34
|
-
|
18
|
+
stat = File.stat(filename)
|
19
|
+
stat.mode & 0777
|
20
|
+
end
|
35
21
|
|
36
|
-
|
37
|
-
|
22
|
+
def self.make_folder(base_path, path, permissions)
|
23
|
+
parts = path.split('/')
|
24
|
+
return if parts.size == 0
|
25
|
+
full_path = File.join(base_path, path)
|
26
|
+
FileUtils.mkdir_p full_path
|
27
|
+
path = base_path
|
28
|
+
parts.each do |part|
|
29
|
+
path = File.join(path, part)
|
30
|
+
FileUtils.chmod permissions, path
|
38
31
|
end
|
39
32
|
end
|
40
33
|
end
|
data/lib/imap/backup/version.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
module Imap
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
module Imap; end
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
MAJOR = 1
|
5
|
+
MINOR = 0
|
6
|
+
REVISION = 10
|
7
|
+
VERSION = [MAJOR, MINOR, REVISION].map(&:to_s).join('.')
|
8
8
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,37 +1,20 @@
|
|
1
1
|
require 'rspec'
|
2
|
+
spec_path = File.dirname(__FILE__)
|
3
|
+
$LOAD_PATH << File.expand_path('../lib', spec_path)
|
4
|
+
|
5
|
+
support_glob = File.join(spec_path, 'support', '**', '*.rb')
|
6
|
+
Dir[support_glob].each { |f| require f }
|
2
7
|
|
3
8
|
if RUBY_VERSION < '1.9'
|
4
9
|
require 'rspec/autorun'
|
5
10
|
else
|
6
11
|
require 'simplecov'
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
add_filter "/vendor/"
|
11
|
-
end
|
12
|
+
SimpleCov.start do
|
13
|
+
add_filter '/spec/'
|
14
|
+
add_filter '/vendor/'
|
12
15
|
end
|
13
16
|
end
|
14
17
|
|
15
|
-
require
|
16
|
-
|
17
|
-
module HighLineTestHelpers
|
18
|
-
def prepare_highline
|
19
|
-
@input = stub('stdin', :eof? => false)
|
20
|
-
# default gets stub
|
21
|
-
@input.stub!(:gets).with().and_return("q\n")
|
22
|
-
@output = StringIO.new
|
23
|
-
Imap::Backup::Configuration::Setup.highline = HighLine.new(@input, @output)
|
24
|
-
[@input, @output]
|
25
|
-
end
|
26
|
-
end
|
18
|
+
require 'imap/backup'
|
27
19
|
|
28
|
-
|
29
|
-
def capturing_output
|
30
|
-
output = StringIO.new
|
31
|
-
$stdout = output
|
32
|
-
yield
|
33
|
-
output.string
|
34
|
-
ensure
|
35
|
-
$stdout = STDOUT
|
36
|
-
end
|
37
|
-
end
|
20
|
+
silence_logging
|
@@ -21,7 +21,7 @@ describe Imap::Backup::Account::Connection do
|
|
21
21
|
allow(Net::IMAP).to receive(:new).and_return(imap)
|
22
22
|
end
|
23
23
|
|
24
|
-
subject {
|
24
|
+
subject { described_class.new(options) }
|
25
25
|
|
26
26
|
shared_examples 'connects to IMAP' do |options|
|
27
27
|
options ||= {}
|
@@ -2,74 +2,55 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Account::Folder do
|
5
|
-
|
6
|
-
|
7
|
-
let(:
|
8
|
-
let(:
|
9
|
-
let(:missing_mailbox_data) { stub('Data', :text => 'Unknown Mailbox: my_folder') }
|
10
|
-
let(:missing_mailbox_response) { stub('Response', :data => missing_mailbox_data) }
|
5
|
+
let(:imap) { double('Net::IMAP', :examine => nil) }
|
6
|
+
let(:connection) { double('Imap::Backup::Account::Connection', :imap => imap) }
|
7
|
+
let(:missing_mailbox_data) { double('Data', :text => 'Unknown Mailbox: my_folder') }
|
8
|
+
let(:missing_mailbox_response) { double('Response', :data => missing_mailbox_data) }
|
11
9
|
let(:missing_mailbox_error) { Net::IMAP::NoResponseError.new(missing_mailbox_response) }
|
12
10
|
|
13
|
-
|
14
|
-
subject { Imap::Backup::Account::Folder.new(connection, 'my_folder') }
|
11
|
+
subject { described_class.new(connection, 'my_folder') }
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
imap.should_receive(:examine).with('my_folder')
|
19
|
-
imap.should_receive(:uid_search).with(['ALL']).and_return([5678, 123])
|
13
|
+
context '#uids' do
|
14
|
+
let(:uids) { [5678, 123] }
|
20
15
|
|
21
|
-
|
22
|
-
|
16
|
+
before { allow(imap).to receive(:uid_search).and_return(uids) }
|
17
|
+
|
18
|
+
it 'lists available messages' do
|
19
|
+
expect(subject.uids).to eq(uids.reverse)
|
20
|
+
end
|
23
21
|
|
24
|
-
|
25
|
-
|
26
|
-
should_receive(:examine).
|
27
|
-
with('my_folder').
|
28
|
-
and_raise(missing_mailbox_error)
|
22
|
+
context 'with missing mailboxes' do
|
23
|
+
before { allow(imap).to receive(:examine).and_raise(missing_mailbox_error) }
|
29
24
|
|
30
|
-
|
31
|
-
|
32
|
-
end
|
25
|
+
it 'returns an empty array' do
|
26
|
+
expect(subject.uids).to eq([])
|
33
27
|
end
|
34
28
|
end
|
29
|
+
end
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
{
|
40
|
-
'RFC822' => message_body,
|
41
|
-
'other' => 'xxx'
|
42
|
-
}
|
43
|
-
end
|
31
|
+
context '#fetch' do
|
32
|
+
let(:message_body) { double('the body', :force_encoding => nil) }
|
33
|
+
let(:message) { {'RFC822' => message_body, 'other' => 'xxx'} }
|
44
34
|
|
45
|
-
|
46
|
-
imap.should_receive(:examine).with('my_folder')
|
47
|
-
imap.should_receive(:uid_fetch).
|
48
|
-
with([123], ['RFC822', 'FLAGS', 'INTERNALDATE']).
|
49
|
-
and_return([[nil, message]])
|
35
|
+
before { allow(imap).to receive(:uid_fetch).and_return([[nil, message]]) }
|
50
36
|
|
51
|
-
|
52
|
-
|
37
|
+
it 'returns the message' do
|
38
|
+
expect(subject.fetch(123)).to eq(message)
|
39
|
+
end
|
53
40
|
|
54
|
-
|
55
|
-
|
56
|
-
should_receive(:examine).
|
57
|
-
with('my_folder').
|
58
|
-
and_raise(missing_mailbox_error)
|
41
|
+
context "if the mailbox doesn't exist" do
|
42
|
+
before { allow(imap).to receive(:examine).and_raise(missing_mailbox_error) }
|
59
43
|
|
60
|
-
|
61
|
-
|
62
|
-
end
|
44
|
+
it 'is nil' do
|
45
|
+
expect(subject.fetch(123)).to be_nil
|
63
46
|
end
|
47
|
+
end
|
64
48
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
message_body.should_receive(:force_encoding).with('utf-8')
|
49
|
+
if RUBY_VERSION > '1.9'
|
50
|
+
it 'sets the encoding on the message' do
|
51
|
+
subject.fetch(123)
|
70
52
|
|
71
|
-
|
72
|
-
end
|
53
|
+
expect(message_body).to have_received(:force_encoding).with('utf-8')
|
73
54
|
end
|
74
55
|
end
|
75
56
|
end
|
@@ -1,11 +1,48 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
|
-
module Imap::Backup
|
5
|
-
describe Asker do
|
4
|
+
module Imap::Backup
|
5
|
+
describe Configuration::Asker do
|
6
6
|
let(:highline) { double }
|
7
|
+
let(:query) do
|
8
|
+
double(
|
9
|
+
'Query',
|
10
|
+
:default= => nil,
|
11
|
+
:readline= => nil,
|
12
|
+
:validate= => nil,
|
13
|
+
:responses => {},
|
14
|
+
:echo= => nil
|
15
|
+
)
|
16
|
+
end
|
17
|
+
let(:answer) { 'foo' }
|
18
|
+
|
19
|
+
before do
|
20
|
+
allow(Configuration::Setup).to receive(:highline).and_return(highline)
|
21
|
+
allow(highline).to receive(:ask) do |&b|
|
22
|
+
b.call query
|
23
|
+
answer
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
subject { described_class.new(highline) }
|
28
|
+
|
29
|
+
[
|
30
|
+
[:email, [], 'email address'],
|
31
|
+
[:password, [], 'password'],
|
32
|
+
[:backup_path, ['x', 'y'], 'backup directory'],
|
33
|
+
].each do |method, params, prompt|
|
34
|
+
context ".#{method}" do
|
35
|
+
it 'asks for input' do
|
36
|
+
described_class.send(method, *params)
|
7
37
|
|
8
|
-
|
38
|
+
expect(highline).to have_received(:ask).with("#{prompt}: ")
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'returns the answer' do
|
42
|
+
expect(described_class.send(method, *params)).to eq(answer)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
9
46
|
|
10
47
|
context '#initialize' do
|
11
48
|
its(:highline) { should eq(highline) }
|
@@ -13,9 +50,9 @@ module Imap::Backup::Configuration
|
|
13
50
|
|
14
51
|
context '#email' do
|
15
52
|
let(:email) { 'email@example.com' }
|
53
|
+
let(:answer) { email }
|
16
54
|
|
17
55
|
before do
|
18
|
-
allow(highline).to receive(:ask).and_return(email)
|
19
56
|
@result = subject.email
|
20
57
|
end
|
21
58
|
|
@@ -70,9 +107,13 @@ module Imap::Backup::Configuration
|
|
70
107
|
|
71
108
|
context '#backup_path' do
|
72
109
|
let(:path) { '/path' }
|
110
|
+
let(:answer) { path }
|
73
111
|
|
74
112
|
before do
|
75
|
-
allow(highline).to receive(:ask)
|
113
|
+
allow(highline).to receive(:ask) do |&b|
|
114
|
+
b.call query
|
115
|
+
path
|
116
|
+
end
|
76
117
|
@result = subject.backup_path('', //)
|
77
118
|
end
|
78
119
|
|
@@ -2,145 +2,105 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::FolderChooser do
|
5
|
-
|
6
5
|
include HighLineTestHelpers
|
7
|
-
include InputOutputTestHelpers
|
8
6
|
|
9
7
|
context '#run' do
|
10
|
-
let(:connection)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
:folders => [{:name => 'my_folder'}]
|
16
|
-
}
|
17
|
-
end
|
18
|
-
let(:empty_account) do
|
19
|
-
{
|
20
|
-
:folders => []
|
21
|
-
}
|
22
|
-
end
|
23
|
-
let(:remote_folders) do
|
24
|
-
folder1 = stub('folder', :name => 'my_folder') # this one is already backed up
|
25
|
-
folder2 = stub('folder', :name => 'another_folder')
|
26
|
-
[folder1, folder2]
|
27
|
-
end
|
8
|
+
let(:connection) { double('Imap::Backup::Account::Connection', :folders => remote_folders) }
|
9
|
+
let(:account) { {:folders => []} }
|
10
|
+
let(:remote_folders) { [] }
|
11
|
+
|
12
|
+
subject { described_class.new(account) }
|
28
13
|
|
29
14
|
before do
|
15
|
+
allow(Imap::Backup::Account::Connection).to receive(:new).with(account).and_return(connection)
|
30
16
|
@input, @output = prepare_highline
|
17
|
+
allow(subject).to receive(:system)
|
18
|
+
allow(Imap::Backup.logger).to receive(:warn)
|
31
19
|
end
|
32
20
|
|
33
|
-
context '
|
34
|
-
|
35
|
-
|
36
|
-
subject { Imap::Backup::Configuration::FolderChooser.new(account) }
|
21
|
+
context 'display' do
|
22
|
+
before { subject.run }
|
37
23
|
|
38
|
-
|
39
|
-
|
40
|
-
Imap::Backup::Account::Connection.stub!(:new).with(account).and_return(connection)
|
41
|
-
subject.stub(:system).with('clear')
|
24
|
+
it 'clears the screen' do
|
25
|
+
expect(subject).to have_received(:system).with('clear')
|
42
26
|
end
|
43
27
|
|
44
|
-
it 'should
|
45
|
-
|
46
|
-
|
47
|
-
subject.run
|
28
|
+
it 'should show the menu' do
|
29
|
+
@output.string.should =~ %r{Add/remove folders}
|
48
30
|
end
|
31
|
+
end
|
49
32
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end.should =~ /connection failed/i
|
33
|
+
context 'folder listing' do
|
34
|
+
let(:account) { {:folders => [{:name => 'my_folder'}]} }
|
35
|
+
let(:remote_folders) do
|
36
|
+
folder1 = double('folder', :name => 'my_folder') # this one is already backed up
|
37
|
+
folder2 = double('folder', :name => 'another_folder')
|
38
|
+
[folder1, folder2]
|
57
39
|
end
|
58
40
|
|
59
|
-
|
60
|
-
|
41
|
+
context 'display' do
|
42
|
+
before { subject.run }
|
61
43
|
|
62
|
-
|
44
|
+
it 'shows folders which are being backed up' do
|
45
|
+
expect(@output.string).to include('+ my_folder')
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'shows folders which are not being backed up' do
|
49
|
+
expect(@output.string).to include('- another_folder')
|
50
|
+
end
|
63
51
|
end
|
64
52
|
|
65
|
-
|
66
|
-
|
53
|
+
context 'adding folders' do
|
54
|
+
before do
|
55
|
+
allow(@input).to receive(:gets).and_return("2\n", "q\n")
|
67
56
|
|
68
|
-
|
69
|
-
|
57
|
+
subject.run
|
58
|
+
end
|
70
59
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
@output.string.should =~ %r{Add/remove folders}
|
60
|
+
specify 'are added to the account' do
|
61
|
+
expect(account[:folders]).to include({:name => 'another_folder'})
|
62
|
+
end
|
75
63
|
end
|
76
64
|
|
77
|
-
|
78
|
-
|
65
|
+
context 'removing folders' do
|
66
|
+
before do
|
67
|
+
allow(@input).to receive(:gets).and_return("1\n", "q\n")
|
79
68
|
|
80
|
-
|
81
|
-
|
82
|
-
|
69
|
+
subject.run
|
70
|
+
end
|
71
|
+
|
72
|
+
specify 'are removed from the account' do
|
73
|
+
expect(account[:folders]).to_not include({:name => 'my_folder'})
|
74
|
+
end
|
83
75
|
end
|
84
76
|
end
|
85
77
|
|
86
|
-
context '
|
87
|
-
let(:
|
88
|
-
|
89
|
-
subject { Imap::Backup::Configuration::FolderChooser.new(account) }
|
78
|
+
context 'when folders are not available' do
|
79
|
+
let(:remote_folders) { nil }
|
90
80
|
|
91
81
|
before do
|
92
|
-
|
93
|
-
Imap::Backup::Account::Connection.stub!(:new).with(account).and_return(connection)
|
94
|
-
subject.stub(:system).with('clear')
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'should list folders' do
|
82
|
+
allow(Imap::Backup::Configuration::Setup.highline).to receive(:ask).and_return("q")
|
98
83
|
subject.run
|
99
|
-
|
100
|
-
@output.string.should =~ /my_folder/
|
101
84
|
end
|
102
85
|
|
103
|
-
it '
|
104
|
-
|
105
|
-
|
106
|
-
@output.string.should =~ /\d+\. \+ my_folder/
|
107
|
-
@output.string.should =~ /\d+\. \- another_folder/
|
86
|
+
it 'asks to press a key' do
|
87
|
+
expect(Imap::Backup::Configuration::Setup.highline).to have_received(:ask).with('Press a key ')
|
108
88
|
end
|
89
|
+
end
|
109
90
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
when :initial
|
115
|
-
state = :added
|
116
|
-
"2\n" # choose 'another_folder'
|
117
|
-
else
|
118
|
-
"q\n"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
91
|
+
context 'with connection errors' do
|
92
|
+
before do
|
93
|
+
allow(Imap::Backup::Account::Connection).to receive(:new).with(account).and_raise('error')
|
94
|
+
allow(Imap::Backup::Configuration::Setup.highline).to receive(:ask).and_return("q")
|
122
95
|
subject.run
|
123
|
-
|
124
|
-
@output.string.should =~ /\d+\. \+ another_folder/
|
125
|
-
account[:folders].should include({:name => 'another_folder'})
|
126
96
|
end
|
127
97
|
|
128
|
-
it '
|
129
|
-
|
130
|
-
|
131
|
-
case state
|
132
|
-
when :initial
|
133
|
-
state = :added
|
134
|
-
"1\n"
|
135
|
-
else
|
136
|
-
"q\n"
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
subject.run
|
98
|
+
it 'prints an error message' do
|
99
|
+
expect(Imap::Backup.logger).to have_received(:warn).with('Connection failed')
|
100
|
+
end
|
141
101
|
|
142
|
-
|
143
|
-
|
102
|
+
it 'asks to continue' do
|
103
|
+
expect(Imap::Backup::Configuration::Setup.highline).to have_received(:ask).with('Press a key ')
|
144
104
|
end
|
145
105
|
end
|
146
106
|
end
|