imap-backup 14.4.1 → 14.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/bin/imap-backup +2 -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