imap-backup 2.0.0 → 2.1.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.
- checksums.yaml +4 -4
- data/.rspec-all +2 -0
- data/.rubocop.yml +10 -1
- data/.travis.yml +1 -0
- data/README.md +1 -1
- data/Rakefile +0 -1
- data/bin/imap-backup +3 -9
- data/imap-backup.gemspec +5 -5
- data/lib/email/mboxrd/message.rb +2 -2
- data/lib/imap/backup/account/connection.rb +11 -3
- data/lib/imap/backup/account/folder.rb +11 -6
- data/lib/imap/backup/configuration/account.rb +7 -7
- data/lib/imap/backup/configuration/asker.rb +2 -1
- data/lib/imap/backup/configuration/connection_tester.rb +1 -1
- data/lib/imap/backup/configuration/folder_chooser.rb +32 -5
- data/lib/imap/backup/configuration/list.rb +2 -0
- data/lib/imap/backup/configuration/setup.rb +2 -1
- data/lib/imap/backup/configuration/store.rb +3 -6
- data/lib/imap/backup/downloader.rb +8 -7
- data/lib/imap/backup/serializer/mbox.rb +2 -1
- data/lib/imap/backup/serializer/mbox_store.rb +14 -6
- data/lib/imap/backup/uploader.rb +1 -0
- data/lib/imap/backup/utils.rb +11 -9
- data/lib/imap/backup/version.rb +1 -1
- data/spec/features/backup_spec.rb +6 -5
- data/spec/features/support/backup_directory.rb +5 -5
- data/spec/features/support/email_server.rb +11 -8
- data/spec/features/support/shared/connection_context.rb +2 -2
- data/spec/support/fixtures.rb +1 -1
- data/spec/support/higline_test_helpers.rb +1 -1
- data/spec/unit/email/mboxrd/message_spec.rb +51 -42
- data/spec/unit/email/provider_spec.rb +0 -2
- data/spec/unit/imap/backup/account/connection_spec.rb +18 -11
- data/spec/unit/imap/backup/account/folder_spec.rb +26 -12
- data/spec/unit/imap/backup/configuration/account_spec.rb +22 -19
- data/spec/unit/imap/backup/configuration/asker_spec.rb +30 -31
- data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +16 -13
- data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +45 -18
- data/spec/unit/imap/backup/configuration/list_spec.rb +8 -13
- data/spec/unit/imap/backup/configuration/setup_spec.rb +36 -30
- data/spec/unit/imap/backup/configuration/store_spec.rb +7 -4
- data/spec/unit/imap/backup/downloader_spec.rb +11 -7
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +2 -5
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +4 -4
- data/spec/unit/imap/backup/uploader_spec.rb +0 -2
- data/spec/unit/imap/backup/utils_spec.rb +1 -3
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5903e540e8d72a63c70ee1f8f5799710bdef2ffaac58bc6bac617942da6e6da
|
4
|
+
data.tar.gz: d4ad4a77a5a7d86146768a889f697f281c344ca5e4db6f29fc792a0894b2fedd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fff913ad0270445d6783430d39672e9abe34a67d20546ad03db5ec0c8c6179c5f98524a83546d6e1790b1550284902bf0a161c998a82301ddbbbfb33d845a708
|
7
|
+
data.tar.gz: d0c64c701e183d1bb01b28af7577c237a5ec0d26607ed1856e1c2b6748efe48479c0f71b502db83a8e68c22faab220372541b16edf5d3e5b7ad0af86e933460e
|
data/.rspec-all
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
inherit_from: https://gitlab.com/snippets/1744945/raw
|
2
2
|
|
3
3
|
AllCops:
|
4
|
-
TargetRubyVersion: 2.
|
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
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
[][Continuous Integration]
|
2
2
|
[](https://codeclimate.com/github/joeyates/imap-backup)
|
3
3
|
[](https://codeclimate.com/github/joeyates/imap-backup/coverage)
|
4
4
|
|
data/Rakefile
CHANGED
data/bin/imap-backup
CHANGED
@@ -46,9 +46,7 @@ parser = OptionParser.new do |opts|
|
|
46
46
|
end
|
47
47
|
parser.parse!
|
48
48
|
|
49
|
-
|
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
|
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
|
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
|
data/imap-backup.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.expand_path("
|
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.
|
17
|
-
gem.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"
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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 &&
|
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 = [
|
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
|
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
|
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
|
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
|
@@ -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 =
|
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
|
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
|
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
|