imap-backup 14.4.4 → 14.5.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -14,10 +14,9 @@ module Imap::Backup
14
14
  # The username of the account (usually the same as the email address)
15
15
  # @return [String]
16
16
  attr_reader :username
17
- # The password of the Account
17
+ # @return [String] password of the Account
18
18
  attr_reader :password
19
- # The path where backups will be saved
20
- # @return [String]
19
+ # @return [String] the path where backups will be saved
21
20
  attr_reader :local_path
22
21
  # @overload folders
23
22
  # The list of folders that have been configured for the Account
@@ -38,13 +37,11 @@ module Imap::Backup
38
37
  attr_reader :folder_blacklist
39
38
  # Should all emails be backed up progressively, or should emails
40
39
  # which are deleted from the server be deleted locally?
40
+ # @return [Boolean]
41
41
  attr_reader :mirror_mode
42
42
  # The address of the IMAP server
43
43
  # @return [String]
44
44
  attr_reader :server
45
- # Extra options to be passed to the IMAP server when connecting
46
- # @return [Hash, void]
47
- attr_reader :connection_options
48
45
  # The name of the download strategy to adopt during backups
49
46
  # @return [String]
50
47
  attr_accessor :download_strategy
@@ -61,18 +58,17 @@ module Imap::Backup
61
58
  # mark messages as '\Seen' when accessed).
62
59
  # @return [Boolean]
63
60
  attr_reader :reset_seen_flags_after_fetch
64
- # Tracks changes to the Account's attributes
65
- attr_reader :changes
66
61
 
67
62
  def initialize(options)
68
63
  @username = options[:username]
69
64
  @password = options[:password]
70
65
  @local_path = options[:local_path]
71
66
  @folders = options[:folders]
72
- @folder_blacklist = options[:folder_blacklist]
73
- @mirror_mode = options[:mirror_mode]
67
+ @folder_blacklist = options[:folder_blacklist] || false
68
+ @mirror_mode = options[:mirror_mode] || false
74
69
  @server = options[:server]
75
- @connection_options = options[:connection_options]
70
+ @connection_options = nil
71
+ @supplied_connection_options = options[:connection_options]
76
72
  @download_strategy = options[:download_strategy]
77
73
  @multi_fetch_size_orignal = options[:multi_fetch_size]
78
74
  @reset_seen_flags_after_fetch = options[:reset_seen_flags_after_fetch]
@@ -123,6 +119,7 @@ module Imap::Backup
123
119
  end
124
120
 
125
121
  # Resets the store of changes, indicating that the current state is the saved state
122
+ # @return [void]
126
123
  def clear_changes
127
124
  @changes = {}
128
125
  end
@@ -134,16 +131,12 @@ module Imap::Backup
134
131
  @marked_for_deletion = true
135
132
  end
136
133
 
137
- # Indicates whether the account has been flagged for deletion during setup
138
- #
139
- # @return [Boolean]
134
+ # @return [Boolean] whether the account has been flagged for deletion during setup
140
135
  def marked_for_deletion?
141
136
  @marked_for_deletion
142
137
  end
143
138
 
144
- # Returns all Account data for serialization
145
- #
146
- # @return [Hash]
139
+ # @return [Hash] all Account data for serialization
147
140
  def to_h
148
141
  h = {username: @username, password: @password}
149
142
  h[:local_path] = @local_path if @local_path
@@ -151,7 +144,7 @@ module Imap::Backup
151
144
  h[:folder_blacklist] = true if @folder_blacklist
152
145
  h[:mirror_mode] = true if @mirror_mode
153
146
  h[:server] = @server if @server
154
- h[:connection_options] = @connection_options if @connection_options
147
+ h[:connection_options] = @connection_options if connection_options
155
148
  h[:multi_fetch_size] = multi_fetch_size
156
149
  if @reset_seen_flags_after_fetch
157
150
  h[:reset_seen_flags_after_fetch] = @reset_seen_flags_after_fetch
@@ -180,25 +173,45 @@ module Imap::Backup
180
173
  update(:local_path, value)
181
174
  end
182
175
 
176
+ # @raise [RuntimeError] if the supplied value is not an Array
177
+ # @return [void]
183
178
  def folders=(value)
184
179
  raise "folders must be an Array" if !value.is_a?(Array)
