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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -137
  3. data/docs/documentation.md +19 -0
  4. data/imap-backup.gemspec +1 -1
  5. data/lib/imap/backup/account/backup.rb +2 -0
  6. data/lib/imap/backup/account/backup_folders.rb +7 -1
  7. data/lib/imap/backup/account/client_factory.rb +1 -0
  8. data/lib/imap/backup/account/folder.rb +26 -0
  9. data/lib/imap/backup/account/folder_backup.rb +4 -1
  10. data/lib/imap/backup/account/folder_ensurer.rb +3 -0
  11. data/lib/imap/backup/account/local_only_folder_deleter.rb +3 -1
  12. data/lib/imap/backup/account/restore.rb +2 -0
  13. data/lib/imap/backup/account/serialized_folders.rb +31 -1
  14. data/lib/imap/backup/account.rb +34 -18
  15. data/lib/imap/backup/cli/backup.rb +3 -0
  16. data/lib/imap/backup/cli/folder_enumerator.rb +6 -0
  17. data/lib/imap/backup/cli/helpers.rb +13 -0
  18. data/lib/imap/backup/cli/local/check.rb +3 -0
  19. data/lib/imap/backup/cli/local.rb +14 -1
  20. data/lib/imap/backup/cli/remote.rb +7 -0
  21. data/lib/imap/backup/cli/restore.rb +4 -0
  22. data/lib/imap/backup/cli/setup.rb +3 -0
  23. data/lib/imap/backup/cli/single/backup.rb +3 -0
  24. data/lib/imap/backup/cli/single.rb +4 -0
  25. data/lib/imap/backup/cli/stats.rb +3 -0
  26. data/lib/imap/backup/cli/transfer.rb +8 -0
  27. data/lib/imap/backup/cli/utils.rb +7 -1
  28. data/lib/imap/backup/cli.rb +8 -0
  29. data/lib/imap/backup/client/apple_mail.rb +2 -0
  30. data/lib/imap/backup/client/automatic_login_wrapper.rb +9 -1
  31. data/lib/imap/backup/client/default.rb +15 -4
  32. data/lib/imap/backup/configuration.rb +13 -0
  33. data/lib/imap/backup/configuration_not_found.rb +1 -0
  34. data/lib/imap/backup/downloader.rb +4 -0
  35. data/lib/imap/backup/email/mboxrd/message.rb +14 -0
  36. data/lib/imap/backup/email/provider/apple_mail.rb +2 -0
  37. data/lib/imap/backup/email/provider/base.rb +3 -3
  38. data/lib/imap/backup/email/provider/fastmail.rb +2 -0
  39. data/lib/imap/backup/email/provider/gmail.rb +2 -0
  40. data/lib/imap/backup/email/provider/purelymail.rb +2 -0
  41. data/lib/imap/backup/email/provider/unknown.rb +2 -6
  42. data/lib/imap/backup/email/provider.rb +5 -0
  43. data/lib/imap/backup/file_mode.rb +2 -0
  44. data/lib/imap/backup/flag_refresher.rb +4 -0
  45. data/lib/imap/backup/local_only_message_deleter.rb +2 -0
  46. data/lib/imap/backup/logger.rb +18 -0
  47. data/lib/imap/backup/migrator.rb +3 -0
  48. data/lib/imap/backup/mirror/map.rb +21 -0
  49. data/lib/imap/backup/mirror.rb +8 -0
  50. data/lib/imap/backup/naming.rb +10 -1
  51. data/lib/imap/backup/retry_on_error.rb +9 -0
  52. data/lib/imap/backup/serializer/appender.rb +9 -0
  53. data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +4 -0
  54. data/lib/imap/backup/serializer/folder_maker.rb +1 -0
  55. data/lib/imap/backup/serializer/imap.rb +38 -2
  56. data/lib/imap/backup/serializer/integrity_checker.rb +1 -0
  57. data/lib/imap/backup/serializer/mbox.rb +26 -0
  58. data/lib/imap/backup/serializer/message.rb +13 -0
  59. data/lib/imap/backup/serializer/message_enumerator.rb +10 -2
  60. data/lib/imap/backup/serializer/permission_checker.rb +6 -0
  61. data/lib/imap/backup/serializer/transaction.rb +18 -0
  62. data/lib/imap/backup/serializer/unused_name_finder.rb +4 -0
  63. data/lib/imap/backup/serializer/version2_migrator.rb +4 -0
  64. data/lib/imap/backup/serializer.rb +56 -2
  65. data/lib/imap/backup/setup/account/header.rb +6 -0
  66. data/lib/imap/backup/setup/account.rb +6 -0
  67. data/lib/imap/backup/setup/asker.rb +16 -0
  68. data/lib/imap/backup/setup/backup_path.rb +6 -0
  69. data/lib/imap/backup/setup/connection_tester.rb +5 -0
  70. data/lib/imap/backup/setup/email_changer.rb +6 -0
  71. data/lib/imap/backup/setup/folder_chooser.rb +4 -0
  72. data/lib/imap/backup/setup/global_options/download_strategy_chooser.rb +4 -0
  73. data/lib/imap/backup/setup/global_options.rb +4 -0
  74. data/lib/imap/backup/setup/helpers.rb +3 -0
  75. data/lib/imap/backup/setup.rb +5 -0
  76. data/lib/imap/backup/text/sanitizer.rb +9 -0
  77. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +8 -1
  78. data/lib/imap/backup/uploader.rb +5 -0
  79. data/lib/imap/backup/version.rb +7 -2
  80. metadata +11 -13
  81. data/docs/api.md +0 -20
  82. data/docs/development.md +0 -110
  83. 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
- attr_reader :imap
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
- # Returns true if there are existing, valid files
48
- # false otherwise (in which case any existing files are deleted)
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
@@ -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