imap-backup 14.4.4 → 14.4.5

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +67 -137
  3. data/docs/documentation.md +19 -0
  4. data/lib/imap/backup/account/backup.rb +2 -0
  5. data/lib/imap/backup/account/backup_folders.rb +7 -1
  6. data/lib/imap/backup/account/client_factory.rb +1 -0
  7. data/lib/imap/backup/account/folder.rb +26 -0
  8. data/lib/imap/backup/account/folder_backup.rb +4 -1
  9. data/lib/imap/backup/account/folder_ensurer.rb +3 -0
  10. data/lib/imap/backup/account/local_only_folder_deleter.rb +2 -0
  11. data/lib/imap/backup/account/restore.rb +2 -0
  12. data/lib/imap/backup/account/serialized_folders.rb +5 -1
  13. data/lib/imap/backup/account.rb +15 -11
  14. data/lib/imap/backup/cli/backup.rb +3 -0
  15. data/lib/imap/backup/cli/folder_enumerator.rb +6 -0
  16. data/lib/imap/backup/cli/helpers.rb +13 -0
  17. data/lib/imap/backup/cli/local/check.rb +3 -0
  18. data/lib/imap/backup/cli/local.rb +13 -0
  19. data/lib/imap/backup/cli/remote.rb +7 -0
  20. data/lib/imap/backup/cli/restore.rb +4 -0
  21. data/lib/imap/backup/cli/setup.rb +3 -0
  22. data/lib/imap/backup/cli/single/backup.rb +3 -0
  23. data/lib/imap/backup/cli/single.rb +4 -0
  24. data/lib/imap/backup/cli/stats.rb +3 -0
  25. data/lib/imap/backup/cli/transfer.rb +8 -0
  26. data/lib/imap/backup/cli/utils.rb +6 -0
  27. data/lib/imap/backup/cli.rb +8 -0
  28. data/lib/imap/backup/client/apple_mail.rb +2 -0
  29. data/lib/imap/backup/client/automatic_login_wrapper.rb +9 -1
  30. data/lib/imap/backup/client/default.rb +15 -4
  31. data/lib/imap/backup/configuration.rb +13 -0
  32. data/lib/imap/backup/configuration_not_found.rb +1 -0
  33. data/lib/imap/backup/downloader.rb +4 -0
  34. data/lib/imap/backup/email/mboxrd/message.rb +14 -0
  35. data/lib/imap/backup/email/provider/apple_mail.rb +2 -0
  36. data/lib/imap/backup/email/provider/base.rb +2 -0
  37. data/lib/imap/backup/email/provider/fastmail.rb +2 -0
  38. data/lib/imap/backup/email/provider/gmail.rb +2 -0
  39. data/lib/imap/backup/email/provider/purelymail.rb +2 -0
  40. data/lib/imap/backup/email/provider/unknown.rb +2 -6
  41. data/lib/imap/backup/email/provider.rb +5 -0
  42. data/lib/imap/backup/file_mode.rb +2 -0
  43. data/lib/imap/backup/flag_refresher.rb +4 -0
  44. data/lib/imap/backup/local_only_message_deleter.rb +2 -0
  45. data/lib/imap/backup/logger.rb +18 -0
  46. data/lib/imap/backup/migrator.rb +3 -0
  47. data/lib/imap/backup/mirror/map.rb +21 -0
  48. data/lib/imap/backup/mirror.rb +8 -0
  49. data/lib/imap/backup/naming.rb +10 -1
  50. data/lib/imap/backup/retry_on_error.rb +9 -0
  51. data/lib/imap/backup/serializer/appender.rb +9 -0
  52. data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +4 -0
  53. data/lib/imap/backup/serializer/folder_maker.rb +1 -0
  54. data/lib/imap/backup/serializer/imap.rb +38 -2
  55. data/lib/imap/backup/serializer/integrity_checker.rb +1 -0
  56. data/lib/imap/backup/serializer/mbox.rb +26 -0
  57. data/lib/imap/backup/serializer/message.rb +13 -0
  58. data/lib/imap/backup/serializer/message_enumerator.rb +10 -2
  59. data/lib/imap/backup/serializer/permission_checker.rb +6 -0
  60. data/lib/imap/backup/serializer/transaction.rb +18 -0
  61. data/lib/imap/backup/serializer/unused_name_finder.rb +4 -0
  62. data/lib/imap/backup/serializer/version2_migrator.rb +4 -0
  63. data/lib/imap/backup/serializer.rb +56 -2
  64. data/lib/imap/backup/setup/account/header.rb +6 -0
  65. data/lib/imap/backup/setup/account.rb +6 -0
  66. data/lib/imap/backup/setup/asker.rb +16 -0
  67. data/lib/imap/backup/setup/backup_path.rb +6 -0
  68. data/lib/imap/backup/setup/connection_tester.rb +5 -0
  69. data/lib/imap/backup/setup/email_changer.rb +6 -0
  70. data/lib/imap/backup/setup/folder_chooser.rb +4 -0
  71. data/lib/imap/backup/setup/global_options/download_strategy_chooser.rb +4 -0
  72. data/lib/imap/backup/setup/global_options.rb +4 -0
  73. data/lib/imap/backup/setup/helpers.rb +3 -0
  74. data/lib/imap/backup/setup.rb +5 -0
  75. data/lib/imap/backup/text/sanitizer.rb +9 -0
  76. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +7 -0
  77. data/lib/imap/backup/uploader.rb +5 -0
  78. data/lib/imap/backup/version.rb +6 -1
  79. metadata +3 -5
  80. data/docs/api.md +0 -20
  81. data/docs/development.md +0 -110
  82. data/docs/migrate-server-keep-address.md +0 -47