185
180
 
186
181
  update(:folders, value)
187
182
  end
188
183
 
184
+ # @return [void]
189
185
  def folder_blacklist=(value)
190
186
  update(:folder_blacklist, value)
191
187
  end
192
188
 
189
+ # @return [void]
193
190
  def mirror_mode=(value)
194
191
  update(:mirror_mode, value)
195
192
  end
196
193
 
194
+ # @return [void]
197
195
  def server=(value)
198
196
  update(:server, value)
199
197
  end
200
198
 
199
+ # Extra options to be passed to the IMAP server when connecting
200
+ # @return [Hash, void]
201
+ def connection_options
202
+ @connection_options ||=
203
+ case @supplied_connection_options
204
+ when String
205
+ JSON.parse(@supplied_connection_options, symbolize_names: true)
206
+ else
207
+ @supplied_connection_options
208
+ end
209
+ end
210
+
211
+ # @return [void]
201
212
  def connection_options=(value)
213
+ # Ensure we've loaded the connection_options
214
+ connection_options
202
215
  parsed =
203
216
  if value == ""
204
217
  nil
@@ -232,12 +245,15 @@ module Imap::Backup
232
245
  update(:multi_fetch_size, parsed)
233
246
  end
234
247
 
248
+ # @return [void]
235
249
  def reset_seen_flags_after_fetch=(value)
236
250
  update(:reset_seen_flags_after_fetch, value)
237
251
  end
238
252
 
239
253
  private
240
254
 
255
+ attr_reader :changes
256
+
241
257
  def update(field, value)
242
258
  key = :"@#{field}"
243
259
  if changes[field]
@@ -10,6 +10,7 @@ module Imap; end
10
10
  module Imap::Backup
11
11
  class CLI < Thor; end
12
12
 
13
+ # Runs backups of configured accounts
13
14
  class CLI::Backup < Thor
14
15
  include Thor::Actions
15
16
  include CLI::Helpers
@@ -19,6 +20,8 @@ module Imap::Backup
19
20
  @options = options
20
21
  end
21
22
 
23
+ # @!method run
24
+ # @return [void]
22
25
  no_commands do
23
26
  def run
24
27
  config = load_config(**options)
@@ -9,6 +9,7 @@ module Imap; end
9
9
  module Imap::Backup
10
10
  class CLI < Thor; end
11
11
 
12
+ # Implements a folder enumerator for backed-up accounts
12
13
  class CLI::FolderEnumerator
