Class: Imap::Backup::Serializer

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/imap/backup/serializer.rb,
lib/imap/backup/serializer/appender.rb,
lib/imap/backup/serializer/directory.rb,
lib/imap/backup/serializer/folder_maker.rb

Defined Under Namespace

Classes: Appender, Directory, FolderIntegrityError, FolderMaker, Imap, Mbox, Message, MessageEnumerator, PermissionChecker, UnusedNameFinder, Version2Migrator

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, folder) ⇒ Serializer

Returns a new instance of Serializer.



30
31
32
33
34
# File 'lib/imap/backup/serializer.rb', line 30

def initialize(path, folder)
  @path = path
  @folder = folder
  @validated = nil
end

Instance Attribute Details

#folderObject (readonly)

Returns the value of attribute folder.



27
28
29
# File 'lib/imap/backup/serializer.rb', line 27

def folder
  @folder
end

#pathObject (readonly)

Returns the value of attribute path.



28
29
30
# File 'lib/imap/backup/serializer.rb', line 28

def path
  @path
end

Class Method Details

.folder_path_for(path:, folder:) ⇒ Object



15
16
17
18
# File 'lib/imap/backup/serializer.rb', line 15

def self.folder_path_for(path:, folder:)
  relative = File.join(path, folder)
  File.expand_path(relative)
end

Instance Method Details

#append(uid, message, flags) ⇒ Object



132
133
134
135
136
137
# File 'lib/imap/backup/serializer.rb', line 132

def append(uid, message, flags)
  validate!

  appender = Serializer::Appender.new(folder: sanitized, imap: imap, mbox: mbox)
  appender.run(uid: uid, message: message, flags: flags)
end

#apply_uid_validity(value) ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/imap/backup/serializer.rb', line 111

def apply_uid_validity(value)
  validate!

  case
  when uid_validity.nil?
    internal_force_uid_validity(value)
    nil
  when uid_validity == value
    # NOOP
    nil
  else
    apply_new_uid_validity(value)
  end
end

#check_integrity!Object

Raises:



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/imap/backup/serializer.rb', line 53

def check_integrity!
  if !imap.valid?
    message = ".imap file '#{imap.pathname}' is corrupt"
    raise FolderIntegrityError, message
  end

  if !mbox.exist?
    message = ".mbox file '#{mbox.pathname}' is missing"
    raise FolderIntegrityError, message
  end

  return if imap.messages.empty?

  offsets = imap.messages.map(&:offset)

  if offsets != offsets.sort
    message = ".imap file '#{imap.pathname}' has offset data which is out of order"
    raise FolderIntegrityError, message
  end

  if mbox.length < offsets[-1]
    message =
      ".imap file '#{imap.pathname}' has offsets past the end " \
      "of .mbox file '#{mbox.pathname}'"
    raise FolderIntegrityError, message
  end

  imap.messages.each do |m|
    text = mbox.read(m.offset, m.length)
    if text.length < m.length
      message = "Message #{m.uid} is incomplete in file '#{mbox.pathname}'"
      raise FolderIntegrityError, message
    end

    next if text.start_with?("From ")

    message =
      "Message #{m.uid} not found at expected offset #{m.offset} " \
      "in file '#{mbox.pathname}'"
    raise FolderIntegrityError, message
  end

  last = imap.messages.last
  expected_length = last.offset + last.length
  actual_length = mbox.length
  return if actual_length == expected_length

  message = "Mbox file '#{mbox.pathname}' contains unexpected trailing data"
  raise FolderIntegrityError, message
end

#deleteObject



104
105
106
107
108
109
# File 'lib/imap/backup/serializer.rb', line 104

def delete
  imap.delete
  @imap = nil
  mbox.delete
  @mbox = nil
end

#each_message(required_uids = nil, &block) ⇒ Object



147
148
149
150
151
152
153
154
155
156
# File 'lib/imap/backup/serializer.rb', line 147

def each_message(required_uids = nil, &block)
  required_uids ||= uids

  validate!

  return enum_for(:each_message, required_uids) if !block

  enumerator = Serializer::MessageEnumerator.new(imap: imap)
  enumerator.run(uids: required_uids, &block)
end

#filter(&block) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/imap/backup/serializer.rb', line 158

def filter(&block)
  temp_name = Serializer::UnusedNameFinder.new(serializer: self).run
  temp_folder_path = self.class.folder_path_for(path: path, folder: temp_name)
  new_mbox = Serializer::Mbox.new(temp_folder_path)
  new_imap = Serializer::Imap.new(temp_folder_path)
  new_imap.uid_validity = imap.uid_validity
  appender = Serializer::Appender.new(folder: temp_name, imap: new_imap, mbox: new_mbox)
  enumerator = Serializer::MessageEnumerator.new(imap: imap)
  enumerator.run(uids: uids) do |message|
    keep = block.call(message)
    appender.run(uid: message.uid, message: message.body, flags: message.flags) if keep
  end
  imap.delete
  new_imap.rename imap.folder_path
  mbox.delete
  new_mbox.rename mbox.folder_path
  @imap = nil
  @mbox = nil
end

#folder_pathObject



178
179
180
# File 'lib/imap/backup/serializer.rb', line 178

def folder_path
  self.class.folder_path_for(path: path, folder: sanitized)
end

#force_uid_validity(value) ⇒ Object



126
127
128
129
130
# File 'lib/imap/backup/serializer.rb', line 126

def force_uid_validity(value)
  validate!

  internal_force_uid_validity(value)
end

#update(uid, flags: nil) ⇒ Object



139
140
141
142
143
144
145
# File 'lib/imap/backup/serializer.rb', line 139

def update(uid, flags: nil)
  message = imap.get(uid)
  return if !message

  message.flags = flags if flags
  imap.save
end

#validate!Object

Returns true if there are existing, valid files false otherwise (in which case any existing files are deleted)



38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/imap/backup/serializer.rb', line 38

def validate!
  return true if @validated

  optionally_migrate2to3

  if imap.valid? && mbox.valid?
    @validated = true
    return true
  end

  delete

  false
end