imap-backup 14.4.1 → 14.4.4

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/bin/imap-backup +5 -2
  3. data/docs/api.md +20 -0
  4. data/docs/development.md +18 -7
  5. data/lib/imap/backup/account/backup.rb +6 -3
  6. data/lib/imap/backup/account/backup_folders.rb +6 -3
  7. data/lib/imap/backup/account/client_factory.rb +3 -2
  8. data/lib/imap/backup/account/folder.rb +10 -9
  9. data/lib/imap/backup/account/folder_backup.rb +5 -4
  10. data/lib/imap/backup/account/folder_ensurer.rb +5 -2
  11. data/lib/imap/backup/account/local_only_folder_deleter.rb +7 -3
  12. data/lib/imap/backup/account/restore.rb +5 -2
  13. data/lib/imap/backup/account/serialized_folders.rb +3 -2
  14. data/lib/imap/backup/account.rb +85 -0
  15. data/lib/imap/backup/cli/backup.rb +14 -12
  16. data/lib/imap/backup/cli/folder_enumerator.rb +5 -5
  17. data/lib/imap/backup/cli/local/check.rb +4 -2
  18. data/lib/imap/backup/cli/local.rb +50 -49
  19. data/lib/imap/backup/cli/remote.rb +46 -46
  20. data/lib/imap/backup/cli/restore.rb +5 -3
  21. data/lib/imap/backup/cli/setup.rb +4 -2
  22. data/lib/imap/backup/cli/single/backup.rb +3 -3
  23. data/lib/imap/backup/cli/stats.rb +54 -52
  24. data/lib/imap/backup/cli/transfer.rb +93 -93
  25. data/lib/imap/backup/cli/utils.rb +28 -28
  26. data/lib/imap/backup/cli.rb +12 -0
  27. data/lib/imap/backup/client/default.rb +5 -5
  28. data/lib/imap/backup/configuration.rb +5 -4
  29. data/lib/imap/backup/downloader.rb +6 -14
  30. data/lib/imap/backup/email/mboxrd/message.rb +2 -3
  31. data/lib/imap/backup/file_mode.rb +4 -2
  32. data/lib/imap/backup/flag_refresher.rb +3 -3
  33. data/lib/imap/backup/local_only_message_deleter.rb +5 -3
  34. data/lib/imap/backup/migrator.rb +6 -4
  35. data/lib/imap/backup/mirror/map.rb +3 -3
  36. data/lib/imap/backup/mirror.rb +5 -5
  37. data/lib/imap/backup/serializer/appender.rb +7 -4
  38. data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +15 -2
  39. data/lib/imap/backup/serializer/directory.rb +12 -3
  40. data/lib/imap/backup/serializer/folder_maker.rb +12 -4
  41. data/lib/imap/backup/serializer/imap.rb +5 -1
  42. data/lib/imap/backup/serializer/integrity_checker.rb +10 -3
  43. data/lib/imap/backup/serializer/mbox.rb +2 -1
  44. data/lib/imap/backup/serializer/message.rb +10 -18
  45. data/lib/imap/backup/serializer/permission_checker.rb +5 -3
  46. data/lib/imap/backup/serializer/transaction.rb +4 -1
  47. data/lib/imap/backup/serializer/unused_name_finder.rb +4 -2
  48. data/lib/imap/backup/serializer/version2_migrator.rb +2 -2
  49. data/lib/imap/backup/serializer.rb +5 -0
  50. data/lib/imap/backup/setup/account/header.rb +3 -3
  51. data/lib/imap/backup/setup/account.rb +6 -6
  52. data/lib/imap/backup/setup/asker.rb +6 -4
  53. data/lib/imap/backup/setup/backup_path.rb +3 -4
  54. data/lib/imap/backup/setup/connection_tester.rb +4 -2
  55. data/lib/imap/backup/setup/{email.rb → email_changer.rb} +4 -4
  56. data/lib/imap/backup/setup/folder_chooser.rb +2 -2
  57. data/lib/imap/backup/setup/global_options/download_strategy_chooser.rb +2 -2
  58. data/lib/imap/backup/setup/global_options.rb +2 -2
  59. data/lib/imap/backup/setup.rb +2 -2
  60. data/lib/imap/backup/text/sanitizer.rb +2 -2
  61. data/lib/imap/backup/thunderbird/mailbox_exporter.rb +10 -8
  62. data/lib/imap/backup/uploader.rb +3 -3
  63. data/lib/imap/backup/version.rb +1 -1
  64. metadata +4 -4
  65. data/lib/imap/backup/cli_coverage.rb +0 -21
