imap-backup 5.0.0 → 5.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 16911b1cea1983b844c3bbf7f74c03ff7436990fb5aa4d5b49fa18f03ae2e229
4
- data.tar.gz: 265f63995e70ba3f68b98ec72d3a12b3e95100f7e6b320026b18bcc156888a92
3
+ metadata.gz: 59babb556b2f47a07590debff15dcd5456f1e61454c3aba2e504610ce4322647
4
+ data.tar.gz: 89909077444746e44fe3955070df6b590942da0d44da58c9b08b282e694000d5
5
5
  SHA512:
6
- metadata.gz: 4a541a7830ddab458f2366bdad4924f3e6cbc3ba858651839e6dcd1a530acbbeb01e2c59a44331af73a2d77f606f2d6b39eacd2b5a705d78ec20e93d2c2f2527
7
- data.tar.gz: 7b60170e3d29e9882f3bcbf13b04daa162434944d59311a5a03a4b1c1022a7a9501b292c3eb929d6e07039c47cc433316a338e062ced4e8a6b7ea424e6c0678b
6
+ metadata.gz: ea00bb55bb853cef73ec12171a4bdde9b0b5ef88a914065bbe307f48712db3329e8c8f1712cfa48b4a49102d20fb38cc465dc463c2d3ea9e0caba000a184766b
7
+ data.tar.gz: 24d368c209c11db4ae7f05a93b9cf4e0435b65eef0cffd7e58f8c669e634ca735aa3187b6a6506b7b10ba3bec961d3aa9d7387df40d4880f1bed73df7f24bbb9
data/imap-backup.gemspec CHANGED
@@ -27,6 +27,7 @@ Gem::Specification.new do |gem|
27
27
  gem.add_runtime_dependency "os"
28
28
  gem.add_runtime_dependency "rake"
29
29
  gem.add_runtime_dependency "thor", "~> 1.1"
30
+ gem.add_runtime_dependency "thunderbird", ">= 0.0.0"
30
31
 
31
32
  gem.add_development_dependency "aruba", ">= 0.0.0"
32
33
  gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
@@ -3,16 +3,32 @@ module Imap::Backup
3
3
  include Thor::Actions
4
4
  include CLI::Helpers
5
5
 
6
+ attr_reader :email
6
7
  attr_reader :account_names
7
8
 
8
- def initialize(options)
9
- super([])
10
- @account_names = (options[:accounts] || "").split(",")
9
+ def initialize(email = nil, options)
10
+ @email = email
11
+ @account_names =
12
+ if options.key?(:accounts)
13
+ options[:accounts].split(",")
14
+ end
11
15
  end
12
16
 
13
17
  no_commands do
14
18
  def run
15
- each_connection(account_names) { |connection| connection.restore }
19
+ case
20
+ when email && !account_names
21
+ connection = connection(email)
22
+ connection.restore
23
+ when !email && !account_names
24
+ Logger.logger.info "Calling restore without an EMAIL parameter is deprecated"
25
+ each_connection([]) { |connection| connection.restore }
26
+ when email && account_names.any?
27
+ raise "Pass either an email or the --accounts option, not both"
28
+ when account_names.any?
29
+ Logger.logger.info "Calling restore with the --account option is deprected, please pass a single EMAIL argument"
30
+ each_connection(account_names) { |connection| connection.restore }
31
+ end
16
32
  end
17
33
  end
18
34
  end
@@ -106,15 +106,14 @@ module Imap::Backup
106
106
  desc "remote SUBCOMMAND [OPTIONS]", "View info about online accounts"
107
107
  subcommand "remote", Remote
108
108
 
109
- desc "restore [OPTIONS]", "This command is deprecated, use `imap-backup restore ACCOUNT`"
109
+ desc "restore EMAIL", "Restores a single account"
110
110
  long_desc <<~DESC
111
- By default, restores all local emails to their respective servers.
112
- This command is deprecated.
113
- Instead, use `imap-backup restore ACCOUNT` to restore a single account.
111
+ Restores all backed-up emails for the supplied account to
112
+ their original server.
114
113
  DESC
115
114
  accounts_option
116
- def restore
117
- Restore.new(symbolized(options)).run
115
+ def restore(email = nil)
116
+ Restore.new(email, symbolized(options)).run
118
117
  end
119
118
 
