imap-backup 1.0.5 → 1.0.6

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