13
14
  def initialize(
14
15
  destination:,
@@ -26,6 +27,11 @@ module Imap::Backup
26
27
  @source_prefix = source_prefix
27
28
  end
28
29
 
30
+ # Enumerates backed-up folders
31
+ # When called without a block, returns an Enumerator
32
+ # @yieldparam serializer [Serializer] the folder's serializer
33
+ # @yieldparam folder [Account::Folder] the online folder
34
+ # @return [Enumerator, void]
29
35
  def each
30
36
  return enum_for(:each) if !block_given?
31
37
 
@@ -8,6 +8,7 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  class CLI < Thor; end
10
10
 
11
+ # Provides helper methods for CLI classes
11
12
  module CLI::Helpers
12
13
  def self.included(base)
13
14
  base.class_eval do
@@ -73,6 +74,10 @@ module Imap::Backup
73
74
  end
74
75
  end
75
76
 
77
+ # Processes command-line parameters
78
+ # @return [Hash] the supplied command-line parameters with
79
+ # with hyphens in keys replaced by underscores
80
+ # and the keys converted to Symbols
76
81
  def options
77
82
  @symbolized_options ||= # rubocop:disable Naming/MemoizedInstanceVariableName
78
83
  begin
@@ -89,6 +94,9 @@ module Imap::Backup
89
94
  end
90
95
  end
91
96
 
97
+ # Loads the application configuration
98
+ # @raise [ConfigurationNotFound] if the configuration file does not exist
99
+ # @return [Configuration]
92
100
  def load_config(**options)
93
101
  path = options[:config]
94
102
  require_exists = options.key?(:require_exists) ? options[:require_exists] : true
@@ -102,6 +110,8 @@ module Imap::Backup
102
110
  Configuration.new(path: path)
103
111
  end
104
112
 
113
+ # @raise [RuntimeError] if the account does not exist
114
+ # @return [Account] the Account information for the email address
105
115
  def account(config, email)
106
116
  account = config.accounts.find { |a| a.username == email }
107
117
  raise "#{email} is not a configured account" if !account
@@ -109,6 +119,9 @@ module Imap::Backup
109
119
  account
110
120
  end
111
121
 
122
+ # @return [Array<Account>] If email addresses have been specified
123
+ # returns the Account configurations for them.
124
+ # If non have been specified, returns all account configurations
112
125
  def requested_accounts(config)
113
126
  emails = (options[:accounts] || "").split(",")
114
127
  if emails.any?
@@ -10,6 +10,7 @@ module Imap::Backup
10
10
  class CLI; end
11
11
  class CLI::Local < Thor; end
12
12
 
13
+ # Runs integrity check on local backups
13
14
  class CLI::Local::Check
14
15
  include CLI::Helpers
15
16
 
@@ -17,6 +18,8 @@ module Imap::Backup
17
18
  @options = options
18
19
  end
19
20
 
21
+ # Runs the check
22
+ # @return [void]
20
23
  def run
21
24
  results = requested_accounts(config).map do |account|
22
25
  serialized_folders = Account::SerializedFolders.new(account: account)
@@ -10,6 +10,7 @@ module Imap; end
10
10
  module Imap::Backup
11
11
  class CLI < Thor; end
12
12
 
13
+ # Implements the CLI functions relating to local storage
13
14
  class CLI::Local < Thor
14
15
  include Thor::Actions
15
16
  include CLI::Helpers
@@ -19,6 +20,8 @@ module Imap::Backup
19
20
  format_option
20
21
  quiet_option
21
22
  verbose_option
23
+ # Lists configured accounts
24
+ # @return [void]
22
25
  def accounts
23
26
  names = config.accounts.map(&:username)
24
27
  case options[:format]
@@ -44,6 +47,8 @@ module Imap::Backup
44
47
  format_option
45
48
  quiet_option
46
49
  verbose_option
50
+ # Runs integrity checks on backups
51
+ # @return [void]
47
52
  def check
48
53
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
49
54
  Check.new(non_logging_options).run
@@ -54,6 +59,8 @@ module Imap::Backup
54
59
  format_option
55
60
  quiet_option
56
61
  verbose_option
62
+ # Lists backed-up folders for an account
63
+ # @return [void]
57
64
  def folders(email)
58
65
  account = account(config, email)
59
66
 
@@ -63,7 +70,7 @@ module Imap::Backup
63
70
  list = serialized_folders.map { |_s, f| {name: f.name} }
64
71
  Kernel.puts list.to_json
65
72
  else
66
- serialized_folders.each do |_s, f|
73
+ serialized_folders.each_value do |f|
67
74
  Kernel.puts %("#{f.name}")
68
75
  end
69
76
  end
@@ -74,6 +81,9 @@ module Imap::Backup
74
81
  format_option
75
82
  quiet_option
76
83
  verbose_option
84
+ # Lists backed-up emails for an account folder
85
+ # @raise [RuntimeError] if the folder does not exist
86
+ # @return [void]
77
87
  def list(email, folder_name)
78
88
  account = account(config, email)
79
89
 
@@ -101,6 +111,9 @@ module Imap::Backup
101
111
  format_option
102
112
  quiet_option
103
113
  verbose_option
114
+ # Shows the content of one or more backed-up email messages
115
+ # @raise [RuntimeError] if the folder does not exist
116
+ # @return [void]
104
117
  def show(email, folder_name, uids)
105
118
  account = account(config, email)
106
119
 
@@ -8,6 +8,7 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  class CLI < Thor; end
10
10
 
11
+ # Implements the CLI functions relating to configured online accounts
11
12
  class CLI::Remote < Thor
12
13
  include Thor::Actions
13
14
  include CLI::Helpers
@@ -17,6 +18,8 @@ module Imap::Backup
17
18
  format_option
18
19
  quiet_option
19
20
  verbose_option
21
+ # Prints an account's folders
22
+ # @return [void]
20
23
  def folders(email)
21
24
  Imap::Backup::Logger.setup_logging options
22
25
  folder_names = folder_names(email)
@@ -36,6 +39,8 @@ module Imap::Backup
36
39
  format_option
37
40
  quiet_option
38
41
  verbose_option
42
+ # Prints an account's IMAP capabilities
43
+ # @return [void]
39
44
  def capabilities(email)
40
45
  Imap::Backup::Logger.setup_logging options
41
46
  config = load_config(**options)
@@ -55,6 +60,8 @@ module Imap::Backup
55
60
  format_option
56
61
  quiet_option
57
62
  verbose_option
63
+ # Prints an account's IMAP namespaces
64
+ # @return [void]
58
65
  def namespaces(email)
59
66
  Imap::Backup::Logger.setup_logging options
60
67
  config = load_config(**options)
@@ -8,6 +8,7 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  class CLI < Thor; end
10
10
 
11
+ # Restores backups for one or more accounts
11
12
  class CLI::Restore < Thor
12
13
  include Thor::Actions
13
14
  include CLI::Helpers
@@ -18,6 +19,9 @@ module Imap::Backup
18
19
  @options = options
19
20
  end
20
21
 
22
+ # @!method run
23
+ # @raise [RuntimeError] if no email is specified
24
+ # @return [void]
21
25
  no_commands do
22
26
  def run
23
27
  config = load_config(**options)
@@ -8,6 +8,7 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  class CLI < Thor; end
10
10
 
11
+ # Runs the menu-driven setup program
11
12
  class CLI::Setup < Thor
12
13
  include Thor::Actions
13
14
  include CLI::Helpers
@@ -17,6 +18,8 @@ module Imap::Backup
17
18
  @options = options
18
19
  end
19
20
 
21
+ # @!method run
22
+ # @return [void]
20
23
  no_commands do
21
24
  def run
22
25
  config = load_config(**options, require_exists: false)
@@ -10,12 +10,15 @@ module Imap::Backup
10
10
  class CLI < Thor; end
11
11
  class CLI::Single < Thor; end
12
12
 
13
+ # Runs a backup without relying on existing configuration
13
14
  class CLI::Single::Backup
14
15
  def initialize(options)
15
16
  @options = options
16
17
  @password = nil
17
18
  end
18
19
 
20
+ # Runs the backup
21
+ # @return [void]
19
22
  def run
20
23
  process_options!
21
24
  account = Account.new(
@@ -9,6 +9,8 @@ module Imap; end
9
9
  module Imap::Backup
10
10
  class CLI < Thor; end
11
11
 
12
+ # Processes parameters to run a backup via command-line parameters
13
+ # (without using a configuration file)
12
14
  class CLI::Single < Thor
13
15
  include CLI::Helpers
14
16
 
@@ -170,6 +172,8 @@ module Imap::Backup
170
172
  )
171
173
  quiet_option
172
174
  verbose_option
175
+ # Launches the backup procedure
176
+ # @return [void]
173
177
  def backup
174
178
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
175
179
  direct = Backup.new(non_logging_options)
@@ -4,6 +4,7 @@ require "imap/backup/serializer"
4
4
  module Imap; end
5
5
 
6
6
  module Imap::Backup
7
+ # Prints various statistics about an account and its backup
7
8
  class CLI::Stats < Thor
8
9
  include Thor::Actions
9
10
  include CLI::Helpers
@@ -14,6 +15,8 @@ module Imap::Backup
14
15
  @options = options
15
16
  end
16
17
 
18
+ # @!method run
19
+ # @return [void]
17
20
  no_commands do
18
21
  def run
19
22
  case options[:format]
@@ -8,10 +8,12 @@ require "imap/backup/mirror"
8
8
  module Imap; end
9
9
 
10
10
  module Imap::Backup
11
+ # Implements migration and mirroring
11
12
  class CLI::Transfer < Thor
12
13
  include Thor::Actions
13
14
  include CLI::Helpers
14
15
 
16
+ # The possible vaues for the action parameter
15
17
  ACTIONS = %i(migrate mirror).freeze
16
18
 
17
19
  def initialize(action, source_email, destination_email, options)
@@ -29,6 +31,12 @@ module Imap::Backup
29
31
  @source_prefix = nil
30
32
  end
31
33
 
34
+ # @!method run
35
+ # @raise [RuntimeError] if the indicated action is unknown,
36
+ # or the source and destination accounts are the same,
37
+ # or either of the accounts is not configured,
38
+ # or incompatible namespace/delimter parameters have been supplied
39
+ # @return [void]
32
40
  no_commands do
33
41
  def run
34
42
  raise "Unknown action '#{action}'" if !ACTIONS.include?(action)
@@ -13,6 +13,7 @@ module Imap; end
13
13
  module Imap::Backup
14
14
  class CLI < Thor; end
15
15
 
16
+ # Implements the CLI utility functions
16
17
  class CLI::Utils < Thor
17
18
  include Thor::Actions
18
19
  include CLI::Helpers
@@ -21,6 +22,9 @@ module Imap::Backup
21
22
  config_option
22
23
  quiet_option
23
24
  verbose_option
25
+ # Creates fake downloaded emails so that only the account's future emails
26
+ # will really get backed up
27
+ # @return [void]
24
28
  def ignore_history(email)
25
29
  Logger.setup_logging options
26
30
  config = load_config(**options)
@@ -59,6 +63,8 @@ module Imap::Backup
59
63
  banner: "the name of the Thunderbird profile to copy emails to",
60
64
  aliases: ["-p"]
61
65
  )
