imap-backup 0.0.5 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/README.md +43 -6
  2. data/bin/imap-backup +0 -1
  3. data/imap-backup.gemspec +1 -1
  4. data/lib/email/mboxrd/message.rb +37 -0
  5. data/lib/imap/backup.rb +2 -0
  6. data/lib/imap/backup/account/connection.rb +2 -4
  7. data/lib/imap/backup/account/folder.rb +0 -2
  8. data/lib/imap/backup/configuration/account.rb +0 -2
  9. data/lib/imap/backup/configuration/asker.rb +0 -2
  10. data/lib/imap/backup/configuration/folder_chooser.rb +0 -2
  11. data/lib/imap/backup/configuration/list.rb +17 -16
  12. data/lib/imap/backup/configuration/setup.rb +1 -3
  13. data/lib/imap/backup/configuration/store.rb +19 -13
  14. data/lib/imap/backup/downloader.rb +0 -2
  15. data/lib/imap/backup/serializer/base.rb +17 -0
  16. data/lib/imap/backup/serializer/directory.rb +3 -7
  17. data/lib/imap/backup/serializer/mbox.rb +96 -0
  18. data/lib/imap/backup/utils.rb +6 -3
  19. data/lib/imap/backup/version.rb +2 -2
  20. data/spec/unit/account/connection_spec.rb +12 -13
  21. data/spec/unit/account/folder_spec.rb +1 -9
  22. data/spec/unit/configuration/account_spec.rb +1 -16
  23. data/spec/unit/configuration/asker_spec.rb +1 -9
  24. data/spec/unit/configuration/connection_tester_spec.rb +1 -5
  25. data/spec/unit/configuration/folder_chooser_spec.rb +1 -7
  26. data/spec/unit/configuration/list_spec.rb +24 -20
  27. data/spec/unit/configuration/setup_spec.rb +3 -9
  28. data/spec/unit/configuration/store_spec.rb +11 -20
  29. data/spec/unit/downloader_spec.rb +1 -11
  30. data/spec/unit/email/mboxrd/message_spec.rb +51 -0
  31. data/spec/unit/serializer/base_spec.rb +19 -0
  32. data/spec/unit/serializer/directory_spec.rb +51 -74
  33. data/spec/unit/serializer/mbox_spec.rb +113 -0
  34. data/spec/unit/utils_spec.rb +3 -9
  35. 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
+
@@ -16,7 +16,7 @@ module Imap
16
16
  def self.stat(filename)
17
17
  return nil unless File.exist?(filename)
18
18
 
19
- stat = File.stat(filename)
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
- first_directory = File.join(base_path, parts[0])
29
- FileUtils.chmod permissions, first_directory
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
@@ -1,8 +1,8 @@
1
1
  module Imap
2
2
  module Backup
3
- MAJOR = 0
3
+ MAJOR = 1
4
4
  MINOR = 0
5
- REVISION = 5
5
+ REVISION = 0
6
6
  VERSION = [ MAJOR, MINOR, REVISION ].map( &:to_s ).join( '.' )
7
7
  end
8
8
  end
@@ -1,10 +1,8 @@
1
1
  # encoding: utf-8
2
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
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::Directory.stub!(:new).with('/base/path', 'my_folder').and_return(@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::Directory.should_receive(:new).with('/base/path', 'my_folder').and_return(@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
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
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
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
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
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
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
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
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
- load File.expand_path( '../../spec_helper.rb', File.dirname(__FILE__) )
2
+ require 'spec_helper'
3
3
 
4
4
  describe Imap::Backup::Configuration::List do
5
- before :each do
6
- @configuration_data = {
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
- @store = stub('Imap::Backup::Configuration::Store', :data => @configuration_data)
17
- Imap::Backup::Configuration::Store.stub!(:new => @store)
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 == @configuration_data[:accounts][1..1]
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(@configuration_data[:accounts][0]).and_return(@connection)
44
- Imap::Backup::Account::Connection.should_receive(:new).with(@configuration_data[:accounts][1]).and_return(@connection)
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(@connection)
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 == @connection
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(@connection)
68
+ Imap::Backup::Account::Connection.stub!(:new).and_return(connection)
62
69
 
63
- @connection.should_receive(:disconnect)
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