imap-backup 6.0.0.rc2 → 6.0.0
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 +5 -1
- data/lib/cli_coverage.rb +11 -11
- data/lib/email/provider/base.rb +2 -0
- data/lib/email/provider/unknown.rb +2 -0
- data/lib/email/provider.rb +2 -0
- data/lib/imap/backup/account/connection/backup_folders.rb +27 -0
- data/lib/imap/backup/account/connection/client_factory.rb +54 -0
- data/lib/imap/backup/account/connection/folder_names.rb +26 -0
- data/lib/imap/backup/account/connection.rb +11 -95
- data/lib/imap/backup/account/folder.rb +9 -6
- data/lib/imap/backup/account.rb +3 -5
- data/lib/imap/backup/cli/backup.rb +1 -3
- data/lib/imap/backup/cli/helpers.rb +24 -22
- data/lib/imap/backup/cli/local.rb +20 -13
- data/lib/imap/backup/cli/migrate.rb +4 -10
- data/lib/imap/backup/cli/restore.rb +8 -7
- data/lib/imap/backup/cli/setup.rb +10 -8
- data/lib/imap/backup/cli/stats.rb +78 -0
- data/lib/imap/backup/cli/status.rb +2 -2
- data/lib/imap/backup/cli/utils.rb +4 -6
- data/lib/imap/backup/cli.rb +24 -3
- data/lib/imap/backup/configuration.rb +9 -11
- data/lib/imap/backup/downloader.rb +48 -30
- data/lib/imap/backup/migrator.rb +5 -5
- data/lib/imap/backup/sanitizer.rb +3 -2
- data/lib/imap/backup/serializer/appender.rb +49 -0
- data/lib/imap/backup/serializer/message_enumerator.rb +29 -0
- data/lib/imap/backup/serializer/unused_name_finder.rb +27 -0
- data/lib/imap/backup/serializer.rb +24 -78
- data/lib/imap/backup/setup/account/header.rb +75 -0
- data/lib/imap/backup/setup/account.rb +14 -91
- data/lib/imap/backup/setup/asker.rb +4 -15
- data/lib/imap/backup/setup/backup_path.rb +41 -0
- data/lib/imap/backup/setup/email.rb +45 -0
- data/lib/imap/backup/setup/folder_chooser.rb +3 -3
- data/lib/imap/backup/setup/helpers.rb +1 -1
- data/lib/imap/backup/setup.rb +5 -4
- data/lib/imap/backup/thunderbird/mailbox_exporter.rb +39 -20
- data/lib/imap/backup/uploader.rb +46 -8
- data/lib/imap/backup/utils.rb +1 -1
- data/lib/imap/backup/version.rb +1 -1
- data/lib/imap/backup.rb +0 -1
- metadata +31 -134
- data/spec/features/backup_spec.rb +0 -100
- data/spec/features/configuration/minimal_configuration.rb +0 -15
- data/spec/features/configuration/missing_configuration.rb +0 -14
- data/spec/features/folders_spec.rb +0 -36
- data/spec/features/helper.rb +0 -2
- data/spec/features/local/list_accounts_spec.rb +0 -12
- data/spec/features/local/list_emails_spec.rb +0 -21
- data/spec/features/local/list_folders_spec.rb +0 -21
- data/spec/features/local/show_an_email_spec.rb +0 -34
- data/spec/features/migrate_spec.rb +0 -35
- data/spec/features/remote/list_account_folders_spec.rb +0 -16
- data/spec/features/restore_spec.rb +0 -162
- data/spec/features/status_spec.rb +0 -43
- data/spec/features/support/aruba.rb +0 -78
- data/spec/features/support/backup_directory.rb +0 -43
- data/spec/features/support/email_server.rb +0 -110
- data/spec/features/support/shared/connection_context.rb +0 -14
- data/spec/features/support/shared/message_fixtures.rb +0 -16
- data/spec/fixtures/connection.yml +0 -7
- data/spec/spec_helper.rb +0 -15
- data/spec/support/fixtures.rb +0 -11
- data/spec/support/higline_test_helpers.rb +0 -8
- data/spec/support/silence_logging.rb +0 -7
- data/spec/unit/email/mboxrd/message_spec.rb +0 -177
- data/spec/unit/email/provider/apple_mail_spec.rb +0 -7
- data/spec/unit/email/provider/base_spec.rb +0 -11
- data/spec/unit/email/provider/fastmail_spec.rb +0 -7
- data/spec/unit/email/provider/gmail_spec.rb +0 -7
- data/spec/unit/email/provider_spec.rb +0 -27
- data/spec/unit/imap/backup/account/connection_spec.rb +0 -433
- data/spec/unit/imap/backup/account/folder_spec.rb +0 -261
- data/spec/unit/imap/backup/account_spec.rb +0 -246
- data/spec/unit/imap/backup/cli/accounts_spec.rb +0 -58
- data/spec/unit/imap/backup/cli/backup_spec.rb +0 -19
- data/spec/unit/imap/backup/cli/folders_spec.rb +0 -39
- data/spec/unit/imap/backup/cli/helpers_spec.rb +0 -87
- data/spec/unit/imap/backup/cli/local_spec.rb +0 -100
- data/spec/unit/imap/backup/cli/migrate_spec.rb +0 -80
- data/spec/unit/imap/backup/cli/restore_spec.rb +0 -67
- data/spec/unit/imap/backup/cli/setup_spec.rb +0 -17
- data/spec/unit/imap/backup/cli/utils_spec.rb +0 -125
- data/spec/unit/imap/backup/cli_spec.rb +0 -93
- data/spec/unit/imap/backup/client/apple_mail_spec.rb +0 -9
- data/spec/unit/imap/backup/client/default_spec.rb +0 -22
- data/spec/unit/imap/backup/configuration_spec.rb +0 -238
- data/spec/unit/imap/backup/downloader_spec.rb +0 -96
- data/spec/unit/imap/backup/logger_spec.rb +0 -48
- data/spec/unit/imap/backup/migrator_spec.rb +0 -58
- data/spec/unit/imap/backup/sanitizer_spec.rb +0 -42
- data/spec/unit/imap/backup/serializer/directory_spec.rb +0 -37
- data/spec/unit/imap/backup/serializer/imap_spec.rb +0 -218
- data/spec/unit/imap/backup/serializer/mbox_enumerator_spec.rb +0 -45
- data/spec/unit/imap/backup/serializer/mbox_spec.rb +0 -101
- data/spec/unit/imap/backup/serializer_spec.rb +0 -296
- data/spec/unit/imap/backup/setup/account_spec.rb +0 -461
- data/spec/unit/imap/backup/setup/asker_spec.rb +0 -137
- data/spec/unit/imap/backup/setup/connection_tester_spec.rb +0 -51
- data/spec/unit/imap/backup/setup/folder_chooser_spec.rb +0 -146
- data/spec/unit/imap/backup/setup/helpers_spec.rb +0 -15
- data/spec/unit/imap/backup/setup_spec.rb +0 -301
- data/spec/unit/imap/backup/thunderbird/mailbox_exporter_spec.rb +0 -116
- data/spec/unit/imap/backup/uploader_spec.rb +0 -54
- data/spec/unit/imap/backup/utils_spec.rb +0 -92
- data/spec/unit/retry_on_error_spec.rb +0 -34
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
require "features/helper"
|
|
2
|
-
|
|
3
|
-
RSpec.describe "restore", type: :aruba, docker: true do
|
|
4
|
-
include_context "imap-backup connection"
|
|
5
|
-
include_context "message-fixtures"
|
|
6
|
-
|
|
7
|
-
let(:local_backup_path) { File.expand_path("~/backup") }
|
|
8
|
-
let(:folder) { "my-stuff" }
|
|
9
|
-
let(:messages_as_mbox) do
|
|
10
|
-
message_as_mbox_entry(msg1) + message_as_mbox_entry(msg2)
|
|
11
|
-
end
|
|
12
|
-
let(:messages_as_server_messages) do
|
|
13
|
-
[message_as_server_message(msg1), message_as_server_message(msg2)]
|
|
14
|
-
end
|
|
15
|
-
let(:message_uids) { [msg1[:uid], msg2[:uid]] }
|
|
16
|
-
let(:existing_imap_content) { imap_data(uid_validity, message_uids).to_json }
|
|
17
|
-
let(:uid_validity) { 1234 }
|
|
18
|
-
|
|
19
|
-
let!(:pre) {}
|
|
20
|
-
let!(:setup) do
|
|
21
|
-
create_directory local_backup_path
|
|
22
|
-
File.write(imap_path(folder), existing_imap_content)
|
|
23
|
-
File.write(mbox_path(folder), messages_as_mbox)
|
|
24
|
-
create_config(accounts: [account.to_h])
|
|
25
|
-
|
|
26
|
-
run_command_and_stop("imap-backup restore #{account.username}")
|
|
27
|
-
end
|
|
28
|
-
let(:cleanup) do
|
|
29
|
-
server_delete_folder folder
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
after { cleanup }
|
|
33
|
-
|
|
34
|
-
context "when the folder doesn't exist" do
|
|
35
|
-
it "restores messages" do
|
|
36
|
-
messages = server_messages(folder).map { |m| server_message_to_body(m) }
|
|
37
|
-
expect(messages).to eq(messages_as_server_messages)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
it "updates local uids to match the new server ones" do
|
|
41
|
-
updated_imap_content = imap_parsed(folder)
|
|
42
|
-
expect(server_uids(folder)).to eq(updated_imap_content[:uids])
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
it "sets the backup uid_validity to match the new folder" do
|
|
46
|
-
updated_imap_content = imap_parsed(folder)
|
|
47
|
-
expect(updated_imap_content[:uid_validity]).
|
|
48
|
-
to eq(server_uid_validity(folder))
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
context "when the folder exists" do
|
|
53
|
-
let(:email3) { send_email folder, msg3 }
|
|
54
|
-
|
|
55
|
-
context "when the uid_validity matches" do
|
|
56
|
-
let(:pre) do
|
|
57
|
-
server_create_folder folder
|
|
58
|
-
email3
|
|
59
|
-
uid_validity
|
|
60
|
-
end
|
|
61
|
-
let(:messages_as_server_messages) do
|
|
62
|
-
[
|
|
63
|
-
message_as_server_message(msg3),
|
|
64
|
-
message_as_server_message(msg1),
|
|
65
|
-
message_as_server_message(msg2)
|
|
66
|
-
]
|
|
67
|
-
end
|
|
68
|
-
let(:uid_validity) { server_uid_validity(folder) }
|
|
69
|
-
|
|
70
|
-
it "appends to the existing folder" do
|
|
71
|
-
messages = server_messages(folder).map { |m| server_message_to_body(m) }
|
|
72
|
-
expect(messages).to eq(messages_as_server_messages)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
context "when the uid_validity doesn't match" do
|
|
77
|
-
context "when the folder is empty" do
|
|
78
|
-
let(:pre) do
|
|
79
|
-
server_create_folder folder
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "sets the backup uid_validity to match the folder" do
|
|
83
|
-
updated_imap_content = imap_parsed(folder)
|
|
84
|
-
expect(updated_imap_content[:uid_validity]).
|
|
85
|
-
to eq(server_uid_validity(folder))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it "uploads to the new folder" do
|
|
89
|
-
messages = server_messages(folder).map do |m|
|
|
90
|
-
server_message_to_body(m)
|
|
91
|
-
end
|
|
92
|
-
expect(messages).to eq(messages_as_server_messages)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
context "when the folder has content" do
|
|
97
|
-
let(:new_folder) { "#{folder}-#{uid_validity}" }
|
|
98
|
-
let(:cleanup) do
|
|
99
|
-
server_delete_folder new_folder
|
|
100
|
-
super()
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
let(:pre) do
|
|
104
|
-
server_create_folder folder
|
|
105
|
-
email3
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it "renames the backup" do
|
|
109
|
-
expect(mbox_content(new_folder)).to eq(messages_as_mbox)
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
it "leaves the existing folder as is" do
|
|
113
|
-
messages = server_messages(folder).map do |m|
|
|
114
|
-
server_message_to_body(m)
|
|
115
|
-
end
|
|
116
|
-
expect(messages).to eq([message_as_server_message(msg3)])
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it "creates the new folder" do
|
|
120
|
-
expect(server_folders.map(&:name)).to include(new_folder)
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
it "sets the backup uid_validity to match the new folder" do
|
|
124
|
-
updated_imap_content = imap_parsed(new_folder)
|
|
125
|
-
expect(updated_imap_content[:uid_validity]).
|
|
126
|
-
to eq(server_uid_validity(new_folder))
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it "uploads to the new folder" do
|
|
130
|
-
messages = server_messages(new_folder).map do |m|
|
|
131
|
-
server_message_to_body(m)
|
|
132
|
-
end
|
|
133
|
-
expect(messages).to eq(messages_as_server_messages)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
context "when non-Unicode encodings are used" do
|
|
140
|
-
let(:server_message) do
|
|
141
|
-
message_as_server_message(msg_iso8859)
|
|
142
|
-
end
|
|
143
|
-
let(:messages_as_mbox) do
|
|
144
|
-
message_as_mbox_entry(msg_iso8859)
|
|
145
|
-
end
|
|
146
|
-
let(:message_uids) { [uid_iso8859] }
|
|
147
|
-
let(:uid_validity) { server_uid_validity(folder) }
|
|
148
|
-
|
|
149
|
-
let(:pre) do
|
|
150
|
-
server_create_folder folder
|
|
151
|
-
uid_validity
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
it "maintains encodings" do
|
|
155
|
-
message =
|
|
156
|
-
server_messages(folder).
|
|
157
|
-
first["BODY[]"]
|
|
158
|
-
|
|
159
|
-
expect(message).to eq(server_message)
|
|
160
|
-
end
|
|
161
|
-
end
|
|
162
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
require "features/helper"
|
|
2
|
-
require "imap/backup/cli/status"
|
|
3
|
-
|
|
4
|
-
RSpec.describe "status", type: :feature, docker: true do
|
|
5
|
-
include_context "imap-backup connection"
|
|
6
|
-
include_context "message-fixtures"
|
|
7
|
-
|
|
8
|
-
context "when there are non-backed-up messages" do
|
|
9
|
-
let(:options) do
|
|
10
|
-
{accounts: "address@example.org"}
|
|
11
|
-
end
|
|
12
|
-
let(:folder) { "my-stuff" }
|
|
13
|
-
let(:backup_folders) { [{name: folder}] }
|
|
14
|
-
let(:email1) { send_email folder, msg1 }
|
|
15
|
-
let(:output) { StringIO.new }
|
|
16
|
-
|
|
17
|
-
before do
|
|
18
|
-
allow(Imap::Backup::CLI::Accounts).to receive(:new) { [account] }
|
|
19
|
-
server_create_folder folder
|
|
20
|
-
email1
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
around do |example|
|
|
24
|
-
stdout = $stdout
|
|
25
|
-
$stdout = output
|
|
26
|
-
example.run
|
|
27
|
-
$stdout = stdout
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
after do
|
|
31
|
-
FileUtils.rm_rf local_backup_path
|
|
32
|
-
delete_emails folder
|
|
33
|
-
server_delete_folder folder
|
|
34
|
-
connection.disconnect
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "prints the number" do
|
|
38
|
-
Imap::Backup::CLI::Status.new(options).run
|
|
39
|
-
|
|
40
|
-
expect(output.string).to eq("address@example.org\nmy-stuff: 1\n")
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
require "aruba/rspec"
|
|
2
|
-
|
|
3
|
-
require_relative "backup_directory"
|
|
4
|
-
require "imap/backup/serializer/mbox"
|
|
5
|
-
|
|
6
|
-
Aruba.configure do |config|
|
|
7
|
-
config.home_directory = File.expand_path("./tmp/home")
|
|
8
|
-
config.allow_absolute_paths = true
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
module ConfigurationHelpers
|
|
12
|
-
def config_path
|
|
13
|
-
File.expand_path("~/.imap-backup")
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def create_config(accounts:, debug: false)
|
|
17
|
-
pathname = File.join(config_path, "config.json")
|
|
18
|
-
save_data = {
|
|
19
|
-
version: Imap::Backup::Configuration::VERSION,
|
|
20
|
-
accounts: accounts,
|
|
21
|
-
debug: debug
|
|
22
|
-
}
|
|
23
|
-
FileUtils.mkdir_p config_path
|
|
24
|
-
File.open(pathname, "w") { |f| f.write(JSON.pretty_generate(save_data)) }
|
|
25
|
-
FileUtils.chmod(0o600, pathname)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
module StoreHelpers
|
|
30
|
-
def store_email(
|
|
31
|
-
email:, folder:,
|
|
32
|
-
uid: 1,
|
|
33
|
-
from: "sender@example.com",
|
|
34
|
-
subject: "The Subject",
|
|
35
|
-
body: "body"
|
|
36
|
-
)
|
|
37
|
-
account = config.accounts.find { |a| a.username == email }
|
|
38
|
-
raise "Account not found" if !account
|
|
39
|
-
FileUtils.mkdir_p account.local_path
|
|
40
|
-
serializer = Imap::Backup::Serializer.new(account.local_path, folder)
|
|
41
|
-
serializer.force_uid_validity("42") if !serializer.uid_validity
|
|
42
|
-
serialized = to_serialized(from: from, subject: subject, body: body)
|
|
43
|
-
serializer.append uid, serialized
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def to_serialized(from:, subject:, body:)
|
|
47
|
-
body_and_headers = <<~BODY
|
|
48
|
-
From: #{from}
|
|
49
|
-
Subject: #{subject}
|
|
50
|
-
|
|
51
|
-
#{body}
|
|
52
|
-
BODY
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def config
|
|
56
|
-
Imap::Backup::Configuration.new(
|
|
57
|
-
File.expand_path("~/.imap-backup/config.json")
|
|
58
|
-
)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
RSpec.configure do |config|
|
|
63
|
-
config.include ConfigurationHelpers, type: :aruba
|
|
64
|
-
config.include StoreHelpers, type: :aruba
|
|
65
|
-
config.include BackupDirectoryHelpers, type: :aruba
|
|
66
|
-
|
|
67
|
-
config.before(:suite) do
|
|
68
|
-
FileUtils.rm_rf "./tmp/home"
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
config.before(:example, type: :aruba) do
|
|
72
|
-
set_environment_variable("COVERAGE", "aruba")
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
config.after do
|
|
76
|
-
FileUtils.rm_rf "./tmp/home"
|
|
77
|
-
end
|
|
78
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module BackupDirectoryHelpers
|
|
2
|
-
def message_as_mbox_entry(options)
|
|
3
|
-
from = fixture("connection")[:username]
|
|
4
|
-
subject = options[:subject]
|
|
5
|
-
body = options[:body]
|
|
6
|
-
body_and_headers = <<~BODY
|
|
7
|
-
From: #{from}
|
|
8
|
-
Subject: #{subject}
|
|
9
|
-
|
|
10
|
-
#{body}
|
|
11
|
-
BODY
|
|
12
|
-
|
|
13
|
-
"From #{from}\n#{body_and_headers}\n"
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def imap_data(uid_validity, uids)
|
|
17
|
-
{
|
|
18
|
-
version: 2,
|
|
19
|
-
uid_validity: uid_validity,
|
|
20
|
-
uids: uids
|
|
21
|
-
}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def mbox_content(name)
|
|
25
|
-
File.read(mbox_path(name))
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def mbox_path(name)
|
|
29
|
-
File.join(local_backup_path, "#{name}.mbox")
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def imap_path(name)
|
|
33
|
-
File.join(local_backup_path, "#{name}.imap")
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def imap_content(name)
|
|
37
|
-
File.read(imap_path(name))
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def imap_parsed(name)
|
|
41
|
-
JSON.parse(imap_content(name), symbolize_names: true)
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
module EmailServerHelpers
|
|
2
|
-
REQUESTED_ATTRIBUTES = ["BODY[]"].freeze
|
|
3
|
-
DEFAULT_EMAIL = "address@example.org".freeze
|
|
4
|
-
|
|
5
|
-
def send_email(folder, options)
|
|
6
|
-
message = message_as_server_message(options)
|
|
7
|
-
imap.append(folder, message, nil, nil)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def message_as_server_message(options)
|
|
11
|
-
from = options[:from] || DEFAULT_EMAIL
|
|
12
|
-
subject = options[:subject]
|
|
13
|
-
body = options[:body]
|
|
14
|
-
|
|
15
|
-
<<~MESSAGE.gsub("\n", "\r\n")
|
|
16
|
-
From: #{from}
|
|
17
|
-
Subject: #{subject}
|
|
18
|
-
|
|
19
|
-
#{body}
|
|
20
|
-
|
|
21
|
-
MESSAGE
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def server_messages(folder)
|
|
25
|
-
server_uids(folder).map do |uid|
|
|
26
|
-
server_fetch_email(folder, uid)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def server_message_to_body(message)
|
|
31
|
-
message["BODY[]"]
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def server_fetch_email(folder, uid)
|
|
35
|
-
examine folder
|
|
36
|
-
|
|
37
|
-
fetch_data_items = imap.uid_fetch([uid.to_i], REQUESTED_ATTRIBUTES)
|
|
38
|
-
return nil if fetch_data_items.nil?
|
|
39
|
-
|
|
40
|
-
fetch_data_item = fetch_data_items[0]
|
|
41
|
-
fetch_data_item.attr
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
def delete_emails(folder)
|
|
45
|
-
imap.select(folder)
|
|
46
|
-
uids = imap.uid_search(["ALL"]).sort
|
|
47
|
-
imap.store(1..uids.size, "+FLAGS", [:Deleted])
|
|
48
|
-
imap.expunge
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def examine(folder)
|
|
52
|
-
imap.examine(folder)
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def server_uids(folder)
|
|
56
|
-
examine(folder)
|
|
57
|
-
imap.uid_search(["ALL"]).sort
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
def server_uid_validity(folder)
|
|
61
|
-
examine(folder)
|
|
62
|
-
imap.responses["UIDVALIDITY"][0]
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def server_folders
|
|
66
|
-
imap.list(root, "*")
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def root
|
|
70
|
-
root_info = imap.list("", "")[0]
|
|
71
|
-
root_info.name
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def server_create_folder(folder)
|
|
75
|
-
imap.create(folder)
|
|
76
|
-
imap.disconnect
|
|
77
|
-
@imap = nil
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def server_rename_folder(from, to)
|
|
81
|
-
imap.rename(from, to)
|
|
82
|
-
imap.disconnect
|
|
83
|
-
@imap = nil
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
def server_delete_folder(folder)
|
|
87
|
-
imap.delete folder
|
|
88
|
-
imap.disconnect
|
|
89
|
-
rescue StandardError => e
|
|
90
|
-
ensure
|
|
91
|
-
@imap = nil
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def imap
|
|
95
|
-
@imap ||=
|
|
96
|
-
begin
|
|
97
|
-
connection = fixture("connection")
|
|
98
|
-
imap = Net::IMAP.new(
|
|
99
|
-
connection[:server], connection[:connection_options]
|
|
100
|
-
)
|
|
101
|
-
imap.login(connection[:username], connection[:password])
|
|
102
|
-
imap
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
RSpec.configure do |config|
|
|
108
|
-
config.include EmailServerHelpers, type: :aruba
|
|
109
|
-
config.include EmailServerHelpers, type: :feature
|
|
110
|
-
end
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
shared_context "imap-backup connection" do
|
|
2
|
-
let(:local_backup_path) { Dir.mktmpdir(nil, "tmp") }
|
|
3
|
-
let(:default_connection) { fixture("connection") }
|
|
4
|
-
let(:backup_folders) { nil }
|
|
5
|
-
let(:account) do
|
|
6
|
-
Imap::Backup::Account.new(
|
|
7
|
-
default_connection.merge(
|
|
8
|
-
local_path: local_backup_path,
|
|
9
|
-
folders: backup_folders
|
|
10
|
-
)
|
|
11
|
-
)
|
|
12
|
-
end
|
|
13
|
-
let(:connection) { Imap::Backup::Account::Connection.new(account) }
|
|
14
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
shared_context "message-fixtures" do
|
|
2
|
-
let(:uid1) { 123 }
|
|
3
|
-
let(:uid2) { 345 }
|
|
4
|
-
let(:uid3) { 567 }
|
|
5
|
-
let(:uid_iso8859) { 890 }
|
|
6
|
-
let(:msg1) { {uid: uid1, subject: "Test 1", body: "body 1\nHi"} }
|
|
7
|
-
let(:msg2) { {uid: uid2, subject: "Test 2", body: "body 2"} }
|
|
8
|
-
let(:msg3) { {uid: uid3, subject: "Test 3", body: "body 3"} }
|
|
9
|
-
let(:msg_iso8859) do
|
|
10
|
-
{
|
|
11
|
-
uid: uid_iso8859,
|
|
12
|
-
subject: "iso8859 Body",
|
|
13
|
-
body: "Ma, perchè?".encode(Encoding::ISO_8859_1).force_encoding("binary")
|
|
14
|
-
}
|
|
15
|
-
end
|
|
16
|
-
end
|
data/spec/spec_helper.rb
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
require "rspec"
|
|
2
|
-
|
|
3
|
-
$LOAD_PATH << File.expand_path("../lib", __dir__)
|
|
4
|
-
|
|
5
|
-
support_glob = File.join(__dir__, "support", "**", "*.rb")
|
|
6
|
-
Dir[support_glob].sort.each { |f| require f }
|
|
7
|
-
|
|
8
|
-
require "simplecov"
|
|
9
|
-
|
|
10
|
-
SimpleCov.command_name "RSpec tests"
|
|
11
|
-
|
|
12
|
-
require "imap/backup"
|
|
13
|
-
require "imap/backup/cli"
|
|
14
|
-
|
|
15
|
-
silence_logging
|
data/spec/support/fixtures.rb
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
require "erb"
|
|
2
|
-
require "yaml"
|
|
3
|
-
|
|
4
|
-
def fixture(name)
|
|
5
|
-
spec_root = File.expand_path("..", File.dirname(__FILE__))
|
|
6
|
-
fixture_path = File.join(spec_root, "fixtures", "#{name}.yml")
|
|
7
|
-
content = File.read(fixture_path)
|
|
8
|
-
template = ERB.new(content)
|
|
9
|
-
yaml = template.result(binding)
|
|
10
|
-
YAML.safe_load(yaml, [Symbol])
|
|
11
|
-
end
|
|
@@ -1,177 +0,0 @@
|
|
|
1
|
-
msg_no_from = <<~NO_FROM
|
|
2
|
-
Delivered-To: you@example.com
|
|
3
|
-
From: example <www.example.com>
|
|
4
|
-
To: FirstName LastName <you@example.com>
|
|
5
|
-
Subject: Re: no subject
|
|
6
|
-
NO_FROM
|
|
7
|
-
|
|
8
|
-
msg_bad_from = <<~BAD_FROM
|
|
9
|
-
Delivered-To: you@example.com
|
|
10
|
-
from: "FirstName LastName (TEXT)" <"TEXT*" <no-reply@example.com>>
|
|
11
|
-
To: FirstName LastName <you@example.com>
|
|
12
|
-
Subject: Re: no subject
|
|
13
|
-
BAD_FROM
|
|
14
|
-
|
|
15
|
-
msg_no_from_but_return_path = <<~RETURN_PATH
|
|
16
|
-
Delivered-To: you@example.com
|
|
17
|
-
From: example <www.example.com>
|
|
18
|
-
To: FirstName LastName <you@example.com>
|
|
19
|
-
Return-Path: <me@example.com>
|
|
20
|
-
Subject: Re: no subject
|
|
21
|
-
RETURN_PATH
|
|
22
|
-
|
|
23
|
-
msg_no_from_but_sender = <<~NOT_SENDER
|
|
24
|
-
Delivered-To: you@example.com
|
|
25
|
-
To: FirstName LastName <you@example.com>
|
|
26
|
-
Subject: Re: no subject
|
|
27
|
-
Sender: FistName LastName <me@example.com>
|
|
28
|
-
NOT_SENDER
|
|
29
|
-
|
|
30
|
-
describe Email::Mboxrd::Message do
|
|
31
|
-
subject { described_class.new(message_body) }
|
|
32
|
-
|
|
33
|
-
let(:from) { "me@example.com" }
|
|
34
|
-
let(:date) { DateTime.new(2012, 12, 13, 18, 23, 45) }
|
|
35
|
-
let(:message_body) do
|
|
36
|
-
instance_double(String, clone: cloned_message_body, force_encoding: nil)
|
|
37
|
-
end
|
|
38
|
-
let(:cloned_message_body) do
|
|
39
|
-
"Foo\nBar\nFrom at the beginning of the line\n>>From quoted"
|
|
40
|
-
end
|
|
41
|
-
let(:msg_good) do
|
|
42
|
-
<<~GOOD
|
|
43
|
-
Delivered-To: you@example.com
|
|
44
|
-
From: Foo <foo@example.com>
|
|
45
|
-
To: FirstName LastName <you@example.com>
|
|
46
|
-
Date: #{date.rfc822}
|
|
47
|
-
Subject: Re: no subject
|
|
48
|
-
GOOD
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
let(:msg_bad_date) do
|
|
52
|
-
<<~BAD
|
|
53
|
-
Delivered-To: you@example.com
|
|
54
|
-
From: Foo <foo@example.com>
|
|
55
|
-
To: FirstName LastName <you@example.com>
|
|
56
|
-
Date: Mon,5 May 2014 08:97:99 GMT
|
|
57
|
-
Subject: Re: no subject
|
|
58
|
-
BAD
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
describe ".from_serialized" do
|
|
62
|
-
let(:serialized_message) { "From foo@a.com\n#{imap_message}" }
|
|
63
|
-
let(:imap_message) { "Delivered-To: me@example.com\nFrom Me\n" }
|
|
64
|
-
let!(:result) { described_class.from_serialized(serialized_message) }
|
|
65
|
-
|
|
66
|
-
it "returns the message" do
|
|
67
|
-
expect(result).to be_a(described_class)
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "removes one level of > before From" do
|
|
71
|
-
expect(result.supplied_body).to eq(imap_message)
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
describe "#to_serialized" do
|
|
76
|
-
let(:mail) { instance_double(Mail::Message, from: [from], date: date) }
|
|
77
|
-
|
|
78
|
-
before do
|
|
79
|
-
allow(Mail).to receive(:new).with(cloned_message_body) { mail }
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
it "adds a 'From ' line at the start" do
|
|
83
|
-
expected = "From #{from} #{date.asctime}\n"
|
|
84
|
-
expect(subject.to_serialized).to start_with(expected)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
it "replaces existing 'From ' with '>From '" do
|
|
88
|
-
expect(subject.to_serialized).to include("\n>From at the beginning")
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
it "appends > before '>+From '" do
|
|
92
|
-
expect(subject.to_serialized).to include("\n>>>From quoted")
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
context "when date is missing" do
|
|
96
|
-
let(:date) { nil }
|
|
97
|
-
|
|
98
|
-
it "does no fail" do
|
|
99
|
-
expect { subject.to_serialized }.to_not raise_error
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
describe "From" do
|
|
105
|
-
before do
|
|
106
|
-
# call original for these tests because we want to test the behaviour of
|
|
107
|
-
# class-under-test given different behaviour of the Mail parser
|
|
108
|
-
allow(Mail).to receive(:new).and_call_original
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
context "when original message 'from' is missing" do
|
|
112
|
-
let(:message_body) { msg_no_from }
|
|
113
|
-
|
|
114
|
-
it "'from' is empty string" do
|
|
115
|
-
expect(subject.to_serialized).to start_with("From \n")
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
context "when original message 'from' is not a well-formed address" do
|
|
120
|
-
let(:message_body) { msg_bad_from }
|
|
121
|
-
|
|
122
|
-
it "'from' is empty string" do
|
|
123
|
-
expect(subject.to_serialized).to start_with("From \n")
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
context "when original message 'from' is nil and " \
|
|
128
|
-
"'envelope from' is nil and 'return path' is available" do
|
|
129
|
-
let(:message_body) { msg_no_from_but_return_path }
|
|
130
|
-
|
|
131
|
-
it "'return path' is used as 'from'" do
|
|
132
|
-
expect(subject.to_serialized).to start_with("From #{from}\n")
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
context "with no from and a 'Sender'" do
|
|
137
|
-
let(:message_body) { msg_no_from_but_sender }
|
|
138
|
-
|
|
139
|
-
it "Sender is used as 'from'" do
|
|
140
|
-
expect(subject.to_serialized).to start_with("From #{from}\n")
|
|
141
|
-
end
|
|
142
|
-
end
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
describe "#date" do
|
|
146
|
-
let(:message_body) { msg_good }
|
|
147
|
-
|
|
148
|
-
it "returns the date" do
|
|
149
|
-
expect(subject.date).to eq(date)
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
context "with incorrect minutes and seconds" do
|
|
153
|
-
let(:message_body) { msg_bad_date }
|
|
154
|
-
|
|
155
|
-
it "returns nil" do
|
|
156
|
-
expect(subject.date).to be_nil
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
describe "#imap_body" do
|
|
162
|
-
let(:message_body) { "Ciao" }
|
|
163
|
-
|
|
164
|
-
it "returns the supplied body" do
|
|
165
|
-
expect(subject.imap_body).to eq(message_body)
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
context "when newlines are not IMAP standard" do
|
|
169
|
-
let(:message_body) { "Ciao\nHello" }
|
|
170
|
-
let(:corrected) { "Ciao\r\nHello" }
|
|
171
|
-
|
|
172
|
-
it "corrects them" do
|
|
173
|
-
expect(subject.imap_body).to eq(corrected)
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
end
|