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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -1
  3. data/.travis.yml +1 -1
  4. data/Gemfile +0 -1
  5. data/README.md +0 -1
  6. data/Rakefile +4 -9
  7. data/bin/imap-backup +0 -1
  8. data/imap-backup.gemspec +0 -1
  9. data/lib/email/mboxrd/message.rb +31 -32
  10. data/lib/imap/backup.rb +17 -4
  11. data/lib/imap/backup/account/connection.rb +70 -64
  12. data/lib/imap/backup/account/folder.rb +21 -26
  13. data/lib/imap/backup/configuration/account.rb +127 -73
  14. data/lib/imap/backup/configuration/asker.rb +38 -31
  15. data/lib/imap/backup/configuration/connection_tester.rb +9 -14
  16. data/lib/imap/backup/configuration/folder_chooser.rb +43 -48
  17. data/lib/imap/backup/configuration/list.rb +19 -24
  18. data/lib/imap/backup/configuration/setup.rb +56 -51
  19. data/lib/imap/backup/configuration/store.rb +47 -52
  20. data/lib/imap/backup/downloader.rb +11 -14
  21. data/lib/imap/backup/serializer/base.rb +8 -11
  22. data/lib/imap/backup/serializer/directory.rb +36 -41
  23. data/lib/imap/backup/serializer/mbox.rb +83 -88
  24. data/lib/imap/backup/utils.rb +0 -3
  25. data/lib/imap/backup/version.rb +1 -2
  26. data/spec/gather_rspec_coverage.rb +0 -1
  27. data/spec/spec_helper.rb +2 -7
  28. data/spec/unit/account/connection_spec.rb +0 -1
  29. data/spec/unit/account/folder_spec.rb +0 -1
  30. data/spec/unit/configuration/account_spec.rb +207 -136
  31. data/spec/unit/configuration/asker_spec.rb +59 -85
  32. data/spec/unit/configuration/connection_tester_spec.rb +36 -26
  33. data/spec/unit/configuration/folder_chooser_spec.rb +3 -6
  34. data/spec/unit/configuration/list_spec.rb +0 -1
  35. data/spec/unit/configuration/setup_spec.rb +8 -9
  36. data/spec/unit/configuration/store_spec.rb +1 -4
  37. data/spec/unit/downloader_spec.rb +0 -1
  38. data/spec/unit/email/mboxrd/message_spec.rb +0 -1
  39. data/spec/unit/serializer/base_spec.rb +0 -1
  40. data/spec/unit/serializer/directory_spec.rb +0 -1
  41. data/spec/unit/serializer/mbox_spec.rb +0 -1
  42. data/spec/unit/utils_spec.rb +0 -1
  43. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: eac9741caa296c80fd63a064ba94efb71757200d
4
- data.tar.gz: f405d8fb91c9f75cba102587d2867f606aa40c81
3
+ metadata.gz: 28c9159f1439c7b12a52c7149284c714218cef47
4
+ data.tar.gz: ff1ed3afecd0e1753734bc2bc8f6bd0ddd42d063
5
5
  SHA512:
6
- metadata.gz: aa78818bc59cca9065fab40444798db9b107c2b3fb88b2ba6485c8f18dca5a26b0ca85fc51446fa25373bf8bbfb6275db6527c7fe271c8126f59bb68a7878a8a
7
- data.tar.gz: 9841997c5f74550963486207905676d9f9d4bcfbb149e5055209a84f93869dfb5c45f545058ab87ecbb6c534a944e8706f39cec7dc94a941f310b2999f624b23
6
+ metadata.gz: fd6d123a800c8e1cfea9897c3f7edd70b5be9e7ad36581ae9aeb90e129d52dbc058372880cb677db40637e5ba4d68015220f8d6ca0c45e7087b1e748568aaae7
7
+ data.tar.gz: b52ce2115eae0a0bc175fe2264eb9842a8b535f16d6a5237e9df4f250cedcc5e4b4fe26ded3e06a43f0f5b4636b839bcf91c983f217efaf2b05e4549bb122507
data/.gitignore CHANGED
@@ -5,4 +5,3 @@ coverage
5
5
  pkg
6
6
  .rbenv-version
7
7
  vendor
8
-
data/.travis.yml CHANGED
@@ -3,5 +3,5 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.2
5
5
  - 1.9.3
6
+ - 2.0.0
6
7
  script: "bundle exec rake spec"