@@ -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
 
@@ -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
@@ -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
 
@@ -9,15 +9,21 @@ require "imap/backup/serializer/permission_checker"
9
9
  module Imap; end
10
10
 
11
11
  module Imap::Backup
12
+ # Handles the application's configuration file
12
13
  class Configuration
14
+ # The default directory of the configuration file
13
15
  CONFIGURATION_DIRECTORY = File.expand_path("~/.imap-backup")
16
+ # The default download strategy key
14
17
  DEFAULT_STRATEGY = "delay_metadata".freeze
18
+ # The available download strategies
15
19
  DOWNLOAD_STRATEGIES = [
16
20
  {key: "direct", description: "write straight to disk"},
17
21
  {key: DEFAULT_STRATEGY, description: "delay writing metadata"}
18
22
  ].freeze
23
+ # The current file version
19
24
  VERSION = "2.2".freeze
20
25
 
26
+ # @return [String] the default configuration file path
21
27
  def self.default_pathname
22
28
  File.join(CONFIGURATION_DIRECTORY, "config.json")
23
29
  end
@@ -33,10 +39,13 @@ module Imap::Backup
33
39
  @download_strategy_modified = false
34
40
  end
35
41
 
42
+ # @return [String] the directory containing the configuration file
36
43
  def path
37
44
  File.dirname(pathname)
38
45
  end
39
46
 
47
+ # Saves the configuration file in JSON format
48
+ # @return [void]
40
49
  def save
41
50
  ensure_loaded!
42
51
  FileUtils.mkdir_p(path) if !File.directory?(path)
@@ -53,6 +62,7 @@ module Imap::Backup
53
62
  @data = nil
54
63
  end
55
64
 
65
+ # @return [Array<Account>] the configured accounts
56
66
  def accounts
57
67
  @accounts ||= begin
58
68
  ensure_loaded!
@@ -63,12 +73,15 @@ module Imap::Backup
63
73
  end
64
74
  end
65
75
 
76
+ # @return [String] the cofigured download strategy
66
77
  def download_strategy
67
78
  ensure_loaded!
68
79
 
69
80
  @download_strategy
70
81
  end
71
82
 
83
+ # @param value [String] the new strategy
84
+ # @return [void]
72
85
  def download_strategy=(value)
73
86
  raise "Unknown strategy '#{value}'" if !DOWNLOAD_STRATEGIES.find { |s| s[:key] == value }
74
87
 
@@ -1,5 +1,6 @@
1
1
  module Imap; end
2
2
 
3
3
  module Imap::Backup
4
+ # Thrown when no configuration file is found
4
5
  class ConfigurationNotFound < StandardError; end
5
6
  end
@@ -3,7 +3,9 @@ require "net/imap"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Downloads as yet undownloaded emails from an account's server
6
7
  class Downloader