@@ -17,8 +17,6 @@ module Imap::Backup
17
17
  include Thor::Actions
18
18
  include CLI::Helpers
19
19
 
20
- FAKE_EMAIL = "fake@email.com".freeze
21
-
22
20
  desc "ignore-history EMAIL", "Skip downloading emails up to today for all configured folders"
23
21
  config_option
24
22
  quiet_option
@@ -87,38 +85,40 @@ module Imap::Backup
87
85
  end
88
86
  end
89
87
 
90
- no_commands do
91
- def do_ignore_folder_history(folder, serializer)
92
- uids = folder.uids - serializer.uids
93
- Logger.logger.info "Folder '#{folder.name}' - #{uids.length} messages"
88
+ private
94
89
 
95
- serializer.apply_uid_validity(folder.uid_validity)
90
+ FAKE_EMAIL = "fake@email.com".freeze
96
91
 
97
- uids.each do |uid|
98
- message = <<~MESSAGE
99
- From: #{FAKE_EMAIL}
100
- Subject: Message #{uid} not backed up
101
- Skipped #{uid}
102
- MESSAGE
92
+ def do_ignore_folder_history(folder, serializer)
93
+ uids = folder.uids - serializer.uids
94
+ Logger.logger.info "Folder '#{folder.name}' - #{uids.length} messages"
103
95
 
104
- serializer.append uid, message, []
105
- end
96
+ serializer.apply_uid_validity(folder.uid_validity)
97
+
98
+ uids.each do |uid|
99
+ message = <<~MESSAGE
100
+ From: #{FAKE_EMAIL}
101
+ Subject: Message #{uid} not backed up
102
+ Skipped #{uid}
103
+ MESSAGE
104
+
105
+ serializer.append uid, message, []
106
106
  end
107
+ end
107
108
 
108
- def thunderbird_profile(name = nil)
109
- profiles = Thunderbird::Profiles.new
110
- if name
111
- profiles.profile(name)
112
- else
113
- if profiles.installs.count > 1
114
- raise <<~MESSAGE
115
- Thunderbird has multiple installs, so no default profile exists.
116
- Please supply a profile name
117
- MESSAGE
118
- end
119
-
120
- profiles.installs[0].default
109
+ def thunderbird_profile(name = nil)
110
+ profiles = ::Thunderbird::Profiles.new
111
+ if name
112
+ profiles.profile(name)
113
+ else
114
+ if profiles.installs.count > 1
115
+ raise <<~MESSAGE
116
+ Thunderbird has multiple installs, so no default profile exists.
117
+ Please supply a profile name
118
+ MESSAGE
121
119
  end
120
+
121
+ profiles.installs[0].default
122
122
  end
123
123
  end
124
124
  end
@@ -6,6 +6,7 @@ require "imap/backup/version"
6
6
  module Imap; end
7
7
 
8
8
  module Imap::Backup
9
+ # Top-level cli call handler
9
10
  class CLI < Thor
10
11
  require "imap/backup/cli/helpers"
11
12
 
@@ -41,9 +42,12 @@ module Imap::Backup
41
42
  To check what values you should use, check the output of the
42
43
  `imap-backup remote namespaces EMAIL` command.
43
44
  DESC
45
+ private_constant :NAMESPACE_CONFIGURATION_DESCRIPTION
44
46
 
45
47
  default_task :backup
46
48
 
49
+ # Overrides {https://www.rubydoc.info/gems/thor/Thor%2FBase%2FClassMethods:start Thor's method}
50
+ # to handle '--version' and rearrange parameters if 'help' is passed
47
51
  def self.start(args)
