imap-backup 14.4.4 → 14.5.0
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/README.md +67 -137
- data/docs/documentation.md +19 -0
- data/imap-backup.gemspec +1 -1
- data/lib/imap/backup/account/backup.rb +2 -0
- data/lib/imap/backup/account/backup_folders.rb +7 -1
- data/lib/imap/backup/account/client_factory.rb +1 -0
- data/lib/imap/backup/account/folder.rb +26 -0
- data/lib/imap/backup/account/folder_backup.rb +4 -1
- data/lib/imap/backup/account/folder_ensurer.rb +3 -0
- data/lib/imap/backup/account/local_only_folder_deleter.rb +3 -1
- data/lib/imap/backup/account/restore.rb +2 -0
- data/lib/imap/backup/account/serialized_folders.rb +31 -1
- data/lib/imap/backup/account.rb +34 -18
- data/lib/imap/backup/cli/backup.rb +3 -0
- data/lib/imap/backup/cli/folder_enumerator.rb +6 -0
- data/lib/imap/backup/cli/helpers.rb +13 -0
- data/lib/imap/backup/cli/local/check.rb +3 -0
- data/lib/imap/backup/cli/local.rb +14 -1
- data/lib/imap/backup/cli/remote.rb +7 -0
- data/lib/imap/backup/cli/restore.rb +4 -0
- data/lib/imap/backup/cli/setup.rb +3 -0
- data/lib/imap/backup/cli/single/backup.rb +3 -0
- data/lib/imap/backup/cli/single.rb +4 -0
- data/lib/imap/backup/cli/stats.rb +3 -0
- data/lib/imap/backup/cli/transfer.rb +8 -0
- data/lib/imap/backup/cli/utils.rb +7 -1
- data/lib/imap/backup/cli.rb +8 -0
- data/lib/imap/backup/client/apple_mail.rb +2 -0
- data/lib/imap/backup/client/automatic_login_wrapper.rb +9 -1
- data/lib/imap/backup/client/default.rb +15 -4
- data/lib/imap/backup/configuration.rb +13 -0
- data/lib/imap/backup/configuration_not_found.rb +1 -0
- data/lib/imap/backup/downloader.rb +4 -0
- data/lib/imap/backup/email/mboxrd/message.rb +14 -0
- data/lib/imap/backup/email/provider/apple_mail.rb +2 -0
- data/lib/imap/backup/email/provider/base.rb +3 -3
- data/lib/imap/backup/email/provider/fastmail.rb +2 -0
- data/lib/imap/backup/email/provider/gmail.rb +2 -0
- data/lib/imap/backup/email/provider/purelymail.rb +2 -0
- data/lib/imap/backup/email/provider/unknown.rb +2 -6
- data/lib/imap/backup/email/provider.rb +5 -0
- data/lib/imap/backup/file_mode.rb +2 -0
- data/lib/imap/backup/flag_refresher.rb +4 -0
- data/lib/imap/backup/local_only_message_deleter.rb +2 -0
- data/lib/imap/backup/logger.rb +18 -0
- data/lib/imap/backup/migrator.rb +3 -0
- data/lib/imap/backup/mirror/map.rb +21 -0
- data/lib/imap/backup/mirror.rb +8 -0
- data/lib/imap/backup/naming.rb +10 -1
- data/lib/imap/backup/retry_on_error.rb +9 -0
- data/lib/imap/backup/serializer/appender.rb +9 -0
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +4 -0
- data/lib/imap/backup/serializer/folder_maker.rb +1 -0
- data/lib/imap/backup/serializer/imap.rb +38 -2
- data/lib/imap/backup/serializer/integrity_checker.rb +1 -0
- data/lib/imap/backup/serializer/mbox.rb +26 -0
- data/lib/imap/backup/serializer/message.rb +13 -0
- data/lib/imap/backup/serializer/message_enumerator.rb +10 -2
- data/lib/imap/backup/serializer/permission_checker.rb +6 -0
- data/lib/imap/backup/serializer/transaction.rb +18 -0
- data/lib/imap/backup/serializer/unused_name_finder.rb +4 -0
- data/lib/imap/backup/serializer/version2_migrator.rb +4 -0
- data/lib/imap/backup/serializer.rb +56 -2
- data/lib/imap/backup/setup/account/header.rb +6 -0
- data/lib/imap/backup/setup/account.rb +6 -0
- data/lib/imap/backup/setup/asker.rb +16 -0
- data/lib/imap/backup/setup/backup_path.rb +6 -0
- data/lib/imap/backup/setup/connection_tester.rb +5 -0
- data/lib/imap/backup/setup/email_changer.rb +6 -0
- data/lib/imap/backup/setup/folder_chooser.rb +4 -0
- data/lib/imap/backup/setup/global_options/download_strategy_chooser.rb +4 -0
- data/lib/imap/backup/setup/global_options.rb +4 -0
- data/lib/imap/backup/setup/helpers.rb +3 -0
- data/lib/imap/backup/setup.rb +5 -0
- data/lib/imap/backup/text/sanitizer.rb +9 -0
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +8 -1
- data/lib/imap/backup/uploader.rb +5 -0
- data/lib/imap/backup/version.rb +7 -2
- metadata +11 -13
- data/docs/api.md +0 -20
- data/docs/development.md +0 -110
- data/docs/migrate-server-keep-address.md +0 -47
@@ -3,14 +3,21 @@ require "imap/backup/serializer/transaction"
|
|
3
3
|
module Imap; end
|
4
4
|
|
5
5
|
module Imap::Backup
|
6
|
+
# Stores messages
|
6
7
|
class Serializer::Mbox
|
8
|
+
# @return [String] The path of the mailbox file, without the '.mbox' extension
|
7
9
|
attr_reader :folder_path
|
8
10
|
|
11
|
+
# @param folder_path [String] The path of the mailbox file, without the '.mbox' extension
|
9
12
|
def initialize(folder_path)
|
10
13
|
@folder_path = folder_path
|
11
14
|
@tsx = nil
|
12
15
|
end
|
13
16
|
|
17
|
+
# Starts a transaction
|
18
|
+
# @param block [block] the block that is wrapped by the transaction
|
19
|
+
# @raise re-raises errors which occur in the block
|
20
|
+
# @return [void]
|
14
21
|
def transaction(&block)
|
15
22
|
tsx.fail_in_transaction!(:transaction, message: "nested transactions are not supported")
|
16
23
|
|
@@ -26,6 +33,8 @@ module Imap::Backup
|
|
26
33
|
end
|
27
34
|
end
|
28
35
|
|
36
|
+
# Returns to the pre-transaction state
|
37
|
+
# @return [void]
|
29
38
|
def rollback
|
30
39
|
tsx.fail_outside_transaction!(:rollback)
|
31
40
|
|
@@ -36,12 +45,19 @@ module Imap::Backup
|
|
36
45
|
exist?
|
37
46
|
end
|
38
47
|
|
48
|
+
# Serializes a message
|
49
|
+
# @param message [String] the message text
|
50
|
+
# @return [void]
|
39
51
|
def append(message)
|
40
52
|
File.open(pathname, "ab") do |file|
|
41
53
|
file.write message
|
42
54
|
end
|
43
55
|
end
|
44
56
|
|
57
|
+
# Reads a message from disk
|
58
|
+
# @param offset [Integer] the start of the message inside the mailbox file
|
59
|
+
# @param length [Integer] the length of the message (as stored on disk)
|
60
|
+
# @return [String] the message
|
45
61
|
def read(offset, length)
|
46
62
|
File.open(pathname, "rb") do |f|
|
47
63
|
f.seek offset
|
@@ -49,6 +65,8 @@ module Imap::Backup
|
|
49
65
|
end
|
50
66
|
end
|
51
67
|
|
68
|
+
# Deletes the mailbox
|
69
|
+
# @return [void]
|
52
70
|
def delete
|
53
71
|
return if !exist?
|
54
72
|
|
@@ -59,16 +77,22 @@ module Imap::Backup
|
|
59
77
|
File.exist?(pathname)
|
60
78
|
end
|
61
79
|
|
80
|
+
# @return [Integer] The lsize of the disk file
|
62
81
|
def length
|
63
82
|
return nil if !exist?
|
64
83
|
|
65
84
|
File.stat(pathname).size
|
66
85
|
end
|
67
86
|
|
87
|
+
# @return [String] The full path name of the mailbox
|
68
88
|
def pathname
|
69
89
|
"#{folder_path}.mbox"
|
70
90
|
end
|
71
91
|
|
92
|
+
# Renames the mailbox, if it exists,
|
93
|
+
# otherwise, simply stores the new name
|
94
|
+
# @param new_path [String] the new path (without extension)
|
95
|
+
# @return [void]
|
72
96
|
def rename(new_path)
|
73
97
|
if exist?
|
74
98
|
old_pathname = pathname
|
@@ -79,6 +103,8 @@ module Imap::Backup
|
|
79
103
|
end
|
80
104
|
end
|
81
105
|
|
106
|
+
# Sets the mailbox file's updated time to the current time
|
107
|
+
# @return [void]
|
82
108
|
def touch
|
83
109
|
File.open(pathname, "a") {}
|
84
110
|
end
|
@@ -5,10 +5,15 @@ require "imap/backup/email/mboxrd/message"
|
|
5
5
|
module Imap; end
|
6
6
|
|
7
7
|
module Imap::Backup
|
8
|
+
# Represents a stored message
|
8
9
|
class Serializer::Message
|
10
|
+
# @return [Array[Symbol]] the message's flags
|
9
11
|
attr_accessor :flags
|
12
|
+
# @return [Integer] the length of the message (as stored on disk)
|
10
13
|
attr_reader :length
|
14
|
+
# @return [Integer] the start of the message inside the mailbox file
|
11
15
|
attr_reader :offset
|
16
|
+
# @return [Integer] the message's UID
|
12
17
|
attr_accessor :uid
|
13
18
|
|
14
19
|
extend Forwardable
|
@@ -16,6 +21,11 @@ module Imap::Backup
|
|
16
21
|
def_delegator :message, :supplied_body, :body
|
17
22
|
def_delegators :message, :imap_body, :date, :subject
|
18
23
|
|
24
|
+
# @param uid [Integer] the message's UID
|
25
|
+
# @param offset [Integer] the start of the message inside the mailbox file
|
26
|
+
# @param length [Integer] the length of the message (as stored on disk)
|
27
|
+
# @param mbox [Serializer::Mbox] the mailbox containing the message
|
28
|
+
# @param flags [Array[Symbol]] the message's flags
|
19
29
|
def initialize(uid:, offset:, length:, mbox:, flags: [])
|
20
30
|
@uid = uid
|
21
31
|
@offset = offset
|
@@ -24,6 +34,7 @@ module Imap::Backup
|
|
24
34
|
@flags = flags.map(&:to_sym)
|
25
35
|
end
|
26
36
|
|
37
|
+
# @return [Hash] the message metadata
|
27
38
|
def to_h
|
28
39
|
{
|
29
40
|
uid: uid,
|
@@ -33,6 +44,8 @@ module Imap::Backup
|
|
33
44
|
}
|
34
45
|
end
|
35
46
|
|
47
|
+
# Reads the message text and returns the original form
|
48
|
+
# @return [String] the message
|
36
49
|
def message
|
37
50
|
@message =
|
38
51
|
begin
|
@@ -1,13 +1,17 @@
|
|
1
1
|
module Imap; end
|
2
2
|
|
3
3
|
module Imap::Backup
|
4
|
+
# Enumerates over a list of stores messages
|
4
5
|
class Serializer::MessageEnumerator
|
5
|
-
|
6
|
-
|
6
|
+
# @param imap [Serializer::Imap] the metadata serializer for the folder
|
7
7
|
def initialize(imap:)
|
8
8
|
@imap = imap
|
9
9
|
end
|
10
10
|
|
11
|
+
# Enumerates over the messages
|
12
|
+
# @param uids [Array<Integer>] the message UIDs of the messages to iterate over
|
13
|
+
# @yieldparam message [Serializer::Message]
|
14
|
+
# @return [void]
|
11
15
|
def run(uids:)
|
12
16
|
uids.each do |uid_maybe_string|
|
13
17
|
uid = uid_maybe_string.to_i
|
@@ -18,5 +22,9 @@ module Imap::Backup
|
|
18
22
|
yield message
|
19
23
|
end
|
20
24
|
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
attr_reader :imap
|
21
29
|
end
|
22
30
|
end
|
@@ -3,12 +3,18 @@ require "imap/backup/file_mode"
|
|
3
3
|
module Imap; end
|
4
4
|
|
5
5
|
module Imap::Backup
|
6
|
+
# Ensures a file has the desired permissions
|
6
7
|
class Serializer::PermissionChecker
|
8
|
+
# @param filename [String] the file name
|
9
|
+
# @param limit [Integer] the maximum permission that should be set
|
7
10
|
def initialize(filename:, limit:)
|
8
11
|
@filename = filename
|
9
12
|
@limit = limit
|
10
13
|
end
|
11
14
|
|
15
|
+
# Runs the check
|
16
|
+
# @raise [RuntimeError] if the permissions are incorrect
|
17
|
+
# @return [void]
|
12
18
|
def run
|
13
19
|
actual = FileMode.new(filename: filename).mode
|
14
20
|
return nil if actual.nil?
|
@@ -1,15 +1,23 @@
|
|
1
1
|
module Imap; end
|
2
2
|
|
3
3
|
module Imap::Backup
|
4
|
+
# Stores data during a transaction
|
4
5
|
class Serializer::Transaction
|
6
|
+
# @return the transaction's stored data
|
5
7
|
attr_reader :data
|
6
8
|
|
9
|
+
# @param owner [any] the class using the transaction -
|
10
|
+
# this is used when raising errors
|
7
11
|
def initialize(owner:)
|
8
12
|
@data = nil
|
9
13
|
@owner = owner
|
10
14
|
@in_transaction = false
|
11
15
|
end
|
12
16
|
|
17
|
+
# Runs the transaction
|
18
|
+
# @param data [any] the data to maintain during the transaction
|
19
|
+
# @param block [block] the block to wrap with the transaction
|
20
|
+
# @return [void]
|
13
21
|
def begin(data, &block)
|
14
22
|
@data = data
|
15
23
|
@in_transaction = true
|
@@ -17,6 +25,8 @@ module Imap::Backup
|
|
17
25
|
@in_transaction = false
|
18
26
|
end
|
19
27
|
|
28
|
+
# Clears rollback data
|
29
|
+
# @return [void]
|
20
30
|
def clear
|
21
31
|
@data = nil
|
22
32
|
end
|
@@ -25,10 +35,18 @@ module Imap::Backup
|
|
25
35
|
@in_transaction
|
26
36
|
end
|
27
37
|
|
38
|
+
# Throws an exception if there is a current transaction
|
39
|
+
# @param method [Symbol] the method where the check is run
|
40
|
+
# @raise [RuntimeError] if called from inside a transaction
|
41
|
+
# @return [void]
|
28
42
|
def fail_in_transaction!(method, message: "not supported inside trasactions")
|
29
43
|
raise "#{owner.class}##{method} #{message}" if in_transaction?
|
30
44
|
end
|
31
45
|
|
46
|
+
# Throws an exception if there is not a current transaction
|
47
|
+
# @param method [Symbol] the method where the check is run
|
48
|
+
# @raise [RuntimeError] if called from outside a transaction
|
49
|
+
# @return [void]
|
32
50
|
def fail_outside_transaction!(method)
|
33
51
|
raise "#{owner.class}##{method} can only be called inside a transaction" if !in_transaction?
|
34
52
|
end
|
@@ -3,11 +3,15 @@ require "imap/backup/serializer"
|
|
3
3
|
module Imap; end
|
4
4
|
|
5
5
|
module Imap::Backup
|
6
|
+
# Finds a name that can be used to rename a serialized folder
|
6
7
|
class Serializer::UnusedNameFinder
|
8
|
+
# @param serializer [Serializer] a folder serializer
|
7
9
|
def initialize(serializer:)
|
8
10
|
@serializer = serializer
|
9
11
|
end
|
10
12
|
|
13
|
+
# Finds the name
|
14
|
+
# @return [String] the name
|
11
15
|
def run
|
12
16
|
digit = 0
|
13
17
|
folder = nil
|
@@ -5,7 +5,9 @@ require "imap/backup/serializer/imap"
|
|
5
5
|
module Imap; end
|
6
6
|
|
7
7
|
module Imap::Backup
|
8
|
+
# Migrates serialized folder metadata from the version 2 format to the version 3 format
|
8
9
|
class Serializer::Version2Migrator
|
10
|
+
# @param folder_path [String] the base pathv(without extension) of the folder backup
|
9
11
|
def initialize(folder_path)
|
10
12
|
@folder_path = folder_path
|
11
13
|
end
|
@@ -21,6 +23,8 @@ module Imap::Backup
|
|
21
23
|
true
|
22
24
|
end
|
23
25
|
|
26
|
+
# Runs the migration
|
27
|
+
# @return [Boolean] whether the migration was run
|
24
28
|
def run
|
25
29
|
return false if !required?
|
26
30
|
|
@@ -15,7 +15,11 @@ require "imap/backup/serializer/unused_name_finder"
|
|
15
15
|
module Imap; end
|
16
16
|
|
17
17
|
module Imap::Backup
|
18
|
+
# Handles serialization for a folder
|
18
19
|
class Serializer
|
20
|
+
# @param path [String] an account's backup path
|
21
|
+
# @param folder [String] a folder name
|
22
|
+
# @return [String] the full path to a serialized folder (without file extensions)
|
19
23
|
def self.folder_path_for(path:, folder:)
|
20
24
|
relative = File.join(path, folder)
|
21
25
|
File.expand_path(relative)
|
@@ -26,9 +30,13 @@ module Imap::Backup
|
|
26
30
|
def_delegator :mbox, :pathname, :mbox_pathname
|
27
31
|
def_delegators :imap, :get, :messages, :uid_validity, :uids, :update_uid
|
28
32
|
|
33
|
+
# @return [String] a folder name
|
29
34
|
attr_reader :folder
|
35
|
+
# @return [String] an account's backup path
|
30
36
|
attr_reader :path
|
31
37
|
|
38
|
+
# @param path [String] an account's backup path
|
39
|
+
# @param folder [String] a folder name
|
32
40
|
def initialize(path, folder)
|
33
41
|
@path = path
|
34
42
|
@folder = folder
|
@@ -38,14 +46,17 @@ module Imap::Backup
|
|
38
46
|
# Calls the supplied block.
|
39
47
|
# This method is present so that this class implements the same
|
40
48
|
# interface as {DelayedMetadataSerializer}
|
49
|
+
# @param block [block] the block that is wrapped by the transaction
|
41
50
|
#
|
42
51
|
# @return [void]
|
43
52
|
def transaction(&block)
|
44
53
|
block.call
|
45
54
|
end
|
46
55
|
|
47
|
-
#
|
48
|
-
#
|
56
|
+
# Checks that the metadata files are valid, migrates the metadata file
|
57
|
+
# from older versions, if necessary,
|
58
|
+
# or deletes any existing files if the pair are not valid.
|
59
|
+
# @return [Boolean] indicates whether there are existing, valid files
|
49
60
|
def validate!
|
50
61
|
return true if @validated
|
51
62
|
|
@@ -61,16 +72,33 @@ module Imap::Backup
|
|
61
72
|
false
|
62
73
|
end
|
63
74
|
|
75
|
+
# Checks that the folder's data is stored correctly
|
76
|
+
# @return [void]
|
64
77
|
def check_integrity!
|
65
78
|
IntegrityChecker.new(imap: imap, mbox: mbox).run
|
66
79
|
end
|
67
80
|
|
81
|
+
# Deletes the serialized data
|
82
|
+
# @return [void]
|
68
83
|
def delete
|
69
84
|
imap.delete
|
70
85
|
mbox.delete
|
71
86
|
reload
|
72
87
|
end
|
73
88
|
|
89
|
+
# Sets the folder's UID validity.
|
90
|
+
# If the existing value is nil, it sets the new value
|
91
|
+
# and ensures that both the metadata file and the mailbox
|
92
|
+
# are saved.
|
93
|
+
# If the supplied value is the same as the existing value,
|
94
|
+
# it does nothing.
|
95
|
+
# If the supplied valued is *different* to the existing value,
|
96
|
+
# it renames the existing folder to a new name, and creates a
|
97
|
+
# new folder with the supplied value.
|
98
|
+
#
|
99
|
+
# @param value [Integer] The new UID validity value
|
100
|
+
#
|
101
|
+
# @return [String, nil] The name of the new folder
|
74
102
|
def apply_uid_validity(value)
|
75
103
|
validate!
|
76
104
|
|
@@ -86,12 +114,22 @@ module Imap::Backup
|
|
86
114
|
end
|
87
115
|
end
|
88
116
|
|
117
|
+
# Overwrites the UID validity of the folder
|
118
|
+
# and ensures that both the metadata file and the mailbox
|
119
|
+
# are saved.
|
120
|
+
# @param value [Integer] the new UID validity
|
121
|
+
# @return [void]
|
89
122
|
def force_uid_validity(value)
|
90
123
|
validate!
|
91
124
|
|
92
125
|
internal_force_uid_validity(value)
|
93
126
|
end
|
94
127
|
|
128
|
+
# Appends a message to the serialized data
|
129
|
+
# @param uid [Integer] the message's UID
|
130
|
+
# @param message [Integer] the message text
|
131
|
+
# @param flags [Array[Symbol]] the message's flags
|
132
|
+
# @return [void]
|
95
133
|
def append(uid, message, flags)
|
96
134
|
validate!
|
97
135
|
|
@@ -99,6 +137,10 @@ module Imap::Backup
|
|
99
137
|
appender.append(uid: uid, message: message, flags: flags)
|
100
138
|
end
|
101
139
|
|
140
|
+
# Updates a messages flags
|
141
|
+
# @param uid [Integer] the message's UID
|
142
|
+
# @param flags [Array<Symbol>] the flags to set on the message
|
143
|
+
# @return [void]
|
102
144
|
def update(uid, flags: nil)
|
103
145
|
message = imap.get(uid)
|
104
146
|
return if !message
|
@@ -107,6 +149,10 @@ module Imap::Backup
|
|
107
149
|
imap.save
|
108
150
|
end
|
109
151
|
|
152
|
+
# Enumerates over a series of messages.
|
153
|
+
# When called without a block, returns an Enumerator
|
154
|
+
# @param required_uids [Array<Integer>] the UIDs of the message to enumerate over
|
155
|
+
# @return [Enumerator, void]
|
110
156
|
def each_message(required_uids = nil, &block)
|
111
157
|
return enum_for(:each_message, required_uids) if !block
|
112
158
|
|
@@ -118,6 +164,10 @@ module Imap::Backup
|
|
118
164
|
enumerator.run(uids: required_uids, &block)
|
119
165
|
end
|
120
166
|
|
167
|
+
# Calls the supplied block on each message in the folder
|
168
|
+
# and discards those for which the block returns a false result
|
169
|
+
# @param block [block] the block to call
|
170
|
+
# @return [void]
|
121
171
|
def filter(&block)
|
122
172
|
temp_name = Serializer::UnusedNameFinder.new(serializer: self).run
|
123
173
|
temp_folder_path = self.class.folder_path_for(path: path, folder: temp_name)
|
@@ -137,14 +187,18 @@ module Imap::Backup
|
|
137
187
|
reload
|
138
188
|
end
|
139
189
|
|
190
|
+
# @return [String] the path to the serialized folder (without file extensions)
|
140
191
|
def folder_path
|
141
192
|
self.class.folder_path_for(path: path, folder: sanitized)
|
142
193
|
end
|
143
194
|
|
195
|
+
# @return [String] The folder's name adapted for using as a file name
|
144
196
|
def sanitized
|
145
197
|
@sanitized ||= Naming.to_local_path(folder)
|
146
198
|
end
|
147
199
|
|
200
|
+
# Forces a reload of the serialized files
|
201
|
+
# @return [void]
|
148
202
|
def reload
|
149
203
|
@imap = nil
|
150
204
|
@mbox = nil
|
@@ -8,12 +8,18 @@ module Imap::Backup
|
|
8
8
|
class Setup; end
|
9
9
|
class Setup::Account; end
|
10
10
|
|
11
|
+
# Displays the header to the account modification menu
|
11
12
|
class Setup::Account::Header
|
13
|
+
# @param menu [Highline::Menu] the menu
|
14
|
+
# @param account [Account] an Account
|
12
15
|
def initialize(menu:, account:)
|
13
16
|
@menu = menu
|
14
17
|
@account = account
|
15
18
|
end
|
16
19
|
|
20
|
+
# Displays the header
|
21
|
+
#
|
22
|
+
# @return [void]
|
17
23
|
def run
|
18
24
|
rows = [
|
19
25
|
email,
|
@@ -10,13 +10,19 @@ module Imap; end
|
|
10
10
|
module Imap::Backup
|
11
11
|
class Setup; end
|
12
12
|
|
13
|
+
# Handles interactive account setup
|
13
14
|
class Setup::Account
|
15
|
+
# @param config [Configuration] the application configuration
|
16
|
+
# @param account [Account] an Account
|
17
|
+
# @param highline [Higline] the configured Highline instance
|
14
18
|
def initialize(config, account, highline)
|
15
19
|
@account = account
|
16
20
|
@config = config
|
17
21
|
@highline = highline
|
18
22
|
end
|
19
23
|
|
24
|
+
# Shows the menu
|
25
|
+
# @return [void]
|
20
26
|
def run
|
21
27
|
if !account.local_path
|
22
28
|
account.local_path = File.join(config.path, account.username.tr("@", "_"))
|
@@ -3,11 +3,17 @@ module Imap; end
|
|
3
3
|
module Imap::Backup
|
4
4
|
class Setup; end
|
5
5
|
|
6
|
+
# Implements interactively requesting information from the user
|
6
7
|
class Setup::Asker
|
8
|
+
# @param highline [Higline] the configured Highline instance
|
7
9
|
def initialize(highline)
|
8
10
|
@highline = highline
|
9
11
|
end
|
10
12
|
|
13
|
+
# Asks for a email address
|
14
|
+
#
|
15
|
+
# @param default [String] the existing email address
|
16
|
+
# @return [String] the email address
|
11
17
|
def email(default = "")
|
12
18
|
highline.ask("email address: ") do |q|
|
13
19
|
q.default = default
|
@@ -16,6 +22,9 @@ module Imap::Backup
|
|
16
22
|
end
|
17
23
|
end
|
18
24
|
|
25
|
+
# Asks for a password
|
26
|
+
#
|
27
|
+
# @return [String] the password
|
19
28
|
def password
|
20
29
|
password = highline.ask("password: ") { |q| q.echo = false }
|
21
30
|
confirmation = highline.ask("repeat password: ") { |q| q.echo = false }
|
@@ -29,10 +38,17 @@ module Imap::Backup
|
|
29
38
|
password
|
30
39
|
end
|
31
40
|
|
41
|
+
# Asks for a email address using the configured menu handler
|
42
|
+
#
|
43
|
+
# @param default [String] the existing email address
|
44
|
+
# @return [String] the email address
|
32
45
|
def self.email(default = "")
|
33
46
|
new(Setup.highline).email(default)
|
34
47
|
end
|
35
48
|
|
49
|
+
# Asks for a password using the configured menu handler
|
50
|
+
#
|
51
|
+
# @return [String] the password
|
36
52
|
def self.password
|
37
53
|
new(Setup.highline).password
|
38
54
|
end
|
@@ -3,12 +3,18 @@ module Imap; end
|
|
3
3
|
module Imap::Backup
|
4
4
|
class Setup; end
|
5
5
|
|
6
|
+
# Requests an updated backup path from the user
|
6
7
|
class Setup::BackupPath
|
8
|
+
# @param account [Account] an Account
|
9
|
+
# @param config [Configuration] the application configuration
|
7
10
|
def initialize(account:, config:)
|
8
11
|
@account = account
|
9
12
|
@config = config
|
10
13
|
end
|
11
14
|
|
15
|
+
# Asks the user for a backup path
|
16
|
+
#
|
17
|
+
# @return [void]
|
12
18
|
def run
|
13
19
|
account.local_path = highline.ask("backup directory: ") do |q|
|
14
20
|
q.default = account.local_path
|
@@ -5,11 +5,16 @@ module Imap; end
|
|
5
5
|
module Imap::Backup
|
6
6
|
class Setup; end
|
7
7
|
|
8
|
+
# Attempts to login to an account and reports the result
|
8
9
|
class Setup::ConnectionTester
|
10
|
+
# @param account [Account] an Account
|
9
11
|
def initialize(account)
|
10
12
|
@account = account
|
11
13
|
end
|
12
14
|
|
15
|
+
# Carries out the attempted login and indicates
|
16
|
+
# whether it was successful
|
17
|
+
# @return [void]
|
13
18
|
def test
|
14
19
|
account.client.login
|
15
20
|
"Connection successful"
|
@@ -6,12 +6,18 @@ module Imap; end
|
|
6
6
|
module Imap::Backup
|
7
7
|
class Setup; end
|
8
8
|
|
9
|
+
# Asks the user for a new email address
|
9
10
|
class Setup::EmailChanger
|
11
|
+
# @param account [Account] an Account
|
12
|
+
# @param config [Configuration] the application configuration
|
10
13
|
def initialize(account:, config:)
|
11
14
|
@account = account
|
12
15
|
@config = config
|
13
16
|
end
|
14
17
|
|
18
|
+
# Asks the user for an email address,
|
19
|
+
# ensuring that the supplied address is not an existing account
|
20
|
+
# @return [void]
|
15
21
|
def run
|
16
22
|
username = Setup::Asker.email(account.username)
|
17
23
|
other_accounts = config.accounts.reject { |a| a == account }
|
@@ -5,11 +5,15 @@ module Imap; end
|
|
5
5
|
module Imap::Backup
|
6
6
|
class Setup; end
|
7
7
|
|
8
|
+
# Allows the user to select and deselect folders to be backed up
|
8
9
|
class Setup::FolderChooser
|
10
|
+
# @param account [Account] an Account
|
9
11
|
def initialize(account)
|
10
12
|
@account = account
|
11
13
|
end
|
12
14
|
|
15
|
+
# Lists account folders and allows the user to toggle whther they are selected
|
16
|
+
# @return [void]
|
13
17
|
def run
|
14
18
|
if client.nil?
|
15
19
|
highline.ask "Press a key "
|
@@ -5,11 +5,15 @@ module Imap::Backup; end
|
|
5
5
|
class Imap::Backup::Setup; end
|
6
6
|
|
7
7
|
class Imap::Backup::Setup::GlobalOptions
|
8
|
+
# Allows changing the globally configured download strategy
|
8
9
|
class DownloadStrategyChooser
|
10
|
+
# @param config [Configuration] the application configuration
|
9
11
|
def initialize(config:)
|
10
12
|
@config = config
|
11
13
|
end
|
12
14
|
|
15
|
+
# Shows the menu
|
16
|
+
# @return [void]
|
13
17
|
def run
|
14
18
|
catch :done do
|
15
19
|
loop do
|
@@ -6,11 +6,15 @@ module Imap; end
|
|
6
6
|
module Imap::Backup
|
7
7
|
class Setup; end
|
8
8
|
|
9
|
+
# Shows the menu of global options
|
9
10
|
class Setup::GlobalOptions
|
11
|
+
# @param config [Configuration] the application configuration
|
10
12
|
def initialize(config:)
|
11
13
|
@config = config
|
12
14
|
end
|
13
15
|
|
16
|
+
# Shows the menu
|
17
|
+
# @return [void]
|
14
18
|
def run
|
15
19
|
catch :done do
|
16
20
|
loop do
|
@@ -5,11 +5,14 @@ module Imap; end
|
|
5
5
|
module Imap::Backup
|
6
6
|
class Setup; end
|
7
7
|
|
8
|
+
# Helpers for the setup system
|
8
9
|
class Setup::Helpers
|
10
|
+
# @return [String] the prefix for setup menus
|
9
11
|
def title_prefix
|
10
12
|
"imap-backup -"
|
11
13
|
end
|
12
14
|
|
15
|
+
# @return [String] the current application version
|
13
16
|
def version
|
14
17
|
VERSION
|
15
18
|
end
|
data/lib/imap/backup/setup.rb
CHANGED
@@ -10,16 +10,21 @@ require "imap/backup/setup/helpers"
|
|
10
10
|
module Imap; end
|
11
11
|
|
12
12
|
module Imap::Backup
|
13
|
+
# Interactively updates the application's configuration file
|
13
14
|
class Setup
|
14
15
|
class << self
|
16
|
+
# @return [Highline]
|
15
17
|
attr_accessor :highline
|
16
18
|
end
|
17
19
|
self.highline = HighLine.new
|
18
20
|
|
21
|
+
# @param config [Configuration] the application configuration
|
19
22
|
def initialize(config:)
|
20
23
|
@config = config
|
21
24
|
end
|
22
25
|
|
26
|
+
# Shows the menu
|
27
|
+
# @return [void]
|
23
28
|
def run
|
24
29
|
catch :done do
|
25
30
|
loop do
|
@@ -5,17 +5,24 @@ module Imap; end
|
|
5
5
|
module Imap::Backup
|
6
6
|
module Text; end
|
7
7
|
|
8
|
+
# Wraps standard output and hides passwords from debug output
|
9
|
+
# Any text matching Net::IMAP debug output of passwords is sanitized
|
8
10
|
class Text::Sanitizer
|
9
11
|
extend Forwardable
|
10
12
|
|
11
13
|
delegate puts: :output
|
12
14
|
delegate write: :output
|
13
15
|
|
16
|
+
# @param output [IO] the stream to write output to
|
14
17
|
def initialize(output)
|
15
18
|
@output = output
|
16
19
|
@current = ""
|
17
20
|
end
|
18
21
|
|
22
|
+
# Outputs everything up to the last newline character,
|
23
|
+
# storing whatever follows the newline.
|
24
|
+
# @param args [Array<String>] lines of text
|
25
|
+
# @return [void]
|
19
26
|
def print(*args)
|
20
27
|
@current << args.join
|
21
28
|
loop do
|
@@ -28,6 +35,8 @@ module Imap::Backup
|
|
28
35
|
end
|
29
36
|
end
|
30
37
|
|
38
|
+
# Outputs any text still not printed
|
39
|
+
# @return [void]
|
31
40
|
def flush
|
32
41
|
return if @current == ""
|
33
42
|
|