imap-backup 6.0.0.rc2 → 6.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/imap-backup.gemspec +5 -1
- data/lib/cli_coverage.rb +11 -11
- data/lib/email/provider/apple_mail.rb +4 -0
- data/lib/email/provider/base.rb +6 -0
- data/lib/email/provider/purelymail.rb +11 -0
- data/lib/email/provider/unknown.rb +2 -0
- data/lib/email/provider.rb +5 -0
- data/lib/imap/backup/account/connection/backup_folders.rb +27 -0
- data/lib/imap/backup/account/connection/client_factory.rb +55 -0
- data/lib/imap/backup/account/connection/folder_names.rb +26 -0
- data/lib/imap/backup/account/connection.rb +16 -96
- data/lib/imap/backup/account/folder.rb +31 -9
- data/lib/imap/backup/account.rb +15 -6
- data/lib/imap/backup/cli/backup.rb +1 -3
- data/lib/imap/backup/cli/helpers.rb +24 -22
- data/lib/imap/backup/cli/local.rb +20 -13
- data/lib/imap/backup/cli/migrate.rb +4 -10
- data/lib/imap/backup/cli/restore.rb +8 -7
- data/lib/imap/backup/cli/setup.rb +10 -8
- data/lib/imap/backup/cli/stats.rb +78 -0
- data/lib/imap/backup/cli/status.rb +2 -2
- data/lib/imap/backup/cli/utils.rb +4 -6
- data/lib/imap/backup/cli.rb +24 -3
- data/lib/imap/backup/configuration.rb +9 -11
- data/lib/imap/backup/downloader.rb +75 -31
- data/lib/imap/backup/migrator.rb +5 -5
- data/lib/imap/backup/sanitizer.rb +3 -2
- data/lib/imap/backup/serializer/appender.rb +49 -0
- data/lib/imap/backup/serializer/imap.rb +27 -3
- data/lib/imap/backup/serializer/mbox.rb +18 -2
- data/lib/imap/backup/serializer/message_enumerator.rb +29 -0
- data/lib/imap/backup/serializer/unused_name_finder.rb +25 -0
- data/lib/imap/backup/serializer.rb +64 -84
- data/lib/imap/backup/setup/account/header.rb +81 -0
- data/lib/imap/backup/setup/account.rb +28 -91
- data/lib/imap/backup/setup/asker.rb +4 -15
- data/lib/imap/backup/setup/backup_path.rb +45 -0
- data/lib/imap/backup/setup/email.rb +45 -0
- data/lib/imap/backup/setup/folder_chooser.rb +3 -3
- data/lib/imap/backup/setup/helpers.rb +1 -1
- data/lib/imap/backup/setup.rb +7 -6
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +39 -20
- data/lib/imap/backup/uploader.rb +46 -8
- data/lib/imap/backup/utils.rb +1 -1
- data/lib/imap/backup/version.rb +2 -2
- data/lib/imap/backup.rb +0 -1
- metadata +32 -134
- data/spec/features/backup_spec.rb +0 -100
- data/spec/features/configuration/minimal_configuration.rb +0 -15
- data/spec/features/configuration/missing_configuration.rb +0 -14
- data/spec/features/folders_spec.rb +0 -36
- data/spec/features/helper.rb +0 -2
- data/spec/features/local/list_accounts_spec.rb +0 -12
- data/spec/features/local/list_emails_spec.rb +0 -21
- data/spec/features/local/list_folders_spec.rb +0 -21
- data/spec/features/local/show_an_email_spec.rb +0 -34
- data/spec/features/migrate_spec.rb +0 -35
- data/spec/features/remote/list_account_folders_spec.rb +0 -16
- data/spec/features/restore_spec.rb +0 -162
- data/spec/features/status_spec.rb +0 -43
- data/spec/features/support/aruba.rb +0 -78
- data/spec/features/support/backup_directory.rb +0 -43
- data/spec/features/support/email_server.rb +0 -110
- data/spec/features/support/shared/connection_context.rb +0 -14
- data/spec/features/support/shared/message_fixtures.rb +0 -16
- data/spec/fixtures/connection.yml +0 -7
- data/spec/spec_helper.rb +0 -15
- data/spec/support/fixtures.rb +0 -11
- data/spec/support/higline_test_helpers.rb +0 -8
- data/spec/support/silence_logging.rb +0 -7
- data/spec/unit/email/mboxrd/message_spec.rb +0 -177
- data/spec/unit/email/provider/apple_mail_spec.rb +0 -7
- data/spec/unit/email/provider/base_spec.rb +0 -11
- data/spec/unit/email/provider/fastmail_spec.rb +0 -7
- data/spec/unit/email/provider/gmail_spec.rb +0 -7
- data/spec/unit/email/provider_spec.rb +0 -27
- data/spec/unit/imap/backup/account/connection_spec.rb +0 -433
- data/spec/unit/imap/backup/account/folder_spec.rb +0 -261
- data/spec/unit/imap/backup/account_spec.rb +0 -246
- data/spec/unit/imap/backup/cli/accounts_spec.rb +0 -58
- data/spec/unit/imap/backup/cli/backup_spec.rb +0 -19
- data/spec/unit/imap/backup/cli/folders_spec.rb +0 -39
- data/spec/unit/imap/backup/cli/helpers_spec.rb +0 -87
- data/spec/unit/imap/backup/cli/local_spec.rb +0 -100
- data/spec/unit/imap/backup/cli/migrate_spec.rb +0 -80
- data/spec/unit/imap/backup/cli/restore_spec.rb +0 -67
- data/spec/unit/imap/backup/cli/setup_spec.rb +0 -17
- data/spec/unit/imap/backup/cli/utils_spec.rb +0 -125
- data/spec/unit/imap/backup/cli_spec.rb +0 -93
- data/spec/unit/imap/backup/client/apple_mail_spec.rb +0 -9
- data/spec/unit/imap/backup/client/default_spec.rb +0 -22
- data/spec/unit/imap/backup/configuration_spec.rb +0 -238
- data/spec/unit/imap/backup/downloader_spec.rb +0 -96
- data/spec/unit/imap/backup/logger_spec.rb +0 -48
- data/spec/unit/imap/backup/migrator_spec.rb +0 -58
- data/spec/unit/imap/backup/sanitizer_spec.rb +0 -42
- data/spec/unit/imap/backup/serializer/directory_spec.rb +0 -37
- data/spec/unit/imap/backup/serializer/imap_spec.rb +0 -218
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +0 -45
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +0 -101
- data/spec/unit/imap/backup/serializer_spec.rb +0 -296
- data/spec/unit/imap/backup/setup/account_spec.rb +0 -461
- data/spec/unit/imap/backup/setup/asker_spec.rb +0 -137
- data/spec/unit/imap/backup/setup/connection_tester_spec.rb +0 -51
- data/spec/unit/imap/backup/setup/folder_chooser_spec.rb +0 -146
- data/spec/unit/imap/backup/setup/helpers_spec.rb +0 -15
- data/spec/unit/imap/backup/setup_spec.rb +0 -301
- data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +0 -116
- data/spec/unit/imap/backup/uploader_spec.rb +0 -54
- data/spec/unit/imap/backup/utils_spec.rb +0 -92
- data/spec/unit/retry_on_error_spec.rb +0 -34
@@ -6,14 +6,20 @@ module Imap::Backup
|
|
6
6
|
@folder_path = folder_path
|
7
7
|
end
|
8
8
|
|
9
|
+
def valid?
|
10
|
+
exist?
|
11
|
+
end
|
12
|
+
|
9
13
|
def append(message)
|
10
14
|
File.open(pathname, "ab") do |file|
|
11
15
|
file.write message
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
15
|
-
def
|
16
|
-
|
19
|
+
def delete
|
20
|
+
return if !exist?
|
21
|
+
|
22
|
+
File.unlink(pathname)
|
17
23
|
end
|
18
24
|
|
19
25
|
def length
|
@@ -41,5 +47,15 @@ module Imap::Backup
|
|
41
47
|
f.truncate(length)
|
42
48
|
end
|
43
49
|
end
|
50
|
+
|
51
|
+
def touch
|
52
|
+
File.open(pathname, "a") {}
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def exist?
|
58
|
+
File.exist?(pathname)
|
59
|
+
end
|
44
60
|
end
|
45
61
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require "email/mboxrd/message"
|
2
|
+
require "imap/backup/serializer/mbox_enumerator"
|
3
|
+
|
4
|
+
module Imap::Backup
|
5
|
+
class Serializer::MessageEnumerator
|
6
|
+
attr_reader :imap
|
7
|
+
attr_reader :mbox
|
8
|
+
|
9
|
+
def initialize(imap:, mbox:)
|
10
|
+
@imap = imap
|
11
|
+
@mbox = mbox
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(uids:)
|
15
|
+
indexes = uids.each.with_object({}) do |uid_maybe_string, acc|
|
16
|
+
uid = uid_maybe_string.to_i
|
17
|
+
index = imap.index(uid)
|
18
|
+
acc[index] = uid if index
|
19
|
+
end
|
20
|
+
enumerator = Serializer::MboxEnumerator.new(mbox.pathname)
|
21
|
+
enumerator.each.with_index do |raw, i|
|
22
|
+
uid = indexes[i]
|
23
|
+
next if !uid
|
24
|
+
|
25
|
+
yield uid, Email::Mboxrd::Message.from_serialized(raw)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class Serializer::UnusedNameFinder
|
3
|
+
attr_reader :serializer
|
4
|
+
|
5
|
+
def initialize(serializer:)
|
6
|
+
@serializer = serializer
|
7
|
+
end
|
8
|
+
|
9
|
+
def run
|
10
|
+
digit = 0
|
11
|
+
folder = nil
|
12
|
+
|
13
|
+
loop do
|
14
|
+
extra = digit.zero? ? "" : "-#{digit}"
|
15
|
+
folder = "#{serializer.folder}-#{serializer.uid_validity}#{extra}"
|
16
|
+
test = Serializer.new(serializer.path, folder)
|
17
|
+
break if !test.validate!
|
18
|
+
|
19
|
+
digit += 1
|
20
|
+
end
|
21
|
+
|
22
|
+
folder
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -1,12 +1,20 @@
|
|
1
1
|
require "forwardable"
|
2
2
|
|
3
3
|
require "email/mboxrd/message"
|
4
|
+
require "imap/backup/serializer/appender"
|
4
5
|
require "imap/backup/serializer/imap"
|
5
6
|
require "imap/backup/serializer/mbox"
|
6
7
|
require "imap/backup/serializer/mbox_enumerator"
|
8
|
+
require "imap/backup/serializer/message_enumerator"
|
9
|
+
require "imap/backup/serializer/unused_name_finder"
|
7
10
|
|
8
11
|
module Imap::Backup
|
9
12
|
class Serializer
|
13
|
+
def self.folder_path_for(path:, folder:)
|
14
|
+
relative = File.join(path, folder)
|
15
|
+
File.expand_path(relative)
|
16
|
+
end
|
17
|
+
|
10
18
|
extend Forwardable
|
11
19
|
|
12
20
|
def_delegator :mbox, :pathname, :mbox_pathname
|
@@ -20,10 +28,25 @@ module Imap::Backup
|
|
20
28
|
@folder = folder
|
21
29
|
end
|
22
30
|
|
31
|
+
# Returns true if there are existing, valid files
|
32
|
+
# false otherwise (in which case any existing files are deleted)
|
33
|
+
def validate!
|
34
|
+
return true if imap.valid? && mbox.valid?
|
35
|
+
|
36
|
+
imap.delete
|
37
|
+
@imap = nil
|
38
|
+
mbox.delete
|
39
|
+
@mbox = nil
|
40
|
+
|
41
|
+
false
|
42
|
+
end
|
43
|
+
|
23
44
|
def apply_uid_validity(value)
|
45
|
+
validate!
|
46
|
+
|
24
47
|
case
|
25
48
|
when uid_validity.nil?
|
26
|
-
|
49
|
+
internal_force_uid_validity(value)
|
27
50
|
nil
|
28
51
|
when uid_validity == value
|
29
52
|
# NOOP
|
@@ -34,94 +57,71 @@ module Imap::Backup
|
|
34
57
|
end
|
35
58
|
|
36
59
|
def force_uid_validity(value)
|
37
|
-
|
60
|
+
validate!
|
61
|
+
|
62
|
+
internal_force_uid_validity(value)
|
38
63
|
end
|
39
64
|
|
40
65
|
def append(uid, message)
|
41
|
-
|
42
|
-
|
43
|
-
uid = uid.to_i
|
44
|
-
if imap.include?(uid)
|
45
|
-
Logger.logger.debug(
|
46
|
-
"[#{folder}] message #{uid} already downloaded - skipping"
|
47
|
-
)
|
48
|
-
return
|
49
|
-
end
|
66
|
+
validate!
|
50
67
|
|
51
|
-
|
68
|
+
appender = Serializer::Appender.new(folder: folder, imap: imap, mbox: mbox)
|
69
|
+
appender.run(uid: uid, message: message)
|
52
70
|
end
|
53
71
|
|
54
72
|
def load(uid_maybe_string)
|
73
|
+
validate!
|
74
|
+
|
55
75
|
uid = uid_maybe_string.to_i
|
56
76
|
message_index = imap.index(uid)
|
57
77
|
return nil if message_index.nil?
|
58
78
|
|
59
|
-
|
79
|
+
internal_load_nth(message_index)
|
60
80
|
end
|
61
81
|
|
62
82
|
def load_nth(index)
|
63
|
-
|
64
|
-
enumerator.each.with_index do |raw, i|
|
65
|
-
next if i != index
|
83
|
+
validate!
|
66
84
|
|
67
|
-
|
68
|
-
end
|
69
|
-
nil
|
85
|
+
internal_load_nth(index)
|
70
86
|
end
|
71
87
|
|
72
|
-
def each_message(required_uids)
|
73
|
-
|
88
|
+
def each_message(required_uids, &block)
|
89
|
+
validate!
|
74
90
|
|
75
|
-
|
76
|
-
uid = uid_maybe_string.to_i
|
77
|
-
index = imap.index(uid)
|
78
|
-
acc[index] = uid if index
|
79
|
-
end
|
80
|
-
enumerator = Serializer::MboxEnumerator.new(mbox.pathname)
|
81
|
-
enumerator.each.with_index do |raw, i|
|
82
|
-
uid = indexes[i]
|
83
|
-
next if !uid
|
91
|
+
return enum_for(:each_message, required_uids) if !block
|
84
92
|
|
85
|
-
|
86
|
-
|
93
|
+
enumerator = Serializer::MessageEnumerator.new(imap: imap, mbox: mbox)
|
94
|
+
enumerator.run(uids: required_uids, &block)
|
87
95
|
end
|
88
96
|
|
97
|
+
private
|
98
|
+
|
89
99
|
def rename(new_name)
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
ensure_containing_directory
|
95
|
-
mbox.rename folder_path
|
96
|
-
imap.rename folder_path
|
100
|
+
destination = self.class.folder_path_for(path: path, folder: new_name)
|
101
|
+
ensure_containing_directory(new_name)
|
102
|
+
mbox.rename destination
|
103
|
+
imap.rename destination
|
97
104
|
end
|
98
105
|
|
99
|
-
|
106
|
+
def internal_force_uid_validity(value)
|
107
|
+
imap.uid_validity = value
|
108
|
+
mbox.touch
|
109
|
+
end
|
110
|
+
|
111
|
+
def internal_load_nth(index)
|
112
|
+
enumerator = Serializer::MboxEnumerator.new(mbox.pathname)
|
113
|
+
enumerator.each.with_index do |raw, i|
|
114
|
+
next if i != index
|
100
115
|
|
101
|
-
|
102
|
-
mboxrd_message = Email::Mboxrd::Message.new(message)
|
103
|
-
initial = mbox.length || 0
|
104
|
-
mbox_appended = false
|
105
|
-
begin
|
106
|
-
mbox.append mboxrd_message.to_serialized
|
107
|
-
mbox_appended = true
|
108
|
-
imap.append uid
|
109
|
-
rescue StandardError => e
|
110
|
-
mbox.rewind(initial) if mbox_appended
|
111
|
-
|
112
|
-
message = <<-ERROR.gsub(/^\s*/m, "")
|
113
|
-
[#{folder}] failed to append message #{uid}:
|
114
|
-
#{message}. #{e}:
|
115
|
-
#{e.backtrace.join("\n")}"
|
116
|
-
ERROR
|
117
|
-
Logger.logger.warn message
|
116
|
+
return Email::Mboxrd::Message.from_serialized(raw)
|
118
117
|
end
|
118
|
+
nil
|
119
119
|
end
|
120
120
|
|
121
121
|
def mbox
|
122
122
|
@mbox ||=
|
123
123
|
begin
|
124
|
-
ensure_containing_directory
|
124
|
+
ensure_containing_directory(folder)
|
125
125
|
Serializer::Mbox.new(folder_path)
|
126
126
|
end
|
127
127
|
end
|
@@ -129,21 +129,16 @@ module Imap::Backup
|
|
129
129
|
def imap
|
130
130
|
@imap ||=
|
131
131
|
begin
|
132
|
-
ensure_containing_directory
|
132
|
+
ensure_containing_directory(folder)
|
133
133
|
Serializer::Imap.new(folder_path)
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
137
|
def folder_path
|
138
|
-
folder_path_for(path, folder)
|
138
|
+
self.class.folder_path_for(path: path, folder: folder)
|
139
139
|
end
|
140
140
|
|
141
|
-
def
|
142
|
-
relative = File.join(path, folder)
|
143
|
-
File.expand_path(relative)
|
144
|
-
end
|
145
|
-
|
146
|
-
def ensure_containing_directory
|
141
|
+
def ensure_containing_directory(folder)
|
147
142
|
relative = File.dirname(folder)
|
148
143
|
directory = Serializer::Directory.new(path, relative)
|
149
144
|
directory.ensure_exists
|
@@ -154,29 +149,14 @@ module Imap::Backup
|
|
154
149
|
# Clear memoization so we get empty data
|
155
150
|
@mbox = nil
|
156
151
|
@imap = nil
|
157
|
-
|
152
|
+
internal_force_uid_validity(value)
|
158
153
|
|
159
154
|
new_name
|
160
155
|
end
|
161
156
|
|
162
157
|
def rename_existing_folder
|
163
|
-
|
164
|
-
new_name
|
165
|
-
loop do
|
166
|
-
extra = digit.zero? ? "" : "-#{digit}"
|
167
|
-
new_name = "#{folder}-#{imap.uid_validity}#{extra}"
|
168
|
-
new_folder_path = folder_path_for(path, new_name)
|
169
|
-
test_mbox = Serializer::Mbox.new(new_folder_path)
|
170
|
-
test_imap = Serializer::Imap.new(new_folder_path)
|
171
|
-
break if !test_mbox.exist? && !test_imap.exist?
|
172
|
-
|
173
|
-
digit += 1
|
174
|
-
end
|
175
|
-
|
176
|
-
previous = folder
|
177
|
-
rename(new_name)
|
178
|
-
@folder = previous
|
179
|
-
|
158
|
+
new_name = Serializer::UnusedNameFinder.new(serializer: self).run
|
159
|
+
rename new_name
|
180
160
|
new_name
|
181
161
|
end
|
182
162
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require "imap/backup/setup/helpers"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class Setup; end
|
5
|
+
class Setup::Account; end
|
6
|
+
|
7
|
+
class Setup::Account::Header
|
8
|
+
attr_reader :account
|
9
|
+
attr_reader :menu
|
10
|
+
|
11
|
+
def initialize(menu:, account:)
|
12
|
+
@menu = menu
|
13
|
+
@account = account
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
menu.header = <<~HEADER.chomp
|
18
|
+
#{helpers.title_prefix} Account#{modified_flag}
|
19
|
+
|
20
|
+
email #{space}#{account.username}
|
21
|
+
password#{space}#{masked_password}
|
22
|
+
path #{space}#{local_path}
|
23
|
+
folders #{space}#{folders.map { |f| f[:name] }.join(', ')}#{multi_fetch_size}
|
24
|
+
server #{space}#{account.server}#{connection_options}#{reset_seen_flags_after_fetch}
|
25
|
+
|
26
|
+
Choose an action
|
27
|
+
HEADER
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def folders
|
33
|
+
account.folders || []
|
34
|
+
end
|
35
|
+
|
36
|
+
def helpers
|
37
|
+
Setup::Helpers.new
|
38
|
+
end
|
39
|
+
|
40
|
+
def modified_flag
|
41
|
+
account.modified? ? "*" : ""
|
42
|
+
end
|
43
|
+
|
44
|
+
def multi_fetch_size
|
45
|
+
"\nmulti-fetch #{account.multi_fetch_size}" if account.multi_fetch_size > 1
|
46
|
+
end
|
47
|
+
|
48
|
+
def connection_options
|
49
|
+
return nil if !account.connection_options
|
50
|
+
|
51
|
+
escaped = JSON.generate(account.connection_options)
|
52
|
+
escaped.gsub!('"', '\"')
|
53
|
+
"\nconnection options '#{escaped}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
def reset_seen_flags_after_fetch
|
57
|
+
return nil if !account.reset_seen_flags_after_fetch
|
58
|
+
|
59
|
+
"\nchanges to unread flags will be reset during download"
|
60
|
+
end
|
61
|
+
|
62
|
+
def space
|
63
|
+
account.connection_options ? " " * 12 : " " * 4
|
64
|
+
end
|
65
|
+
|
66
|
+
def masked_password
|
67
|
+
if (account.password == "") || account.password.nil?
|
68
|
+
"(unset)"
|
69
|
+
else
|
70
|
+
account.password.gsub(/./, "x")
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def local_path
|
75
|
+
# In order to handle backslashes, as Highline effectively
|
76
|
+
# does an eval (!) on its templates, we need to doubly
|
77
|
+
# escape them
|
78
|
+
account.local_path.gsub("\\", "\\\\\\\\")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,11 +1,19 @@
|
|
1
|
-
require "imap/backup/setup/
|
1
|
+
require "imap/backup/setup/account/header"
|
2
|
+
require "imap/backup/setup/backup_path"
|
3
|
+
require "imap/backup/setup/email"
|
2
4
|
|
3
5
|
module Imap::Backup
|
4
6
|
class Setup; end
|
5
7
|
|
6
|
-
Setup::Account
|
8
|
+
class Setup::Account
|
9
|
+
attr_reader :account
|
10
|
+
attr_reader :config
|
11
|
+
attr_reader :highline
|
12
|
+
|
7
13
|
def initialize(config, account, highline)
|
8
|
-
|
14
|
+
@account = account
|
15
|
+
@config = config
|
16
|
+
@highline = highline
|
9
17
|
end
|
10
18
|
|
11
19
|
def run
|
@@ -29,6 +37,7 @@ module Imap::Backup
|
|
29
37
|
modify_multi_fetch_size menu
|
30
38
|
modify_server menu
|
31
39
|
modify_connection_options menu
|
40
|
+
toggle_reset_seen_flags_after_fetch menu
|
32
41
|
test_connection menu
|
33
42
|
delete_account menu
|
34
43
|
menu.choice("(q) return to main menu") { throw :done }
|
@@ -37,54 +46,12 @@ module Imap::Backup
|
|
37
46
|
end
|
38
47
|
|
39
48
|
def header(menu)
|
40
|
-
|
41
|
-
|
42
|
-
if account.multi_fetch_size > 1
|
43
|
-
multi_fetch_size = "\nmulti-fetch #{account.multi_fetch_size}"
|
44
|
-
end
|
45
|
-
|
46
|
-
if account.connection_options
|
47
|
-
escaped =
|
48
|
-
JSON.generate(account.connection_options)
|
49
|
-
connection_options =
|
50
|
-
"\nconnection options '#{escaped}'"
|
51
|
-
space = " " * 12
|
52
|
-
else
|
53
|
-
connection_options = nil
|
54
|
-
space = " " * 4
|
55
|
-
end
|
56
|
-
|
57
|
-
menu.header = <<~HEADER.chomp
|
58
|
-
#{helpers.title_prefix} Account#{modified}
|
59
|
-
|
60
|
-
email #{space}#{account.username}
|
61
|
-
password#{space}#{masked_password}
|
62
|
-
path #{space}#{account.local_path}
|
63
|
-
folders #{space}#{folders.map { |f| f[:name] }.join(', ')}#{multi_fetch_size}
|
64
|
-
server #{space}#{account.server}#{connection_options}
|
65
|
-
|
66
|
-
Choose an action
|
67
|
-
HEADER
|
49
|
+
Setup::Account::Header.new(menu: menu, account: account).run
|
68
50
|
end
|
69
51
|
|
70
52
|
def modify_email(menu)
|
71
53
|
menu.choice("modify email") do
|
72
|
-
|
73
|
-
Kernel.puts "username: #{username}"
|
74
|
-
other_accounts = config.accounts.reject { |a| a == account }
|
75
|
-
others = other_accounts.map { |a| a.username }
|
76
|
-
Kernel.puts "others: #{others.inspect}"
|
77
|
-
if others.include?(username)
|
78
|
-
Kernel.puts(
|
79
|
-
"There is already an account set up with that email address"
|
80
|
-
)
|
81
|
-
else
|
82
|
-
account.username = username
|
83
|
-
default = default_server(username)
|
84
|
-
if default && (account.server.nil? || (account.server == ""))
|
85
|
-
account.server = default
|
86
|
-
end
|
87
|
-
end
|
54
|
+
Setup::Email.new(account: account, config: config).run
|
88
55
|
end
|
89
56
|
end
|
90
57
|
|
@@ -96,25 +63,9 @@ module Imap::Backup
|
|
96
63
|
end
|
97
64
|
end
|
98
65
|
|
99
|
-
def path_modification_validator(path)
|
100
|
-
same = config.accounts.find do |a|
|
101
|
-
a.username != account.username && a.local_path == path
|
102
|
-
end
|
103
|
-
if same
|
104
|
-
Kernel.puts "The path '#{path}' is used to backup " \
|
105
|
-
"the account '#{same.username}'"
|
106
|
-
false
|
107
|
-
else
|
108
|
-
true
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
66
|
def modify_backup_path(menu)
|
113
67
|
menu.choice("modify backup path") do
|
114
|
-
|
115
|
-
account.local_path = Setup::Asker.backup_path(
|
116
|
-
account.local_path, ->(path) { path_modification_validator(path) }
|
117
|
-
)
|
68
|
+
Setup::BackupPath.new(account: account, config: config).run
|
118
69
|
end
|
119
70
|
end
|
120
71
|
|
@@ -153,6 +104,19 @@ module Imap::Backup
|
|
153
104
|
end
|
154
105
|
end
|
155
106
|
|
107
|
+
def toggle_reset_seen_flags_after_fetch(menu)
|
108
|
+
menu_item =
|
109
|
+
if account.reset_seen_flags_after_fetch
|
110
|
+
"don't fix changes to unread flags during download"
|
111
|
+
else
|
112
|
+
"fix changes to unread flags during download"
|
113
|
+
end
|
114
|
+
new_value = account.reset_seen_flags_after_fetch ? nil : true
|
115
|
+
menu.choice(menu_item) do
|
116
|
+
account.reset_seen_flags_after_fetch = new_value
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
156
120
|
def test_connection(menu)
|
157
121
|
menu.choice("test connection") do
|
158
122
|
result = Setup::ConnectionTester.new(account).test
|
@@ -169,32 +133,5 @@ module Imap::Backup
|
|
169
133
|
end
|
170
134
|
end
|
171
135
|
end
|
172
|
-
|
173
|
-
def folders
|
174
|
-
account.folders || []
|
175
|
-
end
|
176
|
-
|
177
|
-
def masked_password
|
178
|
-
if (account.password == "") || account.password.nil?
|
179
|
-
"(unset)"
|
180
|
-
else
|
181
|
-
account.password.gsub(/./, "x")
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
def default_server(username)
|
186
|
-
provider = Email::Provider.for_address(username)
|
187
|
-
|
188
|
-
if provider.is_a?(Email::Provider::Unknown)
|
189
|
-
Kernel.puts "Can't decide provider for email address '#{username}'"
|
190
|
-
return nil
|
191
|
-
end
|
192
|
-
|
193
|
-
provider.host
|
194
|
-
end
|
195
|
-
|
196
|
-
def helpers
|
197
|
-
Setup::Helpers.new
|
198
|
-
end
|
199
136
|
end
|
200
137
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Imap::Backup
|
2
2
|
class Setup; end
|
3
3
|
|
4
|
-
Setup::Asker
|
4
|
+
class Setup::Asker
|
5
5
|
EMAIL_MATCHER = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]+$/i.freeze
|
6
6
|
|
7
|
+
attr_reader :highline
|
8
|
+
|
7
9
|
def initialize(highline)
|
8
|
-
|
10
|
+
@highline = highline
|
9
11
|
end
|
10
12
|
|
11
13
|
def email(default = "")
|
@@ -30,15 +32,6 @@ module Imap::Backup
|
|
30
32
|
password
|
31
33
|
end
|
32
34
|
|
33
|
-
def backup_path(default, validator)
|
34
|
-
highline.ask("backup directory: ") do |q|
|
35
|
-
q.default = default
|
36
|
-
q.readline = true
|
37
|
-
q.validate = validator
|
38
|
-
q.responses[:not_valid] = "Choose a different directory "
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
35
|
def self.email(default = "")
|
43
36
|
new(Setup.highline).email(default)
|
44
37
|
end
|
@@ -46,9 +39,5 @@ module Imap::Backup
|
|
46
39
|
def self.password
|
47
40
|
new(Setup.highline).password
|
48
41
|
end
|
49
|
-
|
50
|
-
def self.backup_path(default, validator)
|
51
|
-
new(Setup.highline).backup_path(default, validator)
|
52
|
-
end
|
53
42
|
end
|
54
43
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Imap::Backup
|
2
|
+
class Setup; end
|
3
|
+
|
4
|
+
class Setup::BackupPath
|
5
|
+
attr_reader :account
|
6
|
+
attr_reader :config
|
7
|
+
|
8
|
+
def initialize(account:, config:)
|
9
|
+
@account = account
|
10
|
+
@config = config
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
account.local_path = highline.ask("backup directory: ") do |q|
|
15
|
+
q.default = account.local_path || default
|
16
|
+
q.readline = true
|
17
|
+
q.validate = ->(path) { path_modification_validator(path) }
|
18
|
+
q.responses[:not_valid] = "Choose a different directory "
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def default
|
23
|
+
File.join(config.path, account.username.tr("@", "_"))
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def highline
|
29
|
+
Setup.highline
|
30
|
+
end
|
31
|
+
|
32
|
+
def path_modification_validator(path)
|
33
|
+
same = config.accounts.find do |a|
|
34
|
+
a.username != account.username && a.local_path == path
|
35
|
+
end
|
36
|
+
if same
|
37
|
+
Kernel.puts "The path '#{path}' is used to backup " \
|
38
|
+
"the account '#{same.username}'"
|
39
|
+
false
|
40
|
+
else
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "email/provider"
|
2
|
+
|
3
|
+
module Imap::Backup
|
4
|
+
class Setup; end
|
5
|
+
|
6
|
+
class Setup::Email
|
7
|
+
attr_reader :account
|
8
|
+
attr_reader :config
|
9
|
+
|
10
|
+
def initialize(account:, config:)
|
11
|
+
@account = account
|
12
|
+
@config = config
|
13
|
+
end
|
14
|
+
|
15
|
+
def run
|
16
|
+
username = Setup::Asker.email(account.username)
|
17
|
+
other_accounts = config.accounts.reject { |a| a == account }
|
18
|
+
others = other_accounts.map(&:username)
|
19
|
+
if others.include?(username)
|
20
|
+
Kernel.puts(
|
21
|
+
"There is already an account set up with that email address"
|
22
|
+
)
|
23
|
+
else
|
24
|
+
account.username = username
|
25
|
+
if account.server.nil? || (account.server == "")
|
26
|
+
default = default_server(username)
|
27
|
+
account.server = default if default
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def default_server(username)
|
35
|
+
provider = Email::Provider.for_address(username)
|
36
|
+
|
37
|
+
if provider.is_a?(Email::Provider::Unknown)
|
38
|
+
Kernel.puts "Can't decide provider for email address '#{username}'"
|
39
|
+
return nil
|
40
|
+
end
|
41
|
+
|
42
|
+
provider.host
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|