7
-
data/Gemfile CHANGED
@@ -1,4 +1,3 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
-
data/README.md CHANGED
@@ -127,4 +127,3 @@ $ imap-backup status
127
127
  3. Commit your changes (`git commit -am 'Added some feature'`)
128
128
  4. Push to the branch (`git push origin my-new-feature`)
129
129
  5. Create new Pull Request
130
-
data/Rakefile CHANGED
@@ -4,24 +4,19 @@ require 'rspec/core/rake_task'
4
4
 
5
5
  task :default => :spec
6
6
 
7
- RSpec::Core::RakeTask.new do | t |
7
+ RSpec::Core::RakeTask.new do |t|
8
8
  t.pattern = 'spec/**/*_spec.rb'
9
9
  end
10
10
 
11
11
  if RUBY_VERSION < '1.9'
12
-
13
- RSpec::Core::RakeTask.new( 'spec:coverage' ) do |t|
12
+ RSpec::Core::RakeTask.new('spec:coverage') do |t|
14
13
  t.pattern = 'spec/**/*_spec.rb'
15
14
  t.rcov = true
16
- t.rcov_opts = [ '--exclude', 'spec/,/gems/,vendor/' ]
15
+ t.rcov_opts = ['--exclude', 'spec/,/gems/,vendor/']
17
16
  end
18
-
19
17
  else
20
-
21
18
  desc 'Run specs and create coverage output'
22
- RSpec::Core::RakeTask.new( 'spec:coverage' ) do |t|
19
+ RSpec::Core::RakeTask.new('spec:coverage') do |t|
23
20
  t.pattern = ['spec/gather_rspec_coverage.rb', 'spec/**/*_spec.rb']
24
21
  end
25
-
26
22
  end
27
-
data/bin/imap-backup CHANGED
@@ -84,4 +84,3 @@ when 'status'
84
84
  end
85
85
  end
86
86
  end
87
-
data/imap-backup.gemspec CHANGED
@@ -32,4 +32,3 @@ Gem::Specification.new do |gem|
32
32
  gem.add_development_dependency 'simplecov'
33
33
  end
34
34
  end
35
-
@@ -1,37 +1,36 @@
1
1
  require 'mail'
2
2
 
3
- module Email
4
- module Mboxrd
5
- class Message
6
- def initialize(body)
7
- @body = body.clone
8
- @body.force_encoding('binary') if RUBY_VERSION >= '1.9.0'
9
- end
10
-
11
- def to_s
12
- 'From ' + from + "\n" + body + "\n"
13
- end
14
-
15
- private
16
-
17
- def parsed
18
- @parsed ||= Mail.new(@body)
19
- end
20
-
21
- def from
22
- parsed.from[0] + ' ' + asctime
23
- end
24
-
25
- def body
26
- mbox = @body.gsub(/\n(>*From)/, "\n>\\1")
27
- mbox + "\n" unless mbox.end_with?("\n")
28
- mbox
29
- end
30
-
31
- def asctime
32
- parsed.date.asctime
33
- end
3
+ module Email; end
4
+
5
+ module Email::Mboxrd
6
+ class Message
7
+ def initialize(body)
8
+ @body = body.clone
9
+ @body.force_encoding('binary') if RUBY_VERSION >= '1.9.0'
10
+ end
11
+
12
+ def to_s
13
+ 'From ' + from + "\n" + body + "\n"
14
+ end
15
+
16
+ private
17
+
18
+ def parsed
19
+ @parsed ||= Mail.new(@body)
20
+ end
21
+
22
+ def from
23
+ parsed.from[0] + ' ' + asctime
24
+ end
25
+
26
+ def body
27
+ mbox = @body.gsub(/\n(>*From)/, "\n>\\1")
28
+ mbox + "\n" unless mbox.end_with?("\n")
29
+ mbox
30
+ end
31
+
32
+ def asctime
33
+ parsed.date.asctime
34
34
  end
35
35
  end
36
36
  end
37
-
data/lib/imap/backup.rb CHANGED
@@ -14,9 +14,22 @@ require 'imap/backup/serializer/directory'
14
14
  require 'imap/backup/serializer/mbox'
15
15
  require 'imap/backup/version'
16
16
 
17
- module Imap
18
- module Backup
19
- class ConfigurationNotFound < StandardError; end
17
+ require 'logger'
18
+
19
+ module Imap::Backup
20
+ class ConfigurationNotFound < StandardError; end
21
+
22
+ class Logger
23
+ include Singleton
24
+
25
+ attr_reader :logger
26
+
27
+ def initialize
28
+ @logger = ::Logger.new(STDOUT)
29
+ end
20
30
  end
