imap-backup 2.1.0 → 2.1.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +0 -1
  3. data/.rubocop.yml +5 -2
  4. data/.rubocop_todo.yml +24 -0
  5. data/.travis.yml +13 -1
  6. data/README.md +9 -21
  7. data/Rakefile +6 -2
  8. data/imap-backup.gemspec +5 -1
  9. data/lib/email/mboxrd/message.rb +13 -12
  10. data/lib/imap/backup/account/connection.rb +3 -2
  11. data/lib/imap/backup/account/folder.rb +2 -0
  12. data/lib/imap/backup/configuration/account.rb +20 -16
  13. data/lib/imap/backup/configuration/asker.rb +1 -1
  14. data/lib/imap/backup/serializer/mbox.rb +39 -25
  15. data/lib/imap/backup/serializer/mbox_enumerator.rb +31 -0
  16. data/lib/imap/backup/serializer/mbox_store.rb +6 -27
  17. data/lib/imap/backup/version.rb +1 -1
  18. data/spec/features/support/email_server.rb +3 -0
  19. data/spec/support/fixtures.rb +1 -1
  20. data/spec/unit/email/mboxrd/message_spec.rb +20 -3
  21. data/spec/unit/email/provider_spec.rb +1 -1
  22. data/spec/unit/imap/backup/account/connection_spec.rb +10 -9
  23. data/spec/unit/imap/backup/account/folder_spec.rb +24 -10
  24. data/spec/unit/imap/backup/configuration/account_spec.rb +47 -22
  25. data/spec/unit/imap/backup/configuration/asker_spec.rb +5 -9
  26. data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -6
  27. data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +6 -6
  28. data/spec/unit/imap/backup/configuration/list_spec.rb +24 -1
  29. data/spec/unit/imap/backup/configuration/setup_spec.rb +29 -9
  30. data/spec/unit/imap/backup/configuration/store_spec.rb +5 -5
  31. data/spec/unit/imap/backup/downloader_spec.rb +11 -13
  32. data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +40 -0
  33. data/spec/unit/imap/backup/serializer/mbox_spec.rb +63 -24
  34. data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +162 -9
  35. data/spec/unit/imap/backup/utils_spec.rb +3 -3
  36. data/spec/unit/imap/backup_spec.rb +28 -0
  37. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f5903e540e8d72a63c70ee1f8f5799710bdef2ffaac58bc6bac617942da6e6da
4
- data.tar.gz: d4ad4a77a5a7d86146768a889f697f281c344ca5e4db6f29fc792a0894b2fedd
3
+ metadata.gz: 78c6ccd7ca7a6f60ae02c24c76c121470372ffbadf268eb6f52b8f6eac633e6d
4
+ data.tar.gz: d5da356a60a6046301c2e66a990f24948c3dc63f6c7f757ca3da43f62772468d
5
5
  SHA512:
6
- metadata.gz: fff913ad0270445d6783430d39672e9abe34a67d20546ad03db5ec0c8c6179c5f98524a83546d6e1790b1550284902bf0a161c998a82301ddbbbfb33d845a708
7
- data.tar.gz: d0c64c701e183d1bb01b28af7577c237a5ec0d26607ed1856e1c2b6748efe48479c0f71b502db83a8e68c22faab220372541b16edf5d3e5b7ad0af86e933460e
6
+ metadata.gz: 82fc5680a656d79012690e7da5038f0b8e56b1c5b14168142c222cb2c336bf44c0ec5570fbed8c50b2b2c2ea445ec17bed286d452795aa0c692d61c71d497274
7
+ data.tar.gz: 7eda772c5b9815dfbbe679981a0a281cecd7e3cc970c95345437d35ddc01b40f5fedb25eabef043271bdd7cfc7084bf7caad6bbd037f5926fabee0d23958252a
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --color
2
2
  --require spec_helper
3
- --tag ~docker
@@ -1,4 +1,6 @@
1
- inherit_from: https://gitlab.com/snippets/1744945/raw
1
+ inherit_from:
2
+ - https://gitlab.com/snippets/1744945/raw
3
+ - .rubocop_todo.yml
2
4
 
3
5
  AllCops:
4
6
  TargetRubyVersion: 2.3
@@ -8,7 +10,8 @@ AllCops:
8
10
  Enabled: true
9
11
 
10
12
  RSpec/ContextWording:
11
- Enabled: false
13
+ Exclude:
14
+ - "spec/features/**/*"
12
15
  RSpec/NestedGroups:
13
16
  Max: 4
14
17
  RSpec/ReturnFromStub:
