imap-backup 1.2.2 → 1.2.3
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/.rubocop.yml +7 -0
- data/Gemfile +1 -1
- data/Rakefile +4 -4
- data/bin/imap-backup +23 -25
- data/imap-backup.gemspec +14 -14
- data/lib/email/mboxrd/message.rb +23 -5
- data/lib/email/provider.rb +4 -4
- data/lib/imap/backup.rb +18 -18
- data/lib/imap/backup/account/connection.rb +6 -6
- data/lib/imap/backup/account/folder.rb +4 -5
- data/lib/imap/backup/configuration/account.rb +20 -22
- data/lib/imap/backup/configuration/asker.rb +8 -10
- data/lib/imap/backup/configuration/connection_tester.rb +3 -5
- data/lib/imap/backup/configuration/folder_chooser.rb +10 -12
- data/lib/imap/backup/configuration/list.rb +1 -3
- data/lib/imap/backup/configuration/setup.rb +13 -14
- data/lib/imap/backup/configuration/store.rb +7 -8
- data/lib/imap/backup/downloader.rb +0 -2
- data/lib/imap/backup/serializer/base.rb +0 -2
- data/lib/imap/backup/serializer/directory.rb +3 -4
- data/lib/imap/backup/serializer/mbox.rb +11 -12
- data/lib/imap/backup/utils.rb +2 -3
- data/lib/imap/backup/version.rb +2 -2
- data/spec/spec_helper.rb +6 -6
- data/spec/support/higline_test_helpers.rb +1 -1
- data/spec/support/shared_examples/account_flagging.rb +6 -6
- data/spec/unit/account/connection_spec.rb +50 -51
- data/spec/unit/account/folder_spec.rb +18 -19
- data/spec/unit/configuration/account_spec.rb +96 -97
- data/spec/unit/configuration/asker_spec.rb +33 -34
- data/spec/unit/configuration/connection_tester_spec.rb +18 -19
- data/spec/unit/configuration/folder_chooser_spec.rb +34 -35
- data/spec/unit/configuration/list_spec.rb +13 -14
- data/spec/unit/configuration/setup_spec.rb +46 -47
- data/spec/unit/configuration/store_spec.rb +56 -57
- data/spec/unit/downloader_spec.rb +18 -19
- data/spec/unit/email/mboxrd/message_spec.rb +55 -11
- data/spec/unit/email/provider_spec.rb +12 -12
- data/spec/unit/serializer/base_spec.rb +7 -9
- data/spec/unit/serializer/directory_spec.rb +18 -19
- data/spec/unit/serializer/mbox_spec.rb +35 -37
- data/spec/unit/utils_spec.rb +26 -27
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 55d07b2ed9eae209a118323f3e8c494b831183468ef92e05cfe2ae139177f82b
|
4
|
+
data.tar.gz: c76ad04b8363dd146fc2abdc5ba6243fe3cd446a107f946187bf45bf16689906
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cd396bf6b70d293ef00ac83bc4e3f3ac0efdfdbf56c835484abf5c503ec84d5690537c0f36c5d23cf9e18f27f64372b66157b45491954fa6dfd0c2779b8b7aac
|
7
|
+
data.tar.gz: 1af9c675e2641debc9e897b4a4ae1a13834550e234887bcdd8ef75cafd3aadde58b296fdd903c377bddf142b340f9ed32fd8e8d6305d673e7fde5a7acb4437a5
|
data/.rubocop.yml
ADDED
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
-
require
|
3
|
-
require
|
2
|
+
require "bundler/gem_tasks"
|
3
|
+
require "rspec/core/rake_task"
|
4
4
|
|
5
|
-
task :
|
5
|
+
task default: :spec
|
6
6
|
|
7
7
|
RSpec::Core::RakeTask.new do |t|
|
8
|
-
t.pattern =
|
8
|
+
t.pattern = "spec/**/*_spec.rb"
|
9
9
|
end
|
data/bin/imap-backup
CHANGED
@@ -1,32 +1,30 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
2
|
+
require "optparse"
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
$:.unshift(File.expand_path('../../lib/', __FILE__))
|
7
|
-
require 'imap/backup'
|
4
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib/", __FILE__))
|
5
|
+
require "imap/backup"
|
8
6
|
|
9
7
|
KNOWN_COMMANDS = [
|
10
|
-
{:
|
11
|
-
{:
|
12
|
-
{:
|
13
|
-
{:
|
14
|
-
{:
|
8
|
+
{name: "setup", help: "Create/edit the configuration file"},
|
9
|
+
{name: "backup", help: "Do the backup (default)"},
|
10
|
+
{name: "folders", help: "List folders for all (or selected) accounts"},
|
11
|
+
{name: "status", help: "List count of non backed-up emails per folder"},
|
12
|
+
{name: "help", help: "Show usage"}
|
15
13
|
]
|
16
14
|
|
17
|
-
options = {:
|
15
|
+
options = {command: "backup"}
|
18
16
|
opts = OptionParser.new do |opts|
|
19
|
-
opts.banner = "Usage: #{$
|
17
|
+
opts.banner = "Usage: #{$PROGRAM_NAME} [options] COMMAND"
|
20
18
|
|
21
|
-
opts.separator
|
22
|
-
opts.separator
|
19
|
+
opts.separator ""
|
20
|
+
opts.separator "Commands:"
|
23
21
|
KNOWN_COMMANDS.each do |command|
|
24
22
|
opts.separator "\t%- 20s %s" % [command[:name], command[:help]]
|
25
23
|
end
|
26
|
-
opts.separator
|
27
|
-
opts.separator
|
24
|
+
opts.separator ""
|
25
|
+
opts.separator "Common options:"
|
28
26
|
|
29
|
-
opts.on(
|
27
|
+
opts.on("-a", "--accounts ACCOUNT1[,ACCOUNT2,...]", Array, "only these accounts") do |account|
|
30
28
|
options[:accounts] = account
|
31
29
|
end
|
32
30
|
|
@@ -35,7 +33,7 @@ opts = OptionParser.new do |opts|
|
|
35
33
|
exit
|
36
34
|
end
|
37
35
|
|
38
|
-
opts.on_tail("--version", "Show version"
|
36
|
+
opts.on_tail("--version", "Show version") do
|
39
37
|
puts Imap::Backup::VERSION
|
40
38
|
exit
|
41
39
|
end
|
@@ -46,11 +44,11 @@ if ARGV.size > 0
|
|
46
44
|
options[:command] = ARGV.shift
|
47
45
|
end
|
48
46
|
|
49
|
-
if KNOWN_COMMANDS.find{ |c| c[:name] == options[:command] }.nil?
|
47
|
+
if KNOWN_COMMANDS.find { |c| c[:name] == options[:command] }.nil?
|
50
48
|
raise "Unknown command '#{options[:command]}'"
|
51
49
|
end
|
52
50
|
|
53
|
-
if options[:command] ==
|
51
|
+
if options[:command] == "help"
|
54
52
|
puts opts
|
55
53
|
exit
|
56
54
|
end
|
@@ -65,23 +63,23 @@ end
|
|
65
63
|
configuration.setup_logging
|
66
64
|
|
67
65
|
case options[:command]
|
68
|
-
when
|
66
|
+
when "setup"
|
69
67
|
Imap::Backup::Configuration::Setup.new.run
|
70
|
-
when
|
68
|
+
when "backup"
|
71
69
|
configuration.each_connection do |connection|
|
72
70
|
connection.run_backup
|
73
71
|
end
|
74
|
-
when
|
72
|
+
when "folders"
|
75
73
|
configuration.each_connection do |connection|
|
76
74
|
puts connection.username
|
77
75
|
folders = connection.folders
|
78
76
|
if folders.nil?
|
79
|
-
warn
|
77
|
+
warn "Unable to list account folders"
|
80
78
|
exit 1
|
81
79
|
end
|
82
80
|
folders.each { |f| puts "\t" + f.name }
|
83
81
|
end
|
84
|
-
when
|
82
|
+
when "status"
|
85
83
|
configuration.each_connection do |connection|
|
86
84
|
puts connection.username
|
87
85
|
folders = connection.status
|
data/imap-backup.gemspec
CHANGED
@@ -1,26 +1,26 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'imap/backup/version'
|
1
|
+
$LOAD_PATH.unshift(File.expand_path("../lib", __FILE__))
|
2
|
+
require "imap/backup/version"
|
4
3
|
|
5
4
|
Gem::Specification.new do |gem|
|
6
|
-
gem.name =
|
5
|
+
gem.name = "imap-backup"
|
7
6
|
gem.description = %q{Backup GMail, or any other IMAP email service, to disk.}
|
8
7
|
gem.summary = %q{Backup GMail (or other IMAP) accounts to disk}
|
9
|
-
gem.authors = [
|
10
|
-
gem.email = [
|
11
|
-
gem.homepage =
|
8
|
+
gem.authors = ["Joe Yates"]
|
9
|
+
gem.email = ["joe.g.yates@gmail.com"]
|
10
|
+
gem.homepage = "https://github.com/joeyates/imap-backup"
|
12
11
|
|
13
12
|
gem.files = `git ls-files`.split($\)
|
14
13
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
15
14
|
gem.test_files = gem.files.grep(%r{^spec/})
|
16
|
-
gem.require_paths = [
|
15
|
+
gem.require_paths = ["lib"]
|
17
16
|
gem.version = Imap::Backup::VERSION
|
18
17
|
|
19
|
-
gem.add_runtime_dependency
|
20
|
-
gem.add_runtime_dependency
|
21
|
-
gem.add_runtime_dependency
|
18
|
+
gem.add_runtime_dependency "rake"
|
19
|
+
gem.add_runtime_dependency "highline"
|
20
|
+
gem.add_runtime_dependency "mail"
|
22
21
|
|
23
|
-
gem.add_development_dependency
|
24
|
-
gem.add_development_dependency
|
25
|
-
gem.add_development_dependency
|
22
|
+
gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
|
23
|
+
gem.add_development_dependency "rspec", ">= 3.0.0"
|
24
|
+
gem.add_development_dependency "rubocop-rspec"
|
25
|
+
gem.add_development_dependency "simplecov"
|
26
26
|
end
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "mail"
|
2
2
|
|
3
3
|
module Email; end
|
4
4
|
|
@@ -8,11 +8,11 @@ module Email::Mboxrd
|
|
8
8
|
|
9
9
|
def initialize(supplied_body)
|
10
10
|
@supplied_body = supplied_body.clone
|
11
|
-
@supplied_body.force_encoding(
|
11
|
+
@supplied_body.force_encoding("binary")
|
12
12
|
end
|
13
13
|
|
14
14
|
def to_s
|
15
|
-
|
15
|
+
"From " + from + "\n" + mboxrd_body + "\n"
|
16
16
|
end
|
17
17
|
|
18
18
|
private
|
@@ -21,8 +21,26 @@ module Email::Mboxrd
|
|
21
21
|
@parsed ||= Mail.new(supplied_body)
|
22
22
|
end
|
23
23
|
|
24
|
+
def best_from
|
25
|
+
if parsed.from.is_a? Enumerable
|
26
|
+
parsed.from.each do |addr|
|
27
|
+
if addr
|
28
|
+
return addr
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
if parsed.envelope_from
|
33
|
+
return parsed.envelope_from
|
34
|
+
end
|
35
|
+
if parsed.return_path
|
36
|
+
return parsed.return_path
|
37
|
+
end
|
38
|
+
|
39
|
+
return ''
|
40
|
+
end
|
41
|
+
|
24
42
|
def from
|
25
|
-
|
43
|
+
best_from + " " + asctime
|
26
44
|
end
|
27
45
|
|
28
46
|
def mboxrd_body
|
@@ -33,7 +51,7 @@ module Email::Mboxrd
|
|
33
51
|
end
|
34
52
|
|
35
53
|
def asctime
|
36
|
-
date ? date.asctime :
|
54
|
+
date ? date.asctime : ""
|
37
55
|
end
|
38
56
|
|
39
57
|
def date
|
data/lib/email/provider.rb
CHANGED
@@ -3,9 +3,9 @@ module Email; end
|
|
3
3
|
class Email::Provider
|
4
4
|
def self.for_address(address)
|
5
5
|
case
|
6
|
-
when address.end_with?(
|
6
|
+
when address.end_with?("@gmail.com")
|
7
7
|
new(:gmail)
|
8
|
-
when address.end_with?(
|
8
|
+
when address.end_with?("@fastmail.fm")
|
9
9
|
new(:fastmail)
|
10
10
|
else
|
11
11
|
new(:default)
|
@@ -32,9 +32,9 @@ class Email::Provider
|
|
32
32
|
def host
|
33
33
|
case provider
|
34
34
|
when :gmail
|
35
|
-
|
35
|
+
"imap.gmail.com"
|
36
36
|
when :fastmail
|
37
|
-
|
37
|
+
"mail.messagingengine.com"
|
38
38
|
end
|
39
39
|
end
|
40
40
|
end
|
data/lib/imap/backup.rb
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
module Imap; end
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
|
20
|
-
require
|
3
|
+
require "imap/backup/utils"
|
4
|
+
require "imap/backup/account/connection"
|
5
|
+
require "imap/backup/account/folder"
|
6
|
+
require "imap/backup/configuration/account"
|
7
|
+
require "imap/backup/configuration/asker"
|
8
|
+
require "imap/backup/configuration/connection_tester"
|
9
|
+
require "imap/backup/configuration/folder_chooser"
|
10
|
+
require "imap/backup/configuration/list"
|
11
|
+
require "imap/backup/configuration/setup"
|
12
|
+
require "imap/backup/configuration/store"
|
13
|
+
require "imap/backup/downloader"
|
14
|
+
require "imap/backup/serializer/base"
|
15
|
+
require "imap/backup/serializer/directory"
|
16
|
+
require "imap/backup/serializer/mbox"
|
17
|
+
require "imap/backup/version"
|
18
|
+
require "email/provider"
|
19
|
+
|
20
|
+
require "logger"
|
21
21
|
|
22
22
|
module Imap::Backup
|
23
23
|
class ConfigurationNotFound < StandardError; end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "net/imap"
|
2
2
|
|
3
3
|
module Imap::Backup
|
4
4
|
module Account; end
|
@@ -20,7 +20,7 @@ module Imap::Backup
|
|
20
20
|
|
21
21
|
def folders
|
22
22
|
return @folders if @folders
|
23
|
-
@folders = imap.list(
|
23
|
+
@folders = imap.list("", "*")
|
24
24
|
if @folders.nil?
|
25
25
|
Imap::Backup.logger.warn "Unable to get folder list for account #{username}"
|
26
26
|
end
|
@@ -31,7 +31,7 @@ module Imap::Backup
|
|
31
31
|
backup_folders.map do |folder|
|
32
32
|
f = Account::Folder.new(self, folder[:name])
|
33
33
|
s = Serializer::Directory.new(local_path, folder[:name])
|
34
|
-
{:
|
34
|
+
{name: folder[:name], local: s.uids, remote: f.uids}
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -83,12 +83,12 @@ module Imap::Backup
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def masked_password
|
86
|
-
password.gsub(/./,
|
86
|
+
password.gsub(/./, "x")
|
87
87
|
end
|
88
88
|
|
89
89
|
def backup_folders
|
90
|
-
return @backup_folders if @backup_folders
|
91
|
-
(folders || []).map { |f| {:
|
90
|
+
return @backup_folders if @backup_folders && (@backup_folders.size > 0)
|
91
|
+
(folders || []).map { |f| {name: f.name} }
|
92
92
|
end
|
93
93
|
|
94
94
|
def provider
|
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
require 'forwardable'
|
1
|
+
require "forwardable"
|
3
2
|
|
4
3
|
module Imap::Backup
|
5
4
|
module Account; end
|
@@ -7,7 +6,7 @@ module Imap::Backup
|
|
7
6
|
class Account::Folder
|
8
7
|
extend Forwardable
|
9
8
|
|
10
|
-
REQUESTED_ATTRIBUTES = [
|
9
|
+
REQUESTED_ATTRIBUTES = ["RFC822", "FLAGS", "INTERNALDATE"]
|
11
10
|
|
12
11
|
attr_reader :connection
|
13
12
|
attr_reader :name
|
@@ -25,7 +24,7 @@ module Imap::Backup
|
|
25
24
|
|
26
25
|
def uids
|
27
26
|
imap.examine(name)
|
28
|
-
imap.uid_search([
|
27
|
+
imap.uid_search(["ALL"]).sort
|
29
28
|
rescue Net::IMAP::NoResponseError => e
|
30
29
|
Imap::Backup.logger.warn "Folder '#{name}' does not exist"
|
31
30
|
[]
|
@@ -37,7 +36,7 @@ module Imap::Backup
|
|
37
36
|
return nil if fetch_data_items.nil?
|
38
37
|
fetch_data_item = fetch_data_items[0]
|
39
38
|
attributes = fetch_data_item.attr
|
40
|
-
attributes[
|
39
|
+
attributes["RFC822"].force_encoding("utf-8")
|
41
40
|
attributes
|
42
41
|
rescue Net::IMAP::NoResponseError => e
|
43
42
|
Imap::Backup.logger.warn "Folder '#{name}' does not exist"
|
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module Imap::Backup
|
4
2
|
module Configuration; end
|
5
3
|
|
@@ -11,7 +9,7 @@ module Imap::Backup
|
|
11
9
|
def run
|
12
10
|
catch :done do
|
13
11
|
loop do
|
14
|
-
system(
|
12
|
+
system("clear")
|
15
13
|
create_menu
|
16
14
|
end
|
17
15
|
end
|
@@ -29,8 +27,8 @@ module Imap::Backup
|
|
29
27
|
choose_folders menu
|
30
28
|
test_connection menu
|
31
29
|
delete_account menu
|
32
|
-
menu.choice(
|
33
|
-
menu.hidden(
|
30
|
+
menu.choice("return to main menu") { throw :done }
|
31
|
+
menu.hidden("quit") { throw :done }
|
34
32
|
end
|
35
33
|
end
|
36
34
|
|
@@ -46,16 +44,16 @@ Account:
|
|
46
44
|
end
|
47
45
|
|
48
46
|
def modify_email(menu)
|
49
|
-
menu.choice(
|
47
|
+
menu.choice("modify email") do
|
50
48
|
username = Configuration::Asker.email(username)
|
51
49
|
puts "username: #{username}"
|
52
|
-
others
|
50
|
+
others = store.accounts.select { |a| a != account }.map { |a| a[:username] }
|
53
51
|
puts "others: #{others.inspect}"
|
54
52
|
if others.include?(username)
|
55
|
-
puts
|
53
|
+
puts "There is already an account set up with that email address"
|
56
54
|
else
|
57
55
|
account[:username] = username
|
58
|
-
if account[:server].nil?
|
56
|
+
if account[:server].nil? || (account[:server] == "")
|
59
57
|
account[:server] = default_server(username)
|
60
58
|
end
|
61
59
|
account[:modified] = true
|
@@ -64,9 +62,9 @@ Account:
|
|
64
62
|
end
|
65
63
|
|
66
64
|
def modify_password(menu)
|
67
|
-
menu.choice(
|
65
|
+
menu.choice("modify password") do
|
68
66
|
password = Configuration::Asker.password
|
69
|
-
if !
|
67
|
+
if !password.nil?
|
70
68
|
account[:password] = password
|
71
69
|
account[:modified] = true
|
72
70
|
end
|
@@ -74,9 +72,9 @@ Account:
|
|
74
72
|
end
|
75
73
|
|
76
74
|
def modify_server(menu)
|
77
|
-
menu.choice(
|
78
|
-
server = highline.ask(
|
79
|
-
if !
|
75
|
+
menu.choice("modify server") do
|
76
|
+
server = highline.ask("server: ")
|
77
|
+
if !server.nil?
|
80
78
|
account[:server] = server
|
81
79
|
account[:modified] = true
|
82
80
|
end
|
@@ -84,7 +82,7 @@ Account:
|
|
84
82
|
end
|
85
83
|
|
86
84
|
def modify_backup_path(menu)
|
87
|
-
menu.choice(
|
85
|
+
menu.choice("modify backup path") do
|
88
86
|
validator = lambda do |p|
|
89
87
|
same = store.accounts.find do |a|
|
90
88
|
a[:username] != account[:username] && a[:local_path] == p
|
@@ -103,21 +101,21 @@ Account:
|
|
103
101
|
end
|
104
102
|
|
105
103
|
def choose_folders(menu)
|
106
|
-
menu.choice(
|
104
|
+
menu.choice("choose backup folders") do
|
107
105
|
Configuration::FolderChooser.new(account).run
|
108
106
|
end
|
109
107
|
end
|
110
108
|
|
111
109
|
def test_connection(menu)
|
112
|
-
menu.choice(
|
110
|
+
menu.choice("test connection") do
|
113
111
|
result = Configuration::ConnectionTester.test(account)
|
114
112
|
puts result
|
115
|
-
highline.ask
|
113
|
+
highline.ask "Press a key "
|
116
114
|
end
|
117
115
|
end
|
118
116
|
|
119
117
|
def delete_account(menu)
|
120
|
-
menu.choice(
|
118
|
+
menu.choice("delete") do
|
121
119
|
if highline.agree("Are you sure? (y/n) ")
|
122
120
|
account[:delete] = true
|
123
121
|
throw :done
|
@@ -130,10 +128,10 @@ Account:
|
|
130
128
|
end
|
131
129
|
|
132
130
|
def masked_password
|
133
|
-
if account[:password] ==
|
134
|
-
|
131
|
+
if (account[:password] == "") || account[:password].nil?
|
132
|
+
"(unset)"
|
135
133
|
else
|
136
|
-
account[:password].gsub(/./,
|
134
|
+
account[:password].gsub(/./, "x")
|
137
135
|
end
|
138
136
|
end
|
139
137
|
|