21
- end
22
31
 
32
+ def self.logger
33
+ Imap::Backup::Logger.instance.logger
34
+ end
35
+ end
@@ -1,79 +1,85 @@
1
1
  require 'net/imap'
2
2
 
3
- module Imap
4
- module Backup
5
- module Account
6
- class Connection
7
- attr_reader :username, :local_path, :backup_folders, :server
3
+ module Imap::Backup::Account
4
+ class Connection
5
+ attr_reader :username, :local_path, :backup_folders, :server
8
6
 
9
- def initialize(options)
10
- @username, @password = options[:username], options[:password]
11
- @local_path, @backup_folders = options[:local_path], options[:folders]
12
- @server = options[:server] || host_for(username)
13
- end
7
+ def initialize(options)
8
+ @username, @password = options[:username], options[:password]
9
+ @local_path, @backup_folders = options[:local_path], options[:folders]
10
+ @server = options[:server] || host_for(username)
11
+ end
12
+
13
+ def folders
14
+ root = root_for(username)
15
+ imap.list(root, '*')
16
+ end
17
+
18
+ def status
19
+ backup_folders.map do |folder|
20
+ f = Imap::Backup::Account::Folder.new(self, folder[:name])
21
+ s = Imap::Backup::Serializer::Directory.new(local_path, folder[:name])
22
+ {:name => folder[:name], :local => s.uids, :remote => f.uids}
23
+ end
24
+ end
14
25
 
15
- def folders
16
- root = root_for(username)
17
- imap.list(root, '*')
18
- end
26
+ def run_backup
27
+ backup_folders.each do |folder|
28
+ f = Imap::Backup::Account::Folder.new(self, folder[:name])
29
+ s = Imap::Backup::Serializer::Mbox.new(local_path, folder[:name])
30
+ d = Imap::Backup::Downloader.new(f, s)
31
+ d.run
32
+ end
33
+ end
19
34
 
20
- def status
21
- backup_folders.map do |folder|
22
- f = Imap::Backup::Account::Folder.new(self, folder[:name])
23
- s = Imap::Backup::Serializer::Directory.new(local_path, folder[:name])
24
- {:name => folder[:name], :local => s.uids, :remote => f.uids}
25
- end
26
- end
35
+ def disconnect
36
+ imap.disconnect
37
+ end
27
38
 
28
- def run_backup
29
- backup_folders.each do |folder|
30
- f = Imap::Backup::Account::Folder.new(self, folder[:name])
31
- s = Imap::Backup::Serializer::Mbox.new(local_path, folder[:name])
32
- d = Imap::Backup::Downloader.new(f, s)
33
- d.run
34
- end
35
- end
39
+ def imap
40
+ return @imap unless @imap.nil?
41
+ options = options_for(username)
42
+ Imap::Backup.logger.debug "Creating IMAP instance: #{server}, options: #{options.inspect}"
43
+ @imap = Net::IMAP.new(server, options)
44
+ Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
45
+ @imap.login(username, password)
46
+ @imap
47
+ end
36
48
 
37
- def disconnect
38
- imap.disconnect
39
- end
49
+ private
40
50
 
41
- def imap
42
- return @imap unless @imap.nil?
43
- options = options_for(username)
44
- @imap = Net::IMAP.new(server, options)
45
- @imap.login(username, @password)
46
- @imap
47
- end
51
+ def password
52
+ @password
53
+ end
48
54
 
49
- private
55
+ def masked_password
56
+ password.gsub(/./, 'x')
57
+ end
50
58
 
51
- def host_for(username)
52
- case username
53
- when /@gmail\.com/
54
- 'imap.gmail.com'
55
- when /@fastmail\.fm/
56
- 'mail.messagingengine.com'
57
- end
58
- end
59
+ def host_for(username)
60
+ case username
61
+ when /@gmail\.com/
62
+ 'imap.gmail.com'
63
+ when /@fastmail\.fm/
64
+ 'mail.messagingengine.com'
65
+ end
66
+ end
59
67
 
60
- def root_for(username)
61
- case username
62
- when /@gmail\.com/
63
- '/'
64
- when /@fastmail\.fm/
65
- 'INBOX'
66
- end
67
- end
68
+ def root_for(username)
69
+ case username
70
+ when /@gmail\.com/
71
+ '/'
72
+ when /@fastmail\.fm/
73
+ 'INBOX'
74
+ end
75
+ end
68
76
 
