imap-backup 2.0.0 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec-all +2 -0
  3. data/.rubocop.yml +10 -1
  4. data/.travis.yml +1 -0
  5. data/README.md +1 -1
  6. data/Rakefile +0 -1
  7. data/bin/imap-backup +3 -9
  8. data/imap-backup.gemspec +5 -5
  9. data/lib/email/mboxrd/message.rb +2 -2
  10. data/lib/imap/backup/account/connection.rb +11 -3
  11. data/lib/imap/backup/account/folder.rb +11 -6
  12. data/lib/imap/backup/configuration/account.rb +7 -7
  13. data/lib/imap/backup/configuration/asker.rb +2 -1
  14. data/lib/imap/backup/configuration/connection_tester.rb +1 -1
  15. data/lib/imap/backup/configuration/folder_chooser.rb +32 -5
  16. data/lib/imap/backup/configuration/list.rb +2 -0
  17. data/lib/imap/backup/configuration/setup.rb +2 -1
  18. data/lib/imap/backup/configuration/store.rb +3 -6
  19. data/lib/imap/backup/downloader.rb +8 -7
  20. data/lib/imap/backup/serializer/mbox.rb +2 -1
  21. data/lib/imap/backup/serializer/mbox_store.rb +14 -6
  22. data/lib/imap/backup/uploader.rb +1 -0
  23. data/lib/imap/backup/utils.rb +11 -9
  24. data/lib/imap/backup/version.rb +1 -1
  25. data/spec/features/backup_spec.rb +6 -5
  26. data/spec/features/support/backup_directory.rb +5 -5
  27. data/spec/features/support/email_server.rb +11 -8
  28. data/spec/features/support/shared/connection_context.rb +2 -2
  29. data/spec/support/fixtures.rb +1 -1
  30. data/spec/support/higline_test_helpers.rb +1 -1
  31. data/spec/unit/email/mboxrd/message_spec.rb +51 -42
  32. data/spec/unit/email/provider_spec.rb +0 -2
  33. data/spec/unit/imap/backup/account/connection_spec.rb +18 -11
  34. data/spec/unit/imap/backup/account/folder_spec.rb +26 -12
  35. data/spec/unit/imap/backup/configuration/account_spec.rb +22 -19
  36. data/spec/unit/imap/backup/configuration/asker_spec.rb +30 -31
  37. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +16 -13
  38. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +45 -18
  39. data/spec/unit/imap/backup/configuration/list_spec.rb +8 -13
  40. data/spec/unit/imap/backup/configuration/setup_spec.rb +36 -30
  41. data/spec/unit/imap/backup/configuration/store_spec.rb +7 -4
  42. data/spec/unit/imap/backup/downloader_spec.rb +11 -7
  43. data/spec/unit/imap/backup/serializer/mbox_spec.rb +2 -5
  44. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +4 -4
  45. data/spec/unit/imap/backup/uploader_spec.rb +0 -2
  46. data/spec/unit/imap/backup/utils_spec.rb +1 -3
  47. metadata +6 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 334bd1417efce8e604aedbe1878d09bf2c79fce40c8efd3d2d70f2fa66a261bc
4
- data.tar.gz: e217a9925ea2a8afeb7d3d26d72e644bbb58a0012c06b6f2b01c4595d24c2ae6
3
+ metadata.gz: f5903e540e8d72a63c70ee1f8f5799710bdef2ffaac58bc6bac617942da6e6da
4
+ data.tar.gz: d4ad4a77a5a7d86146768a889f697f281c344ca5e4db6f29fc792a0894b2fedd
5
5
  SHA512:
6
- metadata.gz: b374fe448bf88411f2681c7c07159f34c06ed8c537f1dc8c18b41c60ca7c690693d0b1c672e7a31e85d38c0b50afbc9e1004344359273ef17f0ca148909a095f
7
- data.tar.gz: 9be2593d7b380bd0c3d1af1a85a79d9a3662de74d078d0564fff08884c336325f1bb33c08f10582e48e0728178d80cc6a8633a26782641ee66861ca313eec9cc
6
+ metadata.gz: fff913ad0270445d6783430d39672e9abe34a67d20546ad03db5ec0c8c6179c5f98524a83546d6e1790b1550284902bf0a161c998a82301ddbbbfb33d845a708
7
+ data.tar.gz: d0c64c701e183d1bb01b28af7577c237a5ec0d26607ed1856e1c2b6748efe48479c0f71b502db83a8e68c22faab220372541b16edf5d3e5b7ad0af86e933460e
data/.rspec-all CHANGED
@@ -1,2 +1,4 @@
1
1
  --color