48
52
  if args.include?("--version")
49
53
  new.version
@@ -64,6 +68,7 @@ module Imap::Backup
64
68
  super
65
69
  end
66
70
 
71
+ # see {https://www.rubydoc.info/gems/thor/Thor/Base/ClassMethods#exit_on_failure%3F-instance_method Thor documentation}
67
72
  def self.exit_on_failure?
68
73
  true
69
74
  end
@@ -81,6 +86,7 @@ module Imap::Backup
81
86
  quiet_option
82
87
  refresh_option
83
88
  verbose_option
89
+ # Runs account backups
84
90
  def backup
85
91
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
86
92
  Backup.new(non_logging_options).run
@@ -141,6 +147,7 @@ module Imap::Backup
141
147
  desc: "the prefix (namespace) to strip from source folder names",
142
148
  aliases: ["-s"]
143
149
  )
150
+ # Migrates emails from one account to another
144
151
  def migrate(source_email, destination_email)
145
152
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
146
153
  Transfer.new(:migrate, source_email, destination_email, non_logging_options).run
@@ -200,6 +207,7 @@ module Imap::Backup
200
207
  desc: "the prefix (namespace) to strip from source folder names",
201
208
  aliases: ["-s"]
202
209
  )
210
+ # Keeps one email account in line with another
203
211
  def mirror(source_email, destination_email)
204
212
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
205
213
  Transfer.new(:mirror, source_email, destination_email, non_logging_options).run
@@ -217,6 +225,7 @@ module Imap::Backup
217
225
  config_option
218
226
  quiet_option
219
227
  verbose_option
228
+ # Restores backed up meails to an account
220
229
  def restore(email = nil)
221
230
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
222
231
  Restore.new(email, non_logging_options).run
@@ -230,6 +239,7 @@ module Imap::Backup
230
239
  config_option
231
240
  quiet_option
232
241
  verbose_option
242
+ # Runs the menu-driven setup program
233
243
  def setup
234
244
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
235
245
  CLI::Setup.new(non_logging_options).run
@@ -253,6 +263,7 @@ module Imap::Backup
253
263
  format_option
254
264
  quiet_option
255
265
  verbose_option
266
+ # Prints various statistics about a configured account
256
267
  def stats(email)
257
268
  non_logging_options = Imap::Backup::Logger.setup_logging(options)
258
269
  Stats.new(email, non_logging_options).run
@@ -262,6 +273,7 @@ module Imap::Backup
262
273
  subcommand "utils", Utils
263
274
 
264
275
  desc "version", "Print the imap-backup version"
276
+ # Prints the program version
265
277
  def version
266
278
  Kernel.puts "imap-backup #{Imap::Backup::VERSION}"
267
279
  end
@@ -15,11 +15,6 @@ module Imap::Backup
15
15
  responses uid_fetch uid_search uid_store
16
16
  )
17
17
 
18
- attr_reader :account
19
- attr_reader :options
20
- attr_reader :server
21
- attr_accessor :state
22
-
23
18
  def initialize(server, account, options)
24
19
  @account = account
25
20
  @options = options
@@ -75,6 +70,11 @@ module Imap::Backup
75
70
 
76
71
  private
77
72
 
73
+ attr_reader :account
74
+ attr_reader :options
75
+ attr_reader :server
76
+ attr_accessor :state
77
+
78
78
  def imap
79
79
  @imap ||= Net::IMAP.new(server, options)
80
80
  end
@@ -11,15 +11,12 @@ module Imap; end
11
11
  module Imap::Backup
12
12
  class Configuration
13
13
  CONFIGURATION_DIRECTORY = File.expand_path("~/.imap-backup")
14
- VERSION_2_1 = "2.1".freeze
15
- VERSION = "2.2".freeze
16
14
  DEFAULT_STRATEGY = "delay_metadata".freeze
17
15
  DOWNLOAD_STRATEGIES = [
18
16
  {key: "direct", description: "write straight to disk"},
19
17
  {key: DEFAULT_STRATEGY, description: "delay writing metadata"}
20
18
  ].freeze
