imap-backup 3.4.0 → 4.0.0.rc3
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/.circleci/config.yml +2 -2
- data/.rubocop_todo.yml +1 -1
- data/CHANGELOG.md +19 -0
- data/README.md +22 -8
- data/bin/imap-backup +3 -93
- data/imap-backup +9 -0
- data/imap-backup.gemspec +2 -3
- data/lib/email/mboxrd/message.rb +2 -2
- data/lib/imap/backup/account/connection.rb +16 -33
- data/lib/imap/backup/cli/backup.rb +21 -0
- data/lib/imap/backup/cli/folders.rb +27 -0
- data/lib/imap/backup/cli/helpers.rb +20 -0
- data/lib/imap/backup/cli/local.rb +70 -0
- data/lib/imap/backup/cli/restore.rb +19 -0
- data/lib/imap/backup/cli/setup.rb +13 -0
- data/lib/imap/backup/cli/status.rb +26 -0
- data/lib/imap/backup/cli.rb +89 -0
- data/lib/imap/backup/configuration/account.rb +1 -11
- data/lib/imap/backup/configuration/list.rb +13 -12
- data/lib/imap/backup/version.rb +3 -3
- data/lib/imap/backup.rb +0 -1
- data/spec/unit/imap/backup/account/connection_spec.rb +31 -51
- data/spec/unit/imap/backup/configuration/account_spec.rb +0 -43
- data/spec/unit/imap/backup/configuration/list_spec.rb +1 -0
- metadata +23 -62
- data/docs/01-credentials-screen.png +0 -0
- data/docs/02-new-project.png +0 -0
- data/docs/03-initial-credentials-for-project.png +0 -0
- data/docs/04-credential-type-selection.png +0 -0
- data/docs/05-cant-create-without-consent-setup.png +0 -0
- data/docs/06-user-type-selection.png +0 -0
- data/docs/07-consent-screen-form.png +0 -0
- data/docs/08-app-scopes.png +0 -0
- data/docs/09-scope-selection.png +0 -0
- data/docs/10-updated-app-scopes.png +0 -0
- data/docs/11-test-users.png +0 -0
- data/docs/12-add-users.png +0 -0
- data/docs/13-create-oauth-client.png +0 -0
- data/docs/14-application-details.png +0 -0
- data/docs/16-initial-menu.png +0 -0
- data/docs/17-inputting-the-email-address.png +0 -0
- data/docs/18-choose-password.png +0 -0
- data/docs/19-supply-client-info.png +0 -0
- data/docs/20-choose-gmail-account.png +0 -0
- data/docs/21-accept-warnings.png +0 -0
- data/docs/22-grant-access.png +0 -0
- data/docs/24-confirm-choices.png +0 -0
- data/docs/25-success-code.png +0 -0
- data/docs/26-type-code-into-imap-backup.png +0 -0
- data/docs/27-success.png +0 -0
- data/docs/setting-up-gmail-with-oauth2.md +0 -166
- data/lib/gmail/authenticator.rb +0 -160
- data/lib/google/auth/stores/in_memory_token_store.rb +0 -9
- data/lib/imap/backup/configuration/gmail_oauth2.rb +0 -102
- data/spec/unit/gmail/authenticator_spec.rb +0 -138
- data/spec/unit/google/auth/stores/in_memory_token_store_spec.rb +0 -15
- data/spec/unit/imap/backup/configuration/gmail_oauth2_spec.rb +0 -121
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 956fde3bc96dc3acd872d51905a88fa9478d9b5cd7022cf55f8e73db769d8ef7
|
4
|
+
data.tar.gz: 28d653dc4e1cfebaed0a730c0231ec13a3c3c04e1a8514352183b11f152db065
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75643fb06176ab9017d0a7673bb428df4907e1350075cf4e3429702b42f03f8a6ef8a2be1095af3b6f4032ebd682d165e05d94b2e3930397ecb31ad95b2be665
|
7
|
+
data.tar.gz: e703041a7ed4e38ced9ef1b762fb3f2ebaf178cd7beb88a204624f6dd7fc53c8f9b9635942018c0f02abfa664db90e52c17eccf3278251e23395072153fe99da
|
data/.circleci/config.yml
CHANGED
@@ -27,7 +27,7 @@ jobs:
|
|
27
27
|
type: string
|
28
28
|
environment:
|
29
29
|
BUNDLE_PATH: ./vendor/bundle
|
30
|
-
|
30
|
+
DOCKER_IMAP_SERVER: 993
|
31
31
|
docker:
|
32
32
|
- image: "cimg/ruby:<< parameters.ruby_version >>"
|
33
33
|
- image: antespi/docker-imap-devel:latest
|
@@ -48,4 +48,4 @@ workflows:
|
|
48
48
|
- test:
|
49
49
|
matrix:
|
50
50
|
parameters:
|
51
|
-
ruby_version: ["2.
|
51
|
+
ruby_version: ["2.5", "2.6", "2.7"]
|
data/.rubocop_todo.yml
CHANGED
@@ -63,7 +63,7 @@ Metrics/AbcSize:
|
|
63
63
|
# Offense count: 3
|
64
64
|
# Configuration parameters: CountComments, CountAsOne.
|
65
65
|
Metrics/ClassLength:
|
66
|
-
Max:
|
66
|
+
Max: 172
|
67
67
|
|
68
68
|
# Offense count: 19
|
69
69
|
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](http://semver.org/).
|
6
|
+
|
7
|
+
## [4.0.0.rc2] - 2021-11-18
|
8
|
+
|
9
|
+
### Removed
|
10
|
+
|
11
|
+
* GMail OAuth2 support. Tokens only last a few days, so this authentication
|
12
|
+
method is not usable for automated backups.
|
13
|
+
|
14
|
+
## [4.0.0.rc1] - 2021-11-17
|
15
|
+
|
16
|
+
### Added
|
17
|
+
|
18
|
+
* `local` commands to list accounts, folders and emails and to view single
|
19
|
+
emails.
|
data/README.md
CHANGED
@@ -20,20 +20,24 @@
|
|
20
20
|
|
21
21
|
To use imap-backup with GMail, you will need to enable 'App passwords' on your account.
|
22
22
|
|
23
|
-
|
23
|
+
# Installation
|
24
|
+
|
25
|
+
```shell
|
26
|
+
$ gem install 'imap-backup'
|
27
|
+
```
|
24
28
|
|
25
|
-
|
26
|
-
users to set up an application specific to their account, the feature
|
27
|
-
is disabled by default.
|
29
|
+
# Commands
|
28
30
|
|
29
|
-
|
31
|
+
For a full list, run
|
30
32
|
|
31
|
-
|
33
|
+
```
|
34
|
+
$ imap-backup help
|
35
|
+
```
|
32
36
|
|
33
|
-
|
37
|
+
For more information about a command, run
|
34
38
|
|
35
39
|
```shell
|
36
|
-
$
|
40
|
+
$ imap-backup help COMMAND
|
37
41
|
```
|
38
42
|
|
39
43
|
# Setup
|
@@ -148,6 +152,16 @@ Each folder is saved to an mbox file.
|
|
148
152
|
Alongside each mbox is a file with extension '.imap', which lists the source IMAP
|
149
153
|
UIDs to allow a full restore.
|
150
154
|
|
155
|
+
# Local commands
|
156
|
+
|
157
|
+
There a various commands for viewing local backup status.
|
158
|
+
|
159
|
+
To view the list, use
|
160
|
+
|
161
|
+
```shell
|
162
|
+
$ imap_backup help local
|
163
|
+
```
|
164
|
+
|
151
165
|
# Troubleshooting
|
152
166
|
|
153
167
|
If you have problems:
|
data/bin/imap-backup
CHANGED
@@ -1,98 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
require "optparse"
|
3
2
|
|
4
3
|
$LOAD_PATH.unshift(File.expand_path("../lib/", __dir__))
|
5
|
-
require "imap/backup"
|
4
|
+
require "imap/backup/cli"
|
6
5
|
|
7
|
-
|
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: "restore", help: "Restore emails to server"},
|
12
|
-
{name: "status", help: "List count of non backed-up emails per folder"},
|
13
|
-
{name: "help", help: "Show usage"}
|
14
|
-
].freeze
|
6
|
+
Imap::Backup::Configuration::List.new.setup_logging
|
15
7
|
|
16
|
-
|
17
|
-
parser = OptionParser.new do |opts|
|
18
|
-
opts.banner = "Usage: #{$PROGRAM_NAME} [options] COMMAND"
|
19
|
-
|
20
|
-
opts.separator ""
|
21
|
-
opts.separator "Commands:"
|
22
|
-
KNOWN_COMMANDS.each do |command|
|
23
|
-
opts.separator format("\t%- 20<name>s %<help>s", command)
|
24
|
-
end
|
25
|
-
opts.separator ""
|
26
|
-
opts.separator "Common options:"
|
27
|
-
|
28
|
-
opts.on(
|
29
|
-
"-a",
|
30
|
-
"--accounts ACCOUNT1[,ACCOUNT2,...]",
|
31
|
-
Array,
|
32
|
-
"only these accounts"
|
33
|
-
) do |account|
|
34
|
-
options[:accounts] = account
|
35
|
-
end
|
36
|
-
|
37
|
-
opts.on_tail("-h", "--help", "Show usage") do
|
38
|
-
puts opts
|
39
|
-
exit
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.on_tail("--version", "Show version") do
|
43
|
-
puts Imap::Backup::VERSION
|
44
|
-
exit
|
45
|
-
end
|
46
|
-
end
|
47
|
-
parser.parse!
|
48
|
-
|
49
|
-
options[:command] = ARGV.shift if !ARGV.empty?
|
50
|
-
|
51
|
-
# rubocop:disable Style/IfUnlessModifier
|
52
|
-
if KNOWN_COMMANDS.find { |c| c[:name] == options[:command] }.nil?
|
53
|
-
raise "Unknown command '#{options[:command]}'"
|
54
|
-
end
|
55
|
-
|
56
|
-
# rubocop:enable Style/IfUnlessModifier
|
57
|
-
|
58
|
-
if options[:command] == "help"
|
59
|
-
puts parser
|
60
|
-
exit
|
61
|
-
end
|
62
|
-
|
63
|
-
begin
|
64
|
-
configuration = Imap::Backup::Configuration::List.new(options[:accounts])
|
65
|
-
rescue Imap::Backup::ConfigurationNotFound
|
66
|
-
Imap::Backup::Configuration::Setup.new.run
|
67
|
-
exit
|
68
|
-
end
|
69
|
-
|
70
|
-
configuration.setup_logging
|
71
|
-
|
72
|
-
case options[:command]
|
73
|
-
when "setup"
|
74
|
-
Imap::Backup::Configuration::Setup.new.run
|
75
|
-
when "backup"
|
76
|
-
configuration.each_connection(&:run_backup)
|
77
|
-
when "folders"
|
78
|
-
configuration.each_connection do |connection|
|
79
|
-
puts connection.username
|
80
|
-
folders = connection.folders
|
81
|
-
if folders.nil?
|
82
|
-
warn "Unable to list account folders"
|
83
|
-
exit 1
|
84
|
-
end
|
85
|
-
folders.each { |f| puts "\t#{f}" }
|
86
|
-
end
|
87
|
-
when "restore"
|
88
|
-
configuration.each_connection(&:restore)
|
89
|
-
when "status"
|
90
|
-
configuration.each_connection do |connection|
|
91
|
-
puts connection.username
|
92
|
-
folders = connection.status
|
93
|
-
folders.each do |f|
|
94
|
-
missing_locally = f[:remote] - f[:local]
|
95
|
-
puts "#{f[:name]}: #{missing_locally.size}"
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
8
|
+
Imap::Backup::CLI.start(ARGV)
|
data/imap-backup
ADDED
data/imap-backup.gemspec
CHANGED
@@ -13,14 +13,13 @@ Gem::Specification.new do |gem|
|
|
13
13
|
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
14
14
|
gem.test_files = gem.files.grep(%r{^spec/})
|
15
15
|
gem.require_paths = ["lib"]
|
16
|
-
gem.required_ruby_version =
|
16
|
+
gem.required_ruby_version = ">= 2.5"
|
17
17
|
gem.version = Imap::Backup::VERSION
|
18
18
|
|
19
|
-
gem.add_runtime_dependency "gmail_xoauth"
|
20
|
-
gem.add_runtime_dependency "googleauth"
|
21
19
|
gem.add_runtime_dependency "highline"
|
22
20
|
gem.add_runtime_dependency "mail"
|
23
21
|
gem.add_runtime_dependency "rake"
|
22
|
+
gem.add_runtime_dependency "thor", "~> 1.1"
|
24
23
|
|
25
24
|
gem.add_development_dependency "codeclimate-test-reporter", "~> 0.4.8"
|
26
25
|
if RUBY_ENGINE == "jruby"
|
data/lib/email/mboxrd/message.rb
CHANGED
@@ -1,15 +1,11 @@
|
|
1
1
|
require "net/imap"
|
2
|
-
require "gmail_xoauth"
|
3
2
|
|
4
|
-
require "gmail/authenticator"
|
5
3
|
require "retry_on_error"
|
6
4
|
|
7
5
|
module Imap::Backup
|
8
6
|
module Account; end
|
9
7
|
|
10
8
|
class Account::Connection
|
11
|
-
class InvalidGmailOauth2RefreshToken < StandardError; end
|
12
|
-
|
13
9
|
include RetryOnError
|
14
10
|
|
15
11
|
LOGIN_RETRY_CLASSES = [EOFError, Errno::ECONNRESET, SocketError].freeze
|
@@ -73,6 +69,19 @@ module Imap::Backup
|
|
73
69
|
end
|
74
70
|
end
|
75
71
|
|
72
|
+
def local_folders
|
73
|
+
return enum_for(:local_folders) if !block_given?
|
74
|
+
|
75
|
+
glob = File.join(local_path, "**", "*.imap")
|
76
|
+
base = Pathname.new(local_path)
|
77
|
+
Pathname.glob(glob) do |path|
|
78
|
+
name = path.relative_path_from(base).to_s[0..-6]
|
79
|
+
serializer = Serializer::Mbox.new(local_path, name)
|
80
|
+
folder = Account::Folder.new(self, name)
|
81
|
+
yield serializer, folder
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
76
85
|
def restore
|
77
86
|
local_folders do |serializer, folder|
|
78
87
|
restore_folder serializer, folder
|
@@ -80,7 +89,7 @@ module Imap::Backup
|
|
80
89
|
end
|
81
90
|
|
82
91
|
def disconnect
|
83
|
-
imap.disconnect
|
92
|
+
imap.disconnect if @imap
|
84
93
|
end
|
85
94
|
|
86
95
|
def reconnect
|
@@ -96,17 +105,8 @@ module Imap::Backup
|
|
96
105
|
"Creating IMAP instance: #{server}, options: #{options.inspect}"
|
97
106
|
)
|
98
107
|
imap = Net::IMAP.new(server, options)
|
99
|
-
|
100
|
-
|
101
|
-
credentials = authenticator.credentials
|
102
|
-
raise InvalidGmailOauth2RefreshToken if !credentials
|
103
|
-
|
104
|
-
Imap::Backup.logger.debug "Logging in with OAuth2 token: #{username}"
|
105
|
-
imap.authenticate("XOAUTH2", username, credentials.access_token)
|
106
|
-
else
|
107
|
-
Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
|
108
|
-
imap.login(username, password)
|
109
|
-
end
|
108
|
+
Imap::Backup.logger.debug "Logging in: #{username}/#{masked_password}"
|
109
|
+
imap.login(username, password)
|
110
110
|
Imap::Backup.logger.debug "Login complete"
|
111
111
|
imap
|
112
112
|
end
|
@@ -168,23 +168,6 @@ module Imap::Backup
|
|
168
168
|
password.gsub(/./, "x")
|
169
169
|
end
|
170
170
|
|
171
|
-
def use_gmail_oauth2?
|
172
|
-
# TODO: test use of ENV
|
173
|
-
server == Email::Provider::GMAIL_IMAP_SERVER &&
|
174
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"]
|
175
|
-
end
|
176
|
-
|
177
|
-
def local_folders
|
178
|
-
glob = File.join(local_path, "**", "*.imap")
|
179
|
-
base = Pathname.new(local_path)
|
180
|
-
Pathname.glob(glob) do |path|
|
181
|
-
name = path.relative_path_from(base).to_s[0..-6]
|
182
|
-
serializer = Serializer::Mbox.new(local_path, name)
|
183
|
-
folder = Account::Folder.new(self, name)
|
184
|
-
yield serializer, folder
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
171
|
def backup_folders
|
189
172
|
@backup_folders ||=
|
190
173
|
begin
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Backup < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
attr_reader :account_names
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
super([])
|
10
|
+
@account_names = (options[:accounts] || "").split(",")
|
11
|
+
end
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def run
|
15
|
+
each_connection(account_names) do |connection|
|
16
|
+
connection.run_backup
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Folders < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
attr_reader :account_names
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
super([])
|
10
|
+
@account_names = (options[:accounts] || "").split(",")
|
11
|
+
end
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def run
|
15
|
+
each_connection(account_names) do |connection|
|
16
|
+
puts connection.username
|
17
|
+
folders = connection.folders
|
18
|
+
if folders.nil?
|
19
|
+
warn "Unable to list account folders"
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
folders.each { |f| puts "\t#{f}" }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "imap/backup"
|
2
|
+
|
3
|
+
module Imap::Backup::CLI::Helpers
|
4
|
+
def symbolized(options)
|
5
|
+
options.each.with_object({}) { |(k, v), acc| acc[k.intern] = v }
|
6
|
+
end
|
7
|
+
|
8
|
+
def each_connection(names)
|
9
|
+
begin
|
10
|
+
connections = Imap::Backup::Configuration::List.new(names)
|
11
|
+
rescue Imap::Backup::ConfigurationNotFound
|
12
|
+
raise "imap-backup is not configured. Run `imap-backup setup`"
|
13
|
+
return
|
14
|
+
end
|
15
|
+
|
16
|
+
connections.each_connection do |connection|
|
17
|
+
yield connection
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Local < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
desc "accounts", "List locally backed-up accounts"
|
7
|
+
def accounts
|
8
|
+
connections = Imap::Backup::Configuration::List.new
|
9
|
+
connections.accounts.each { |a| puts a[:username] }
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "folders EMAIL", "List account folders"
|
13
|
+
def folders(email)
|
14
|
+
connections = Imap::Backup::Configuration::List.new
|
15
|
+
account = connections.accounts.find { |a| a[:username] == email }
|
16
|
+
raise "#{email} is not a configured account" if !account
|
17
|
+
|
18
|
+
account_connection = Imap::Backup::Account::Connection.new(account)
|
19
|
+
account_connection.local_folders.each do |_s, f|
|
20
|
+
puts %("#{f.name}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "emails EMAIL FOLDER", "List emails in a folder"
|
25
|
+
def emails(email, folder_name)
|
26
|
+
connections = Imap::Backup::Configuration::List.new
|
27
|
+
account = connections.accounts.find { |a| a[:username] == email }
|
28
|
+
raise "#{email} is not a configured account" if !account
|
29
|
+
|
30
|
+
account_connection = Imap::Backup::Account::Connection.new(account)
|
31
|
+
folder_serializer, folder = account_connection.local_folders.find do |(_s, f)|
|
32
|
+
f.name == folder_name
|
33
|
+
end
|
34
|
+
raise "Folder '#{folder_name}' not found" if !folder_serializer
|
35
|
+
|
36
|
+
max_subject = 60
|
37
|
+
puts format("%-10<uid>s %-#{max_subject}<subject>s - %<date>s", {uid: "UID", subject: "Subject", date: "Date"})
|
38
|
+
puts "-" * (12 + max_subject + 28)
|
39
|
+
|
40
|
+
uids = folder_serializer.uids
|
41
|
+
|
42
|
+
folder_serializer.each_message(uids).map do |uid, message|
|
43
|
+
m = {uid: uid, date: message.parsed.date.to_s, subject: message.parsed.subject}
|
44
|
+
if m[:subject].length > max_subject
|
45
|
+
puts format("% 10<uid>u: %.#{max_subject - 3}<subject>s... - %<date>s", m)
|
46
|
+
else
|
47
|
+
puts format("% 10<uid>u: %-#{max_subject}<subject>s - %<date>s", m)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
desc "email EMAIL FOLDER UID", "Show an email"
|
53
|
+
def email(email, folder_name, uid)
|
54
|
+
connections = Imap::Backup::Configuration::List.new
|
55
|
+
account = connections.accounts.find { |a| a[:username] == email }
|
56
|
+
raise "#{email} is not a configured account" if !account
|
57
|
+
|
58
|
+
account_connection = Imap::Backup::Account::Connection.new(account)
|
59
|
+
folder_serializer, _folder = account_connection.local_folders.find do |(_s, f)|
|
60
|
+
f.name == folder_name
|
61
|
+
end
|
62
|
+
raise "Folder '#{folder_name}' not found" if !folder_serializer
|
63
|
+
|
64
|
+
loaded_message = folder_serializer.load(uid)
|
65
|
+
raise "Message #{uid} not found in folder '#{folder_name}'" if !loaded_message
|
66
|
+
|
67
|
+
puts loaded_message.supplied_body
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Restore < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
attr_reader :account_names
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
super([])
|
10
|
+
@account_names = (options[:accounts] || "").split(",")
|
11
|
+
end
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def run
|
15
|
+
each_connection(account_names) { |connection| connection.restore }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class CLI::Status < Thor
|
3
|
+
include Thor::Actions
|
4
|
+
include CLI::Helpers
|
5
|
+
|
6
|
+
attr_reader :account_names
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
super([])
|
10
|
+
@account_names = (options[:accounts] || "").split(",")
|
11
|
+
end
|
12
|
+
|
13
|
+
no_commands do
|
14
|
+
def run
|
15
|
+
each_connection(account_names) do |connection|
|
16
|
+
puts connection.username
|
17
|
+
folders = connection.status
|
18
|
+
folders.each do |f|
|
19
|
+
missing_locally = f[:remote] - f[:local]
|
20
|
+
puts "#{f[:name]}: #{missing_locally.size}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class CLI < Thor
|
5
|
+
require "imap/backup/cli/helpers"
|
6
|
+
|
7
|
+
autoload :Backup, "imap/backup/cli/backup"
|
8
|
+
autoload :Folders, "imap/backup/cli/folders"
|
9
|
+
autoload :Local, "imap/backup/cli/local"
|
10
|
+
autoload :Remote, "imap/backup/cli/remote"
|
11
|
+
autoload :Restore, "imap/backup/cli/restore"
|
12
|
+
autoload :Setup, "imap/backup/cli/setup"
|
13
|
+
autoload :Status, "imap/backup/cli/status"
|
14
|
+
|
15
|
+
include Helpers
|
16
|
+
|
17
|
+
default_task :backup
|
18
|
+
|
19
|
+
def self.exit_on_failure?
|
20
|
+
true
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.accounts_option
|
24
|
+
method_option(
|
25
|
+
"accounts",
|
26
|
+
type: :string,
|
27
|
+
banner: "a comma-separated list of accounts (defaults to all configured accounts)",
|
28
|
+
aliases: ["-a"]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
desc "backup [OPTIONS]", "Run the backup"
|
33
|
+
long_desc <<~DESC
|
34
|
+
Downloads any emails not yet present locally.
|
35
|
+
Runs the backup for each configured account,
|
36
|
+
or for those requested via the --accounts option.
|
37
|
+
By default all folders, are backed up.
|
38
|
+
The setup tool can be used to choose a specific list of folders to back up.
|
39
|
+
DESC
|
40
|
+
accounts_option
|
41
|
+
def backup
|
42
|
+
Backup.new(symbolized(options)).run
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "folders [OPTIONS]", "This command is deprecated, use `imap-backup remote folders ACCOUNT`"
|
46
|
+
long_desc <<~DESC
|
47
|
+
Lists all folders of all configured accounts.
|
48
|
+
This command is deprecated.
|
49
|
+
Instead, use a combination of `imap-backup local accounts` to get the list of accounts,
|
50
|
+
and `imap-backup remote folders ACCOUNT` to get the folder list.
|
51
|
+
DESC
|
52
|
+
accounts_option
|
53
|
+
def folders
|
54
|
+
Folders.new(symbolized(options)).run
|
55
|
+
end
|
56
|
+
|
57
|
+
desc "restore [OPTIONS]", "This command is deprecated, use `imap-backup restore ACCOUNT`"
|
58
|
+
long_desc <<~DESC
|
59
|
+
By default, restores all local emails to their respective servers.
|
60
|
+
This command is deprecated.
|
61
|
+
Instead, use `imap-backup restore ACCOUNT` to restore a single account.
|
62
|
+
DESC
|
63
|
+
accounts_option
|
64
|
+
def restore
|
65
|
+
Restore.new(symbolized(options)).run
|
66
|
+
end
|
67
|
+
|
68
|
+
desc "setup", "Configure imap-backup"
|
69
|
+
long_desc <<~DESC
|
70
|
+
A menu-driven command-line application used to configure imap-backup.
|
71
|
+
Configure email accounts to back up.
|
72
|
+
DESC
|
73
|
+
def setup
|
74
|
+
Setup.new().run
|
75
|
+
end
|
76
|
+
|
77
|
+
desc "status", "Show backup status"
|
78
|
+
long_desc <<~DESC
|
79
|
+
For each configured account and folder, lists the number of emails yet to be downloaded.
|
80
|
+
DESC
|
81
|
+
accounts_option
|
82
|
+
def status
|
83
|
+
Status.new(symbolized(options)).run
|
84
|
+
end
|
85
|
+
|
86
|
+
desc "local SUBCOMMAND [OPTIONS]", "View local info"
|
87
|
+
subcommand "local", Local
|
88
|
+
end
|
89
|
+
end
|
@@ -68,12 +68,7 @@ module Imap::Backup
|
|
68
68
|
|
69
69
|
def modify_password(menu)
|
70
70
|
menu.choice("modify password") do
|
71
|
-
password =
|
72
|
-
if use_gmail_oauth2?(account)
|
73
|
-
Configuration::GmailOauth2.new(account).run
|
74
|
-
else
|
75
|
-
Configuration::Asker.password
|
76
|
-
end
|
71
|
+
password = Configuration::Asker.password
|
77
72
|
|
78
73
|
if !password.nil?
|
79
74
|
account[:password] = password
|
@@ -82,11 +77,6 @@ module Imap::Backup
|
|
82
77
|
end
|
83
78
|
end
|
84
79
|
|
85
|
-
def use_gmail_oauth2?(account)
|
86
|
-
account[:server] == Email::Provider::GMAIL_IMAP_SERVER &&
|
87
|
-
ENV["IMAP_BACKUP_ENABLE_GMAIL_OAUTH2"]
|
88
|
-
end
|
89
|
-
|
90
80
|
def modify_server(menu)
|
91
81
|
menu.choice("modify server") do
|
92
82
|
server = highline.ask("server: ")
|