66
+ # Exports the account's emails to Thunderbird
67
+ # @return [void]
62
68
  def export_to_thunderbird(email)
63
69
  Imap::Backup::Logger.setup_logging options
64
70
  force = options.key?(:force) ? options[:force] : false
@@ -78,7 +84,7 @@ module Imap::Backup
78
84
 
79
85
  raise "No serialized folders were found for account '#{email}'" if serialized_folders.none?
80
86
 
81
- serialized_folders.each do |serializer, _folder|
87
+ serialized_folders.each_key do |serializer|
82
88
  Thunderbird::MailboxExporter.new(
83
89
  email, serializer, profile, force: force
84
90
  ).run
@@ -48,6 +48,7 @@ module Imap::Backup
48
48
 
49
49
  # Overrides {https://www.rubydoc.info/gems/thor/Thor%2FBase%2FClassMethods:start Thor's method}
50
50
  # to handle '--version' and rearrange parameters if 'help' is passed
51
+ # @return [void]
51
52
  def self.start(args)
52
53
  if args.include?("--version")
53
54
  new.version
@@ -87,6 +88,7 @@ module Imap::Backup
87
88
  refresh_option
88
89
  verbose_option
89
90
  # Runs account backups
91
+ # @return [void]
90
92
  def backup
91
93
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
92
94
  Backup.new(non_logging_options).run