2
+ --format documentation
3
+ --order random
2
4
  --require spec_helper
@@ -1,8 +1,17 @@
1
1
  inherit_from: https://gitlab.com/snippets/1744945/raw
2
2
 
3
3
  AllCops:
4
- TargetRubyVersion: 2.1
4
+ TargetRubyVersion: 2.3
5
5
  Exclude:
6
6
  - "bin/stubs/*"
7
7
  DisplayCopNames:
8
8
  Enabled: true
9
+
10
+ RSpec/ContextWording:
11
+ Enabled: false
12
+ RSpec/NestedGroups:
13
+ Max: 4
14
+ RSpec/ReturnFromStub:
15
+ Enabled: false
16
+ Style/EmptyCaseCondition:
17
+ Enabled: false
@@ -3,6 +3,7 @@ rvm:
3
3
  - 2.3
4
4
  - 2.4
5
5
  - 2.5
6
+ - 2.6
6
7
  branches:
7
8
  only:
8
9
  - master
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://secure.travis-ci.org/joeyates/imap-backup.png)][Continuous Integration]
1
+ [![Build Status](https://secure.travis-ci.org/joeyates/imap-backup.svg)][Continuous Integration]
2
2
  [![Source Analysis](https://codeclimate.com/github/joeyates/imap-backup/badges/gpa.svg)](https://codeclimate.com/github/joeyates/imap-backup)
3
3
  [![Test Coverage](https://codeclimate.com/github/joeyates/imap-backup/badges/coverage.svg)](https://codeclimate.com/github/joeyates/imap-backup/coverage)
4
4
 
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- #!/usr/bin/env rake
2
1
  require "bundler/gem_tasks"
3
2
  require "rspec/core/rake_task"
4
3
 
@@ -46,9 +46,7 @@ parser = OptionParser.new do |opts|
46
46
  end
47
47
  parser.parse!
48
48
 
49
- if ARGV.size > 0
50
- options[:command] = ARGV.shift
51
- end
49
+ options[:command] = ARGV.shift if !ARGV.empty?
52
50
 
53
51
  if KNOWN_COMMANDS.find { |c| c[:name] == options[:command] }.nil?
54
52
  raise "Unknown command '#{options[:command]}'"
@@ -72,9 +70,7 @@ case options[:command]
72
70
  when "setup"
73
71
  Imap::Backup::Configuration::Setup.new.run
74
72
  when "backup"
75
- configuration.each_connection do |connection|
76
- connection.run_backup
77
- end
73
+ configuration.each_connection(&:run_backup)
78
74
  when "folders"
79
75
  configuration.each_connection do |connection|
80
76
  puts connection.username
@@ -86,9 +82,7 @@ when "folders"
86
82
  folders.each { |f| puts "\t" + f.name }
87
83
  end
88
84
  when "restore"
89
- configuration.each_connection do |connection|
90
- connection.restore
91
- end
85
+ configuration.each_connection(&:restore)
92
86
  when "status"
93
87
  configuration.each_connection do |connection|
94
88
  puts connection.username
@@ -1,4 +1,4 @@
1
- $LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
1
+ $LOAD_PATH.unshift(File.expand_path("lib", __dir__))
2
2
  require "imap/backup/version"
3
3
 
4
4
  Gem::Specification.new do |gem|
@@ -9,12 +9,12 @@ Gem::Specification.new do |gem|
9
9
  gem.email = ["joe.g.yates@gmail.com"]
10
10
  gem.homepage = "https://github.com/joeyates/imap-backup"
11
11
 
12
- gem.files = `git ls-files`.split($\)
12
+ gem.files = `git ls-files`.split($OUTPUT_RECORD_SEPARATOR)
13
13
  gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
14
14
  gem.test_files = gem.files.grep(%r{^spec/})
15
15
  gem.require_paths = ["lib"]
16
- gem.required_ruby_version = [">= 2.2.0"]
17
- gem.version = Imap::Backup::VERSION
16
+ gem.required_ruby_version = [">= 2.3.0"]
17
+ gem.version = Imap::Backup::VERSION
18
18
 
19
19
  gem.post_install_message = <<-MESSAGE.gsub(/^\s{4}/m, "")
20
20
  Note that, when upgrading #{gem.name} from version 1.x to 2.x,
@@ -24,9 +24,9 @@ Gem::Specification.new do |gem|
24
24
  **deleted** and a full new backup created.
25
25
  MESSAGE
26
26
 
27
- gem.add_runtime_dependency "rake"
28
27
  gem.add_runtime_dependency "highline"
29
28
  gem.add_runtime_dependency "mail"
29
+ gem.add_runtime_dependency "rake"
30
30
 
31
31
  gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
32
32
  gem.add_development_dependency "pry-byebug"
@@ -27,7 +27,7 @@ module Email::Mboxrd
27
27
 
28
28
  def date
29
29
  parsed.date
30
- rescue
30
+ rescue StandardError
31
31
  nil
32
32
  end
33
33
 
@@ -52,7 +52,7 @@ module Email::Mboxrd
52
52
  return parsed.envelope_from if parsed.envelope_from
53
53
  return parsed.return_path if parsed.return_path
54
54
 
55
- return ""
55
+ ""
56
56
  end
57
57
 
58
58
  def from
@@ -10,7 +10,8 @@ module Imap::Backup
10
10
  attr_reader :username
11
11
 
12
12
  def initialize(options)
13
- @username, @password = options[:username], options[:password]
13
+ @username = options[:username]
14
+ @password = options[:password]
14
15
  @local_path = options[:local_path]
15
16
  @backup_folders = options[:folders]
16
17
  @server = options[:server]
@@ -46,6 +47,7 @@ module Imap::Backup
46
47
  # start the connection so we get logging messages in the right order
47
48
  imap
48
49
  each_folder do |folder, serializer|
50
+ next if !folder.exist?
49
51
  Imap::Backup.logger.debug "[#{folder.name}] running backup"
50
52
  serializer.set_uid_validity(folder.uid_validity)
51
53
  Downloader.new(folder, serializer).run
@@ -59,7 +61,9 @@ module Imap::Backup
59
61
  new_name = serializer.set_uid_validity(folder.uid_validity)
60
62
  old_name = serializer.folder
61
63
  if new_name
62
- Imap::Backup.logger.debug "Backup '#{old_name}' renamed and restored to '#{new_name}'"
64
+ Imap::Backup.logger.debug(
65
+ "Backup '#{old_name}' renamed and restored to '#{new_name}'"
66
+ )
63
67
  new_serializer = Serializer::Mbox.new(local_path, new_name)
64
68
  new_folder = Account::Folder.new(self, new_name)
65
69
  new_folder.create
@@ -87,6 +91,7 @@ module Imap::Backup
87
91
 
88
92
  def imap
89
93
  return @imap unless @imap.nil?
94
+
90
95
  options = provider_options
91
96
  Imap::Backup.logger.debug(
92
97
  "Creating IMAP instance: #{server}, options: #{options.inspect}"
@@ -121,7 +126,8 @@ module Imap::Backup
121
126
  end
122
127
 
123
128
  def backup_folders
124
- return @backup_folders if @backup_folders && (@backup_folders.size > 0)
129
+ return @backup_folders if @backup_folders && !@backup_folders.empty?
130
+
125
131
  (folders || []).map { |f| {name: f.name} }
126
132
  end
127
133
 
@@ -143,6 +149,7 @@ module Imap::Backup
143
149
  def server
144
150
  return @server if @server
145
151
  return nil if provider.nil?
152
+
146
153
  @server = provider.host
147
154
  end
148
155
 
@@ -156,6 +163,7 @@ module Imap::Backup
156
163
  # in the reference.
157
164
  def provider_root
158
165
  return @provider_root if @provider_root
166
+
159
167
  root_info = imap.list("", "")[0]
160
168
  @provider_root = root_info.name
161
169
  end
@@ -3,10 +3,12 @@ require "forwardable"
3
3
  module Imap::Backup
4
4
  module Account; end
5
5
 
6
+ class FolderNotFound < StandardError; end
7
+
6
8
  class Account::Folder
7
9
  extend Forwardable
8
10
 
9
- REQUESTED_ATTRIBUTES = ["RFC822", "FLAGS", "INTERNALDATE"].freeze
11
+ REQUESTED_ATTRIBUTES = %w[RFC822 FLAGS INTERNALDATE].freeze
10
12
 
11
13
  attr_reader :connection
12
14
  attr_reader :name
@@ -27,12 +29,13 @@ module Imap::Backup
27
29
  def exist?
28
30
  examine
29
31
  true
30
- rescue Net::IMAP::NoResponseError => e
32
+ rescue FolderNotFound
31
33
  false
32
34
  end
33
35
 
34
36
  def create
35
37
  return if exist?
38
+
36
39
  imap.create(name)
37
40
  end
38
41
 
@@ -47,8 +50,7 @@ module Imap::Backup
47
50
  def uids
48
51
  examine
49
52
  imap.uid_search(["ALL"]).sort
50
- rescue Net::IMAP::NoResponseError
51
- Imap::Backup.logger.warn "Folder '#{name}' does not exist"
53
+ rescue FolderNotFound
52
54
  []
53
55
  end
54
56
 
@@ -56,12 +58,12 @@ module Imap::Backup
56
58
  examine
57
59
  fetch_data_items = imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)
58
60
  return nil if fetch_data_items.nil?
61
+
59
62
  fetch_data_item = fetch_data_items[0]
60
63
  attributes = fetch_data_item.attr
61
64
  attributes["RFC822"].force_encoding("utf-8")
62
65
  attributes
63
- rescue Net::IMAP::NoResponseError
64
- Imap::Backup.logger.warn "Folder '#{name}' does not exist"
66
+ rescue FolderNotFound
65
67
  nil
66
68
  end
67
69
 
@@ -76,6 +78,9 @@ module Imap::Backup
76
78
 
77
79
  def examine
78
80
  imap.examine(name)
81
+ rescue Net::IMAP::NoResponseError
82
+ Imap::Backup.logger.warn "Folder '#{name}' does not exist"
83
+ raise FolderNotFound, "Folder '#{name}' does not exist"
79
84
  end
80
85
 
81
86
  def extract_uid(response)
@@ -9,7 +9,7 @@ module Imap::Backup
9
9
  def run
10
10
  catch :done do
11
11
  loop do
12
- system("clear")
12
+ Kernel.system("clear")
13
13
  create_menu
14
14
  end
15
15
  end
@@ -46,12 +46,12 @@ module Imap::Backup
46
46
  def modify_email(menu)
47
47
  menu.choice("modify email") do
48
48
  username = Configuration::Asker.email(username)
49
- puts "username: #{username}"
49
+ Kernel.puts "username: #{username}"
50
50
  other_accounts = store.accounts.reject { |a| a == account }
51
51
  others = other_accounts.map { |a| a[:username] }
52
- puts "others: #{others.inspect}"
52
+ Kernel.puts "others: #{others.inspect}"
53
53
  if others.include?(username)
54
- puts "There is already an account set up with that email address"
54
+ Kernel.puts "There is already an account set up with that email address"
55
55
  else
56
56
  account[:username] = username
57
57
  if account[:server].nil? || (account[:server] == "")
@@ -89,7 +89,7 @@ module Imap::Backup
89
89
  a[:username] != account[:username] && a[:local_path] == p
90
90
  end
91
91
  if same
92
- puts "The path '#{p}' is used to backup " \
92
+ Kernel.puts "The path '#{p}' is used to backup " \
93
93
  "the account '#{same[:username]}'"
94
94
  false
95
95
  else
@@ -112,7 +112,7 @@ module Imap::Backup
112
112
  def test_connection(menu)
113
113
  menu.choice("test connection") do
114
114
  result = Configuration::ConnectionTester.test(account)
115
- puts result
115
+ Kernel.puts result
116
116
  highline.ask "Press a key "
117
117
  end
118
118
  end
@@ -141,7 +141,7 @@ module Imap::Backup
141
141
  def default_server(username)
142
142
  provider = Email::Provider.for_address(username)
143
143
  if provider.provider == :default
144
- puts "Can't decide provider for email address '#{username}'"
144
+ Kernel.puts "Can't decide provider for email address '#{username}'"
145
145
  return nil
146
146
  end
147
147
  provider.host
@@ -2,7 +2,7 @@ module Imap::Backup
2
2
  module Configuration; end
3
3
 
4
4
  class Configuration::Asker < Struct.new(:highline)
5
- EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i
5
+ EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i.freeze
6
6
 
7
7
  def initialize(highline)
8
8
  super
@@ -24,6 +24,7 @@ module Imap::Backup
24
24
  return nil if !highline.agree(
25
25
  "the password and confirmation did not match.\nContinue? (y/n) "
26
26
  )
27
+
27
28
  return self.password
28
29
  end
29
30
  password
@@ -7,7 +7,7 @@ module Imap::Backup
7
7
  "Connection successful"
8
8
  rescue Net::IMAP::NoResponseError
9
9
  "No response"
10
- rescue Exception => e
10
+ rescue StandardError => e
11
11
  "Unexpected error: #{e}"
12
12
  end
13
13
  end
@@ -21,9 +21,11 @@ module Imap::Backup
21
21
  return
22
22
  end
23
23
 
24
+ remove_missing
25
+
24
26
  catch :done do
25
27
  loop do
26
- system("clear")
28
+ Kernel.system("clear")
27
29
  show_menu
28
30
  end
29
31
  end
@@ -44,21 +46,46 @@ module Imap::Backup
44
46
  def add_folders(menu)
45
47
  folders.each do |folder|
46
48
  name = folder.name
47
- mark = is_selected?(name) ? "+" : "-"
49
+ mark = selected?(name) ? "+" : "-"
48
50
  menu.choice("#{mark} #{name}") do
49
51
  toggle_selection name
50
52
  end
51
53
  end
52
54
  end
53
55
 
54
- def is_selected?(folder_name)
56
+ def selected?(folder_name)
55
57
  backup_folders = account[:folders]
56
58
  return false if backup_folders.nil?
59
+
57
60
  backup_folders.find { |f| f[:name] == folder_name }
58
61
  end
59
62
 
63
+ def remove_missing
64
+ removed = []
65
+ backup_folders = []
66
+ account[:folders].each do |f|
67
+ found = folders.find { |folder| folder.name == f[:name] }
68
+ if found
69
+ backup_folders << f
70
+ else
71
+ removed << f[:name]
72
+ end
73
+ end
74
+
75
+ return if removed.empty?
76
+
77
+ account[:folders] = backup_folders
78
+ account[:modified] = true
79
+
80
+ Kernel.puts <<~MESSAGE
81
+ The following folders have been removed: #{removed.join(', ')}
82
+ MESSAGE
83
+
84
+ highline.ask "Press a key "
85
+ end
86
+
60
87
  def toggle_selection(folder_name)
61
- if is_selected?(folder_name)
88
+ if selected?(folder_name)
62
89
  changed = account[:folders].reject! { |f| f[:name] == folder_name }
63
90
  account[:modified] = true if changed
64
91
  else
@@ -70,7 +97,7 @@ module Imap::Backup
70
97
 
71
98
  def connection
72
99
  @connection ||= Account::Connection.new(account)
73
- rescue
100
+ rescue StandardError
74
101
  nil
75
102
  end
76
103
 
@@ -10,6 +10,7 @@ module Imap::Backup
10
10
 
11
11
  def setup_logging
12
12
  return if !config_exists?
13
+
13
14
  Imap::Backup.setup_logging config
14
15
  end
15
16
 
@@ -25,6 +26,7 @@ module Imap::Backup
25
26
 
26
27
  def config
27
28
  return @config if @config
29
+
28
30
  if !config_exists?
29
31
  path = Configuration::Store.default_pathname
30
32
  raise ConfigurationNotFound, "Configuration file '#{path}' not found"
@@ -13,7 +13,7 @@ module Imap::Backup
13
13
  Imap::Backup.setup_logging config
14
14
  catch :done do
15
15
  loop do
16
- system("clear")
16
+ Kernel.system("clear")
17
17
  show_menu
18
18
  end
19
19
  end
@@ -40,6 +40,7 @@ module Imap::Backup
40
40
  def account_items(menu)
41
41
  config.accounts.each do |account|
42
42
  next if account[:delete]
43
+
43
44
  item = account[:username].clone
44
45
  item << " *" if account[:modified]
45
46
  menu.choice(item) do