imap-backup 9.2.0 → 9.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2817e61358b848f7dbef08e1a16562def8cd364e66d317e4dac56c1f5e62f958
4
- data.tar.gz: ec0e2d2963e863b204b0529ca3746492703486e8f77a9e7c08bc301b203249de
3
+ metadata.gz: 87355fbc67849a32cdb0def02245d0101198089a15e8fcffbeadcb717413af26
4
+ data.tar.gz: 55c13492ab22718193472e76a5d79ee30c3d3ee444ca1240220c687939c07339
5
5
  SHA512:
6
- metadata.gz: 66871fce3faebbcbd87737fef230803b46f32025b28753fe4f83813e652ef14817fce302da0570000f0d0ecb582862589a84f535a94aa1fa7cc1578eaf30591c
7
- data.tar.gz: 274cfc47767322e31eb1e7206c3d1d449b2098ba1a65073c80aa5010c06924abfc7a952aeace01436715a7f95599503d86bcecbbdf210f7c8b5529bf7bee4fdf
6
+ metadata.gz: a4583f55d69565cf81552a0ba295ba21983be0d2ad2d3a83f65e0d1c7970df6cd73ced117b625de04d858e7d71c39f386b020ce437e10fa6c9d7b734b13e3715
7
+ data.tar.gz: a02472800916ad71c7f8129084bb1a2920624c0c87fc672bebb009990e21ecf1055d8d0a8d80b42481038f3694bab3ad32c4386006a108f2dd928553420c9606
data/README.md CHANGED
@@ -76,6 +76,7 @@ and exported via [utils export-to-thunderbird](docs/commands/utils-export-to-thu
76
76
 
77
77
  * [backup](docs/commands/backup.md)
78
78
  * [local accounts](docs/commands/local-accounts.md)
79
+ * [local check](docs/commands/local-check.md)
79
80
  * [local folders](docs/commands/local-folders.md)
80
81
  * [local list](docs/commands/local-list.md)
81
82
  * [local show](docs/commands/local-show.md)
@@ -0,0 +1,29 @@
1
+ # Migrate to a new e-mail server while keeping your existing address
2
+
3
+ While switching e-mail provider (from provider `A` to `B`), you might want to keep the same address (`mymail@domain.com`), and restrieve all your existing e-mails on your new server `B`. `imap-backup` can do that too!
4
+
5
+ 1. Backup your e-mails: use [`imap-backup setup`](/docs/commands/setup.md) to setup connection to your old provider `A`, then launch [`imap-backup backup`](/docs/commands/backup.md).
6
+ 1. Actually switch your e-mail service provider (update your DNS MX and all that...).
7
+ 1. It is best to use [`imap-backup migrate`](/docs/commands/migrate.md) and not [`imap-backup restore`](/docs/commands/restore.md) here, but both the source and the destination have the same address... You need to manually rename your old account first:
8
+
9
+ 1. Modify your configuration file manually (i.e. not via `imap-backup setup`) and rename your account to `mymail-old@domain.com`:
10
+
11
+ ```diff
12
+ "accounts": [
13
+ {
14
+ - "username": "mymail@domain.com",
15
+ + "username": "mymail-old@domain.com",
16
+ "password": "...",
17
+ - "local_path": "/some/path/.imap-backup/mymail_domain.com",
18
+ + "local_path": "/some/path/.imap-backup/mymail-old_domain.com",
19
+ "folders": [...],
20
+ "server": "..."
21
+ }
22
+ ```
23
+
24
+ 1. Rename the backup directory from `mymail_domain.com` to `mymail-old_domain.com`.
25
+
26
+ 1. Set up a new account giving access to the new provider `B` using `imap-backup setup`.
27
+ 1. Now you can use `imap-backup migrate`, optionnally adapting [delimiters and prefixes configuration](/docs/delimiters-and-prefixes.md) if need be:
28
+
29
+ imap-backup migrate mymail-old@domain.com mymail@domain.com [options]
@@ -14,6 +14,8 @@ module Imap::Backup
14
14
  BODY_ATTRIBUTE = "BODY[]".freeze
15
15
  UID_FETCH_RETRY_CLASSES = [EOFError, Errno::ECONNRESET, IOError].freeze
16
16
  APPEND_RETRY_CLASSES = [Net::IMAP::BadResponseError].freeze
17
+ CREATE_RETRY_CLASSES = [Net::IMAP::BadResponseError].freeze
18
+ EXAMINE_RETRY_CLASSES = [Net::IMAP::BadResponseError].freeze
17
19
  PERMITTED_FLAGS = %i(Answered Draft Flagged Seen).freeze
18
20
 
19
21
  attr_reader :connection
@@ -33,7 +35,9 @@ module Imap::Backup
33
35
  end
34
36
 
35
37
  def exist?
36
- examine
38
+ retry_on_error(errors: EXAMINE_RETRY_CLASSES) do
39
+ examine
40
+ end
37
41
  true
38
42
  rescue FolderNotFound
39
43
  false
@@ -42,7 +46,9 @@ module Imap::Backup
42
46
  def create
43
47
  return if exist?
44
48
 
45
- client.create(utf7_encoded_name)
49
+ retry_on_error(errors: CREATE_RETRY_CLASSES) do
50
+ client.create(utf7_encoded_name)
51
+ end
46
52
  end
47
53
 
48
54
  def uid_validity
@@ -85,6 +85,8 @@ module Imap::Backup
85
85
  end
86
86
 
87
87
  def each_connection(config, names)
88
+ return enum_for(:each_connection, config, names) if !block_given?
89
+
88
90
  config.accounts.each do |account|
89
91
  next if names.any? && !names.include?(account.username)
90
92
 
@@ -20,6 +20,47 @@ module Imap::Backup
20
20
  end
21
21
  end
22
22
 
23
+ desc(
24
+ "check",
25
+ "Check the integrity of backups for all accounts (or the selected account(s))"
26
+ )
27
+ method_option(
28
+ "delete_corrupt",
29
+ type: :boolean,
30
+ desc: "deletes any corrupted folders - USE WITH CAUTION!"
31
+ )
32
+ config_option
33
+ format_option
34
+ def check
35
+ config = load_config(**options)
36
+ results = each_connection(config, emails).map do |connection|
37
+ folders = connection.local_folders
38
+ folder_results = folders.map do |serializer|
39
+ serializer.check_integrity!
40
+ {name: serializer.folder, result: "OK"}
41
+ rescue Serializer::FolderIntegrityError => e
42
+ message = e.to_s
43
+ if options[:delete_corrupt]
44
+ serializer.delete
45
+ message << " and has been deleted"
46
+ end
47
+
48
+ {
49
+ name: serializer.folder,
50
+ result: message
51
+ }
52
+ end
53
+ {account: connection.account.username, folders: folder_results}
54
+ end
55
+
56
+ case options[:format]
57
+ when "json"
58
+ print_check_results_as_json(results)
59
+ else
60
+ print_check_results_as_text(results)
61
+ end
62
+ end
63
+
23
64
  desc "folders EMAIL", "List backed up folders"
24
65
  config_option
25
66
  format_option
@@ -87,6 +128,19 @@ module Imap::Backup
87
128
  end
88
129
 
89
130
  no_commands do
131
+ def print_check_results_as_json(results)
132
+ Kernel.puts results.to_json
133
+ end
134
+
135
+ def print_check_results_as_text(results)
136
+ results.each do |account_results|
137
+ Kernel.puts "Account: #{account_results[:account]}"
138
+ account_results[:folders].each do |folder_results|
139
+ Kernel.puts "\t#{folder_results[:name]}: #{folder_results[:result]}"
140
+ end
141
+ end
142
+ end
143
+
90
144
  def list_emails_as_json(serializer)
91
145
  emails = serializer.each_message.map do |message|
92
146
  {
@@ -142,6 +196,10 @@ module Imap::Backup
142
196
  Kernel.puts message.body
143
197
  end
144
198
  end
199
+
200
+ def emails
201
+ (options[:accounts] || "").split(",")
202
+ end
145
203
  end
146
204
  end
147
205
  end
@@ -12,7 +12,7 @@ module Imap::Backup
12
12
  quiet_option
13
13
  verbose_option
14
14
  def ignore_history(email)
15
- Imap::Backup::Logger.setup_logging options
15
+ Logger.setup_logging options
16
16
  config = load_config(**options)
17
17
  connection = connection(config, email)
18
18
 
@@ -15,6 +15,14 @@ module Imap::Backup
15
15
  @version = nil
16
16
  end
17
17
 
18
+ def pathname
19
+ "#{folder_path}.imap"
20
+ end
21
+
22
+ def exist?
23
+ File.exist?(pathname)
24
+ end
25
+
18
26
  def valid?
19
27
  return false if !exist?
20
28
  return false if version != CURRENT_VERSION
@@ -114,14 +122,6 @@ module Imap::Backup
114
122
 
115
123
  private
116
124
 
117
- def pathname
118
- "#{folder_path}.imap"
119
- end
120
-
121
- def exist?
122
- File.exist?(pathname)
123
- end
124
-
125
125
  def ensure_loaded
126
126
  return if loaded
127
127
 
@@ -59,8 +59,6 @@ module Imap::Backup
59
59
  File.open(pathname, "a") {}
60
60
  end
61
61
 
62
- private
63
-
64
62
  def exist?
65
63
  File.exist?(pathname)
66
64
  end
@@ -19,6 +19,8 @@ module Imap::Backup
19
19
 
20
20
  extend Forwardable
21
21
 
22
+ class FolderIntegrityError < StandardError; end
23
+
22
24
  def_delegator :mbox, :pathname, :mbox_pathname
23
25
  def_delegators :imap, :get, :messages, :uid_validity, :uids, :update_uid
24
26
 
@@ -42,6 +44,57 @@ module Imap::Backup
42
44
  false
43
45
  end
44
46
 
47
+ def check_integrity!
48
+ if !imap.valid?
49
+ message = ".imap file '#{imap.pathname}' is corrupt"
50
+ raise FolderIntegrityError, message
51
+ end
52
+
53
+ if !mbox.exist?
54
+ message = ".mbox file '#{mbox.pathname}' is missing"
55
+ raise FolderIntegrityError, message
56
+ end
57
+
58
+ return if imap.messages.empty?
59
+
60
+ offsets = imap.messages.map(&:offset)
61
+
62
+ if offsets != offsets.sort
63
+ message = ".imap file '#{imap.pathname}' has offset data which is out of order"
64
+ raise FolderIntegrityError, message
65
+ end
66
+
67
+ if mbox.length < offsets[-1]
68
+ message =
69
+ ".imap file '#{imap.pathname}' has offsets past the end " \
70
+ "of .mbox file '#{mbox.pathname}'"
71
+ raise FolderIntegrityError, message
72
+ end
73
+
74
+ imap.messages.each do |m|
75
+ text = mbox.read(m.offset, m.length)
76
+ if text.length < m.length
77
+ message = "Message #{m.uid} is incomplete in file '#{mbox.pathname}'"
78
+ raise FolderIntegrityError, message
79
+ end
80
+
81
+ next if text.start_with?("From ")
82
+
83
+ message =
84
+ "Message #{m.uid} not found at expected offset #{m.offset} " \
85
+ "in file '#{mbox.pathname}'"
86
+ raise FolderIntegrityError, message
87
+ end
88
+
89
+ last = imap.messages.last
90
+ expected_length = last.offset + last.length
91
+ actual_length = mbox.length
92
+ return if actual_length == expected_length
93
+
94
+ message = "Mbox file '#{mbox.pathname}' contains unexpected trailing data"
95
+ raise FolderIntegrityError, message
96
+ end
97
+
45
98
  def delete
46
99
  imap.delete
47
100
  @imap = nil
@@ -2,7 +2,7 @@ module Imap; end
2
2
 
3
3
  module Imap::Backup
4
4
  MAJOR = 9
5
- MINOR = 2
5
+ MINOR = 3
6
6
  REVISION = 0
7
7
  PRE = nil
8
8
  VERSION = [MAJOR, MINOR, REVISION, PRE].compact.map(&:to_s).join(".")
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imap-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.2.0
4
+ version: 9.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Joe Yates
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-01 00:00:00.000000000 Z
11
+ date: 2023-04-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline
@@ -219,6 +219,7 @@ files:
219
219
  - bin/imap-backup
220
220
  - docs/delimiters-and-prefixes.md
221
221
  - docs/development.md
222
+ - docs/migrate-server-keep-address.md
222
223
  - imap-backup.gemspec
223
224
  - lib/cli_coverage.rb
224
225
  - lib/email/mboxrd/message.rb