Improved email loop protection. Remember permanent delivery failed on user to prevent email loops.

This commit is contained in:
Martin Edenhofer 2017-05-30 08:55:51 +02:00
parent 2fe0b19a3d
commit 48e3da57ee
9 changed files with 841 additions and 42 deletions

View file

@ -0,0 +1,73 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Channel::Filter::BounceDeliveryPermanentFailed
def self.run(_channel, mail)
return if !mail[:mail_instance]
return if !mail[:mail_instance].bounced?
return if !mail[:attachments]
# remember, do not send notifications to certain recipients again if failed permanent
mail[:attachments].each { |attachment|
next if !attachment[:preferences]
next if attachment[:preferences]['Mime-Type'] != 'message/rfc822'
next if !attachment[:data]
result = Channel::EmailParser.new.parse(attachment[:data])
next if !result[:message_id]
message_id_md5 = Digest::MD5.hexdigest(result[:message_id])
article = Ticket::Article.where(message_id_md5: message_id_md5).order('created_at DESC, id DESC').limit(1).first
next if !article
# check user preferences
next if mail[:mail_instance].action != 'failed'
next if mail[:mail_instance].retryable? != false
next if mail[:mail_instance].error_status != '5.1.1'
# get recipient of origin article, if only one - mark this user to not sent notifications anymore
recipients = []
if article.sender.name == 'System' || article.sender.name == 'Agent'
%w(to cc).each { |line|
next if article[line].blank?
recipients = []
begin
list = Mail::AddressList.new(article[line])
list.addresses.each { |address|
next if address.address.blank?
recipients.push address.address.downcase
}
rescue
Rails.logger.info "Unable to parse email address in '#{article[line]}'"
end
}
if recipients.count > 1
recipients = []
end
end
# get recipient bounce mail, mark this user to not sent notifications anymore
final_recipient = mail[:mail_instance].final_recipient
if final_recipient.present?
final_recipient.sub!(/rfc822;\s{0,10}/, '')
if final_recipient.present?
recipients.push final_recipient.downcase
end
end
# set user preferences
recipients.each { |recipient|
users = User.where(email: recipient)
users.each { |user|
next if !user
user.preferences[:mail_delivery_failed] = true
user.preferences[:mail_delivery_failed_data] = Time.zone.now
user.save!
}
}
}
true
end
end

View file

@ -1,6 +1,6 @@
# Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
module Channel::Filter::BounceCheck
module Channel::Filter::BounceFollowUpCheck
def self.run(_channel, mail)
@ -13,13 +13,17 @@ module Channel::Filter::BounceCheck
next if !attachment[:preferences]
next if attachment[:preferences]['Mime-Type'] != 'message/rfc822'
next if !attachment[:data]
result = Channel::EmailParser.new.parse(attachment[:data])
next if !result[:message_id]
message_id_md5 = Digest::MD5.hexdigest(result[:message_id])
article = Ticket::Article.where(message_id_md5: message_id_md5).order('created_at DESC, id DESC').limit(1).first
next if !article
Rails.logger.debug "Follow up for '##{article.ticket.number}' in bounce email."
mail[ 'x-zammad-ticket-id'.to_sym ] = article.ticket_id
mail[ 'x-zammad-is-auto-response'.to_sym ] = true
return true
}

View file

@ -813,6 +813,19 @@ perform changes on ticket
recipients_checked = []
recipients_raw.each { |recipient_email|
skip_user = false
users = User.where(email: recipient_email)
users.each { |user|
next if user.preferences[:mail_delivery_failed] != true
next if !user.preferences[:mail_delivery_failed_data]
till_blocked = ((user.preferences[:mail_delivery_failed_data] - Time.zone.now - 60.days) / 60 / 60 / 24).round
next if till_blocked.positive?
logger.info "Send no trigger based notification to #{recipient_email} because email is marked as mail_delivery_failed for #{till_blocked} days"
skip_user = true
break
}
next if skip_user
# send notifications only to email adresses
next if !recipient_email
next if recipient_email !~ /@/

View file

@ -0,0 +1,35 @@
class SettingDeliveryPermanentFailed < ActiveRecord::Migration
def up
# return if it's a new setup
return if !Setting.find_by(name: 'system_init_done')
setting = Setting.find_by(name: '0900_postmaster_filter_bounce_check')
if setting
setting.name = '0900_postmaster_filter_bounce_follow_up_check'
setting.state = 'Channel::Filter::BounceFollowUpCheck'
setting.save!
else
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '0900_postmaster_filter_bounce_follow_up_check',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify postmaster bounced - to handle it as follow-up of the original ticket.',
options: {},
state: 'Channel::Filter::BounceFollowUpCheck',
frontend: false
)
end
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '0950_postmaster_filter_bounce_delivery_permanent_failed',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify postmaster bounced - disable sending notification on permanent deleivery failed.',
options: {},
state: 'Channel::Filter::BounceDeliveryPermanentFailed',
frontend: false
)
end
end