120
119
  desc "setup", "Configure imap-backup"
@@ -2,7 +2,7 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 5
5
- MINOR = 0
5
+ MINOR = 1
6
6
  REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
@@ -1,28 +1,28 @@
1
1
  require "features/helper"
2
2
 
3
- RSpec.describe "backup", type: :feature, docker: true do
3
+ RSpec.describe "backup", type: :aruba, docker: true do
4
4
  include_context "imap-backup connection"
5
5
  include_context "message-fixtures"
6
6
 
7
+ let(:local_backup_path) { File.expand_path("~/backup") }
8
+ let(:backup_folders) { [{name: folder}] }
9
+ let(:folder) { "my-stuff" }
7
10
  let(:messages_as_mbox) do
8
11
  message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
9
12
  end
10
- let(:folder) { "my-stuff" }
11
- let(:email1) { send_email folder, msg1 }
12
- let(:email2) { send_email folder, msg2 }
13
+
13
14
  let!(:pre) {}
14
15
  let!(:setup) do
15
16
  server_create_folder folder
16
- email1
17
- email2
18
- connection.run_backup
17
+ send_email folder, msg1
18
+ send_email folder, msg2
19
+ create_config(accounts: [account.to_h])
20
+
21
+ run_command_and_stop("imap-backup backup")
19
22
  end
20
23
 
21
24
  after do
22
- FileUtils.rm_rf local_backup_path
23
- delete_emails folder
24
25
  server_delete_folder folder
25
- connection.disconnect
26
26
  end
27
27
 
28
28
  it "downloads messages" do
@@ -51,11 +51,10 @@ RSpec.describe "backup", type: :feature, docker: true do
51
51
 
52
52
  context "when uid_validity does not match" do
53
53
  let(:new_name) { "NEWNAME" }
54
- let(:email3) { send_email folder, msg3 }
55
54
  let(:original_folder_uid_validity) { server_uid_validity(folder) }
56
55
  let!(:pre) do
57
56
  server_create_folder folder
58
- email3
57
+ send_email folder, msg3
59
58
  original_folder_uid_validity
60
59
  connection.run_backup
61
60
  connection.disconnect
@@ -78,6 +77,7 @@ RSpec.describe "backup", type: :feature, docker: true do
78
77
  context "when a renamed local backup exists" do
79
78
  let!(:pre) do
80
79
  super()
80
+ create_directory local_backup_path
81
81
  File.write(imap_path(renamed_folder), "existing imap")
82
82
  File.write(mbox_path(renamed_folder), "existing mbox")
83
83
  end
@@ -91,6 +91,7 @@ RSpec.describe "backup", type: :feature, docker: true do
91
91
 
92
92
  context "when an unversioned .imap file is found" do
93
93
  let!(:pre) do
94
+ create_directory local_backup_path
94
95
  File.open(imap_path(folder), "w") { |f| f.write "old format imap" }
95
96
  File.open(mbox_path(folder), "w") { |f| f.write "old format emails" }
96
97
  end
@@ -1,9 +1,11 @@
1
1
  require "features/helper"
2
2
 
3
- RSpec.describe "restore", type: :feature, docker: true do
3
+ RSpec.describe "restore", type: :aruba, docker: true do
4
4
  include_context "imap-backup connection"
5
5
  include_context "message-fixtures"
6
6
 
7
+ let(:local_backup_path) { File.expand_path("~/backup") }
8
+ let(:folder) { "my-stuff" }
7
9
  let(:messages_as_mbox) do
8
10
  message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
9
11
  end
@@ -12,19 +14,19 @@ RSpec.describe "restore", type: :feature, docker: true do
12
14
  end
13
15
  let(:message_uids) { [msg1[:uid], msg2[:uid]] }
14
16
  let(:existing_imap_content) { imap_data(uid_validity, message_uids).to_json }
15
- let(:folder) { "my-stuff" }
16
17
  let(:uid_validity) { 1234 }
17
18
 
18
19
  let!(:pre) {}
19
20
  let!(:setup) do
21
+ create_directory local_backup_path
20
22
  File.write(imap_path(folder), existing_imap_content)
21
23
  File.write(mbox_path(folder), messages_as_mbox)
22
- connection.restore
24
+ create_config(accounts: [account.to_h])
25
+
26
+ run_command_and_stop("imap-backup restore #{account.username}")
23
27
  end
