2022-01-01 13:38:12 +00:00
|
|
|
# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/
|
2021-06-01 12:20:20 +00:00
|
|
|
|
2019-08-06 15:26:29 +00:00
|
|
|
require 'test_helper'
|
|
|
|
require 'net/imap'
|
|
|
|
|
|
|
|
class EmailPostmasterToSender < ActiveSupport::TestCase
|
|
|
|
|
2021-07-09 13:05:05 +00:00
|
|
|
setup do
|
2019-08-06 15:26:29 +00:00
|
|
|
Setting.set('postmaster_max_size', 0.1)
|
|
|
|
|
2021-09-20 10:47:05 +00:00
|
|
|
@test_id = SecureRandom.uuid
|
2019-08-06 15:26:29 +00:00
|
|
|
|
|
|
|
# setup the IMAP account info for Zammad
|
|
|
|
if ENV['MAIL_SERVER'].blank?
|
|
|
|
raise "Need MAIL_SERVER as ENV variable like export MAIL_SERVER='mx.example.com'"
|
|
|
|
end
|
|
|
|
if ENV['MAIL_SERVER_ACCOUNT'].blank?
|
|
|
|
raise "Need MAIL_SERVER_ACCOUNT as ENV variable like export MAIL_SERVER_ACCOUNT='user:somepass'"
|
|
|
|
end
|
|
|
|
|
|
|
|
@server_address = ENV['MAIL_SERVER']
|
|
|
|
@server_login = ENV['MAIL_SERVER_ACCOUNT'].split(':')[0]
|
|
|
|
@server_password = ENV['MAIL_SERVER_ACCOUNT'].split(':')[1]
|
|
|
|
|
|
|
|
@folder = "postmaster_to_sender_#{@test_id}"
|
|
|
|
|
|
|
|
if ENV['MAIL_SERVER_EMAIL'].blank?
|
2021-08-17 12:10:02 +00:00
|
|
|
raise "Need MAIL_SERVER_EMAIL as ENV variable like export MAIL_SERVER_EMAIL='admin@example.com'"
|
2019-08-06 15:26:29 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
@sender_email_address = ENV['MAIL_SERVER_EMAIL']
|
|
|
|
|
|
|
|
@email_address = EmailAddress.create!(
|
|
|
|
realname: 'me Helpdesk',
|
2019-09-09 12:45:44 +00:00
|
|
|
email: "some-zammad-#{ENV['MAIL_SERVER_EMAIL']}",
|
2019-08-06 15:26:29 +00:00
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
|
|
|
)
|
|
|
|
|
|
|
|
group = Group.create_or_update(
|
|
|
|
name: 'PostmasterToSenderTest',
|
|
|
|
email_address_id: @email_address.id,
|
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
|
|
|
)
|
|
|
|
|
|
|
|
@channel = Channel.create!(
|
|
|
|
area: 'Email::Account',
|
|
|
|
group_id: group.id,
|
|
|
|
options: {
|
|
|
|
inbound: {
|
|
|
|
adapter: 'imap',
|
|
|
|
options: {
|
|
|
|
host: @server_address,
|
|
|
|
user: @server_login,
|
|
|
|
password: @server_password,
|
|
|
|
ssl: true,
|
|
|
|
folder: @folder,
|
|
|
|
keep_on_server: false,
|
|
|
|
}
|
|
|
|
},
|
|
|
|
outbound: {
|
|
|
|
adapter: 'smtp',
|
|
|
|
options: {
|
|
|
|
host: @server_address,
|
|
|
|
port: 25,
|
|
|
|
start_tls: true,
|
|
|
|
user: @server_login,
|
|
|
|
password: @server_password,
|
|
|
|
email: @email_address.email
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
active: true,
|
|
|
|
updated_by_id: 1,
|
|
|
|
created_by_id: 1,
|
|
|
|
)
|
|
|
|
@email_address.channel_id = @channel.id
|
|
|
|
@email_address.save!
|
|
|
|
end
|
|
|
|
|
|
|
|
test 'postmaster reply with email on oversized incoming emails' do
|
|
|
|
imap = Net::IMAP.new(@server_address, 993, true, nil, false)
|
|
|
|
imap.login(@server_login, @server_password)
|
|
|
|
imap.create(@folder)
|
|
|
|
imap.select(@folder)
|
|
|
|
|
|
|
|
# put a very large message in it
|
|
|
|
large_message = "Subject: Oversized Email Message
|
|
|
|
From: Max Mustermann <#{@sender_email_address}>
|
|
|
|
To: shugo@example.com
|
|
|
|
Message-ID: <#{@test_id}@zammad.test.com>
|
|
|
|
|
|
|
|
Oversized Email Message Body #{'#' * 120_000}
|
2021-05-12 11:37:44 +00:00
|
|
|
".gsub(%r{\n}, "\r\n")
|
2019-08-06 15:26:29 +00:00
|
|
|
|
|
|
|
large_message_md5 = Digest::MD5.hexdigest(large_message)
|
2020-02-18 19:51:31 +00:00
|
|
|
large_message_size = format('%<MB>.2f', MB: large_message.size.to_f / 1024 / 1024)
|
2019-08-06 15:26:29 +00:00
|
|
|
|
|
|
|
imap.append(@folder, large_message, [], Time.zone.now)
|
|
|
|
|
|
|
|
@channel.fetch(true)
|
|
|
|
|
|
|
|
# 1. verify that the oversized email has been saved locally to:
|
|
|
|
# /tmp/oversized_mail/yyyy-mm-ddThh:mm:ss-:md5.eml
|
2020-02-18 19:51:31 +00:00
|
|
|
path = Rails.root.join('tmp/oversized_mail')
|
2021-09-30 07:18:16 +00:00
|
|
|
target_files = Dir.entries(path).grep(%r{^#{large_message_md5}\.eml$})
|
2019-08-06 15:26:29 +00:00
|
|
|
assert(target_files.present?, 'Large message .eml log file must be present.')
|
|
|
|
|
|
|
|
# pick the latest file that matches the criteria
|
|
|
|
target_file = target_files.max
|
|
|
|
|
|
|
|
# verify that the file is byte for byte identical to the sent message
|
2020-02-18 19:51:31 +00:00
|
|
|
file_path = Rails.root.join('tmp/oversized_mail', target_file)
|
2019-08-06 15:26:29 +00:00
|
|
|
eml_data = File.read(file_path)
|
|
|
|
assert_equal(large_message, eml_data)
|
|
|
|
|
|
|
|
# 2. verify that a postmaster response email has been sent to the sender
|
2021-02-26 10:29:27 +00:00
|
|
|
message_ids = nil
|
|
|
|
5.times do |sleep_offset|
|
|
|
|
imap.select('inbox')
|
|
|
|
message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
|
|
|
|
|
|
|
|
break if message_ids.count.positive?
|
|
|
|
|
|
|
|
# send mail hasn't arrived yet in the inbox
|
|
|
|
sleep sleep_offset
|
|
|
|
end
|
|
|
|
|
2019-08-06 15:26:29 +00:00
|
|
|
assert(message_ids.count.positive?, 'Must have received a reply from the postmaster')
|
|
|
|
imap_message_id = message_ids.last
|
|
|
|
msg = imap.fetch(imap_message_id, 'RFC822')[0].attr['RFC822']
|
|
|
|
assert(msg.present?, 'Must have received a reply from the postmaster')
|
|
|
|
imap.store(imap_message_id, '+FLAGS', [:Deleted])
|
2021-07-16 13:29:38 +00:00
|
|
|
imap.expunge
|
2019-08-06 15:26:29 +00:00
|
|
|
|
|
|
|
# parse the reply mail and verify the various headers
|
|
|
|
parser = Channel::EmailParser.new
|
|
|
|
mail = parser.parse(msg)
|
|
|
|
assert_equal(mail[:from_email], @email_address.email)
|
2019-08-09 10:47:21 +00:00
|
|
|
assert_equal(mail[:subject], '[undeliverable] Message too large')
|
2019-08-06 15:26:29 +00:00
|
|
|
assert_equal("<#{@test_id}@zammad.test.com>",
|
|
|
|
mail['references'],
|
|
|
|
'Reply\'s Referecnes header must match the send message ID')
|
|
|
|
assert_equal("<#{@test_id}@zammad.test.com>",
|
|
|
|
mail['in-reply-to'],
|
|
|
|
'Reply\'s In-Reply-To header must match the send message ID')
|
|
|
|
|
|
|
|
# verify the reply mail body content
|
|
|
|
body = mail[:body]
|
|
|
|
assert(body.start_with?('Dear Max Mustermann'), 'Body must contain sender name')
|
|
|
|
assert(body.include?('Oversized Email Message'), 'Body must contain original subject')
|
|
|
|
assert(body.include?('0.1 MB'), 'Body must contain max allowed message size')
|
|
|
|
assert(body.include?("#{large_message_size} MB"), 'Body must contain the original message size')
|
|
|
|
assert(body.include?(Setting.get('fqdn')), 'Body must contain the Zammad instance name')
|
|
|
|
|
|
|
|
# 3. check if original mail got removed
|
|
|
|
imap.select(@folder)
|
|
|
|
message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
|
|
|
|
assert_equal(message_ids.count, 0, 'Original customer mail must be deleted.')
|
|
|
|
|
|
|
|
# final clean up
|
|
|
|
imap.delete(@folder)
|
|
|
|
@channel.destroy!
|
|
|
|
end
|
|
|
|
|
|
|
|
test 'postmaster reply with no email on oversized incoming emails' do
|
|
|
|
Setting.set('postmaster_send_reject_if_mail_too_large', false)
|
|
|
|
imap = Net::IMAP.new(@server_address, 993, true, nil, false)
|
|
|
|
imap.login(@server_login, @server_password)
|
|
|
|
|
|
|
|
imap.select('inbox')
|
|
|
|
message_count = imap.sort(['DATE'], ['ALL'], 'US-ASCII').count
|
|
|
|
|
|
|
|
imap.create(@folder)
|
|
|
|
imap.select(@folder)
|
|
|
|
|
|
|
|
# put a very large message in it
|
|
|
|
large_message = "Subject: Oversized Email Message
|
|
|
|
From: Max Mustermann <#{@sender_email_address}>
|
|
|
|
To: shugo@example.com
|
|
|
|
Message-ID: <#{@test_id}@zammad.test.com>
|
|
|
|
|
|
|
|
Oversized Email Message Body #{'#' * 120_000}
|
2021-05-12 11:37:44 +00:00
|
|
|
".gsub(%r{\n}, "\r\n")
|
2019-08-06 15:26:29 +00:00
|
|
|
|
|
|
|
imap.append(@folder, large_message, [], Time.zone.now)
|
|
|
|
|
|
|
|
@channel.fetch(true)
|
|
|
|
|
|
|
|
# 1. verify that the oversized email has been saved locally to:
|
|
|
|
# /tmp/oversized_mail/yyyy-mm-ddThh:mm:ss-:md5.eml
|
2020-02-18 19:51:31 +00:00
|
|
|
path = Rails.root.join('tmp/oversized_mail')
|
2021-09-30 07:18:16 +00:00
|
|
|
target_files = Dir.entries(path).grep(%r{^.+?\.eml$})
|
2019-08-06 15:26:29 +00:00
|
|
|
assert_not(target_files.blank?, 'Large message .eml log file must be blank.')
|
|
|
|
|
|
|
|
# 2. verify that a postmaster response email has been sent to the sender
|
|
|
|
imap.select('inbox')
|
|
|
|
message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
|
|
|
|
assert_equal(message_ids.count, message_count, 'Must not have received a reply from the postmaster')
|
|
|
|
|
|
|
|
# 3. check if original mail got removed
|
|
|
|
imap.select(@folder)
|
|
|
|
message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
|
|
|
|
imap_message_id = message_ids.last
|
|
|
|
msg = imap.fetch(imap_message_id, 'RFC822')[0].attr['RFC822']
|
|
|
|
imap.store(imap_message_id, '+FLAGS', [:Deleted])
|
2021-07-16 13:29:38 +00:00
|
|
|
imap.expunge
|
2019-08-06 15:26:29 +00:00
|
|
|
assert(msg.present?, 'Oversized Email Message')
|
|
|
|
assert_equal(message_ids.count, 1, 'Original customer mail must be deleted.')
|
|
|
|
|
|
|
|
# final clean up
|
|
|
|
imap.delete(@folder)
|
|
|
|
@channel.destroy!
|
|
|
|
end
|
|
|
|
|
|
|
|
teardown do
|
|
|
|
Setting.set('postmaster_max_size', 10)
|
|
|
|
end
|
|
|
|
end
|