69
- def options_for(username)
70
- case username
71
- when /@gmail\.com/
72
- {:port => 993, :ssl => true}
73
- when /@fastmail\.fm/
74
- {:port => 993, :ssl => true}
75
- end
76
- end
77
+ def options_for(username)
78
+ case username
79
+ when /@gmail\.com/
80
+ {:port => 993, :ssl => true}
81
+ when /@fastmail\.fm/
82
+ {:port => 993, :ssl => true}
77
83
  end
78
84
  end
79
85
  end
@@ -1,34 +1,29 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap
4
- module Backup
5
- module Account
6
- class Folder
7
- REQUESTED_ATTRIBUTES = ['RFC822', 'FLAGS', 'INTERNALDATE']
3
+ module Imap::Backup::Account
4
+ class Folder
5
+ REQUESTED_ATTRIBUTES = ['RFC822', 'FLAGS', 'INTERNALDATE']
8
6
 
9
- def initialize(connection, folder)
10
- @connection, @folder = connection, folder
11
- end
7
+ def initialize(connection, folder)
8
+ @connection, @folder = connection, folder
9
+ end
12
10
 
13
- def uids
14
- @connection.imap.examine(@folder)
15
- @connection.imap.uid_search(['ALL']).sort
16
- rescue Net::IMAP::NoResponseError => e
17
- warn "Folder '#{@folder}' does not exist"
18
- []
19
- end
11
+ def uids
12
+ @connection.imap.examine(@folder)
13
+ @connection.imap.uid_search(['ALL']).sort
14
+ rescue Net::IMAP::NoResponseError => e
15
+ warn "Folder '#{@folder}' does not exist"
16
+ []
17
+ end
20
18
 
21
- def fetch(uid)
22
- @connection.imap.examine(@folder)
23
- message = @connection.imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)[0][1]
24
- message['RFC822'].force_encoding('utf-8') if RUBY_VERSION > '1.9'
25
- message
26
- rescue Net::IMAP::NoResponseError => e
27
- warn "Folder '#{@folder}' does not exist"
28
- nil
29
- end
30
- end
19
+ def fetch(uid)
20
+ @connection.imap.examine(@folder)
21
+ message = @connection.imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)[0][1]
22
+ message['RFC822'].force_encoding('utf-8') if RUBY_VERSION > '1.9'
23
+ message
24
+ rescue Net::IMAP::NoResponseError => e
25
+ warn "Folder '#{@folder}' does not exist"
26
+ nil
31
27
  end
32
28
  end
33
29
  end
34
-
@@ -1,84 +1,138 @@
1
1
  # encoding: utf-8
2
2
 
3
- module Imap
4
- module Backup
5
- module Configuration
6
- class Account
7
- def initialize(store, account)
8
- @store, @account = store, account
3
+ module Imap::Backup::Configuration
4
+ class Account < Struct.new(:store, :account, :highline)
5
+ def run
6
+ catch :done do
7
+ loop do
8
+ system('clear')
9
+ create_menu
9
10
  end
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def create_menu
17
+ highline.choose do |menu|
18
+ header menu
19
+ modify_email menu
20
+ modify_password menu
21
+ modify_server menu
22
+ modify_backup_path menu
23
+ choose_folders menu
24
+ test_connection menu
25
+ delete_account menu
26
+ menu.choice('return to main menu') { throw :done }
27
+ menu.hidden('quit') { throw :done }
28
+ end
29
+ end
10
30
 
11
- def run
12
- loop do
13
- system('clear')
14
- Setup.highline.choose do |menu|
15
- password =
16
- if @account[:password] == ''
17
- '(unset)'
18
- else
19
- @account[:password].gsub(/./, 'x')
20
- end
21
- menu.header = <<EOT
31
+ def header(menu)
32
+ menu.header = <<-EOT
22
33
  Account:
23
- email: #{@account[:username]}
24
- path: #{@account[:local_path]}
25
- folders: #{@account[:folders].map { |f| f[:name] }.join(', ')}
26
- password: #{password}
27
- EOT
28
- menu.choice('modify email') do
29
- username = Asker.email(username)
30
- others = @store.data[:accounts].select { |a| a != @account}.map { |a| a[:username] }
31
- if others.include?(username)
32
- puts 'There is already an account set up with that email address'
33
- else
34
- @account[:username] = username
35
- end
36
- end
37
- menu.choice('modify password') do
38
- password = Asker.password
39
- if ! password.nil?
40
- @account[:password] = password
41
- end
42
- end
43
- menu.choice('modify backup path') do
44
- validator = lambda do |p|
45
- same = @store.data[:accounts].find do |a|
46
- a[:username] != @account[:username] && a[:local_path] == p
47
- end
48
- if same
49
- puts "The path '#{p}' is used to backup the account '#{same[:username]}'"
50
- false
51
- else
52
- true
53
- end
54
- end
55
- @account[:local_path] = Asker.backup_path(@account[:local_path], validator)
56
- end
57
- menu.choice('choose backup folders') do
58
- FolderChooser.new(@account).run
59
- end
60
- menu.choice 'test authentication' do
61
- result = ConnectionTester.test(@account)
62
- puts result
63
- Setup.highline.ask 'Press a key '
64
- end
65
- menu.choice(:delete) do
66
- if Setup.highline.agree("Are you sure? (y/n) ")
67
- @store.data[:accounts].reject! { |a| a[:username] == @account[:username] }
68
- return
69
- end
70
- end
71
- menu.choice('return to main menu') do
72
- return
73
- end
74
- menu.hidden('quit') do
75
- return
76
- end
77
- end
34
+ email: #{account[:username]}
35
+ server: #{account[:server]}
36
+ path: #{account[:local_path]}
37
+ folders: #{folders.map { |f| f[:name] }.join(', ')}
38
+ password: #{masked_password}
39
+ EOT
40
+ end
41
+
42
+ def modify_email(menu)
43
+ menu.choice('modify email') do
44
+ username = Asker.email(username)
45
+ puts "username: #{username}"
46
+ others = store.data[:accounts].select { |a| a != account}.map { |a| a[:username] }
47
+ puts "others: #{others.inspect}"
48
+ if others.include?(username)
49
+ puts 'There is already an account set up with that email address'
50
+ else
51
+ account[:username] = username
52
+ if account[:server].nil? or account[:server] == ''
53
+ account[:server] = default_server(username)
78
54
  end
79
55
  end
80
56
  end
81
57
  end
58
+
59
+ def modify_password(menu)
60
+ menu.choice('modify password') do
61
+ password = Asker.password
62
+ if ! password.nil?
63
+ account[:password] = password
64
+ end
65
+ end
66
+ end
67
+
68
+ def modify_server(menu)
69
+ menu.choice('modify server') do
70
+ server = highline.ask('server: ')
71
+ if ! server.nil?
72
+ account[:server] = server
73
+ end
74
+ end
75
+ end
76
+
77
+ def modify_backup_path(menu)
78
+ menu.choice('modify backup path') do
79
+ validator = lambda do |p|
80
+ same = store.data[:accounts].find do |a|
81
+ a[:username] != account[:username] && a[:local_path] == p
82
+ end
83
+ if same
84
+ puts "The path '#{p}' is used to backup the account '#{same[:username]}'"
85
+ false
86
+ else
87
+ true
88
+ end
89
+ end
90
+ account[:local_path] = Asker.backup_path(account[:local_path], validator)
91
+ end
92
+ end
93
+
94
+ def choose_folders(menu)
95
+ menu.choice('choose backup folders') do
96
+ FolderChooser.new(account).run
97
+ end
98
+ end
99
+
100
+ def test_connection(menu)
101
+ menu.choice('test connection') do
102
+ result = ConnectionTester.test(account)
103
+ puts result
104
+ highline.ask 'Press a key '
105
+ end
106
+ end
107
+
108
+ def delete_account(menu)
109
+ menu.choice('delete') do
110
+ if highline.agree("Are you sure? (y/n) ")
111
+ store.data[:accounts].reject! { |a| a[:username] == account[:username] }
112
+ throw :done
113
+ end
114
+ end
115
+ end
116
+
117
+ def folders
118
+ account[:folders] || []
119
+ end
120
+
121
+ def masked_password
122
+ if account[:password] == '' or account[:password].nil?
123
+ '(unset)'
124
+ else
125
+ account[:password].gsub(/./, 'x')
126
+ end
127
+ end
128
+
129
+ def default_server(username)
130
+ case username
131
+ when /@gmail\.com/
132
+ 'imap.gmail.com'
133
+ when /@fastmail\.fm/
134
+ 'mail.messagingengine.com'
135
+ end
136
+ end
82
137
  end
83
138
  end
84
-