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.
- checksums.yaml +4 -4
- data/.rspec +0 -1
- data/.rubocop.yml +5 -2
- data/.rubocop_todo.yml +24 -0
- data/.travis.yml +13 -1
- data/README.md +9 -21
- data/Rakefile +6 -2
- data/imap-backup.gemspec +5 -1
- data/lib/email/mboxrd/message.rb +13 -12
- data/lib/imap/backup/account/connection.rb +3 -2
- data/lib/imap/backup/account/folder.rb +2 -0
- data/lib/imap/backup/configuration/account.rb +20 -16
- data/lib/imap/backup/configuration/asker.rb +1 -1
- data/lib/imap/backup/serializer/mbox.rb +39 -25
- data/lib/imap/backup/serializer/mbox_enumerator.rb +31 -0
- data/lib/imap/backup/serializer/mbox_store.rb +6 -27
- data/lib/imap/backup/version.rb +1 -1
- data/spec/features/support/email_server.rb +3 -0
- data/spec/support/fixtures.rb +1 -1
- data/spec/unit/email/mboxrd/message_spec.rb +20 -3
- data/spec/unit/email/provider_spec.rb +1 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +10 -9
- data/spec/unit/imap/backup/account/folder_spec.rb +24 -10
- data/spec/unit/imap/backup/configuration/account_spec.rb +47 -22
- data/spec/unit/imap/backup/configuration/asker_spec.rb +5 -9
- data/spec/unit/imap/backup/configuration/connection_tester_spec.rb +6 -6
- data/spec/unit/imap/backup/configuration/folder_chooser_spec.rb +6 -6
- data/spec/unit/imap/backup/configuration/list_spec.rb +24 -1
- data/spec/unit/imap/backup/configuration/setup_spec.rb +29 -9
- data/spec/unit/imap/backup/configuration/store_spec.rb +5 -5
- data/spec/unit/imap/backup/downloader_spec.rb +11 -13
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +40 -0
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +63 -24
- data/spec/unit/imap/backup/serializer/mbox_store_spec.rb +162 -9
- data/spec/unit/imap/backup/utils_spec.rb +3 -3
- data/spec/unit/imap/backup_spec.rb +28 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78c6ccd7ca7a6f60ae02c24c76c121470372ffbadf268eb6f52b8f6eac633e6d
|
4
|
+
data.tar.gz: d5da356a60a6046301c2e66a990f24948c3dc63f6c7f757ca3da43f62772468d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 82fc5680a656d79012690e7da5038f0b8e56b1c5b14168142c222cb2c336bf44c0ec5570fbed8c50b2b2c2ea445ec17bed286d452795aa0c692d61c71d497274
|
7
|
+
data.tar.gz: 7eda772c5b9815dfbbe679981a0a281cecd7e3cc970c95345437d35ddc01b40f5fedb25eabef043271bdd7cfc7084bf7caad6bbd037f5926fabee0d23958252a
|
data/.rspec
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
inherit_from:
|
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
|
-
|
13
|
+
Exclude:
|
14
|
+
- "spec/features/**/*"
|
12
15
|
RSpec/NestedGroups:
|
13
16
|
Max: 4
|
14
17
|
RSpec/ReturnFromStub:
|
data/.rubocop_todo.yml
ADDED
@@ -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
|
data/.travis.yml
CHANGED
@@ -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
|
-
|
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
|
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
|
-
|
216
|
+
To exclude Docker-based tests:
|
221
217
|
|
222
|
-
|
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
|
data/imap-backup.gemspec
CHANGED
@@ -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
|
-
|
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"
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -41,13 +41,17 @@ module Email::Mboxrd
|
|
41
41
|
@parsed ||= Mail.new(supplied_body)
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
|
46
|
-
|
47
|
-
|
44
|
+
def from
|
45
|
+
@from ||=
|
46
|
+
begin
|
47
|
+
from = best_from.dup
|
48
|
+
from << " " + asctime if asctime != ""
|
49
|
+
from
|
48
50
|
end
|
49
|
-
|
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
|
59
|
-
|
60
|
-
|
61
|
-
|
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.
|
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.
|
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(
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module Imap::Backup
|
2
2
|
module Configuration; end
|
3
3
|
|
4
|
-
|
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
|
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
|
-
|
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
|
@@ -10,31 +10,16 @@ module Imap::Backup
|
|
10
10
|
@folder = folder
|
11
11
|
end
|
12
12
|
|
13
|
-
def
|
14
|
-
existing_uid_validity = store.uid_validity
|
13
|
+
def apply_uid_validity(value)
|
15
14
|
case
|
16
|
-
when
|
15
|
+
when store.uid_validity.nil?
|
17
16
|
store.uid_validity = value
|
18
17
|
nil
|
19
|
-
when
|
18
|
+
when store.uid_validity == value
|
20
19
|
# NOOP
|
21
20
|
nil
|
22
21
|
else
|
23
|
-
|
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
|
77
|
-
|
78
|
-
|
79
|
-
|
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
|
-
|
96
|
+
Utils.make_folder(
|
83
97
|
path, relative_path, Serializer::DIRECTORY_PERMISSIONS
|
84
98
|
)
|
85
99
|
end
|
86
100
|
|
87
|
-
if
|
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
|