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.
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