24
28
  let(:cleanup) do
25
- FileUtils.rm_rf local_backup_path
26
29
  server_delete_folder folder
27
- connection.disconnect
28
30
  end
29
31
 
30
32
  after { cleanup }
@@ -1,7 +1,10 @@
1
1
  require "aruba/rspec"
2
2
 
3
+ require_relative "backup_directory"
4
+
3
5
  Aruba.configure do |config|
4
6
  config.home_directory = File.expand_path("./tmp/home")
7
+ config.allow_absolute_paths = true
5
8
  end
6
9
 
7
10
  module ConfigurationHelpers
@@ -58,6 +61,7 @@ end
58
61
  RSpec.configure do |config|
59
62
  config.include ConfigurationHelpers, type: :aruba
60
63
  config.include StoreHelpers, type: :aruba
64
+ config.include BackupDirectoryHelpers, type: :aruba
61
65
 
62
66
  config.before(:suite) do
63
67
  FileUtils.rm_rf "./tmp/home"
@@ -41,7 +41,3 @@ module BackupDirectoryHelpers
41
41
  JSON.parse(imap_content(name), symbolize_names: true)
42
42
  end
43
43
  end
44
-
45
- RSpec.configure do |config|
46
- config.include BackupDirectoryHelpers, type: :feature
47
- end
@@ -87,7 +87,6 @@ module EmailServerHelpers
87
87
  imap.delete folder
88
88
  imap.disconnect
89
89
  rescue StandardError => e
90
- puts e.to_s
91
90
  ensure
92
91
  @imap = nil
93
92
  end
data/spec/spec_helper.rb CHANGED
@@ -3,10 +3,9 @@ require "rspec"
3
3
 
4
4
  CodeClimate::TestReporter.start
5
5
 
6
- spec_path = File.dirname(__FILE__)
7
- $LOAD_PATH << File.expand_path("../lib", spec_path)
6
+ $LOAD_PATH << File.expand_path("../lib", __dir__)
8
7
 
9
- support_glob = File.join(spec_path, "support", "**", "*.rb")
8
+ support_glob = File.join(__dir__, "support", "**", "*.rb")
10
9
  Dir[support_glob].sort.each { |f| require f }
11
10
 
12
11
  require "simplecov"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-06 00:00:00.000000000 Z
11
+ date: 2022-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '1.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: thunderbird
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: 0.0.0
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: 0.0.0
83
97
  - !ruby/object:Gem::Dependency
84
98
  name: aruba
85
99
  requirement: !ruby/object:Gem::Requirement
@@ -238,13 +252,6 @@ files:
238
252
  - lib/imap/backup/utils.rb
239
253
  - lib/imap/backup/version.rb
240
254
  - lib/retry_on_error.rb
241
- - lib/thunderbird.rb
242
- - lib/thunderbird/install.rb
243
- - lib/thunderbird/local_folder.rb
244
- - lib/thunderbird/profile.rb
245
- - lib/thunderbird/profiles.rb
246
- - lib/thunderbird/subdirectory.rb
247
- - lib/thunderbird/subdirectory_placeholder.rb
248
255
  - spec/features/backup_spec.rb
249
256
  - spec/features/configuration/minimal_configuration.rb
250
257
  - spec/features/configuration/missing_configuration.rb
@@ -264,7 +271,6 @@ files:
264
271
  - spec/features/support/shared/connection_context.rb
265
272
  - spec/features/support/shared/message_fixtures.rb
266
273
  - spec/fixtures/connection.yml
267
- - spec/gather_rspec_coverage.rb
268
274
  - spec/spec_helper.rb
269
275
  - spec/support/fixtures.rb
270
276
  - spec/support/higline_test_helpers.rb
@@ -370,4 +376,3 @@ test_files:
370
376
  - spec/support/silence_logging.rb
371
377
  - spec/support/higline_test_helpers.rb
372
378
  - spec/fixtures/connection.yml
