imap-backup 0.0.5 → 1.0.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.
- data/README.md +43 -6
- data/bin/imap-backup +0 -1
- data/imap-backup.gemspec +1 -1
- data/lib/email/mboxrd/message.rb +37 -0
- data/lib/imap/backup.rb +2 -0
- data/lib/imap/backup/account/connection.rb +2 -4
- data/lib/imap/backup/account/folder.rb +0 -2
- data/lib/imap/backup/configuration/account.rb +0 -2
- data/lib/imap/backup/configuration/asker.rb +0 -2
- data/lib/imap/backup/configuration/folder_chooser.rb +0 -2
- data/lib/imap/backup/configuration/list.rb +17 -16
- data/lib/imap/backup/configuration/setup.rb +1 -3
- data/lib/imap/backup/configuration/store.rb +19 -13
- data/lib/imap/backup/downloader.rb +0 -2
- data/lib/imap/backup/serializer/base.rb +17 -0
- data/lib/imap/backup/serializer/directory.rb +3 -7
- data/lib/imap/backup/serializer/mbox.rb +96 -0
- data/lib/imap/backup/utils.rb +6 -3
- data/lib/imap/backup/version.rb +2 -2
- data/spec/unit/account/connection_spec.rb +12 -13
- data/spec/unit/account/folder_spec.rb +1 -9
- data/spec/unit/configuration/account_spec.rb +1 -16
- data/spec/unit/configuration/asker_spec.rb +1 -9
- data/spec/unit/configuration/connection_tester_spec.rb +1 -5
- data/spec/unit/configuration/folder_chooser_spec.rb +1 -7
- data/spec/unit/configuration/list_spec.rb +24 -20
- data/spec/unit/configuration/setup_spec.rb +3 -9
- data/spec/unit/configuration/store_spec.rb +11 -20
- data/spec/unit/downloader_spec.rb +1 -11
- data/spec/unit/email/mboxrd/message_spec.rb +51 -0
- data/spec/unit/serializer/base_spec.rb +19 -0
- data/spec/unit/serializer/directory_spec.rb +51 -74
- data/spec/unit/serializer/mbox_spec.rb +113 -0
- data/spec/unit/utils_spec.rb +3 -9
- metadata +29 -4
@@ -0,0 +1,96 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'csv'
|
3
|
+
require 'email/mboxrd/message'
|
4
|
+
|
5
|
+
module Imap
|
6
|
+
module Backup
|
7
|
+
module Serializer
|
8
|
+
class Mbox < Base
|
9
|
+
def initialize(path, folder)
|
10
|
+
super
|
11
|
+
create_containing_directory
|
12
|
+
assert_files
|
13
|
+
end
|
14
|
+
|
15
|
+
# TODO: cleanup locks, close file handles
|
16
|
+
|
17
|
+
def uids
|
18
|
+
return @uids if @uids
|
19
|
+
|
20
|
+
@uids = []
|
21
|
+
return @uids if not exist?
|
22
|
+
|
23
|
+
CSV.foreach(imap_pathname) do |row|
|
24
|
+
@uids << row[0]
|
25
|
+
end
|
26
|
+
@uids
|
27
|
+
end
|
28
|
+
|
29
|
+
def save(uid, message)
|
30
|
+
uid = uid.to_s
|
31
|
+
return if uids.include?(uid)
|
32
|
+
message = Email::Mboxrd::Message.new(message['RFC822'])
|
33
|
+
mbox = imap = nil
|
34
|
+
begin
|
35
|
+
mbox = File.open(mbox_pathname, 'ab')
|
36
|
+
imap = File.open(imap_pathname, 'ab')
|
37
|
+
mbox.write message.to_s
|
38
|
+
imap.write uid + "\n"
|
39
|
+
ensure
|
40
|
+
mbox.close if mbox
|
41
|
+
imap.close if imap
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def assert_files
|
48
|
+
mbox = mbox_exist?
|
49
|
+
imap = imap_exist?
|
50
|
+
raise '.imap file missing' if mbox and not imap
|
51
|
+
raise '.mbox file missing' if imap and not mbox
|
52
|
+
end
|
53
|
+
|
54
|
+
def create_containing_directory
|
55
|
+
mbox_relative_path = File.dirname(mbox_relative_pathname)
|
56
|
+
return if mbox_relative_path == '.'
|
57
|
+
Imap::Backup::Utils.make_folder(@path, mbox_relative_path, DIRECTORY_PERMISSIONS)
|
58
|
+
end
|
59
|
+
|
60
|
+
def exist?
|
61
|
+
mbox_exist? and imap_exist?
|
62
|
+
end
|
63
|
+
|
64
|
+
def mbox_exist?
|
65
|
+
File.exist?(mbox_pathname)
|
66
|
+
end
|
67
|
+
|
68
|
+
def imap_exist?
|
69
|
+
File.exist?(imap_pathname)
|
70
|
+
end
|
71
|
+
|
72
|
+
def mbox_relative_pathname
|
73
|
+
@folder + '.mbox'
|
74
|
+
end
|
75
|
+
|
76
|
+
def mbox_pathname
|
77
|
+
File.join(@path, mbox_relative_pathname)
|
78
|
+
end
|
79
|
+
|
80
|
+
def imap_pathname
|
81
|
+
filename = @folder + '.imap'
|
82
|
+
File.join(@path, filename)
|
83
|
+
end
|
84
|
+
|
85
|
+
def lock
|
86
|
+
# lock mbox and imap files
|
87
|
+
# create both empty if missing
|
88
|
+
end
|
89
|
+
|
90
|
+
def unlock
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
data/lib/imap/backup/utils.rb
CHANGED
@@ -16,7 +16,7 @@ module Imap
|
|
16
16
|
def self.stat(filename)
|
17
17
|
return nil unless File.exist?(filename)
|
18
18
|
|
19
|
-
stat
|
19
|
+
stat = File.stat(filename)
|
20
20
|
stat.mode & 0777
|
21
21
|
end
|
22
22
|
|
@@ -25,8 +25,11 @@ module Imap
|
|
25
25
|
return if parts.size == 0
|
26
26
|
full_path = File.join(base_path, path)
|
27
27
|
FileUtils.mkdir_p full_path
|
28
|
-
|
29
|
-
|
28
|
+
path = base_path
|
29
|
+
parts.each do |part|
|
30
|
+
path = File.join(path, part)
|
31
|
+
FileUtils.chmod permissions, path
|
32
|
+
end
|
30
33
|
end
|
31
34
|
|
32
35
|
private
|
data/lib/imap/backup/version.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Account::Connection do
|
5
|
-
|
6
5
|
context '#initialize' do
|
7
|
-
|
8
6
|
it 'should login to the imap server' do
|
9
7
|
imap = stub('Net::IMAP')
|
10
8
|
Net::IMAP.should_receive(:new).with('imap.gmail.com', 993, true).and_return(imap)
|
@@ -13,10 +11,18 @@ describe Imap::Backup::Account::Connection do
|
|
13
11
|
Imap::Backup::Account::Connection.new(:username => 'myuser', :password => 'secret')
|
14
12
|
end
|
15
13
|
|
14
|
+
context "with specific server" do
|
15
|
+
it 'should login to the imap server' do
|
16
|
+
imap = stub('Net::IMAP')
|
17
|
+
Net::IMAP.should_receive(:new).with('my.imap.example.com', 993, true).and_return(imap)
|
18
|
+
imap.should_receive('login').with('myuser', 'secret')
|
19
|
+
|
20
|
+
Imap::Backup::Account::Connection.new(:username => 'myuser', :password => 'secret', :server => 'my.imap.example.com')
|
21
|
+
end
|
22
|
+
end
|
16
23
|
end
|
17
24
|
|
18
25
|
context 'instance methods' do
|
19
|
-
|
20
26
|
before :each do
|
21
27
|
@imap = stub('Net::IMAP', :login => nil)
|
22
28
|
Net::IMAP.stub!(:new).and_return(@imap)
|
@@ -46,9 +52,7 @@ describe Imap::Backup::Account::Connection do
|
|
46
52
|
end
|
47
53
|
end
|
48
54
|
|
49
|
-
|
50
55
|
context '#status' do
|
51
|
-
|
52
56
|
before :each do
|
53
57
|
@folder = stub('Imap::Backup::Account::Folder', :uids => [])
|
54
58
|
Imap::Backup::Account::Folder.stub!(:new).with(subject, 'my_folder').and_return(@folder)
|
@@ -71,16 +75,14 @@ describe Imap::Backup::Account::Connection do
|
|
71
75
|
|
72
76
|
subject.status[0][:remote].should == [101, 234]
|
73
77
|
end
|
74
|
-
|
75
78
|
end
|
76
79
|
|
77
80
|
context '#run_backup' do
|
78
|
-
|
79
81
|
before :each do
|
80
82
|
@folder = stub('Imap::Backup::Account::Folder', :uids => [])
|
81
83
|
Imap::Backup::Account::Folder.stub!(:new).with(subject, 'my_folder').and_return(@folder)
|
82
84
|
@serializer = stub('Imap::Backup::Serializer')
|
83
|
-
Imap::Backup::Serializer::
|
85
|
+
Imap::Backup::Serializer::Mbox.stub!(:new).with('/base/path', 'my_folder').and_return(@serializer)
|
84
86
|
@downloader = stub('Imap::Backup::Downloader', :run => nil)
|
85
87
|
Imap::Backup::Downloader.stub!(:new).with(@folder, @serializer).and_return(@downloader)
|
86
88
|
end
|
@@ -92,7 +94,7 @@ describe Imap::Backup::Account::Connection do
|
|
92
94
|
end
|
93
95
|
|
94
96
|
it 'should instantiate serializers' do
|
95
|
-
Imap::Backup::Serializer::
|
97
|
+
Imap::Backup::Serializer::Mbox.should_receive(:new).with('/base/path', 'my_folder').and_return(@serializer)
|
96
98
|
|
97
99
|
subject.run_backup
|
98
100
|
end
|
@@ -108,10 +110,7 @@ describe Imap::Backup::Account::Connection do
|
|
108
110
|
|
109
111
|
subject.run_backup
|
110
112
|
end
|
111
|
-
|
112
113
|
end
|
113
|
-
|
114
114
|
end
|
115
|
-
|
116
115
|
end
|
117
116
|
|
@@ -1,11 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
3
|
-
load File.expand_path('../../spec_helper.rb', File.dirname(__FILE__))
|
2
|
+
require 'spec_helper'
|
4
3
|
|
5
4
|
describe Imap::Backup::Account::Folder do
|
6
|
-
|
7
5
|
context 'with instance' do
|
8
|
-
|
9
6
|
before :each do
|
10
7
|
@imap = stub('Net::IMAP')
|
11
8
|
@connection = stub('Imap::Backup::Account::Connection', :imap => @imap)
|
@@ -14,14 +11,12 @@ describe Imap::Backup::Account::Folder do
|
|
14
11
|
subject { Imap::Backup::Account::Folder.new(@connection, 'my_folder') }
|
15
12
|
|
16
13
|
context '#uids' do
|
17
|
-
|
18
14
|
it 'should list available messages' do
|
19
15
|
@imap.should_receive(:examine).with('my_folder')
|
20
16
|
@imap.should_receive(:uid_search).with(['ALL']).and_return([5678, 123])
|
21
17
|
|
22
18
|
subject.uids.should == [123, 5678]
|
23
19
|
end
|
24
|
-
|
25
20
|
end
|
26
21
|
|
27
22
|
context '#fetch' do
|
@@ -51,10 +46,7 @@ describe Imap::Backup::Account::Folder do
|
|
51
46
|
subject.fetch(123)
|
52
47
|
end
|
53
48
|
end
|
54
|
-
|
55
49
|
end
|
56
|
-
|
57
50
|
end
|
58
|
-
|
59
51
|
end
|
60
52
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::Account do
|
5
|
-
|
6
5
|
include HighLineTestHelpers
|
7
6
|
include InputOutputTestHelpers
|
8
7
|
|
@@ -20,7 +19,6 @@ describe Imap::Backup::Configuration::Account do
|
|
20
19
|
end
|
21
20
|
|
22
21
|
context '#run' do
|
23
|
-
|
24
22
|
before :each do
|
25
23
|
@account1_path = '/backup/path'
|
26
24
|
@account1 = {
|
@@ -45,7 +43,6 @@ describe Imap::Backup::Configuration::Account do
|
|
45
43
|
subject { Imap::Backup::Configuration::Account.new(@store, @account1) }
|
46
44
|
|
47
45
|
context 'menu' do
|
48
|
-
|
49
46
|
it 'should show a menu' do
|
50
47
|
subject.run
|
51
48
|
|
@@ -78,7 +75,6 @@ describe Imap::Backup::Configuration::Account do
|
|
78
75
|
end
|
79
76
|
|
80
77
|
context 'email' do
|
81
|
-
|
82
78
|
it 'should modify the email address' do
|
83
79
|
Imap::Backup::Configuration::Asker.should_receive(:email).once.and_return('new@example.com')
|
84
80
|
|
@@ -99,11 +95,9 @@ describe Imap::Backup::Configuration::Account do
|
|
99
95
|
subject.run
|
100
96
|
end.should =~ /there is already an account set up with that email address/i
|
101
97
|
end
|
102
|
-
|
103
98
|
end
|
104
99
|
|
105
100
|
context 'password' do
|
106
|
-
|
107
101
|
it 'should update the password' do
|
108
102
|
Imap::Backup::Configuration::Asker.should_receive(:password).once.and_return('new_pwd')
|
109
103
|
|
@@ -123,11 +117,9 @@ describe Imap::Backup::Configuration::Account do
|
|
123
117
|
|
124
118
|
@account1[:password].should == 'secret'
|
125
119
|
end
|
126
|
-
|
127
120
|
end
|
128
121
|
|
129
122
|
context 'backup_path' do
|
130
|
-
|
131
123
|
it 'should update the path' do
|
132
124
|
Imap::Backup::Configuration::Asker.should_receive(:backup_path).once do |default, validator|
|
133
125
|
validator.call('new/path')
|
@@ -153,7 +145,6 @@ describe Imap::Backup::Configuration::Account do
|
|
153
145
|
subject.run
|
154
146
|
end.should =~ %r{The path '/existing/path' is used to backup the account 'existing@example.com'}
|
155
147
|
end
|
156
|
-
|
157
148
|
end
|
158
149
|
|
159
150
|
it 'should add/remove folders' do
|
@@ -177,7 +168,6 @@ describe Imap::Backup::Configuration::Account do
|
|
177
168
|
end
|
178
169
|
|
179
170
|
context 'deletion' do
|
180
|
-
|
181
171
|
it 'should confirm deletion' do
|
182
172
|
Imap::Backup::Configuration::Setup.highline.should_receive(:agree).with("Are you sure? (y/n) ").and_return(true)
|
183
173
|
|
@@ -205,20 +195,15 @@ describe Imap::Backup::Configuration::Account do
|
|
205
195
|
|
206
196
|
@data[:accounts].should include(@account1)
|
207
197
|
end
|
208
|
-
|
209
198
|
end
|
210
199
|
|
211
200
|
context 'return to main menu' do
|
212
|
-
|
213
201
|
it 'should return' do
|
214
202
|
@input.stub!(:gets).with().and_return("return\n")
|
215
203
|
|
216
204
|
subject.run.should be_nil
|
217
205
|
end
|
218
|
-
|
219
206
|
end
|
220
|
-
|
221
207
|
end
|
222
|
-
|
223
208
|
end
|
224
209
|
|
@@ -1,10 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::Asker do
|
5
|
-
|
6
5
|
context '.email' do
|
7
|
-
|
8
6
|
it 'should ask for an email' do
|
9
7
|
Imap::Backup::Configuration::Setup.highline.should_receive(:ask).with(/email/)
|
10
8
|
|
@@ -29,11 +27,9 @@ describe Imap::Backup::Configuration::Asker do
|
|
29
27
|
|
30
28
|
Imap::Backup::Configuration::Asker.email.should == 'new@example.com'
|
31
29
|
end
|
32
|
-
|
33
30
|
end
|
34
31
|
|
35
32
|
context '.password' do
|
36
|
-
|
37
33
|
before :each do
|
38
34
|
Imap::Backup::Configuration::Setup.highline.stub!(:ask).with(/^password/).and_return('secret')
|
39
35
|
Imap::Backup::Configuration::Setup.highline.stub!(:ask).with(/^repeat password/).and_return('secret')
|
@@ -92,11 +88,9 @@ describe Imap::Backup::Configuration::Asker do
|
|
92
88
|
|
93
89
|
Imap::Backup::Configuration::Asker.password.should be_nil
|
94
90
|
end
|
95
|
-
|
96
91
|
end
|
97
92
|
|
98
93
|
context '.backup_path' do
|
99
|
-
|
100
94
|
it 'should ask for a directory' do
|
101
95
|
validator = /validator/
|
102
96
|
Imap::Backup::Configuration::Setup.highline.should_receive(:ask).with(/directory/) do |&block|
|
@@ -115,8 +109,6 @@ describe Imap::Backup::Configuration::Asker do
|
|
115
109
|
|
116
110
|
Imap::Backup::Configuration::Asker.backup_path('default path', //).should == '/path'
|
117
111
|
end
|
118
|
-
|
119
112
|
end
|
120
|
-
|
121
113
|
end
|
122
114
|
|
@@ -1,10 +1,8 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::ConnectionTester do
|
5
|
-
|
6
5
|
context '.test' do
|
7
|
-
|
8
6
|
it 'should try to connect' do
|
9
7
|
Imap::Backup::Account::Connection.should_receive(:new).with('foo')
|
10
8
|
|
@@ -40,8 +38,6 @@ describe Imap::Backup::Configuration::ConnectionTester do
|
|
40
38
|
|
41
39
|
result.should =~ /unexpected error/i
|
42
40
|
end
|
43
|
-
|
44
41
|
end
|
45
|
-
|
46
42
|
end
|
47
43
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::FolderChooser do
|
5
5
|
|
@@ -7,7 +7,6 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
7
7
|
include InputOutputTestHelpers
|
8
8
|
|
9
9
|
context '#run' do
|
10
|
-
|
11
10
|
before :each do
|
12
11
|
empty_account = {
|
13
12
|
:folders => []
|
@@ -62,7 +61,6 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
62
61
|
end
|
63
62
|
|
64
63
|
context 'folder listing' do
|
65
|
-
|
66
64
|
before :each do
|
67
65
|
account = {
|
68
66
|
:folders => [{:name => 'my_folder'}]
|
@@ -121,11 +119,7 @@ describe Imap::Backup::Configuration::FolderChooser do
|
|
121
119
|
@output.string.should =~ /\d+\. \- my_folder/
|
122
120
|
@account[:folders].should_not include( { :name => 'my_folder' } )
|
123
121
|
end
|
124
|
-
|
125
|
-
|
126
122
|
end
|
127
|
-
|
128
123
|
end
|
129
|
-
|
130
124
|
end
|
131
125
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
|
2
|
+
require 'spec_helper'
|
3
3
|
|
4
4
|
describe Imap::Backup::Configuration::List do
|
5
|
-
|
6
|
-
|
5
|
+
let(:configuration_data) do
|
6
|
+
{
|
7
7
|
:accounts => [
|
8
8
|
{
|
9
9
|
:username => 'a1@example.com'
|
@@ -13,61 +13,65 @@ describe Imap::Backup::Configuration::List do
|
|
13
13
|
},
|
14
14
|
]
|
15
15
|
}
|
16
|
-
|
17
|
-
|
16
|
+
end
|
17
|
+
let(:store) { stub('Imap::Backup::Configuration::Store', :data => configuration_data) }
|
18
|
+
|
19
|
+
before do
|
20
|
+
Imap::Backup::Configuration::Store.stub!(:new => store)
|
21
|
+
Imap::Backup::Configuration::Store.stub!(:exist? => true)
|
18
22
|
end
|
19
23
|
|
20
24
|
context '#initialize' do
|
25
|
+
it 'fails if the configuration file is missing' do
|
26
|
+
Imap::Backup::Configuration::Store.should_receive(:exist?).and_return(false)
|
27
|
+
|
28
|
+
expect {
|
29
|
+
Imap::Backup::Configuration::List.new
|
30
|
+
}.to raise_error(Imap::Backup::ConfigurationNotFound, /not found/)
|
31
|
+
end
|
21
32
|
|
22
33
|
context 'with account parameter' do
|
23
34
|
it 'should only create requested accounts' do
|
24
35
|
configuration = Imap::Backup::Configuration::List.new(['a2@example.com'])
|
25
36
|
|
26
|
-
configuration.accounts.should ==
|
37
|
+
configuration.accounts.should == configuration_data[:accounts][1..1]
|
27
38
|
end
|
28
39
|
end
|
29
|
-
|
30
40
|
end
|
31
41
|
|
32
42
|
context 'instance methods' do
|
33
|
-
|
34
|
-
before :each do
|
35
|
-
@connection = stub('Imap::Backup::Account::Connection', :disconnect => nil)
|
36
|
-
end
|
43
|
+
let(:connection) { stub('Imap::Backup::Account::Connection', :disconnect => nil) }
|
37
44
|
|
38
45
|
subject { Imap::Backup::Configuration::List.new }
|
39
46
|
|
40
47
|
context '#each_connection' do
|
41
48
|
|
42
49
|
it 'should instantiate connections' do
|
43
|
-
Imap::Backup::Account::Connection.should_receive(:new).with(
|
44
|
-
Imap::Backup::Account::Connection.should_receive(:new).with(
|
50
|
+
Imap::Backup::Account::Connection.should_receive(:new).with(configuration_data[:accounts][0]).and_return(connection)
|
51
|
+
Imap::Backup::Account::Connection.should_receive(:new).with(configuration_data[:accounts][1]).and_return(connection)
|
45
52
|
|
46
53
|
subject.each_connection{}
|
47
54
|
end
|
48
55
|
|
49
56
|
it 'should call the block' do
|
50
|
-
Imap::Backup::Account::Connection.stub!(:new).and_return(
|
57
|
+
Imap::Backup::Account::Connection.stub!(:new).and_return(connection)
|
51
58
|
calls = 0
|
52
59
|
|
53
60
|
subject.each_connection do |a|
|
54
61
|
calls += 1
|
55
|
-
a.should ==
|
62
|
+
a.should == connection
|
56
63
|
end
|
57
64
|
calls.should == 2
|
58
65
|
end
|
59
66
|
|
60
67
|
it 'should disconnect connections' do
|
61
|
-
Imap::Backup::Account::Connection.stub!(:new).and_return(
|
68
|
+
Imap::Backup::Account::Connection.stub!(:new).and_return(connection)
|
62
69
|
|
63
|
-
|
70
|
+
connection.should_receive(:disconnect)
|
64
71
|
|
65
72
|
subject.each_connection {}
|
66
73
|
end
|
67
|
-
|
68
74
|
end
|
69
|
-
|
70
75
|
end
|
71
|
-
|
72
76
|
end
|
73
77
|
|