8
+ # @private
7
9
  class MultiFetchFailedError < StandardError; end
8
10
 
9
11
  def initialize(folder, serializer, multi_fetch_size: 1, reset_seen_flags_after_fetch: false)
@@ -14,6 +16,8 @@ module Imap::Backup
14
16
  @uids = nil
15
17
  end
16
18
 
19
+ # Runs the downloader
20
+ # @return [void]
17
21
  def run
18
22
  info("#{uids.count} new messages") if uids.any?
19
23
 
@@ -6,7 +6,13 @@ module Imap::Backup
6
6
  module Email; end
7
7
 
8
8
  module Email::Mboxrd
9
+ # Handles serialization and deserialization of messages
9
10
  class Message
11
+ # @param serialized [String] an email message
12
+ #
13
+ # @return [String] The message without the initial 'From ' line
14
+ # and with one level of '>' quoting removed from other lines
15
+ # that start with 'From'
10
16
  def self.clean_serialized(serialized)
11
17
  cleaned = serialized.gsub(/^>(>*From)/, "\\1")
12
18
  # Serialized messages in this format *should* start with a line
@@ -19,32 +25,40 @@ module Imap::Backup
19
25
  cleaned
20
26
  end
21
27
 
28
+ # @param serialized [String] the on-disk version of the message
29
+ #
30
+ # @return [Message] the original message
22
31
  def self.from_serialized(serialized)
23
32
  new(clean_serialized(serialized))
24
33
  end
25
34
 
35
+ # @return [String] the original message body
26
36
  attr_reader :supplied_body
27
37
 
28
38
  def initialize(supplied_body)
29
39
  @supplied_body = supplied_body.clone
30
40
  end
31
41
 
42
+ # @return [String] the message with an initial 'From ADDRESS' line
32
43
  def to_serialized
33
44
  from_line = "From #{from}\n"
34
45
  body = mboxrd_body.dup.force_encoding(Encoding::UTF_8)
35
46
  from_line + body
36
47
  end
37
48
 
49
+ # @return [Date, nil] the date of the message
38
50
  def date
39
51
  parsed.date
40
52
  rescue StandardError
41
53
  nil
42
54
  end
43
55
 
56
+ # @return [String] the message's subject line
44
57
  def subject
45
58
  parsed.subject
46
59
  end
47
60
 
61
+ # @return [String] the original message ready for transmission to an IMAP server
48
62
  def imap_body
49
63
  supplied_body.gsub(/(?<!\r)\n/, "\r\n")
50
64
  end
@@ -3,7 +3,9 @@ require "imap/backup/email/provider/base"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Provides overrides for Apple mail accounts
6
7
  class Email::Provider::AppleMail < Email::Provider::Base
8
+ # @return [String] the Apple Mail IMAP server host name
7
9
  def host
8
10
  "imap.mail.me.com"
9
11
  end
@@ -4,7 +4,9 @@ module Imap::Backup
4
4
  module Email; end
5
5
  class Email::Provider; end
6
6
 
7
+ # Supplies defaults for email provider behaviour
7
8
  class Email::Provider::Base
9
+ # @return [Hash] defaults for the Net::IMAP connection
8
10
  def options
9
11
  # rubocop:disable Naming/VariableNumber
10
12
  {port: 993, ssl: {ssl_version: :TLSv1_2}}
@@ -3,7 +3,9 @@ require "imap/backup/email/provider/base"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Provides overrides for Fastmail accounts
6
7
  class Email::Provider::Fastmail < Email::Provider::Base
8
+ # @return [String] the Fastmail IMAP server host name
7
9
  def host
8
10
  "imap.fastmail.com"
9
11
  end
@@ -3,7 +3,9 @@ require "imap/backup/email/provider/base"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Provides overrides for GMail accounts
6
7
  class Email::Provider::GMail < Email::Provider::Base
8
+ # @return [String] the GMail IMAP server host name
7
9
  def host
8
10
  "imap.gmail.com"
9
11
  end
@@ -3,7 +3,9 @@ require "imap/backup/email/provider/base"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
+ # Provides overrides for Purelymail accounts
6
7
  class Email::Provider::Purelymail < Email::Provider::Base
8
+ # @return [String] The Purelymail IMAP server host name
7
9
  def host
8
10
  "mailserver.purelymail.com"
9
11
  end