imap-backup 16.4.1 → 16.4.2
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.
- checksums.yaml +4 -4
- data/imap-backup.gemspec +1 -1
- data/lib/imap/backup/account/backup.rb +3 -4
- data/lib/imap/backup/account/folder_backup.rb +7 -1
- data/lib/imap/backup/account/folder_mapper.rb +5 -1
- data/lib/imap/backup/account/locker.rb +1 -2
- data/lib/imap/backup/account/serialized_folders.rb +14 -4
- data/lib/imap/backup/account.rb +5 -1
- data/lib/imap/backup/cli/stats.rb +5 -1
- data/lib/imap/backup/cli/utils.rb +36 -9
- data/lib/imap/backup/mirror.rb +1 -1
- data/lib/imap/backup/serializer/appender.rb +17 -15
- data/lib/imap/backup/serializer/delayed_metadata_serializer.rb +2 -2
- data/lib/imap/backup/serializer/directory_maker.rb +43 -0
- data/lib/imap/backup/serializer/files/path.rb +27 -0
- data/lib/imap/backup/serializer/files.rb +167 -0
- data/lib/imap/backup/serializer/imap.rb +15 -12
- data/lib/imap/backup/serializer/mbox.rb +7 -7
- data/lib/imap/backup/serializer/unused_name_finder.rb +12 -7
- data/lib/imap/backup/serializer.rb +47 -206
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +6 -0
- data/lib/imap/backup/uploader.rb +5 -1
- data/lib/imap/backup/version.rb +1 -1
- metadata +8 -9
- data/lib/imap/backup/account/folder_ensurer.rb +0 -33
- data/lib/imap/backup/serializer/directory.rb +0 -51
- data/lib/imap/backup/serializer/folder_maker.rb +0 -44
- data/lib/imap/backup/serializer/version2_migrator.rb +0 -123
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc471fce2bd514e0eee4cec922e8f368df0b2fb99f8b89b2d852e07799a2be77
|
|
4
|
+
data.tar.gz: fd563c2ee648d5e384a10cdfc79a7d1d5be128bdce5908711396d414c801173f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 36088caab383a7ff2528d8ad83813f58918abda4d80a56b0f2b2f1f45d5fc840934d1bc31e1907de11cc665f5aecb5b12152bb8dee686fb656294eb55440897e
|
|
7
|
+
data.tar.gz: 389a82b7755342dd591fd0e2d45d760eb8350f697d46414d8d90776624bd0ee7263acfe163ab1e8dd42c869d12a544c2d5faa4656d4ef5c802f4991b9f33c1b4
|
data/imap-backup.gemspec
CHANGED
|
@@ -30,7 +30,7 @@ Gem::Specification.new do |gem|
|
|
|
30
30
|
gem.add_dependency "rake"
|
|
31
31
|
gem.add_dependency "sys-proctable"
|
|
32
32
|
gem.add_dependency "thor", "~> 1.1"
|
|
33
|
-
gem.add_dependency "thunderbird", "0.
|
|
33
|
+
gem.add_dependency "thunderbird", "~> 0.6.0"
|
|
34
34
|
|
|
35
35
|
gem.metadata = {
|
|
36
36
|
"rubygems_mfa_required" => "true"
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
require "imap/backup/account/backup_folders"
|
|
2
2
|
require "imap/backup/account/folder_backup"
|
|
3
|
-
require "imap/backup/account/folder_ensurer"
|
|
4
3
|
require "imap/backup/account/local_only_folder_deleter"
|
|
5
4
|
require "imap/backup/account/locker"
|
|
6
5
|
|
|
@@ -25,7 +24,7 @@ module Imap::Backup
|
|
|
25
24
|
# start the connection so we get logging messages in the right order
|
|
26
25
|
account.client.login
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
ensure_directory
|
|
29
28
|
delete_local_only_folders if account.mirror_mode
|
|
30
29
|
|
|
31
30
|
if backup_folders.none?
|
|
@@ -53,8 +52,8 @@ module Imap::Backup
|
|
|
53
52
|
Account::LocalOnlyFolderDeleter.new(account: account).run
|
|
54
53
|
end
|
|
55
54
|
|
|
56
|
-
def
|
|
57
|
-
|
|
55
|
+
def ensure_directory
|
|
56
|
+
Serializer::DirectoryMaker.new(files_path: account.files_path).run
|
|
58
57
|
end
|
|
59
58
|
|
|
60
59
|
def locker
|
|
@@ -4,6 +4,7 @@ require "imap/backup/flag_refresher"
|
|
|
4
4
|
require "imap/backup/local_only_message_deleter"
|
|
5
5
|
require "imap/backup/logger"
|
|
6
6
|
require "imap/backup/serializer"
|
|
7
|
+
require "imap/backup/serializer/files/path"
|
|
7
8
|
|
|
8
9
|
module Imap; end
|
|
9
10
|
|
|
@@ -86,7 +87,12 @@ module Imap::Backup
|
|
|
86
87
|
end
|
|
87
88
|
|
|
88
89
|
def raw_serializer
|
|
89
|
-
@raw_serializer ||=
|
|
90
|
+
@raw_serializer ||= begin
|
|
91
|
+
path = Serializer::Files::Path.new(
|
|
92
|
+
base_path: account.local_path, folder_name: folder.name
|
|
93
|
+
)
|
|
94
|
+
Serializer.new(files_path: path)
|
|
95
|
+
end
|
|
90
96
|
end
|
|
91
97
|
end
|
|
92
98
|
end
|
|
@@ -3,6 +3,7 @@ require "pathname"
|
|
|
3
3
|
|
|
4
4
|
require "imap/backup/account/folder"
|
|
5
5
|
require "imap/backup/serializer"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
6
7
|
|
|
7
8
|
module Imap; end
|
|
8
9
|
|
|
@@ -44,7 +45,10 @@ module Imap::Backup
|
|
|
44
45
|
glob = File.join(source_local_path, "**", "*.imap")
|
|
45
46
|
Pathname.glob(glob) do |path|
|
|
46
47
|
name = source_folder_name(path)
|
|
47
|
-
|
|
48
|
+
path = Serializer::Files::Path.new(
|
|
49
|
+
base_path: source_local_path, folder_name: name
|
|
50
|
+
)
|
|
51
|
+
serializer = Serializer.new(files_path: path)
|
|
48
52
|
folder = destination_folder_for_path(name)
|
|
49
53
|
block.call(serializer, folder)
|
|
50
54
|
end
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
require "imap/backup/account/folder_ensurer"
|
|
2
1
|
require "imap/backup/lockfile"
|
|
3
2
|
|
|
4
3
|
module Imap; end
|
|
@@ -25,7 +24,7 @@ module Imap::Backup
|
|
|
25
24
|
Logger.logger.info("Stale lockfile '#{account.lockfile_path}' found. Removing it.")
|
|
26
25
|
lockfile.remove
|
|
27
26
|
else
|
|
28
|
-
|
|
27
|
+
Serializer::DirectoryMaker.new(files_path: account.files_path).run
|
|
29
28
|
end
|
|
30
29
|
|
|
31
30
|
begin
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
require "pathname"
|
|
2
2
|
|
|
3
3
|
require "imap/backup/account/folder"
|
|
4
|
-
require "imap/backup/account/folder_ensurer"
|
|
5
4
|
require "imap/backup/serializer"
|
|
5
|
+
require "imap/backup/serializer/directory_maker"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
6
7
|
|
|
7
8
|
module Imap; end
|
|
8
9
|
|
|
@@ -27,7 +28,10 @@ module Imap::Backup
|
|
|
27
28
|
|
|
28
29
|
glob.each do |path|
|
|
29
30
|
name = path.relative_path_from(base).to_s[0..-6]
|
|
30
|
-
|
|
31
|
+
files_path = Serializer::Files::Path.new(
|
|
32
|
+
base_path: account.local_path, folder_name: name
|
|
33
|
+
)
|
|
34
|
+
serializer = Serializer.new(files_path: files_path)
|
|
31
35
|
folder = Account::Folder.new(account.client, name)
|
|
32
36
|
block.call(serializer, folder)
|
|
33
37
|
end
|
|
@@ -41,7 +45,10 @@ module Imap::Backup
|
|
|
41
45
|
|
|
42
46
|
glob.each do |path|
|
|
43
47
|
name = path.relative_path_from(base).to_s[0..-6]
|
|
44
|
-
|
|
48
|
+
files_path = Serializer::Files::Path.new(
|
|
49
|
+
base_path: account.local_path, folder_name: name
|
|
50
|
+
)
|
|
51
|
+
serializer = Serializer.new(files_path: files_path)
|
|
45
52
|
block.call(serializer)
|
|
46
53
|
end
|
|
47
54
|
end
|
|
@@ -69,7 +76,10 @@ module Imap::Backup
|
|
|
69
76
|
|
|
70
77
|
def glob
|
|
71
78
|
@glob ||= begin
|
|
72
|
-
|
|
79
|
+
files_path = Serializer::Files::Path.new(
|
|
80
|
+
base_path: account.local_path, folder_name: nil
|
|
81
|
+
)
|
|
82
|
+
Serializer::DirectoryMaker.new(files_path: files_path).run
|
|
73
83
|
|
|
74
84
|
pattern = File.join(account.local_path, "**", "*.imap")
|
|
75
85
|
Pathname.glob(pattern)
|
data/lib/imap/backup/account.rb
CHANGED
|
@@ -4,7 +4,6 @@ require "imap/backup/account/client_factory"
|
|
|
4
4
|
|
|
5
5
|
module Imap; end
|
|
6
6
|
|
|
7
|
-
# rubocop:disable Metrics/ClassLength
|
|
8
7
|
module Imap::Backup
|
|
9
8
|
# Contains the attributes relating to an email account.
|
|
10
9
|
class Account
|
|
@@ -188,6 +187,11 @@ module Imap::Backup
|
|
|
188
187
|
update(:local_path, value)
|
|
189
188
|
end
|
|
190
189
|
|
|
190
|
+
# @return [Serializer::Files::Path] the base files path for the account
|
|
191
|
+
def files_path
|
|
192
|
+
Serializer::Files::Path.new(base_path: local_path, folder_name: nil)
|
|
193
|
+
end
|
|
194
|
+
|
|
191
195
|
# @raise [RuntimeError] if the local_path is not set
|
|
192
196
|
# @return [String] the path to the lockfile for the account
|
|
193
197
|
def lockfile_path
|
|
@@ -3,6 +3,7 @@ require "thor"
|
|
|
3
3
|
require "imap/backup/account/backup_folders"
|
|
4
4
|
require "imap/backup/cli/helpers"
|
|
5
5
|
require "imap/backup/serializer"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
6
7
|
|
|
7
8
|
module Imap; end
|
|
8
9
|
|
|
@@ -63,7 +64,10 @@ module Imap::Backup
|
|
|
63
64
|
backup_folders.map do |folder|
|
|
64
65
|
next if !folder.exist?
|
|
65
66
|
|
|
66
|
-
|
|
67
|
+
path = Serializer::Files::Path.new(
|
|
68
|
+
base_path: account.local_path, folder_name: folder.name
|
|
69
|
+
)
|
|
70
|
+
serializer = Serializer.new(files_path: path)
|
|
67
71
|
local_uids = serializer.uids
|
|
68
72
|
Logger.logger.debug("[Stats] fetching email list for '#{folder.name}'")
|
|
69
73
|
remote_uids = folder.uids
|
|
@@ -7,6 +7,7 @@ require "imap/backup/cli/helpers"
|
|
|
7
7
|
require "imap/backup/logger"
|
|
8
8
|
require "imap/backup/serializer"
|
|
9
9
|
require "imap/backup/thunderbird/mailbox_exporter"
|
|
10
|
+
require "imap/backup/serializer/files/path"
|
|
10
11
|
|
|
11
12
|
module Imap; end
|
|
12
13
|
|
|
@@ -103,7 +104,10 @@ module Imap::Backup
|
|
|
103
104
|
backup_folders.each do |folder|
|
|
104
105
|
next if !folder.exist?
|
|
105
106
|
|
|
106
|
-
|
|
107
|
+
path = Serializer::Files::Path.new(
|
|
108
|
+
base_path: account.local_path, folder_name: folder.name
|
|
109
|
+
)
|
|
110
|
+
serializer = Serializer.new(files_path: path)
|
|
107
111
|
ignore_folder_history(folder, serializer)
|
|
108
112
|
end
|
|
109
113
|
end
|
|
@@ -126,18 +130,41 @@ module Imap::Backup
|
|
|
126
130
|
end
|
|
127
131
|
|
|
128
132
|
def thunderbird_profile(name = nil)
|
|
133
|
+
Logger.logger.info("[CLI::Utils] Fetching Thunderbird profile")
|
|
129
134
|
profiles = ::Thunderbird::Profiles.new
|
|
135
|
+
Logger.logger.debug("[CLI::Utils] Found #{profiles.installs.count} Thunderbird install(s)")
|
|
130
136
|
if name
|
|
137
|
+
Logger.logger.debug("[CLI::Utils] Using profile name '#{name}'")
|
|
131
138
|
profiles.profile(name)
|
|
132
139
|
else
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
140
|
+
choose_default_profile(profiles)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
def choose_default_profile(profiles)
|
|
145
|
+
Logger.logger.debug("[CLI::Utils] No profile name supplied, so looking for default profile")
|
|
146
|
+
case profiles.installs.count
|
|
147
|
+
when 0
|
|
148
|
+
raise "No Thunderbird installs found, so no default profile exists"
|
|
149
|
+
when 1
|
|
150
|
+
install = profiles.installs.first
|
|
151
|
+
Logger.logger.debug(
|
|
152
|
+
"[CLI::Utils] Only one Thunderbird install found '#{install.title}', " \
|
|
153
|
+
"so using its default profile"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
profile = install.default_profile
|
|
157
|
+
raise "Thunderbird install '#{install.title}' does not have a default profile" if !profile
|
|
158
|
+
|
|
159
|
+
Logger.logger.debug(
|
|
160
|
+
"[CLI::Utils] Default profile '#{profile.title}' has path '#{profile.root}'"
|
|
161
|
+
)
|
|
162
|
+
profile
|
|
163
|
+
else
|
|
164
|
+
raise <<~MESSAGE
|
|
165
|
+
Thunderbird has multiple installs, so no default profile exists.
|
|
166
|
+
Please supply a profile name
|
|
167
|
+
MESSAGE
|
|
141
168
|
end
|
|
142
169
|
end
|
|
143
170
|
end
|
data/lib/imap/backup/mirror.rb
CHANGED
|
@@ -7,13 +7,9 @@ module Imap::Backup
|
|
|
7
7
|
|
|
8
8
|
# Appends messages to the local store
|
|
9
9
|
class Serializer::Appender
|
|
10
|
-
# @param
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def initialize(folder:, imap:, mbox:)
|
|
14
|
-
@folder = folder
|
|
15
|
-
@imap = imap
|
|
16
|
-
@mbox = mbox
|
|
10
|
+
# @param files [Serializer::Files] the folder's files
|
|
11
|
+
def initialize(files:)
|
|
12
|
+
@files = files
|
|
17
13
|
end
|
|
18
14
|
|
|
19
15
|
# Adds a message to the metadata file and the mailbox.
|
|
@@ -31,7 +27,7 @@ module Imap::Backup
|
|
|
31
27
|
existing = imap.get(uid)
|
|
32
28
|
if existing
|
|
33
29
|
Logger.logger.debug(
|
|
34
|
-
"[#{
|
|
30
|
+
"[#{files.files_path}] message #{uid} already downloaded - skipping"
|
|
35
31
|
)
|
|
36
32
|
return
|
|
37
33
|
end
|
|
@@ -42,7 +38,7 @@ module Imap::Backup
|
|
|
42
38
|
raise wrap_error(
|
|
43
39
|
error: e,
|
|
44
40
|
note: "failed to serialize message",
|
|
45
|
-
|
|
41
|
+
files_path: files.files_path,
|
|
46
42
|
uid: uid,
|
|
47
43
|
message: message
|
|
48
44
|
)
|
|
@@ -55,7 +51,7 @@ module Imap::Backup
|
|
|
55
51
|
raise wrap_error(
|
|
56
52
|
error: e,
|
|
57
53
|
note: "failed to append message",
|
|
58
|
-
|
|
54
|
+
files_path: files.files_path,
|
|
59
55
|
uid: uid,
|
|
60
56
|
message: message
|
|
61
57
|
)
|
|
@@ -64,13 +60,19 @@ module Imap::Backup
|
|
|
64
60
|
|
|
65
61
|
private
|
|
66
62
|
|
|
67
|
-
attr_reader :
|
|
68
|
-
attr_reader :folder
|
|
69
|
-
attr_reader :mbox
|
|
63
|
+
attr_reader :files
|
|
70
64
|
|
|
71
|
-
def
|
|
65
|
+
def imap
|
|
66
|
+
files.imap
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def mbox
|
|
70
|
+
files.mbox
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def wrap_error(error:, note:, files_path:, uid:, message:)
|
|
72
74
|
<<-ERROR.gsub(/^\s*/m, "")
|
|
73
|
-
[#{
|
|
75
|
+
[#{files_path}] #{note} #{uid}: #{message}.
|
|
74
76
|
#{error}:
|
|
75
77
|
#{error.backtrace.join("\n")}"
|
|
76
78
|
ERROR
|
|
@@ -104,11 +104,11 @@ module Imap::Backup
|
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
def mbox
|
|
107
|
-
@mbox ||= Serializer::Mbox.new(serializer.
|
|
107
|
+
@mbox ||= Serializer::Mbox.new(files_path: serializer.files_path)
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
def imap
|
|
111
|
-
@imap ||= Serializer::Imap.new(serializer.
|
|
111
|
+
@imap ||= Serializer::Imap.new(files_path: serializer.files_path)
|
|
112
112
|
end
|
|
113
113
|
|
|
114
114
|
def tsx
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require "fileutils"
|
|
2
|
+
require "os"
|
|
3
|
+
|
|
4
|
+
require "imap/backup/serializer/files/path"
|
|
5
|
+
|
|
6
|
+
module Imap; end
|
|
7
|
+
|
|
8
|
+
module Imap::Backup
|
|
9
|
+
class Serializer; end
|
|
10
|
+
|
|
11
|
+
# Creates any directories needed to store backups
|
|
12
|
+
class Serializer::DirectoryMaker
|
|
13
|
+
# @param files_path [Serializer::Files::Path] the folder path components
|
|
14
|
+
# @param permissions [Integer] The permissions to set on the folder
|
|
15
|
+
def initialize(files_path:)
|
|
16
|
+
@files_path = files_path
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Creates the containing directory and any missing parent directories,
|
|
20
|
+
# ensuring the required permissions (except on Windows).
|
|
21
|
+
# @return [void]
|
|
22
|
+
def run
|
|
23
|
+
directory = files_path.directory
|
|
24
|
+
FileUtils.mkdir_p(directory)
|
|
25
|
+
|
|
26
|
+
return if windows?
|
|
27
|
+
|
|
28
|
+
FileUtils.chmod DIRECTORY_PERMISSIONS, directory
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
attr_reader :files_path
|
|
34
|
+
|
|
35
|
+
# The desired permissions for all directories that store backups
|
|
36
|
+
DIRECTORY_PERMISSIONS = 0o700
|
|
37
|
+
private_constant :DIRECTORY_PERMISSIONS
|
|
38
|
+
|
|
39
|
+
def windows?
|
|
40
|
+
OS.windows?
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Imap; end
|
|
2
|
+
|
|
3
|
+
module Imap::Backup
|
|
4
|
+
class Serializer; end
|
|
5
|
+
class Serializer::Files; end
|
|
6
|
+
|
|
7
|
+
# A Data class representing the path to a folder's serialized data
|
|
8
|
+
# it contains two elements: the base path and the folder name.
|
|
9
|
+
# The base path is the root path for an account's backup,
|
|
10
|
+
# and the folder name is the name of the folder within that account.
|
|
11
|
+
# If the folder name is nil, the path represents the base path only.
|
|
12
|
+
Serializer::Files::Path = Data.define(:base_path, :folder_name) do
|
|
13
|
+
# @return [String] the full path to the folder's serialized data
|
|
14
|
+
def to_s
|
|
15
|
+
File.join(base_path, folder_name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @return [String] the directory containing the folder's serialized data
|
|
19
|
+
def directory
|
|
20
|
+
if folder_name
|
|
21
|
+
File.dirname(to_s)
|
|
22
|
+
else
|
|
23
|
+
base_path
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
require "imap/backup/naming"
|
|
2
|
+
require "imap/backup/serializer/directory_maker"
|
|
3
|
+
require "imap/backup/serializer/imap"
|
|
4
|
+
require "imap/backup/serializer/integrity_checker"
|
|
5
|
+
require "imap/backup/serializer/mbox"
|
|
6
|
+
require "imap/backup/serializer/files/path"
|
|
7
|
+
|
|
8
|
+
module Imap; end
|
|
9
|
+
|
|
10
|
+
module Imap::Backup
|
|
11
|
+
# Provides memoized file helpers for Serializer
|
|
12
|
+
class Serializer::Files
|
|
13
|
+
extend Forwardable
|
|
14
|
+
|
|
15
|
+
def_delegator :imap, :update
|
|
16
|
+
|
|
17
|
+
attr_reader :files_path
|
|
18
|
+
|
|
19
|
+
# @param files_path [Serializer::Files::Path] the path of the folder
|
|
20
|
+
def initialize(files_path:)
|
|
21
|
+
@files_path = files_path
|
|
22
|
+
@directory_ensured = false
|
|
23
|
+
@imap = nil
|
|
24
|
+
@mbox = nil
|
|
25
|
+
@sanitized_files_path = nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def imap
|
|
29
|
+
@imap ||= begin
|
|
30
|
+
ensure_directory
|
|
31
|
+
Serializer::Imap.new(files_path: sanitized_files_path)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def mbox
|
|
36
|
+
@mbox ||= begin
|
|
37
|
+
ensure_directory
|
|
38
|
+
Serializer::Mbox.new(files_path: sanitized_files_path)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Checks that the folder's data is stored correctly
|
|
43
|
+
# @return [void]
|
|
44
|
+
def check_integrity!
|
|
45
|
+
Serializer::IntegrityChecker.new(imap: imap, mbox: mbox).run
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Deletes the serialized data
|
|
49
|
+
# @return [void]
|
|
50
|
+
def delete
|
|
51
|
+
imap.delete
|
|
52
|
+
mbox.delete
|
|
53
|
+
reload
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Enumerates over a series of messages.
|
|
57
|
+
# When called without a block, returns an Enumerator
|
|
58
|
+
# @param required_uids [Array<Integer>] the UIDs of the message to enumerate over
|
|
59
|
+
# @return [Enumerator, void]
|
|
60
|
+
def each_message(required_uids = nil, &block)
|
|
61
|
+
return enum_for(:each_message, required_uids) if !block
|
|
62
|
+
|
|
63
|
+
required_uids ||= uids
|
|
64
|
+
|
|
65
|
+
validate!
|
|
66
|
+
|
|
67
|
+
enumerator = Serializer::MessageEnumerator.new(imap: imap)
|
|
68
|
+
enumerator.run(uids: required_uids, &block)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Get message metadata
|
|
72
|
+
# @param uid [Integer] a message UID
|
|
73
|
+
# @return [Serializer::Message]
|
|
74
|
+
def get(uid)
|
|
75
|
+
validate!
|
|
76
|
+
imap.get(uid)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# @return [Array<Hash>]
|
|
80
|
+
def messages
|
|
81
|
+
validate!
|
|
82
|
+
imap.messages
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Forces a reload of the serialized files
|
|
86
|
+
# @return [void]
|
|
87
|
+
def reload
|
|
88
|
+
@imap = nil
|
|
89
|
+
@mbox = nil
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Renames the serialized files
|
|
93
|
+
# @param new_files_path [Serializer::Files::Path] the new path of the folder
|
|
94
|
+
# @return [void]
|
|
95
|
+
def rename(new_files_path)
|
|
96
|
+
Serializer::DirectoryMaker.new(files_path: new_files_path).run
|
|
97
|
+
mbox.rename new_files_path
|
|
98
|
+
imap.rename new_files_path
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# @return [Integer] the UID validity for the folder
|
|
102
|
+
def uid_validity
|
|
103
|
+
validate!
|
|
104
|
+
imap.uid_validity
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def uid_validity=(value)
|
|
108
|
+
validate!
|
|
109
|
+
imap.uid_validity = value
|
|
110
|
+
mbox.touch
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# @return [Array<Integer>] The uids of all messages
|
|
114
|
+
def uids
|
|
115
|
+
validate!
|
|
116
|
+
imap.uids
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Update a message's metadata, replacing its UID
|
|
120
|
+
# @param old [Integer] the existing message UID
|
|
121
|
+
# @param new [Integer] the new UID to apply to the message
|
|
122
|
+
# @return [void]
|
|
123
|
+
def update_uid(old, new)
|
|
124
|
+
validate!
|
|
125
|
+
imap.update_uid(old, new)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Checks that the metadata files are valid,
|
|
129
|
+
# or deletes any existing files if the pair are not valid.
|
|
130
|
+
# @return [Boolean] indicates whether there are existing, valid files
|
|
131
|
+
def validate!
|
|
132
|
+
return true if @validated
|
|
133
|
+
|
|
134
|
+
imap_valid = imap.valid?
|
|
135
|
+
mbox_valid = mbox.valid?
|
|
136
|
+
if imap_valid && mbox_valid
|
|
137
|
+
@validated = true
|
|
138
|
+
return true
|
|
139
|
+
end
|
|
140
|
+
warn_imap = !imap_valid && imap.exist?
|
|
141
|
+
Logger.logger.info("Metadata file '#{imap.pathname}' is invalid") if warn_imap
|
|
142
|
+
|
|
143
|
+
delete
|
|
144
|
+
|
|
145
|
+
false
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private
|
|
149
|
+
|
|
150
|
+
def ensure_directory
|
|
151
|
+
return if @directory_ensured
|
|
152
|
+
|
|
153
|
+
Serializer::DirectoryMaker.new(files_path: sanitized_files_path).run
|
|
154
|
+
@directory_ensured = true
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def sanitized
|
|
158
|
+
@sanitized ||= Naming.to_local_path(files_path.folder_name)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def sanitized_files_path
|
|
162
|
+
@sanitized_files_path ||= Serializer::Files::Path.new(
|
|
163
|
+
base_path: files_path.base_path, folder_name: sanitized
|
|
164
|
+
)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
@@ -14,12 +14,14 @@ module Imap::Backup
|
|
|
14
14
|
# The version number to store in the metadata file
|
|
15
15
|
CURRENT_VERSION = 3
|
|
16
16
|
|
|
17
|
-
# @return [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
# @return [Serializer::Files::Path] The path of the imap metadata file, without the '.imap'
|
|
18
|
+
# extension
|
|
19
|
+
attr_reader :files_path
|
|
20
|
+
|
|
21
|
+
# @param files_path [Serializer::Files::Path] The path of the imap metadata file, without
|
|
22
|
+
# the '.imap' extension
|
|
23
|
+
def initialize(files_path:)
|
|
24
|
+
@files_path = files_path
|
|
23
25
|
@loaded = false
|
|
24
26
|
@uid_validity = nil
|
|
25
27
|
@messages = nil
|
|
@@ -61,7 +63,7 @@ module Imap::Backup
|
|
|
61
63
|
|
|
62
64
|
# @return [String] The full path name of the metadata file
|
|
63
65
|
def pathname
|
|
64
|
-
"#{
|
|
66
|
+
"#{files_path}.imap"
|
|
65
67
|
end
|
|
66
68
|
|
|
67
69
|
def exist?
|
|
@@ -136,15 +138,16 @@ module Imap::Backup
|
|
|
136
138
|
|
|
137
139
|
# Renames the metadata file, if it exists,
|
|
138
140
|
# otherwise, simply stores the new name
|
|
139
|
-
# @param new_path [
|
|
141
|
+
# @param new_path [Serializer::Files::Path] the new path
|
|
140
142
|
# @return [void]
|
|
141
143
|
def rename(new_path)
|
|
142
144
|
if exist?
|
|
143
145
|
old_pathname = pathname
|
|
144
|
-
@
|
|
145
|
-
|
|
146
|
+
@files_path = new_path
|
|
147
|
+
new_pathname = pathname
|
|
148
|
+
File.rename(old_pathname, new_pathname)
|
|
146
149
|
else
|
|
147
|
-
@
|
|
150
|
+
@files_path = new_path
|
|
148
151
|
end
|
|
149
152
|
end
|
|
150
153
|
|
|
@@ -260,7 +263,7 @@ module Imap::Backup
|
|
|
260
263
|
end
|
|
261
264
|
|
|
262
265
|
def mbox
|
|
263
|
-
@mbox ||= Serializer::Mbox.new(
|
|
266
|
+
@mbox ||= Serializer::Mbox.new(files_path: files_path)
|
|
264
267
|
end
|
|
265
268
|
|
|
266
269
|
def tsx
|