@@ -148,6 +150,7 @@ module Imap::Backup
148
150
  aliases: ["-s"]
149
151
  )
150
152
  # Migrates emails from one account to another
153
+ # @return [void]
151
154
  def migrate(source_email, destination_email)
152
155
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
153
156
  Transfer.new(:migrate, source_email, destination_email, non_logging_options).run
@@ -208,6 +211,7 @@ module Imap::Backup
208
211
  aliases: ["-s"]
209
212
  )
210
213
  # Keeps one email account in line with another
214
+ # @return [void]
211
215
  def mirror(source_email, destination_email)
212
216
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
213
217
  Transfer.new(:mirror, source_email, destination_email, non_logging_options).run
@@ -226,6 +230,7 @@ module Imap::Backup
226
230
  quiet_option
227
231
  verbose_option
228
232
  # Restores backed up meails to an account
233
+ # @return [void]
229
234
  def restore(email = nil)
230
235
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
231
236
  Restore.new(email, non_logging_options).run
@@ -240,6 +245,7 @@ module Imap::Backup
240
245
  quiet_option
241
246
  verbose_option
242
247
  # Runs the menu-driven setup program
248
+ # @return [void]
243
249
  def setup
244
250
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
245
251
  CLI::Setup.new(non_logging_options).run
@@ -264,6 +270,7 @@ module Imap::Backup
264
270
  quiet_option
265
271
  verbose_option
266
272
  # Prints various statistics about a configured account
273
+ # @return [void]
267
274
  def stats(email)
268
275
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
269
276
  Stats.new(email, non_logging_options).run
@@ -274,6 +281,7 @@ module Imap::Backup
274
281
 
275
282
  desc "version", "Print the imap-backup version"