21
-
22
- attr_reader :pathname
19
+ VERSION = "2.2".freeze
23
20
 
24
21
  def self.default_pathname
25
22
  File.join(CONFIGURATION_DIRECTORY, "config.json")
@@ -98,6 +95,10 @@ module Imap::Backup
98
95
 
99
96
  private
100
97
 
98
+ VERSION_2_1 = "2.1".freeze
99
+
100
+ attr_reader :pathname
101
+
101
102
  def ensure_loaded!
102
103
  return true if @data
103
104
 
@@ -3,21 +3,8 @@ require "net/imap"
3
3
  module Imap; end
4
4
 
5
5
  module Imap::Backup
6
- class MultiFetchFailedError < StandardError; end
7
-
8
6
  class Downloader
9
- attr_reader :folder
10
- attr_reader :serializer
11
- attr_reader :multi_fetch_size
12
- # Some IMAP providers, notably Apple Mail, set the '\Seen' flag
13
- # on emails when they are fetched. By setting `:reset_seen_flags_after_fetch`,
14
- # a workaround is activated which checks which emails are 'unseen' before
15
- # and after the fetch, and removes the '\Seen' flag from those which have changed.
16
- # As this check is susceptible to 'race conditions', i.e. when a different
17
- # client sets the '\Seen' flag while imap-backup is fetching, it is best
18
- # to only use it when required (i.e. for IMAP providers which always
19
- # mark messages as '\Seen' when accessed).
20
- attr_reader :reset_seen_flags_after_fetch
7
+ class MultiFetchFailedError < StandardError; end
21
8
 
22
9
  def initialize(folder, serializer, multi_fetch_size: 1, reset_seen_flags_after_fetch: false)
23
10
  @folder = folder
@@ -46,6 +33,11 @@ module Imap::Backup
46
33
 
47
34
  private
48
35
 
36
+ attr_reader :folder
37
+ attr_reader :serializer
38
+ attr_reader :multi_fetch_size
39
+ attr_reader :reset_seen_flags_after_fetch
40
+
49
41
  def download_block(block, index)
50
42
  uids_and_bodies =
51
43
  if reset_seen_flags_after_fetch
@@ -1,4 +1,3 @@
1
- require "forwardable"
2
1
  require "mail"
3
2
 
4
3
  module Imap; end
@@ -8,8 +7,6 @@ module Imap::Backup
8
7
 
9
8
  module Email::Mboxrd
10
9
  class Message
11
- attr_reader :supplied_body
12
-
13
10
  def self.clean_serialized(serialized)
14
11
  cleaned = serialized.gsub(/^>(>*From)/, "\\1")
15
12
  # Serialized messages in this format *should* start with a line
@@ -26,6 +23,8 @@ module Imap::Backup
26
23
  new(clean_serialized(serialized))
27
24
  end
28
25
 
26
+ attr_reader :supplied_body
27
+
29
28
  def initialize(supplied_body)
30
29
  @supplied_body = supplied_body.clone
31
30
  end
@@ -2,8 +2,6 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  class FileMode
5
- attr_reader :filename
6
-
7
5
  def initialize(filename:)
8
6
  @filename = filename
9
7
  end
@@ -14,5 +12,9 @@ module Imap::Backup
14
12
  stat = File.stat(filename)
15
13
  stat.mode & 0o777
16
14
  end
15
+
16
+ private
17
+
18
+ attr_reader :filename
17
19
  end
18
20
  end
@@ -2,9 +2,6 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  class FlagRefresher
5
- attr_reader :folder
6
- attr_reader :serializer
7
-
8
5
  CHUNK_SIZE = 100
9
6
 
10
7
  def initialize(folder, serializer)
@@ -22,6 +19,9 @@ module Imap::Backup
22
19
 
23
20
  private
24
21
 
22
+ attr_reader :folder
23
+ attr_reader :serializer
24
+
25
25
  def refresh_block(uids)
26
26
  uids_and_flags = folder.fetch_multi(uids, ["FLAGS"])
27
27
  uids_and_flags.each do |uid_and_flags|