@@ -0,0 +1,24 @@
1
+ # Offense count: 8
2
+ Metrics/AbcSize:
3
+ Max: 29
4
+
5
+ # Offense count: 2
6
+ # Configuration parameters: CountComments, ExcludedMethods.
7
+ # ExcludedMethods: refine
8
+ Metrics/BlockLength:
9
+ Max: 133
10
+
11
+ # Offense count: 2
12
+ # Configuration parameters: CountComments.
13
+ Metrics/ClassLength:
14
+ Max: 154
15
+
16
+ # Offense count: 11
17
+ # Configuration parameters: CountComments, ExcludedMethods.
18
+ Metrics/MethodLength:
19
+ Max: 25
20
+
21
+ # Offense count: 1
22
+ # Configuration parameters: CountComments.
23
+ Metrics/ModuleLength:
24
+ Max: 136
@@ -1,13 +1,25 @@
1
1
  language: ruby
2
+
3
+ services:
4
+ - docker
5
+
2
6
  rvm:
3
7
  - 2.3
4
8
  - 2.4
5
9
  - 2.5
6
10
  - 2.6
11
+ - jruby-19mode
12
+
7
13
  branches:
8
14
  only:
9
15
  - master
16
+
10
17
  before_install:
11
18
  - gem update --system
12
19
  - gem update bundler
13
- script: "bundle exec rspec --tag ~docker"
20
+
21
+ script:
22
+ - docker pull antespi/docker-imap-devel:latest
23
+ - docker run -d --env MAIL_ADDRESS=address@example.org --env MAIL_PASS=pass --env MAILNAME=example.org --publish 8993:993 antespi/docker-imap-devel:latest
24
+ - sleep 10
25
+ - bundle exec rake
data/README.md CHANGED
@@ -46,12 +46,13 @@ specific folders.
46
46
 
47
47
  ## Configuration file
48
48
 
49
- `setup` creates ~/.imap-backup directory and configuration file. E.g.:
49
+ `setup` creates the file `~/.imap-backup/config.json`
50
+
51
+ E.g.:
50
52
 
51
53
  ```json
52
54
  {
53
- "accounts":
54
- [
55
+ "accounts": [
55
56
  {
56
57
  "username": "my.user@gmail.com",
57
58
  "password": "secret",
@@ -70,8 +71,7 @@ It connects to GMail by default, but you can also specify a server:
70
71
 
71
72
  ```json