373
- - spec/gather_rspec_coverage.rb
@@ -1,16 +0,0 @@
1
- require "thunderbird/profiles"
2
-
3
- class Thunderbird::Install
4
- attr_reader :title
5
- attr_reader :entries
6
-
7
- # entries are lines from profile.ini
8
- def initialize(title, entries)
9
- @title = title
10
- @entries = entries
11
- end
12
-
13
- def default
14
- Thunderbird::Profiles.new.profile_for_path(entries[:Default])
15
- end
16
- end
@@ -1,65 +0,0 @@
1
- require "thunderbird/profile"
2
- require "thunderbird/subdirectory"
3
-
4
- # A local folder is a file containing emails
5
- class Thunderbird::LocalFolder
6
- attr_reader :path
7
- attr_reader :profile
8
-
9
- def initialize(profile, path)
10
- @profile = profile
11
- @path = path
12
- end
13
-
14
- def set_up
15
- return if path_elements.empty?
16
-
17
- return true if !in_subdirectory?
18
-
19
- subdirectory.set_up
20
- end
21
-
22
- def full_path
23
- if in_subdirectory?
24
- File.join(subdirectory.full_path, folder_name)
25
- else
26
- folder_name
27
- end
28
- end
29
-
30
- def exists?
31
- File.exist?(full_path)
32
- end
33
-
34
- def msf_path
35
- "#{path}.msf"
36
- end
37
-
38
- def msf_exists?
39
- File.exist?(msf_path)
40
- end
41
-
42
- private
43
-
44
- def in_subdirectory?
45
- path_elements.count > 1
46
- end
47
-
48
- def subdirectory
49
- return nil if !in_subdirectory?
50
-
51
- Thunderbird::Subdirectory.new(profile, subdirectory_path)
52
- end
53
-
54
- def path_elements
55
- path.split(File::SEPARATOR)
56
- end
57
-
58
- def subdirectory_path
59
- File.join(path_elements[0..-2])
60
- end
61
-
62
- def folder_name
63
- path_elements[-1]
64
- end
65
- end
@@ -1,30 +0,0 @@
1
- require "thunderbird"
2
-
3
- class Thunderbird::Profile
4
- attr_reader :title
5
- attr_reader :entries
6
-
7
- # entries are lines from profile.ini
8
- def initialize(title, entries)
9
- @title = title
10
- @entries = entries
11
- end
12
-
13
- def root
14
- if relative?
15
- File.join(Thunderbird.new.data_path, entries[:Path])
16
- else
17
- entries[:Path]
18
- end
19
- end
20
-
21
- def local_folders_path
22
- File.join(root, "Mail", "Local Folders")
23
- end
24
-
25
- private
26
-
27
- def relative?
28
- entries[:IsRelative] == "1"
29
- end
30
- end
@@ -1,71 +0,0 @@
1
- require "thunderbird"
2
- require "thunderbird/install"
3
- require "thunderbird/profile"
4
-
5
- # http://kb.mozillazine.org/Profiles.ini_file
6
- class Thunderbird::Profiles
7
- def profile_for_path(path)
8
- title, entries = blocks.find { |_name, entries| entries[:Path] == path }
9
-
10
- Thunderbird::Profile.new(title, entries) if title
11
- end
12
-
13
- def profile(name)
14
- title, entries = blocks.find { |_name, entries| entries[:Name] == name }
15
-
16
- Thunderbird::Profile.new(title, entries) if title
17
- end
18
-
19
- def installs
20
- @installs ||= begin
21
- pairs = blocks.filter { |name, _entries| name.start_with?("Install") }
22
- pairs.map { |title, entries| Thunderbird::Install.new(title, entries) }
23
- end
24
- end
25
-
26
- private
27
-
28
- # Parse profiles.ini.
29
- # Blocks start with a title, e.g. '[Abc]'
30
- # and are followed by a number of lines
31
- def blocks
32
- @blocks ||= begin
33
- blocks = {}
34
- File.open(profiles_ini_path, "rb") do |f|
35
- title = nil
36
- entries = nil
37
-
38
- loop do
39
- line = f.gets
40
- if !line
41
- blocks[title] = entries if title
42
- break
43
- end
44
-
45
- line.chomp!
46
-
47
- # Is this line the start of a new block
48
- match = line.match(/\A\[([A-Za-z0-9]+)\]\z/)
49
- if match
50
- # Store what we got before this title as a new block
51
- blocks[title] = entries if title
52
-
53
- # Start a new block
54
- title = match[1]
55
- entries = {}
56
- elsif line != ""
57
- # Collect entries until we get to the next title
58
- key, value = line.split("=")
59
- entries[key.to_sym] = value
60
- end
61
- end
62
- end
63
-
64
- blocks
65
- end
66
- end
67
-
68
- def profiles_ini_path
69
- File.join(Thunderbird.new.data_path, "profiles.ini")
70
- end
71
- end
@@ -1,93 +0,0 @@
1
- require "thunderbird/subdirectory_placeholder"
2
-
3
- class Thunderbird::Subdirectory
4
- # `path` is the UI path, it doesn't have the '.sbd' extensions
5
- # that are present in the real, file system path
6
- attr_reader :path
7
- attr_reader :profile
8
-
9
- def initialize(profile, path)
10
- @profile = profile
11
- @path = path
12
- end
13
-
14
- def set_up
15
- raise "Cannot create a subdirectory without a path" if !sub_directory?
16
-
17
- if sub_sub_directory?
18
- parent_ok = parent.set_up
19
- return false if !parent_ok
20
- end
21
-
22
- ok = check
23
- return false if !ok
24
-
25
- FileUtils.mkdir_p full_path
26
- placeholder.touch
27
-
28
- true
29
- end
30
-
31
- # subdirectory relative path is 'Foo.sbd/Bar.sbd/Baz.sbd'
32
- def full_path
33
- relative_path = File.join(subdirectories)
34
- File.join(profile.local_folders_path, relative_path)
35
- end
36
-
37
- private
38
-
39
- def sub_directory?
40
- path_elements.any?
41
- end
42
-
43
- def sub_sub_directory?
44
- path_elements.count > 1
45
- end
46
-
47
- def parent
48
- return nil if !sub_sub_directory?
49
-
50
- self.class.new(profile, File.join(path_elements[0..-2]))
51
- end
52
-
53
- # placeholder relative path is 'Foo.sbd/Bar.sbd/Baz'
54
- def placeholder
55
- @placeholder = begin
56
- relative_path = File.join(subdirectories[0..-2], path_elements[-1])
57
- path = File.join(profile.local_folders_path, relative_path)
58
- Thunderbird::SubdirectoryPlaceholder.new(path)
59
- end
60
- end
61
-
62
- def path_elements
63
- path.split(File::SEPARATOR)
64
- end
65
-
66
- def exists?
67
- File.exist?(full_path)
68
- end
69
-
70
- def directory?
71
- File.directory?(full_path)
72
- end
73
-
74
- def subdirectories
75
- path_elements.map { |p| "#{p}.sbd" }
76
- end
77
-
78
- def check
79
- case
80
- when exists? && !placeholder.exists?
81
- Kernel.puts "Can't set up folder '#{full_path}': '#{full_path}' exists, but '#{placeholder.path}' is missing"
82
- false
83
- when placeholder.exists? && !placeholder.regular?
84
- Kernel.puts "Can't set up folder '#{full_path}': '#{placeholder.path}' exists, but it is not a regular file"
85
- false
86
- when exists? && !directory?
87
- Kernel.puts "Can't set up folder '#{full_path}': '#{full_path}' exists, but it is not a directory"
88
- false
89
- else
90
- true
91
- end
92
- end
93
- end
@@ -1,21 +0,0 @@
1
- # Each subdirectory is "accompanied" by a blank
2
- # file of the same name (without the '.sbd' extension)
3
- class Thunderbird::SubdirectoryPlaceholder
4
- attr_reader :path
5
-
6
- def initialize(path)
7
- @path = path
8
- end
9
-
10
- def exists?
11
- File.exist?(path)
12
- end
13
-
14
- def regular?
15
- File.file?(path)
16
- end
17
-
18
- def touch
19
- FileUtils.touch path
20
- end
21
- end
data/lib/thunderbird.rb DELETED
@@ -1,14 +0,0 @@
1
- require "os"
2
-
3
- class Thunderbird
4
- def data_path
5
- case
6
- when OS.linux?
7
- File.join(Dir.home, ".thunderbird")
8
- when OS.mac?
9
- File.join(Dir.home, "Library", "Thunderbird")
10
- when OS.windows?
11
- File.join(ENV["APPDATA"].gsub("\\", "/"), "Thunderbird")
12
- end
13
- end
14
- end
@@ -1 +0,0 @@
1
- GATHER_RSPEC_COVERAGE = 1