@@ -4,9 +4,6 @@ module Imap; end
4
4
 
5
5
  module Imap::Backup
6
6
  class LocalOnlyMessageDeleter
7
- attr_reader :folder
8
- attr_reader :serializer
9
-
10
7
  def initialize(folder, serializer)
11
8
  @folder = folder
12
9
  @serializer = serializer
@@ -29,5 +26,10 @@ module Imap::Backup
29
26
  !local_only_uids.include?(message.uid)
30
27
  end
31
28
  end
29
+
30
+ private
31
+
32
+ attr_reader :folder
33
+ attr_reader :serializer
32
34
  end
33
35
  end
@@ -4,10 +4,6 @@ module Imap; end
4
4
 
5
5
  module Imap::Backup
6
6
  class Migrator
7
- attr_reader :folder
8
- attr_reader :reset
9
- attr_reader :serializer
10
-
11
7
  def initialize(serializer, folder, reset: false)
12
8
  @folder = folder
13
9
  @reset = reset
@@ -35,5 +31,11 @@ module Imap::Backup
35
31
  end
36
32
  end
37
33
  end
34
+
35
+ private
36
+
37
+ attr_reader :folder
38
+ attr_reader :reset
39
+ attr_reader :serializer
38
40
  end
39
41
  end
@@ -6,9 +6,6 @@ module Imap::Backup
6
6
  class Mirror; end
7
7
 
8
8
  class Mirror::Map
9
- attr_reader :pathname
10
- attr_reader :destination
11
-
12
9
  def initialize(pathname:, destination:)
13
10
  @pathname = pathname
14
11
  @destination = destination
@@ -64,6 +61,9 @@ module Imap::Backup
64
61
 
65
62
  private
66
63
 
64
+ attr_reader :pathname
65
+ attr_reader :destination
66
+
67
67
  def store
68
68
  @store ||=
69
69
  if File.exist?(pathname)
@@ -4,11 +4,6 @@ module Imap; end
4
4
 
5
5
  module Imap::Backup
6
6
  class Mirror
7
- attr_reader :serializer
8
- attr_reader :folder
9
-
10
- CHUNK_SIZE = 100
11
-
12
7
  def initialize(serializer, folder)
13
8
  @serializer = serializer
14
9
  @folder = folder
@@ -24,6 +19,11 @@ module Imap::Backup
24
19
 
25
20
  private
26
21
 
22
+ CHUNK_SIZE = 100
23
+
24
+ attr_reader :serializer
25
+ attr_reader :folder
26
+
27
27
  def ensure_destination_folder
28
28
  return if folder.exist?
29
29
 
@@ -5,17 +5,16 @@ module Imap; end
5
5
  module Imap::Backup
6
6
  class Serializer; end
7
7
 
8
+ # Appends messages to the local store
8
9
  class Serializer::Appender
9
- attr_reader :imap
10
- attr_reader :folder
11
- attr_reader :mbox
12
-
13
10
  def initialize(folder:, imap:, mbox:)
14
11
  @folder = folder
15
12
  @imap = imap
16
13
  @mbox = mbox
17
14
  end
18
15
 
16
+ # Adds a message to the metadata file and the mailbox.
17
+ # Wraps any errors with information about the message that caused them.
19
18
  def append(uid:, message:, flags:)
20
19
  raise "Can't add messages without uid_validity" if !imap.uid_validity
21
20
 
@@ -56,6 +55,10 @@ module Imap::Backup
56
55
 
57
56
  private
58
57
 
58
+ attr_reader :imap
59
+ attr_reader :folder
60
+ attr_reader :mbox
61
+
59
62
  def wrap_error(error:, note:, folder:, uid:, message:)
60
63
  <<-ERROR.gsub(/^\s*/m, "")
