imap-backup 5.0.0 → 5.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 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