Implemented issue #2092 - Send postmaster email to sender if email is too big

This commit is contained in:
Billy Zhou 2019-08-06 17:26:29 +02:00 committed by Martin Edenhofer
parent bc38f1c1ae
commit fae194918e
13 changed files with 661 additions and 31 deletions

View file

@ -172,6 +172,7 @@ test:integration:email_helper_deliver:
- bundle exec rails test test/integration/email_helper_test.rb - bundle exec rails test test/integration/email_helper_test.rb
- bundle exec rails test test/integration/email_deliver_test.rb - bundle exec rails test test/integration/email_deliver_test.rb
- bundle exec rails test test/integration/email_keep_on_server_test.rb - bundle exec rails test test/integration/email_keep_on_server_test.rb
- bundle exec rails test test/integration/email_postmaster_to_sender.rb
test:integration:facebook: test:integration:facebook:
<<: *test_integration_definition <<: *test_integration_definition

View file

@ -206,6 +206,7 @@ example
count = 0 count = 0
count_fetched = 0 count_fetched = 0
count_max = 5000 count_max = 5000
too_large_messages = []
active_check_interval = 20 active_check_interval = 20
notice = '' notice = ''
message_ids.each do |message_id| message_ids.each do |message_id|
@ -226,13 +227,6 @@ example
# ignore verify messages # ignore verify messages
next if !messages_is_too_old_verify?(message_meta, count, count_all) next if !messages_is_too_old_verify?(message_meta, count, count_all)
# ignore to big messages
info = too_big?(message_meta, count, count_all)
if info
notice += "#{info}\n"
next
end
# ignore deleted messages # ignore deleted messages
next if deleted?(message_meta, count, count_all) next if deleted?(message_meta, count, count_all)
@ -251,7 +245,24 @@ example
end end
next if !msg next if !msg
# do not process too big messages, instead download & send postmaster reply
too_large_info = too_large?(message_meta)
if too_large_info
if Setting.get('postmaster_send_reject_if_mail_too_large') == true
info = " - download message #{count}/#{count_all} - ignore message because it's too large (is:#{too_large_info[0]} MB/max:#{too_large_info[1]} MB)"
Rails.logger.info info
notice += "#{info}\n"
process_oversized_mail(channel, msg)
else
info = " - ignore message #{count}/#{count_all} - because message is too large (is:#{too_large_info[0]} MB/max:#{too_large_info[1]} MB)"
Rails.logger.info info
notice += "#{info}\n"
too_large_messages.push info
next
end
else
process(channel, msg, false) process(channel, msg, false)
end
begin begin
timeout(FETCH_MSG_TIMEOUT) do timeout(FETCH_MSG_TIMEOUT) do
@ -282,6 +293,11 @@ example
if count.zero? if count.zero?
Rails.logger.info ' - no message' Rails.logger.info ' - no message'
end end
if too_large_messages.present?
raise too_large_messages.join("\n")
end
Rails.logger.info 'done' Rails.logger.info 'done'
{ {
result: 'ok', result: 'ok',
@ -420,7 +436,7 @@ returns
check if email is to big check if email is to big
Channel::Driver::IMAP.too_big?(message_meta, count, count_all) Channel::Driver::IMAP.too_large?(message_meta, count, count_all)
returns returns
@ -428,14 +444,13 @@ returns
=end =end
def too_big?(message_meta, count, count_all) def too_large?(message_meta)
max_message_size = Setting.get('postmaster_max_size').to_f max_message_size = Setting.get('postmaster_max_size').to_f
real_message_size = message_meta.attr['RFC822.SIZE'].to_f / 1024 / 1024 real_message_size = message_meta.attr['RFC822.SIZE'].to_f / 1024 / 1024
if real_message_size > max_message_size if real_message_size > max_message_size
info = " - ignore message #{count}/#{count_all} - because message is too big (is:#{real_message_size} MB/max:#{max_message_size} MB)" return [real_message_size, max_message_size]
Rails.logger.info info
return info
end end
false false
end end

View file

@ -136,6 +136,7 @@ returns
count_all = mails.size count_all = mails.size
count = 0 count = 0
count_fetched = 0 count_fetched = 0
too_large_messages = []
active_check_interval = 20 active_check_interval = 20
notice = '' notice = ''
mails.first(2000).each do |m| mails.first(2000).each do |m|
@ -165,18 +166,28 @@ returns
end end
end end
# ignore to big messages # do not process too large messages, instead download and send postmaster reply
max_message_size = Setting.get('postmaster_max_size').to_f max_message_size = Setting.get('postmaster_max_size').to_f
real_message_size = mail.size.to_f / 1024 / 1024 real_message_size = mail.size.to_f / 1024 / 1024
if real_message_size > max_message_size if real_message_size > max_message_size
info = " - ignore message #{count}/#{count_all} - because message is too big (is:#{real_message_size} MB/max:#{max_message_size} MB)" if Setting.get('postmaster_send_reject_if_mail_too_large') == true
info = " - download message #{count}/#{count_all} - ignore message because it's too large (is:#{real_message_size} MB/max:#{max_message_size} MB)"
Rails.logger.info info Rails.logger.info info
notice += "#{info}\n" notice += "#{info}\n"
process_oversized_mail(channel, mail)
else
info = " - ignore message #{count}/#{count_all} - because message is too large (is:#{real_message_size} MB/max:#{max_message_size} MB)"
Rails.logger.info info
notice += "#{info}\n"
too_large_messages.push info
next next
end end
# delete email from server after article was created # delete email from server after article was created
else
process(channel, m.pop, false) process(channel, m.pop, false)
end
m.delete m.delete
count_fetched += 1 count_fetched += 1
end end
@ -184,6 +195,11 @@ returns
if count.zero? if count.zero?
Rails.logger.info ' - no message' Rails.logger.info ' - no message'
end end
if too_large_messages.present?
raise too_large_messages.join("\n")
end
Rails.logger.info 'done' Rails.logger.info 'done'
{ {
result: 'ok', result: 'ok',

View file

@ -116,18 +116,15 @@ returns
end end
rescue => e rescue => e
# store unprocessable email for bug reporting # store unprocessable email for bug reporting
path = Rails.root.join('tmp', 'unprocessable_mail') filename = archive_mail('unprocessable_mail', msg)
FileUtils.mkpath path
md5 = Digest::MD5.hexdigest(msg)
filename = "#{path}/#{md5}.eml"
message = "ERROR: Can't process email, you will find it for bug reporting under #{filename}, please create an issue at https://github.com/zammad/zammad/issues" message = "ERROR: Can't process email, you will find it for bug reporting under #{filename}, please create an issue at https://github.com/zammad/zammad/issues"
p message # rubocop:disable Rails/Output p message # rubocop:disable Rails/Output
p 'ERROR: ' + e.inspect # rubocop:disable Rails/Output p 'ERROR: ' + e.inspect # rubocop:disable Rails/Output
Rails.logger.error message Rails.logger.error message
Rails.logger.error e Rails.logger.error e
File.open(filename, 'wb') do |file|
file.write msg
end
return false if exception == false return false if exception == false
raise e.inspect + "\n" + e.backtrace.join("\n") raise e.inspect + "\n" + e.backtrace.join("\n")
@ -486,6 +483,19 @@ process unprocessable_mails (tmp/unprocessable_mail/*.eml) again
files files
end end
=begin
process oversized emails by:
1. Archiving the oversized mail as tmp/oversized_mail/timestamp_md5.eml
2. Reply with a postmaster message to inform the sender
=end
def process_oversized_mail(channel, msg)
archive_mail('oversized_mail', msg)
postmaster_response(channel, msg)
end
private private
def message_header_hash(mail) def message_header_hash(mail)
@ -783,6 +793,70 @@ process unprocessable_mails (tmp/unprocessable_mail/*.eml) again
[attach] [attach]
end end
# Archive the given message as tmp/folder/timestamp_md5.eml
def archive_mail(folder, msg)
path = Rails.root.join('tmp', folder)
FileUtils.mkpath path
# MD5 hash the msg and save it as "timestamp_md5.eml"
md5 = Digest::MD5.hexdigest(msg)
filename = "#{Time.zone.now.iso8601}_#{md5}.eml"
file_path = Rails.root.join('tmp', folder, filename)
File.open(file_path, 'wb') do |file|
file.write msg
end
file_path
end
# Auto reply as the postmaster to oversized emails with:
# [ALERT] Message too large
def postmaster_response(channel, msg)
begin
reply_mail = compose_postmaster_reply(msg)
rescue NotificationFactory::FileNotFoundError => e
Rails.logger.error 'No valid postmaster email_oversized template found. Skipping postmaster reply. ' + e.inspect
return
end
Rails.logger.error "Send mail too large postmaster message to: #{reply_mail[:to]}"
reply_mail[:from] = EmailAddress.find_by(channel: channel).email
channel.deliver(reply_mail)
rescue => e
Rails.logger.error "Error during sending of postmaster oversized email auto-reply: #{e.inspect}\n#{e.backtrace}"
end
# Compose a "Message too large" reply to the given message
def compose_postmaster_reply(raw_incoming_mail, locale = nil)
parsed_incoming_mail = Channel::EmailParser.new.parse(raw_incoming_mail)
# construct a dummy mail object
mail = OpenStruct.new
mail.from_display_name = parsed_incoming_mail[:from_display_name]
mail.subject = parsed_incoming_mail[:subject]
mail.msg_size = format('%.2f', raw_incoming_mail.size.to_f / 1024 / 1024)
reply = NotificationFactory::Mailer.template(
template: 'email_oversized',
locale: locale,
format: 'txt',
objects: {
mail: mail,
},
raw: true, # will not add application template
standalone: true, # default: false - will send header & footer
)
reply.merge(
to: parsed_incoming_mail[:from_email],
body: reply[:body].gsub(/\n/, "\r\n"),
content_type: 'text/plain',
References: parsed_incoming_mail[:message_id],
'In-Reply-To': parsed_incoming_mail[:message_id],
)
end
end end
module Mail module Mail

View file

@ -0,0 +1,12 @@
[Unzustellbar] Nachricht zu groß
Hallo #{mail.from_display_name},
Ihre E-Mail mit dem Betreff "#{mail.subject}" konnte nicht an einen oder mehrere Empfänger zugestellt werden.
Die Nachricht hatte eine Größe von #{mail.msg_size} MB, wir akzeptieren jedoch nur E-Mails mit einer Größe von bis zu #{config.postmaster_max_size} MB.
Bitte reduzieren Sie die Größe Ihrer Nachricht und versuchen Sie es erneut. Vielen Dank für Ihr Verständnis.
Mit freundlichen Grüßen
Postmaster von #{config.fqdn}

View file

@ -0,0 +1,12 @@
[ALERT] Message too large
Dear #{mail.from_display_name},
Unfortunately your email titled "#{mail.subject}" could not be delivered to one or more recipients.
Your message was #{mail.msg_size} MB but we only accept messages up to #{config.postmaster_max_size} MB.
Please reduce the message size and try again. Thank you for your understanding.
Regretfully,
Postmaster of #{config.fqdn}

View file

@ -0,0 +1,34 @@
class SettingPostmasterSendRejectEmail < ActiveRecord::Migration[5.2]
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
Setting.create_if_not_exists(
title: 'Send postmaster mail if mail too large',
name: 'postmaster_send_reject_if_mail_too_large',
area: 'Email::Base',
description: 'Send postmaster reject mail to sender of mail if mail is too large.',
options: {
form: [
{
display: '',
null: true,
name: 'postmaster_send_reject_if_mail_too_large',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: true,
preferences: {
online_service_disable: true,
permission: ['admin.channel_email'],
},
frontend: false
)
end
end

View file

@ -2582,6 +2582,33 @@ Setting.create_if_not_exists(
frontend: false frontend: false
) )
Setting.create_if_not_exists(
title: 'Send postmaster mail if mail too large',
name: 'postmaster_send_reject_if_mail_too_large',
area: 'Email::Base',
description: 'Send postmaster reject mail to sender of mail if mail is too large.',
options: {
form: [
{
display: '',
null: true,
name: 'postmaster_send_reject_if_mail_too_large',
tag: 'boolean',
options: {
true => 'yes',
false => 'no',
},
},
],
},
state: true,
preferences: {
online_service_disable: true,
permission: ['admin.channel_email'],
},
frontend: false
)
Setting.create_if_not_exists( Setting.create_if_not_exists(
title: 'Notification Sender', title: 'Notification Sender',
name: 'notification_sender', name: 'notification_sender',

View file

@ -29,17 +29,26 @@ returns
=end =end
class FileNotFoundError < StandardError; end
def self.template_read(data) def self.template_read(data)
template = File.readlines(template_path(data)) template_path = template_path(data)
template = File.readlines(template_path)
{ subject: template.shift, body: template.join } { subject: template.shift, body: template.join }
end end
def self.template_path(data) def self.template_path(data)
template_filenames(data) candidates = template_filenames(data)
.map { |filename| data.merge(filename: filename) } .map { |filename| data.merge(filename: filename) }
.map { |data_hash| TEMPLATE_PATH_STRING % data_hash } .map { |data_hash| TEMPLATE_PATH_STRING % data_hash }
.find(&File.method(:exist?))
found = candidates.find(&File.method(:exist?))
raise FileNotFoundError, "Missing template files #{candidates}!" if !found
found
end end
private_class_method :template_path private_class_method :template_path

View file

@ -281,7 +281,7 @@ returns
template = NotificationFactory.template_read( template = NotificationFactory.template_read(
locale: data[:locale] || Setting.get('locale_default') || 'en-us', locale: data[:locale] || Setting.get('locale_default') || 'en-us',
template: data[:template], template: data[:template],
format: 'html', format: data[:format] || 'html',
type: 'mailer', type: 'mailer',
) )
@ -292,6 +292,10 @@ returns
template: template[:subject], template: template[:subject],
escape: false escape: false
).render ).render
# strip off the extra newline at the end for the subjects of plaintext templates
message_subject.chomp! if data[:format] == 'txt'
message_body = NotificationFactory::Renderer.new( message_body = NotificationFactory::Renderer.new(
objects: data[:objects], objects: data[:objects],
locale: data[:locale], locale: data[:locale],

View file

@ -0,0 +1,93 @@
require 'rails_helper'
require 'ostruct'
RSpec.describe NotificationFactory::Mailer do
describe '#template' do
context 'for postmaster oversized mail' do
let(:raw_incoming_mail) { File.read(Rails.root.join('test', 'data', 'mail', 'mail010.box')) }
let(:parsed_incoming_mail) { Channel::EmailParser.new.parse raw_incoming_mail }
let(:incoming_mail) do
mail = OpenStruct.new
mail.from_display_name = parsed_incoming_mail[:from_display_name]
mail.subject = parsed_incoming_mail[:subject]
mail.msg_size = format('%.2f', raw_incoming_mail.size.to_f / 1024 / 1024)
mail
end
let(:en_expected_subject) { '[ALERT] Message too large' }
let(:en_expected_body) do
<<~BODY
Dear Smith Sepp,
Unfortunately your email titled \"Gruß aus Oberalteich\" could not be delivered to one or more recipients.
Your message was 0.01 MB but we only accept messages up to 10 MB.
Please reduce the message size and try again. Thank you for your understanding.
Regretfully,
Postmaster of zammad.example.com
BODY
end
shared_examples 'plaintext mail templating' do
it 'templates correctly' do
result = described_class.template(
template: 'email_oversized',
locale: locale,
format: 'txt',
objects: {
mail: incoming_mail,
},
raw: true, # will not add application template
standalone: true, # default: false - will send header & footer
)
expect(result[:subject]).to eq(expected_subject)
expect(result[:body]).to eq(expected_body)
end
end
context 'English locale (en)' do
include_examples 'plaintext mail templating' do
let(:locale) { 'en' }
let(:expected_subject) { en_expected_subject }
let(:expected_body) { en_expected_body }
end
end
context 'German locale (de)' do
include_examples 'plaintext mail templating' do
let(:locale) { 'de' }
let(:expected_subject) { '[Unzustellbar] Nachricht zu groß' }
let(:expected_body) do
<<~BODY
Hallo Smith Sepp,
Ihre E-Mail mit dem Betreff \"Gruß aus Oberalteich\" konnte nicht an einen oder mehrere Empfänger zugestellt werden.
Die Nachricht hatte eine Größe von 0.01 MB, wir akzeptieren jedoch nur E-Mails mit einer Größe von bis zu 10 MB.
Bitte reduzieren Sie die Größe Ihrer Nachricht und versuchen Sie es erneut. Vielen Dank für Ihr Verständnis.
Mit freundlichen Grüßen
Postmaster von zammad.example.com
BODY
end
end
end
context 'unsupported locale, which defaults back to English locale (en)' do
include_examples 'plaintext mail templating' do
let(:locale) { 'UNSUPPORTED_LOCALE' }
let(:expected_subject) { en_expected_subject }
let(:expected_body) { en_expected_body }
end
end
end
end
end

View file

@ -997,4 +997,124 @@ RSpec.describe Channel::EmailParser, type: :model do
end end
end end
end end
describe '#compose_postmaster_reply' do
let(:raw_incoming_mail) { File.read(Rails.root.join('test', 'data', 'mail', 'mail010.box')) }
shared_examples 'postmaster reply' do
it 'composes postmaster reply' do
reply = Channel::EmailParser.new.send(:compose_postmaster_reply, raw_incoming_mail, locale)
expect(reply[:to]).to eq('smith@example.com')
expect(reply[:content_type]).to eq('text/plain')
expect(reply[:subject]).to eq(expected_subject)
expect(reply[:body]).to eq(expected_body)
end
end
context 'for English locale (en)' do
include_examples 'postmaster reply' do
let(:locale) { 'en' }
let(:expected_subject) { '[ALERT] Message too large' }
let(:expected_body) do
body = <<~BODY
Dear Smith Sepp,
Unfortunately your email titled \"Gruß aus Oberalteich\" could not be delivered to one or more recipients.
Your message was 0.01 MB but we only accept messages up to 10 MB.
Please reduce the message size and try again. Thank you for your understanding.
Regretfully,
Postmaster of zammad.example.com
BODY
body.gsub(/\n/, "\r\n")
end
end
end
context 'for German locale (de)' do
include_examples 'postmaster reply' do
let(:locale) { 'de' }
let(:expected_subject) { '[Unzustellbar] Nachricht zu groß' }
let(:expected_body) do
body = <<~BODY
Hallo Smith Sepp,
Ihre E-Mail mit dem Betreff \"Gruß aus Oberalteich\" konnte nicht an einen oder mehrere Empfänger zugestellt werden.
Die Nachricht hatte eine Größe von 0.01 MB, wir akzeptieren jedoch nur E-Mails mit einer Größe von bis zu 10 MB.
Bitte reduzieren Sie die Größe Ihrer Nachricht und versuchen Sie es erneut. Vielen Dank für Ihr Verständnis.
Mit freundlichen Grüßen
Postmaster von zammad.example.com
BODY
body.gsub(/\n/, "\r\n")
end
end
end
end
describe '#compose_postmaster_reply' do
let(:raw_incoming_mail) { File.read(Rails.root.join('test', 'data', 'mail', 'mail010.box')) }
shared_examples 'postmaster reply' do
it 'composes postmaster reply' do
reply = Channel::EmailParser.new.send(:compose_postmaster_reply, raw_incoming_mail, locale)
expect(reply[:to]).to eq('smith@example.com')
expect(reply[:content_type]).to eq('text/plain')
expect(reply[:subject]).to eq(expected_subject)
expect(reply[:body]).to eq(expected_body)
end
end
context 'for English locale (en)' do
include_examples 'postmaster reply' do
let(:locale) { 'en' }
let(:expected_subject) { '[ALERT] Message too large' }
let(:expected_body) do
body = <<~BODY
Dear Smith Sepp,
Unfortunately your email titled \"Gruß aus Oberalteich\" could not be delivered to one or more recipients.
Your message was 0.01 MB but we only accept messages up to 10 MB.
Please reduce the message size and try again. Thank you for your understanding.
Regretfully,
Postmaster of zammad.example.com
BODY
body.gsub(/\n/, "\r\n")
end
end
end
context 'for German locale (de)' do
include_examples 'postmaster reply' do
let(:locale) { 'de' }
let(:expected_subject) { '[Unzustellbar] Nachricht zu groß' }
let(:expected_body) do
body = <<~BODY
Hallo Smith Sepp,
Ihre E-Mail mit dem Betreff \"Gruß aus Oberalteich\" konnte nicht an einen oder mehrere Empfänger zugestellt werden.
Die Nachricht hatte eine Größe von 0.01 MB, wir akzeptieren jedoch nur E-Mails mit einer Größe von bis zu 10 MB.
Bitte reduzieren Sie die Größe Ihrer Nachricht und versuchen Sie es erneut. Vielen Dank für Ihr Verständnis.
Mit freundlichen Grüßen
Postmaster von zammad.example.com
BODY
body.gsub(/\n/, "\r\n")
end
end
end
end
end end

View file

@ -0,0 +1,213 @@
require 'test_helper'
require 'net/imap'
class EmailPostmasterToSender < ActiveSupport::TestCase
setup do
Setting.set('postmaster_max_size', 0.1)
@test_id = rand(999_999_999)
# 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?
raise "Need MAIL_SERVER_EMAIL as ENV variable like export MAIL_SERVER_EMAIL='master@example.com'"
end
@sender_email_address = ENV['MAIL_SERVER_EMAIL']
@email_address = EmailAddress.create!(
realname: 'me Helpdesk',
email: "me#{@test_id}@example.com",
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}
".gsub(/\n/, "\r\n")
large_message_md5 = Digest::MD5.hexdigest(large_message)
large_message_size = format('%.2f', large_message.size.to_f / 1024 / 1024)
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
path = Rails.root.join('tmp', 'oversized_mail')
target_files = Dir.entries(path).select do |filename|
filename =~ /^.+_#{large_message_md5}\.eml$/
end
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
file_path = Rails.root.join('tmp', 'oversized_mail', target_file)
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
imap.select('inbox')
message_ids = imap.sort(['DATE'], ['ALL'], 'US-ASCII')
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])
imap.expunge()
# 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)
assert_equal(mail[:subject], '[ALERT] Message too large')
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}
".gsub(/\n/, "\r\n")
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
path = Rails.root.join('tmp', 'oversized_mail')
target_files = Dir.entries(path).select do |filename|
filename =~ /^.+?\.eml$/
end
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])
imap.expunge()
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