View file

@ -2327,11 +2327,20 @@ Setting.create_if_not_exists(
)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '0900_postmaster_filter_bounce_check',
name: '0900_postmaster_filter_bounce_follow_up_check',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify postmaster bounced - to handle it as follow-up of the original ticket.',
options: {},
state: 'Channel::Filter::BounceCheck',
state: 'Channel::Filter::BounceFollowUpCheck',
frontend: false
)
Setting.create_if_not_exists(
title: 'Defines postmaster filter.',
name: '0950_postmaster_filter_bounce_delivery_permanent_failed',
area: 'Postmaster::PreFilter',
description: 'Defines postmaster filter to identify postmaster bounced - disable sending notification on permanent deleivery failed.',
options: {},
state: 'Channel::Filter::BounceDeliveryPermanentFailed',
frontend: false
)
Setting.create_if_not_exists(

230
test/fixtures/mail55.box vendored Normal file
View file

@ -0,0 +1,230 @@
Return-Path: <MAILER-DAEMON>
Delivered-To: example@zammad.com
Received: by mx1.zammad.loc (Postfix)
id 738F920A13B2; Fri, 26 May 2017 17:01:45 +0200 (CEST)
Date: Fri, 26 May 2017 17:01:45 +0200 (CEST)
From: MAILER-DAEMON@mx1.zammad.loc (Mail Delivery System)
Subject: Undelivered Mail Returned to Sender
To: example@zammad.com
Auto-Submitted: auto-replied
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
boundary="207FF20398ED.1495810905/mx1.zammad.loc"
Message-Id: <20170526150145.738F920A13B2@mx1.zammad.loc>
This is a MIME-encapsulated message.
--207FF20398ED.1495810905/mx1.zammad.loc
Content-Description: Notification
Content-Type: text/plain; charset=us-ascii
This is the mail system at host mx1.zammad.loc.
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients. It's attached below.
For further assistance, please send mail to postmaster.
If you do so, please include this problem report. You can
delete your own text from the attached returned message.
The mail system
<ticket-bounce-trigger2@example.com>: host aspmx.l.example.com[108.177.96.26] said:
550-5.1.1 The email account that you tried to reach does not exist. Please
try 550-5.1.1 double-checking the recipient's email address for typos or
550-5.1.1 unnecessary spaces. Learn more at 550 5.1.1
https://support.example.com/mail/?p=NoSuchUser l59si1635011edl.281 - gsmtp
(in reply to RCPT TO command)
--207FF20398ED.1495810905/mx1.zammad.loc
Content-Description: Delivery report
Content-Type: message/delivery-status
Reporting-MTA: dns; mx1.zammad.loc
X-Postfix-Queue-ID: 207FF20398ED
X-Postfix-Sender: rfc822; example@zammad.com
Arrival-Date: Fri, 26 May 2017 17:01:45 +0200 (CEST)
Final-Recipient: rfc822; ticket-bounce-trigger2@example.com
Original-Recipient: rfc822;ticket-bounce-trigger2@example.com
Action: failed
Status: 5.1.1
Remote-MTA: dns; aspmx.l.example.com
Diagnostic-Code: smtp; 550-5.1.1 The email account that you tried to reach does
not exist. Please try 550-5.1.1 double-checking the recipient's email
address for typos or 550-5.1.1 unnecessary spaces. Learn more at 550 5.1.1
https://support.example.com/mail/?p=NoSuchUser l59si1635011edl.281 - gsmtp
--207FF20398ED.1495810905/mx1.zammad.loc
Content-Description: Undelivered Message
Content-Type: message/rfc822
Return-Path: <example@zammad.com>
Received: from apn0000.dc.zammad.com (apn0000.dc.zammad.com [88.0.0.0])
by mx1.zammad.loc (Postfix) with ESMTP id 207FF20398ED
for <ticket-bounce-trigger2@example.com>; Fri, 26 May 2017 17:01:45 +0200 (CEST)
Received: by apn0000.dc.zammad.com (Postfix, from userid 1050)
id 08443420973; Fri, 26 May 2017 17:01:45 +0200 (CEST)
Date: Fri, 26 May 2017 17:01:45 +0200
From: Twelve SaaS GmbH Helpdesk <example@zammad.com>
To: ticket-bounce-trigger2@example.com
Message-ID: <20170526150141.232.13312@example.zammad.loc>
In-Reply-To:
References: <20170526150142.232.819805@example.zammad.loc>
<20170526150119.6C5E520A13B2@mx1.zammad.loc>
<20170526150141.232.799457@example.zammad.loc>
<20170526150117.0560820A13B3@mx1.zammad.loc>
<20170526150115.232.175460@example.zammad.loc>
<20170526150108.232.482766@example.zammad.loc>
<20170526150041.F3D2C20A13B3@mx1.zammad.loc>
<20170526150036.232.513248@example.zammad.loc>
<20170526150008.6AE8A20A13B8@mx1.zammad.loc>
<20170526150004.232.103372@example.zammad.loc>
<20170526145940.D799220A13B3@mx1.zammad.loc>
<20170526145932.232.91897@example.zammad.loc>
<20170526145906.8FCA520A13B2@mx1.zammad.loc>
<20170526145901.232.269971@example.zammad.loc>
<lyHYjBDWwaU5KGbDrCyOfA@notifications.example.com>
Subject: =?UTF-8?Q?[Ticket#1705265400361]_RE:_Thanks_for_your_follow_up?=
=?UTF-8?Q?_=28G_Suite:_Benachrichtigung_=C3=BCber_Verl=C3=A4ngerung_in_30?=
=?UTF-8?Q?_Tagen=29?=
Mime-Version: 1.0
Content-Type: multipart/mixed;
boundary="--==_mimepart_5928435950b1_22d42086504355bc";
charset=UTF-8
Content-Transfer-Encoding: 7bit
Organization: Twelve SaaS GmbH
X-Loop: yes
Precedence: bulk
Auto-Submitted: auto-generated
X-Auto-Response-Suppress: All
X-Powered-By: Zammad - Helpdesk/Support (https://zammad.org/)
X-Mailer: Zammad Mail Service
----==_mimepart_5928435950b1_22d42086504355bc
Content-Type: multipart/alternative;
boundary="--==_mimepart_592843594d35_22d4208650435394";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_592843594d35_22d4208650435394
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
Your follow up for (Ticket#1705265400361) has been received and will be reviewed by our support staff.
To provide additional information, please reply to this email or click on the following link:[1] https://example.zammad.loc/#ticket/zoom/232
Your Twelve SaaS Helpdesk Team
[2] Zammad, your customer support system
[1] https://example.zammad.loc/#ticket/zoom/232
[2] https://zammad.com
----==_mimepart_592843594d35_22d4208650435394
Content-Type: multipart/related;
boundary="--==_mimepart_592843594e47_22d4208650435484";
charset=UTF-8
Content-Transfer-Encoding: 7bit
----==_mimepart_592843594e47_22d4208650435484
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: 7bit
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style type="text/css">
body {
font-family:'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; font-size: 12px;;
}
img {
outline: none;
text-decoration: none;
-ms-interpolation-mode: bicubic;
}
a img {
border: none;
}
table td {
border-collapse: collapse;
}
table {
border-collapse: collapse;
mso-table-lspace: 0pt;
mso-table-rspace: 0pt;
border: none;
table-layout: auto;
display: block;
width: 100%;
overflow: auto;
word-break: keep-all;
}
table,
pre,
blockquote {
margin: 0 0 16px;
}
td, th {
padding: 7px 12px;
border: 1px solid hsl(0,0%,87%);
}
th {
font-weight: bold;
text-align: center;
}
tbody tr:nth-child(even) {
background: hsl(0,0%,97%);
}
col {
width: auto;
}
p {
margin: 0;
}
code {
border: none;
background: hsl(0,0%,97%);
white-space: pre-wrap;
}
blockquote {
padding: 8px 12px;
border-left: 5px solid #eee;
}
pre {
padding: 12px 15px;
font-size: 13px;
line-height: 1.45;
background: hsl(0,0%,97%);
white-space: pre-wrap;
border-radius: 3px;
border: none;
overflow: auto;
}
</style>
</head>
<body style="font-family:'Helvetica Neue', Helvetica, Arial, Geneva, sans-serif; font-size: 12px;"><div>Your follow up for <b>(Ticket#1705265400361)</b> has been received and will be reviewed by our support staff.</div>
<br>
<div>To provide additional information, please reply to this email or click on the following link:
<a href="https://example.zammad.loc/#ticket/zoom/232" rel="nofollow noreferrer noopener" target="_blank">https://example.zammad.loc/#ticket/zoom/232</a>
</div>
<br>
<div>Your Twelve SaaS Helpdesk Team</div>
<br>
<div><i><a href="https://zammad.com" rel="nofollow noreferrer noopener" target="_blank">Zammad</a>, your customer support system</i></div></body>
</html>
----==_mimepart_592843594e47_22d4208650435484--
----==_mimepart_592843594d35_22d4208650435394--
----==_mimepart_5928435950b1_22d42086504355bc--
--207FF20398ED.1495810905/mx1.zammad.loc--

View file

@ -0,0 +1,220 @@
# encoding: utf-8
require 'test_helper'
class EmailProcessBounceDeliveryPermanentFailedTest < ActiveSupport::TestCase
test 'process with bounce trigger email loop check - article based blocker' do
roles = Role.where(name: %w(Customer))
customer1 = User.create_or_update(
login: 'ticket-bounce-trigger1@example.com',
firstname: 'Notification',
lastname: 'Customer1',
email: 'ticket-bounce-trigger1@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: customer1.email,
subject: 'bounce check 2',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail33-undelivered-mail-returned-to-sender.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - bounce based blocker' do
roles = Role.where(name: %w(Customer))
customer2 = User.create_or_update(
login: 'ticket-bounce-trigger2@example.com',
firstname: 'Notification',
lastname: 'Customer2',
email: 'ticket-bounce-trigger2@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check 2',
message_id: '<20170526150141.232.13312@example.zammad.loc>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail55.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
end

View file

@ -0,0 +1,254 @@
# encoding: utf-8
require 'test_helper'
class EmailProcessBounceFollowUpTest < ActiveSupport::TestCase
test 'process with bounce follow up check' do
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail33-undelivered-mail-returned-to-sender.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('new', ticket_p.state.name)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - article based blocker' do
roles = Role.where(name: %w(Customer))
customer1 = User.create_or_update(
login: 'ticket-bounce-trigger1@example.com',
firstname: 'Notification',
lastname: 'Customer1',
email: 'ticket-bounce-trigger1@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer1,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: customer1.email,
subject: 'bounce check 2',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail33-undelivered-mail-returned-to-sender.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
test 'process with bounce trigger email loop check - bounce based blocker' do
roles = Role.where(name: %w(Customer))
customer2 = User.create_or_update(
login: 'ticket-bounce-trigger2@example.com',
firstname: 'Notification',
lastname: 'Customer2',
email: 'ticket-bounce-trigger2@example.com',
active: true,
roles: roles,
preferences: {},
updated_by_id: 1,
created_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply new ticket',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'create',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your inquiry (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
Trigger.create_or_update(
name: 'auto reply followup',
condition: {
'ticket.action' => {
'operator' => 'is',
'value' => 'update',
},
},
perform: {
'notification.email' => {
'body' => 'some text<br>#{ticket.customer.lastname}<br>#{ticket.title}<br>#{article.body}',
'recipient' => 'ticket_customer',
'subject' => 'Thanks for your follow up (#{ticket.title})!',
},
},
disable_notification: true,
active: true,
created_by_id: 1,
updated_by_id: 1,
)
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup(name: 'Users'),
customer: customer2,
state: Ticket::State.lookup(name: 'new'),
priority: Ticket::Priority.lookup(name: '2 normal'),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.6088xx@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal('new', ticket.state.name)
assert_equal(2, ticket.articles.count)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check 2',
message_id: '<20170526150141.232.13312@example.zammad.loc>',
body: 'some message bounce check 2',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Agent').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
Observer::Transaction.commit
assert_equal(4, ticket.articles.count)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail55.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('open', ticket_p.state.name)
assert_equal(5, ticket_p.articles.count)
travel_back
ticket.destroy
end
end

View file

@ -1,39 +0,0 @@
# encoding: utf-8
require 'test_helper'
class EmailProcessBounceTest < ActiveSupport::TestCase
test 'process with bounce check' do
ticket = Ticket.create(
title: 'bounce check',
group: Group.lookup( name: 'Users'),
customer_id: 2,
state: Ticket::State.lookup( name: 'new' ),
priority: Ticket::Priority.lookup( name: '2 normal' ),
updated_by_id: 1,
created_by_id: 1,
)
article = Ticket::Article.create(
ticket_id: ticket.id,
from: 'some_sender@example.com',
to: 'some_recipient@example.com',
subject: 'bounce check',
message_id: '<20150830145601.30.608881@edenhofer.zammad.com>',
body: 'some message bounce check',
internal: false,
sender: Ticket::Article::Sender.where(name: 'Customer').first,
type: Ticket::Article::Type.where(name: 'email').first,
updated_by_id: 1,
created_by_id: 1,
)
travel 1.second
email_raw_string = IO.binread('test/fixtures/mail33-undelivered-mail-returned-to-sender.box')
ticket_p, article_p, user_p = Channel::EmailParser.new.process({}, email_raw_string)
assert_equal(ticket.id, ticket_p.id)
assert_equal('new', ticket_p.state.name)
travel_back
ticket.destroy
end
end