imap-backup 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +1 -1
- data/Gemfile +0 -1
- data/README.md +0 -1
- data/Rakefile +4 -9
- data/bin/imap-backup +0 -1
- data/imap-backup.gemspec +0 -1
- data/lib/email/mboxrd/message.rb +31 -32
- data/lib/imap/backup.rb +17 -4
- data/lib/imap/backup/account/connection.rb +70 -64
- data/lib/imap/backup/account/folder.rb +21 -26
- data/lib/imap/backup/configuration/account.rb +127 -73
- data/lib/imap/backup/configuration/asker.rb +38 -31
- data/lib/imap/backup/configuration/connection_tester.rb +9 -14
- data/lib/imap/backup/configuration/folder_chooser.rb +43 -48
- data/lib/imap/backup/configuration/list.rb +19 -24
- data/lib/imap/backup/configuration/setup.rb +56 -51
- data/lib/imap/backup/configuration/store.rb +47 -52
- data/lib/imap/backup/downloader.rb +11 -14
- data/lib/imap/backup/serializer/base.rb +8 -11
- data/lib/imap/backup/serializer/directory.rb +36 -41
- data/lib/imap/backup/serializer/mbox.rb +83 -88
- data/lib/imap/backup/utils.rb +0 -3
- data/lib/imap/backup/version.rb +1 -2
- data/spec/gather_rspec_coverage.rb +0 -1
- data/spec/spec_helper.rb +2 -7
- data/spec/unit/account/connection_spec.rb +0 -1
- data/spec/unit/account/folder_spec.rb +0 -1
- data/spec/unit/configuration/account_spec.rb +207 -136
- data/spec/unit/configuration/asker_spec.rb +59 -85
- data/spec/unit/configuration/connection_tester_spec.rb +36 -26
- data/spec/unit/configuration/folder_chooser_spec.rb +3 -6
- data/spec/unit/configuration/list_spec.rb +0 -1
- data/spec/unit/configuration/setup_spec.rb +8 -9
- data/spec/unit/configuration/store_spec.rb +1 -4
- data/spec/unit/downloader_spec.rb +0 -1
- data/spec/unit/email/mboxrd/message_spec.rb +0 -1
- data/spec/unit/serializer/base_spec.rb +0 -1
- data/spec/unit/serializer/directory_spec.rb +0 -1
- data/spec/unit/serializer/mbox_spec.rb +0 -1
- data/spec/unit/utils_spec.rb +0 -1
- metadata +2 -2
@@ -1,40 +1,47 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module Imap
|
4
|
-
|
5
|
-
|
6
|
-
module Asker
|
7
|
-
EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
|
3
|
+
module Imap::Backup::Configuration
|
4
|
+
class Asker < Struct.new(:highline)
|
5
|
+
EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
def email(default = '')
|
8
|
+
highline.ask('email address: ') do |q|
|
9
|
+
q.default = default
|
10
|
+
q.readline = true
|
11
|
+
q.validate = EMAIL_MATCHER
|
12
|
+
q.responses[:not_valid] = 'Enter a valid email address '
|
13
|
+
end
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
16
|
+
def password
|
17
|
+
password = highline.ask('password: ') { |q| q.echo = false }
|
18
|
+
confirmation = highline.ask('repeat password: ') { |q| q.echo = false }
|
19
|
+
if password != confirmation
|
20
|
+
return nil unless highline.agree("the password and confirmation did not match.\nContinue? (y/n) ")
|
21
|
+
return self.password
|
22
|
+
end
|
23
|
+
password
|
24
|
+
end
|
27
25
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
26
|
+
def backup_path(default, validator)
|
27
|
+
highline.ask('backup directory: ') do |q|
|
28
|
+
q.default = default
|
29
|
+
q.readline = true
|
30
|
+
q.validate = validator
|
31
|
+
q.responses[:not_valid] = 'Choose a different directory '
|
36
32
|
end
|
37
33
|
end
|
34
|
+
|
35
|
+
def self.email(default = '')
|
36
|
+
new(Setup.highline).email(default)
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.password
|
40
|
+
new(Setup.highline).password
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.backup_path(default, validator)
|
44
|
+
new(Setup.highline).backup_path(default, validator)
|
45
|
+
end
|
38
46
|
end
|
39
47
|
end
|
40
|
-
|
@@ -1,19 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module Imap
|
4
|
-
module
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rescue Exception => e
|
13
|
-
return "Unexpected error: #{e}"
|
14
|
-
end
|
15
|
-
end
|
3
|
+
module Imap::Backup::Configuration
|
4
|
+
module ConnectionTester
|
5
|
+
def self.test(account)
|
6
|
+
Imap::Backup::Account::Connection.new(account).imap
|
7
|
+
return 'Connection successful'
|
8
|
+
rescue Net::IMAP::NoResponseError
|
9
|
+
return 'No response'
|
10
|
+
rescue Exception => e
|
11
|
+
return "Unexpected error: #{e}"
|
16
12
|
end
|
17
13
|
end
|
18
14
|
end
|
19
|
-
|
@@ -1,60 +1,55 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module Imap
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
@account = account
|
9
|
-
end
|
3
|
+
module Imap::Backup::Configuration
|
4
|
+
class FolderChooser
|
5
|
+
def initialize(account)
|
6
|
+
@account = account
|
7
|
+
end
|
10
8
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
39
|
-
menu.choice("#{mark} #{name}") do
|
40
|
-
if found
|
41
|
-
@account[:folders].reject! { |f| f[:name] == name }
|
42
|
-
else
|
43
|
-
@account[:folders] << { :name => name }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
menu.choice('return to the account menu') do
|
48
|
-
return
|
9
|
+
def run
|
10
|
+
begin
|
11
|
+
@connection = Imap::Backup::Account::Connection.new(@account)
|
12
|
+
rescue => e
|
13
|
+
puts 'Connection failed'
|
14
|
+
Setup.highline.ask 'Press a key '
|
15
|
+
return
|
16
|
+
end
|
17
|
+
@folders = @connection.folders
|
18
|
+
if @folders.nil?
|
19
|
+
puts 'Unable to get folder list'
|
20
|
+
Setup.highline.ask 'Press a key '
|
21
|
+
return
|
22
|
+
end
|
23
|
+
loop do
|
24
|
+
system('clear')
|
25
|
+
Setup.highline.choose do |menu|
|
26
|
+
menu.header = 'Add/remove folders'
|
27
|
+
menu.index = :number
|
28
|
+
@folders.each do |folder|
|
29
|
+
name = folder.name
|
30
|
+
found = @account[:folders].find { |f| f[:name] == name }
|
31
|
+
mark =
|
32
|
+
if found
|
33
|
+
'+'
|
34
|
+
else
|
35
|
+
'-'
|
49
36
|
end
|
50
|
-
|
51
|
-
|
37
|
+
menu.choice("#{mark} #{name}") do
|
38
|
+
if found
|
39
|
+
@account[:folders].reject! { |f| f[:name] == name }
|
40
|
+
else
|
41
|
+
@account[:folders] << { :name => name }
|
52
42
|
end
|
53
43
|
end
|
54
44
|
end
|
45
|
+
menu.choice('return to the account menu') do
|
46
|
+
return
|
47
|
+
end
|
48
|
+
menu.hidden('quit') do
|
49
|
+
return
|
50
|
+
end
|
55
51
|
end
|
56
52
|
end
|
57
53
|
end
|
58
54
|
end
|
59
55
|
end
|
60
|
-
|
@@ -1,33 +1,28 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
module Imap
|
4
|
-
|
5
|
-
|
6
|
-
class List
|
7
|
-
attr_reader :accounts
|
3
|
+
module Imap::Backup::Configuration
|
4
|
+
class List
|
5
|
+
attr_reader :accounts
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
def initialize(accounts = nil)
|
8
|
+
if not Imap::Backup::Configuration::Store.exist?
|
9
|
+
raise Imap::Backup::ConfigurationNotFound.new("Configuration file '#{Imap::Backup::Configuration::Store.default_pathname}' not found")
|
10
|
+
end
|
11
|
+
@config = Imap::Backup::Configuration::Store.new
|
14
12
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
13
|
+
if accounts.nil?
|
14
|
+
@accounts = @config.data[:accounts]
|
15
|
+
else
|
16
|
+
@accounts = @config.data[:accounts].select{ |account| accounts.include?(account[:username]) }
|
17
|
+
end
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
end
|
20
|
+
def each_connection
|
21
|
+
@accounts.each do |account|
|
22
|
+
connection = Imap::Backup::Account::Connection.new(account)
|
23
|
+
yield connection
|
24
|
+
connection.disconnect
|
29
25
|
end
|
30
26
|
end
|
31
27
|
end
|
32
28
|
end
|
33
|
-
|
@@ -2,66 +2,71 @@
|
|
2
2
|
require 'rubygems' if RUBY_VERSION < '1.9'
|
3
3
|
require 'highline'
|
4
4
|
|
5
|
-
module Imap
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
self.highline = HighLine.new
|
13
|
-
|
14
|
-
def initialize
|
15
|
-
@config = Imap::Backup::Configuration::Store.new
|
16
|
-
end
|
5
|
+
module Imap::Backup::Configuration
|
6
|
+
class Setup
|
7
|
+
class << self
|
8
|
+
attr_accessor :highline
|
9
|
+
end
|
10
|
+
self.highline = HighLine.new
|
17
11
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
end
|
28
|
-
menu.choice('add account') do
|
29
|
-
username = Asker.email
|
30
|
-
edit_account username
|
31
|
-
end
|
32
|
-
menu.choice('save and exit') do
|
33
|
-
@config.save
|
34
|
-
return
|
35
|
-
end
|
36
|
-
menu.choice(:quit) do
|
37
|
-
return
|
38
|
-
end
|
12
|
+
def run
|
13
|
+
setup_logging
|
14
|
+
loop do
|
15
|
+
system('clear')
|
16
|
+
self.class.highline.choose do |menu|
|
17
|
+
menu.header = 'Choose an action'
|
18
|
+
config.data[:accounts].each do |account|
|
19
|
+
menu.choice("#{account[:username]}") do
|
20
|
+
edit_account account[:username]
|
39
21
|
end
|
40
22
|
end
|
23
|
+
menu.choice('add account') do
|
24
|
+
username = Asker.email
|
25
|
+
edit_account username
|
26
|
+
end
|
27
|
+
menu.choice('save and exit') do
|
28
|
+
config.save
|
29
|
+
return
|
30
|
+
end
|
31
|
+
menu.choice(:quit) do
|
32
|
+
return
|
33
|
+
end
|
41
34
|
end
|
35
|
+
end
|
36
|
+
end
|
42
37
|
|
43
|
-
|
38
|
+
private
|
44
39
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
:password => '',
|
49
|
-
:local_path => File.join(@config.path, username.gsub('@', '_')),
|
50
|
-
:folders => []
|
51
|
-
}
|
52
|
-
@config.data[:accounts] << account
|
53
|
-
account
|
54
|
-
end
|
40
|
+
def config
|
41
|
+
@config ||= Imap::Backup::Configuration::Store.new
|
42
|
+
end
|
55
43
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
44
|
+
def setup_logging
|
45
|
+
Imap::Backup.logger.level =
|
46
|
+
if config.data[:debug]
|
47
|
+
::Logger::Severity::DEBUG
|
48
|
+
else
|
49
|
+
::Logger::Severity::ERROR
|
62
50
|
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def add_account(username)
|
54
|
+
account = {
|
55
|
+
:username => username,
|
56
|
+
:password => '',
|
57
|
+
:local_path => File.join(config.path, username.gsub('@', '_')),
|
58
|
+
:folders => []
|
59
|
+
}
|
60
|
+
config.data[:accounts] << account
|
61
|
+
account
|
62
|
+
end
|
63
|
+
|
64
|
+
def edit_account(username)
|
65
|
+
account = config.data[:accounts].find { |a| a[:username] == username }
|
66
|
+
if account.nil?
|
67
|
+
account = add_account(username)
|
63
68
|
end
|
69
|
+
Account.new(config, account, Imap::Backup::Configuration::Setup.highline).run
|
64
70
|
end
|
65
71
|
end
|
66
72
|
end
|
67
|
-
|
@@ -2,69 +2,64 @@
|
|
2
2
|
require 'rubygems' if RUBY_VERSION < '1.9'
|
3
3
|
require 'json'
|
4
4
|
|
5
|
-
module Imap
|
6
|
-
|
7
|
-
|
8
|
-
class Store
|
9
|
-
CONFIGURATION_DIRECTORY = File.expand_path('~/.imap-backup')
|
5
|
+
module Imap::Backup::Configuration
|
6
|
+
class Store
|
7
|
+
CONFIGURATION_DIRECTORY = File.expand_path('~/.imap-backup')
|
10
8
|
|
11
|
-
|
12
|
-
|
9
|
+
attr_reader :data
|
10
|
+
attr_reader :path
|
13
11
|
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
def self.default_pathname
|
13
|
+
File.join(CONFIGURATION_DIRECTORY, 'config.json')
|
14
|
+
end
|
17
15
|
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
def self.exist?(pathname = default_pathname)
|
17
|
+
File.exist?(pathname)
|
18
|
+
end
|
21
19
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
20
|
+
def initialize(pathname = self.class.default_pathname)
|
21
|
+
@pathname = pathname
|
22
|
+
if File.directory?(path)
|
23
|
+
Imap::Backup::Utils.check_permissions path, 0700
|
24
|
+
end
|
25
|
+
if File.exist?(@pathname)
|
26
|
+
Imap::Backup::Utils.check_permissions @pathname, 0600
|
27
|
+
@data = JSON.parse(File.read(@pathname), :symbolize_names => true)
|
28
|
+
else
|
29
|
+
@data = {:accounts => []}
|
30
|
+
end
|
31
|
+
end
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
48
|
-
end
|
33
|
+
def save
|
34
|
+
mkdir_private path
|
35
|
+
File.open(@pathname, 'w') { |f| f.write(JSON.pretty_generate(@data)) }
|
36
|
+
FileUtils.chmod 0600, @pathname
|
37
|
+
@data[:accounts].each do |account|
|
38
|
+
mkdir_private account[:local_path]
|
39
|
+
account[:folders].each do |f|
|
40
|
+
parts = f[:name].split('/')
|
41
|
+
p = account[:local_path].clone
|
42
|
+
parts.each do |part|
|
43
|
+
p = File.join(p, part)
|
44
|
+
mkdir_private p
|
49
45
|
end
|
50
46
|
end
|
47
|
+
end
|
48
|
+
end
|
51
49
|
|
52
|
-
|
53
|
-
|
54
|
-
|
50
|
+
def path
|
51
|
+
File.dirname(@pathname)
|
52
|
+
end
|
55
53
|
|
56
|
-
|
54
|
+
private
|
57
55
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
end
|
65
|
-
end
|
56
|
+
def mkdir_private(path)
|
57
|
+
if ! File.directory?(path)
|
58
|
+
FileUtils.mkdir path
|
59
|
+
end
|
60
|
+
if Imap::Backup::Utils::stat(path) != 0700
|
61
|
+
FileUtils.chmod 0700, path
|
66
62
|
end
|
67
63
|
end
|
68
64
|
end
|
69
65
|
end
|
70
|
-
|