276
283
  # Prints the program version
284
+ # @return [void]
277
285
  def version
278
286
  Kernel.puts "imap-backup #{Imap::Backup::VERSION}"
279
287
  end
@@ -3,9 +3,11 @@ require "imap/backup/client/default"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Overrides default IMAP client behaviour for Apple Mail accounts
6
7
  class Client::AppleMail < Client::Default
7
8
  # With Apple Mails's IMAP, passing "/" to list
8
9
  # results in an empty list
10
+ # @return [String] the value to use when requesting the list of account folders
9
11
  def provider_root
10
12
  ""
11
13
  end
@@ -5,19 +5,24 @@ module Imap; end
5
5
  module Imap::Backup
6
6
  module Client; end
7
7
 
8
+ # Transparently wraps a client instance, while delaying login until it becomes necessary
8
9
  class Client::AutomaticLoginWrapper
9
10
  include RetryOnError
10
11
 
12
+ # @private
11
13
  LOGIN_RETRY_CLASSES = [::EOFError, ::Errno::ECONNRESET, ::SocketError].freeze
12
14
 
15
+ # @return [Client]
13
16
  attr_reader :client
14
- attr_reader :login_called
15
17
 
16
18
  def initialize(client:)
17
19
  @client = client
18
20
  @login_called = false
19
21
  end
20
22
 
23
+ # Proxies calls to the client.
24
+ # Before the first call does login
25
+ # @return the return value of the client method called
21
26
  def method_missing(method_name, *arguments, &block)
22
27
  if login_called
23
28
  client.send(method_name, *arguments, &block)
@@ -27,12 +32,15 @@ module Imap::Backup
27
32
  end
28
33
  end
29
34
 
35
+ # @return [Boolean] whether the client responds to the method
30
36
  def respond_to_missing?(method_name, _include_private = false)
31
37
  client.respond_to?(method_name)
32
38
  end
33
39
 
34
40
  private
35
41
 
42
+ attr_reader :login_called
43
+
36
44
  def do_first_login
37
45
  retry_on_error(errors: LOGIN_RETRY_CLASSES) do
38
46
  client.login
@@ -8,6 +8,8 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  module Client; end
10
10
 
11
+ # Wraps a Net::IMAP instance
12
+ # Tracks the latest folder selection in order to avoid repeated calls
11
13
  class Client::Default
12
14
  extend Forwardable
13
15
  def_delegators :imap, *%i(
@@ -22,6 +24,7 @@ module Imap::Backup
22
24
  @state = nil
23
25
  end
24
26
 
27
+ # @return [Array<String>] the account folders
25
28
  def list
26
29
  root = provider_root
27
30
  mailbox_lists = imap.list(root, "*")
@@ -31,36 +34,44 @@ module Imap::Backup
31
34
  mailbox_lists.map { |ml| extract_name(ml) }
32
35
  end
33
36
 
37
+ # Logs in to the account on the IMAP server
38
+ # @return [void]
34
39
  def login
35
40
  Logger.logger.debug "Logging in: #{account.username}/#{masked_password}"
36
41
  imap.login(account.username, account.password)
37
42
  Logger.logger.debug "Login complete"
38
43
  end
39
44
 
45
+ # Logs out and back in to the server
46
+ # @return [void]
40
47
  def reconnect
41
48
  disconnect
42
49
  login
43
50
  end
44
51
 
52
+ # @return [String] the account username
45
53
  def username
46
54
  account.username
47
55
  end
48
56
 
49
- # Track mailbox selection during delegation to Net::IMAP instance
50
-
57
+ # Disconects from the server
58
+ # @return [void]
51
59
  def disconnect
52
60
  imap.disconnect
53
61
  self.state = nil
54
62
  end
55
63
 
64
+ # Prepares read-only access to a folder
65
+ # @return [void]
56
66
  def examine(mailbox)
57
67
  return if state == [:examine, mailbox]
58
68
 
59
- result = imap.examine(mailbox)
69
+ imap.examine(mailbox)
60
70
  self.state = [:examine, mailbox]
61
- result
62
71
  end
63
72
 
73
+ # Prepares read-write access to a folder
74
+ # @return [void]
64
75
  def select(mailbox)
65
76
  return if state == [:select, mailbox]
66
77