72
73
  {
73
- "accounts":
74
- [
74
+ "accounts": [
75
75
  {
76
76
  "username": "my.user@gmail.com",
77
77
  "password": "secret",
@@ -96,8 +96,7 @@ Specifically, if you are using a self-signed certificate and get SSL errors, e.g
96
96
 
97
97
  ```json
98
98
  {
99
- "accounts":
100
- [
99
+ "accounts": [
101
100
  {
102
101
  "username": "my.user@gmail.com",
103
102
  "password": "secret",
@@ -194,9 +193,6 @@ $ imap-backup status
194
193
 
195
194
  # Similar Software
196
195
 
197
- * https://github.com/thefloweringash/gmail-imap-backup
198
- * https://github.com/mleonhard/imapbackup
199
- * https://github.com/rgrove/larch - copies between IMAP servers
200
196
  * https://github.com/OfflineIMAP/offlineimap
201
197
 
202
198
  # Testing
@@ -217,18 +213,10 @@ docker run \
217
213
  antespi/docker-imap-devel:latest
218
214
  ```
219
215
 
220
- Currently, the integration tests with Docker are excluded from the CI run.
216
+ To exclude Docker-based tests:
221
217
 
222
- To run **just** the Docker-based tests:
223
-
224
- ```
225
- rspec -t docker
226
- ```
227
-
228
- To run **all** specs, including the integration tests, do the following:
229
-
230
- ```
231
- rspec -O .rspec-all
218
+ ```sh
219
+ $ rspec --tag ~docker
232
220
  ```
233
221
 
234
222
  ## Contributing
data/Rakefile CHANGED
@@ -1,8 +1,12 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
-
4
- task default: :spec
3
+ require "rubocop/rake_task"
5
4
 
6
5
  RSpec::Core::RakeTask.new do |t|
7
6
  t.pattern = "spec/**/*_spec.rb"
8
7
  end
8
+
9
+ RuboCop::RakeTask.new
10
+
11
+ task default: :spec
12
+ task default: :rubocop
@@ -29,7 +29,11 @@ Gem::Specification.new do |gem|
29
29
  gem.add_runtime_dependency "rake"
30
30
 
31
31
  gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
32
- gem.add_development_dependency "pry-byebug"
32
+ if RUBY_ENGINE == "jruby"
33
+ gem.add_development_dependency "pry-debugger-jruby"
34
+ else
35
+ gem.add_development_dependency "pry-byebug"
36
+ end
33
37
  gem.add_development_dependency "rspec", ">= 3.0.0"
34
38
  gem.add_development_dependency "rubocop-rspec"
35
39
  gem.add_development_dependency "simplecov"
@@ -41,13 +41,17 @@ module Email::Mboxrd
41
41
  @parsed ||= Mail.new(supplied_body)
42
42
  end
43
43
 
44
- def best_from
45
- if parsed.from.is_a?(Enumerable)
46
- parsed.from.each do |addr|
47
- return addr if addr
44
+ def from
45
+ @from ||=
46
+ begin
47
+ from = best_from.dup
48
+ from << " " + asctime if asctime != ""
49
+ from
48
50
  end
49
- end
51
+ end
50
52
 
53
+ def best_from
54
+ return first_from if first_from
51
55
  return parsed.sender if parsed.sender
52
56
  return parsed.envelope_from if parsed.envelope_from
53
57
  return parsed.return_path if parsed.return_path
@@ -55,13 +59,10 @@ module Email::Mboxrd
55
59
  ""
56
60
  end
57
61
 
58
- def from
59
- @from ||=
60
- begin
61
- from = best_from.dup
62
- from << " " + asctime if asctime != ""
63
- from
64
- end
62
+ def first_from
63
+ return nil if !parsed.from.is_a?(Enumerable)
64
+
65
+ parsed.from.find { |from| from }
65
66
  end
66
67
 
67
68
  def mboxrd_body
@@ -48,8 +48,9 @@ module Imap::Backup
48
48
  imap
49
49
  each_folder do |folder, serializer|
50
50
  next if !folder.exist?
51
+
51
52
  Imap::Backup.logger.debug "[#{folder.name}] running backup"
52
- serializer.set_uid_validity(folder.uid_validity)
53
+ serializer.apply_uid_validity(folder.uid_validity)
53
54
  Downloader.new(folder, serializer).run
54
55
  end
55
56
  end
@@ -58,7 +59,7 @@ module Imap::Backup
58
59
  local_folders do |serializer, folder|
59
60
  exists = folder.exist?
60
61
  if exists
61
- new_name = serializer.set_uid_validity(folder.uid_validity)
62
+ new_name = serializer.apply_uid_validity(folder.uid_validity)
62
63
  old_name = serializer.folder
63
64
  if new_name
64
65
  Imap::Backup.logger.debug(
@@ -61,6 +61,8 @@ module Imap::Backup
61
61
 
62
62
  fetch_data_item = fetch_data_items[0]
63
63
  attributes = fetch_data_item.attr
64
+ return nil if !attributes.key?("RFC822")
65
+
64
66
  attributes["RFC822"].force_encoding("utf-8")
65
67
  attributes
66
68
  rescue FolderNotFound
@@ -1,7 +1,7 @@
1
1
  module Imap::Backup
2
2
  module Configuration; end
3
3
 
4
- class Configuration::Account < Struct.new(:store, :account, :highline)
4
+ Configuration::Account = Struct.new(:store, :account, :highline) do
5
5
  def initialize(store, account, highline)
6
6
  super
7
7
  end
@@ -51,7 +51,9 @@ module Imap::Backup
51
51
  others = other_accounts.map { |a| a[:username] }
52
52
  Kernel.puts "others: #{others.inspect}"
53
53
  if others.include?(username)
54
- Kernel.puts "There is already an account set up with that email address"
54
+ Kernel.puts(
55
+ "There is already an account set up with that email address"
56
+ )
55
57
  else
56
58
  account[:username] = username
57
59
  if account[:server].nil? || (account[:server] == "")
@@ -82,23 +84,25 @@ module Imap::Backup
82
84
  end
83
85
  end
84
86
 
87
+ def path_modification_validator(path)
88
+ same = store.accounts.find do |a|
89
+ a[:username] != account[:username] && a[:local_path] == path
90
+ end
91
+ if same
92
+ Kernel.puts "The path '#{path}' is used to backup " \
93
+ "the account '#{same[:username]}'"
94
+ false
95
+ else
96
+ true
97
+ end
98
+ end
99
+
85
100
  def modify_backup_path(menu)
86
101
  menu.choice("modify backup path") do
87
- validator = ->(p) do
88
- same = store.accounts.find do |a|
89
- a[:username] != account[:username] && a[:local_path] == p
90
- end
91
- if same
92
- Kernel.puts "The path '#{p}' is used to backup " \
93
- "the account '#{same[:username]}'"
94
- false
95
- else
96
- true
97
- end
98
- end
99
102
  existing = account[:local_path].clone
100
- account[:local_path] =
101
- Configuration::Asker.backup_path(account[:local_path], validator)
103
+ account[:local_path] = Configuration::Asker.backup_path(
104
+ account[:local_path], ->(path) { path_modification_validator(path) }
105
+ )
102
106
  account[:modified] = true if existing != account[:local_path]
103
107
  end
104
108
  end
@@ -1,7 +1,7 @@
1
1
  module Imap::Backup
2
2
  module Configuration; end
3
3
 
4
- class Configuration::Asker < Struct.new(:highline)
4
+ Configuration::Asker = Struct.new(:highline) do
5
5
  EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i.freeze
6
6
 
7
7
  def initialize(highline)
@@ -10,31 +10,16 @@ module Imap::Backup
10
10
  @folder = folder
11
11
  end
12
12
 
13
- def set_uid_validity(value)
14
- existing_uid_validity = store.uid_validity
13
+ def apply_uid_validity(value)
15
14
  case
16
- when existing_uid_validity.nil?
15
+ when store.uid_validity.nil?
17
16
  store.uid_validity = value
18
17
  nil
19
- when existing_uid_validity == value
18
+ when store.uid_validity == value
20
19
  # NOOP
21
20
  nil
22
21
  else
23
- digit = nil
24
- new_name = nil
25
- loop do
26
- extra = digit ? ".#{digit}" : ""
27
- new_name = "#{folder}.#{existing_uid_validity}#{extra}"
28
- test_store = Serializer::MboxStore.new(path, new_name)
29
- break if !test_store.exist?
30
-
31
- digit ||= 0
32
- digit += 1
33
- end
34
- store.rename new_name
35
- @store = nil
36
- store.uid_validity = value
37
- new_name
22
+ apply_new_uid_validity value
38
23
  end
39
24
  end
40
25
 
@@ -73,18 +58,47 @@ module Imap::Backup
73
58
  end
74
59
  end
75
60
 
76
- def create_containing_directory
77
- relative_path = File.dirname(folder)
78
- containing_directory = File.join(path, relative_path)
79
- full_path = File.expand_path(containing_directory)
61
+ def apply_new_uid_validity(value)
62
+ digit = 0
63
+ new_name = nil
64
+ loop do
65
+ extra = digit.zero? ? "" : ".#{digit}"
66
+ new_name = "#{folder}.#{store.uid_validity}#{extra}"
67
+ test_store = Serializer::MboxStore.new(path, new_name)
68
+ break if !test_store.exist?
80
69
 
70
+ digit += 1
71
+ end
72
+ rename_store new_name, value
73
+ end
74
+
75
+ def rename_store(new_name, value)
76
+ store.rename new_name
77
+ @store = nil
78
+ store.uid_validity = value
79
+ new_name
80
+ end
81
+
82
+ def relative_path
83
+ File.dirname(folder)
84
+ end
85
+
86
+ def containing_directory
87
+ File.join(path, relative_path)
88
+ end
89
+
90
+ def full_path
91
+ File.expand_path(containing_directory)
92
+ end
93
+
94
+ def create_containing_directory
81
95
  if !File.directory?(full_path)
82
- Imap::Backup::Utils.make_folder(
96
+ Utils.make_folder(
83
97
  path, relative_path, Serializer::DIRECTORY_PERMISSIONS
84
98
  )
85
99
  end
86
100
 
87
- if Imap::Backup::Utils.mode(full_path) !=
101
+ if Utils.mode(full_path) !=
88
102
  Serializer::DIRECTORY_PERMISSIONS
89
103
  FileUtils.chmod Serializer::DIRECTORY_PERMISSIONS, full_path
90
104
  end
@@ -0,0 +1,31 @@
1
+ module Imap::Backup
2
+ class Serializer::MboxEnumerator
3
+ attr_reader :mbox_pathname
4
+
5
+ def initialize(mbox_pathname)
6
+ @mbox_pathname = mbox_pathname
7
+ end
8
+
9
+ def each
10
+ return enum_for(:each) if !block_given?
11
+
12
+ File.open(mbox_pathname) do |f|
13
+ lines = []
14
+
15
+ loop do
16
+ line = f.gets
17
+ break if !line
18
+
19
+ if line.start_with?("From ")
20
+ yield lines.join if lines.count.positive?
21
+ lines = [line]
22
+ else
23
+ lines << line
24
+ end
25
+ end
26
+
27
+ yield lines.join if lines.count.positive?
28
+ end
29
+ end
30
+ end
31
+ end