61
64
  [#{folder}] #{note} #{uid}: #{message}.
@@ -1,3 +1,5 @@
1
+ require "forwardable"
2
+
1
3
  require "imap/backup/email/mboxrd/message"
2
4
  require "imap/backup/serializer/imap"
3
5
  require "imap/backup/serializer/mbox"
@@ -9,8 +11,6 @@ module Imap::Backup
9
11
  class Serializer::DelayedMetadataSerializer
10
12
  extend Forwardable
11
13
 
12
- attr_reader :serializer
13
-
14
14
  def_delegator :serializer, :uids
15
15
 
16
16
  def initialize(serializer:)
@@ -18,6 +18,10 @@ module Imap::Backup
18
18
  @tsx = nil
19
19
  end
20
20
 
21
+ # Initializes the metadata and mailbox transactions, then calls the supplied block.
22
+ # Once the block has finished, commits changes to metadata
23
+ #
24
+ # @return [void]
21
25
  def transaction(&block)
22
26
  tsx.fail_in_transaction!(:transaction, message: "nested transactions are not supported")
23
27
 
@@ -32,6 +36,13 @@ module Imap::Backup
32
36
  end
33
37
  end
34
38
 
39
+ # Appends a message to the mbox file and adds the metadata
40
+ # to the transaction
41
+ #
42
+ # @param uid [Integer] the UID of the message
43
+ # @param message [String] the message
44
+ # @param flags [Array<Symbol>] the flags for the message
45
+ # @return [void]
35
46
  def append(uid, message, flags)
36
47
  tsx.fail_outside_transaction!(:append)
37
48
  mboxrd_message = Email::Mboxrd::Message.new(message)
@@ -42,6 +53,8 @@ module Imap::Backup
42
53
 
43
54
  private
44
55
 
56
+ attr_reader :serializer
57
+
45
58
  def commit
46
59
  # rubocop:disable Lint/RescueException
47
60
  imap.transaction do
@@ -8,17 +8,23 @@ module Imap; end
8
8
  module Imap::Backup
9
9
  class Serializer; end
10
10
 
11
+ # Ensures that serialization directories exist and have the correct permissions.
11
12
  class Serializer::Directory
13
+ # The desired permissions for all directories that store backups
12
14
  DIRECTORY_PERMISSIONS = 0o700
13
15
 
14
- attr_reader :relative
15
- attr_reader :path
16
-
16
+ # @param path [String] The base path of the account backup
17
+ # @param relative [String] The path relative from the base
18
+ #
19
+ # @return [void]
17
20
  def initialize(path, relative)
18
21
  @path = path
19
22
  @relative = relative
20
23
  end
21
24
 
25
+ # Creates the directory, if present and sets it's access permissions
26
+ #
27
+ # @return [void]
22
28
  def ensure_exists
23
29
  if !File.directory?(full_path)
24
30
  Serializer::FolderMaker.new(
@@ -34,6 +40,9 @@ module Imap::Backup
34
40
 
35
41
  private
36
42
 
43
+ attr_reader :relative
44
+ attr_reader :path
45
+
37
46
  def full_path
38
47
  containing_directory = File.join(path, relative)
39
48
  File.expand_path(containing_directory)
@@ -5,17 +5,19 @@ module Imap; end
5
5
  module Imap::Backup
6
6
  class Serializer; end
7
7
 
8
+ # Creates directories
8
9
  class Serializer::FolderMaker
9
- attr_reader :base
10
- attr_reader :path
11
- attr_reader :permissions
12
-
10
+ # @param base [String] The base directory of the account
11
+ # @param path [String] The path to the folder, relative to the base
12
+ # @param permissions [Integer] The permissions to set on the folder
13
13
  def initialize(base:, path:, permissions:)
14
14
  @base = base
15
15
  @path = path
16
16
  @permissions = permissions
17
17
  end
18
18
 
19
+ # Creates the directory and any missing parent directories,
20
+ # ensuring the desired permissions.
19
21
  def run
20
22
  parts = path.split("/")
21
23
  return if parts.empty?
@@ -28,6 +30,12 @@ module Imap::Backup
28
30
  end
29
31
  end
30
32
 
33
+ private
34
+
35
+ attr_reader :base
36
+ attr_reader :path
37
+ attr_reader :permissions
38
+
31
39
  def full_path
32
40
  File.join(base, path)
33
41
  end
@@ -7,12 +7,14 @@ require "imap/backup/serializer/transaction"
7
7
  module Imap; end
8
8
 
9
9
  module Imap::Backup
10
+ # Stores message metadata
10
11
  class Serializer::Imap
12
+ # The version number to store in the metadata file
11
13
  CURRENT_VERSION = 3
12
14
 
13
15
  attr_reader :folder_path
14
- attr_reader :loaded
15
16
 
17
+ # @param folder_path [String] The path of the imap metadata file, without the '.imap' extension
16
18
  def initialize(folder_path)
17
19
  @folder_path = folder_path
18
20
  @loaded = false
@@ -149,6 +151,8 @@ module Imap::Backup
149
151
 
150
152
  private
151
153
 
154
+ attr_reader :loaded
155
+
152
156
  def save_internal(version:, uid_validity:, messages:)
153
157
  raise "Cannot save metadata without a uid_validity" if !uid_validity
154
158
 
@@ -5,15 +5,19 @@ module Imap::Backup
5
5
 
6
6
  class Serializer::FolderIntegrityError < StandardError; end
7
7
 
8
+ # Checks that both the mailbox and its associated metadata file match
8
9
  class Serializer::IntegrityChecker
9
- attr_reader :imap
10
- attr_reader :mbox
11
-
10
+ # @param imap [Imap]
11
+ # @param mbox [Mbox]
12
12
  def initialize(imap:, mbox:)
13
13
  @imap = imap
14
14
  @mbox = mbox
15
15
  end
16
16
 
17
+ # Runs the integrity check
18
+ #
19
+ # @raise [FolderIntegrityError] if the files do not match
20
+ # @return [void]
17
21
  def run
18
22
  Logger.logger.debug(
19
23
  "[IntegrityChecker] checking '#{imap.pathname}' against '#{mbox.pathname}'"
@@ -47,6 +51,9 @@ module Imap::Backup
47
51
 
48
52
  private
49
53
 
54
+ attr_reader :imap
55
+ attr_reader :mbox
56
+
50
57
  def check_offset_ordering!
51
58
  offsets = imap.messages.map(&:offset)
52
59
 
@@ -5,7 +5,6 @@ module Imap; end
5
5
  module Imap::Backup
6
6
  class Serializer::Mbox
7
7
  attr_reader :folder_path
8
- attr_reader :savepoint
9
8
 
10
9
  def initialize(folder_path)
11
10
  @folder_path = folder_path
@@ -86,6 +85,8 @@ module Imap::Backup
86
85
 
87
86
  private
88
87
 
88
+ attr_reader :savepoint
89
+
89
90
  def rewind(length)
90
91
  File.open(pathname, File::RDWR | File::CREAT, 0o644) do |f|
91
92
  f.truncate(length)
@@ -1,16 +1,20 @@
1
+ require "forwardable"
2
+
1
3
  require "imap/backup/email/mboxrd/message"
2
4
 
3
5
  module Imap; end
4
6
 
5
7
  module Imap::Backup
6
8
  class Serializer::Message
7
- attr_accessor :uid
8
9
  attr_accessor :flags
9
- attr_reader :offset
10
10
  attr_reader :length
11
- attr_reader :mbox
11
+ attr_reader :offset
12
+ attr_accessor :uid
13
+
14
+ extend Forwardable
12
15
 
13
- # TODO: delegate to Mboxrd::Message
16
+ def_delegator :message, :supplied_body, :body
17
+ def_delegators :message, :imap_body, :date, :subject
14
18
 
15
19
  def initialize(uid:, offset:, length:, mbox:, flags: [])
16
20
  @uid = uid
@@ -37,20 +41,8 @@ module Imap::Backup
37
41
  end
38
42
  end
39
43
 
40
- def body
41
- @body ||= message.supplied_body
42
- end
43
-
44
- def imap_body
45
- @imap_body ||= message.imap_body
46
- end
44
+ private
47
45
 
48
- def date
49
- @date ||= message.date
50
- end
51
-
52
- def subject
53
- @subject ||= message.subject
54
- end
46
+ attr_reader :mbox
55
47
  end
56
48
  end