Skip to content

Commit

Permalink
Fix mail parsing in collector
Browse files Browse the repository at this point in the history
- Prevent exception on addresses parsing; fixes #7643
- Correctly fetch additionnal headers that may be used in rules
- Fix exclusion of mailer-daemon/postmaster
- Prevent exception when "Content-Transfer-Encoding" is not set; fixes #7649
  • Loading branch information
cedric-anne committed Jul 13, 2020
1 parent d76b785 commit 4e22c46
Show file tree
Hide file tree
Showing 4 changed files with 225 additions and 83 deletions.
171 changes: 94 additions & 77 deletions inc/mailcollector.class.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
}

use LitEmoji\LitEmoji;
use Laminas\Mail\Address;
use Laminas\Mail\Header\AbstractAddressList;
use Laminas\Mail\Storage\Message;
use Laminas\Mime\Mime as Laminas_Mime;

/**
Expand Down Expand Up @@ -717,9 +720,7 @@ function collect($mailgateID, $display = 0) {
$rejinput = [];
$rejinput['mailcollectors_id'] = $mailgateID;

$req_field = $this->getRequesterField();
$h_requester = $message->getHeader($req_field)->getAddressList();
$requester = $h_requester->current()->getEmail();
$requester = $this->getRequesterEmail($message);

if (!$tkt['_blacklisted']) {
global $DB;
Expand Down Expand Up @@ -951,9 +952,7 @@ function buildTicket($uid, \Laminas\Mail\Storage\Message $message, $options = []
}

// Who is the user ?
$req_field = $this->getRequesterField();
$h_requester = $message->getHeader($req_field)->getAddressList();
$requester = $h_requester->current()->getEmail();
$requester = $this->getRequesterEmail($message);

$tkt['_users_id_requester'] = User::getOrImportByEmail($requester);
$tkt["_users_id_requester_notif"]['use_notification'][0] = 1;
Expand Down Expand Up @@ -1320,8 +1319,10 @@ function getAdditionnalHeaders(\Laminas\Mail\Storage\Message $message) {
$head = [];
$headers = $message->getHeaders();

foreach ($headers as $key => $value) {
foreach ($headers as $header) {
// is line with additional header?
$key = $header->getFieldName();
$value = $header->getFieldValue();
if (preg_match("/^X-/i", $key)
|| preg_match("/^Auto-Submitted/i", $key)
|| preg_match("/^Received/i", $key)) {
Expand Down Expand Up @@ -1354,79 +1355,71 @@ function getAdditionnalHeaders(\Laminas\Mail\Storage\Message $message) {
**/
function getHeaders(\Laminas\Mail\Storage\Message $message) {

$h_sender = $message->getHeader('from')->getAddressList();
$sender = $h_sender->current();
$sender_email = $this->getEmailFromHeader($message, 'from');

$h_to = $message->getHeader('to')->getAddressList();
$h_to->rewind();
$to = $h_to->current();

$reply_to_addr = null;
if (isset($message->reply_to)) {
$h_reply_to = $message->getHeader('reply_to')->getAddressList();
$reply_to = $h_reply_to->current();
$reply_to_addr = Toolbox::strtolower($reply_to->getEmail());
if (preg_match('/^(mailer-daemon|postmaster)@/i', $sender_email) === 1) {
return [];
}

$to = $this->getEmailFromHeader($message, 'to');

$reply_to_addr = Toolbox::strtolower($this->getEmailFromHeader($message, 'reply-to'));

$date = date("Y-m-d H:i:s", strtotime($message->date));
$mail_details = [];

if ((Toolbox::strtolower($sender->getEmail()) != 'mailer-daemon')
&& (Toolbox::strtolower($sender->getEmail()) != 'postmaster')) {

// Construct to and cc arrays
$h_tos = $message->getHeader('to');
$tos = [];
foreach ($h_tos->getAddressList() as $address) {
$mailto = Toolbox::strtolower($address->getEmail());
if ($mailto === $this->fields['name']) {
$to = $address;
}
$tos[] = $mailto;
// Construct to and cc arrays
$h_tos = $message->getHeader('to');
$tos = [];
foreach ($h_tos->getAddressList() as $address) {
$mailto = Toolbox::strtolower($address->getEmail());
if ($mailto === $this->fields['name']) {
$to = $mailto;
}
$tos[] = $mailto;
}

$ccs = [];
if (isset($message->cc)) {
$h_ccs = $message->getHeader('cc');
foreach ($h_ccs->getAddressList() as $address) {
$ccs[] = Toolbox::strtolower($address->getEmail());
}
$ccs = [];
if (isset($message->cc)) {
$h_ccs = $message->getHeader('cc');
foreach ($h_ccs->getAddressList() as $address) {
$ccs[] = Toolbox::strtolower($address->getEmail());
}
}

// secu on subject setting
try {
$subject = $message->getHeader('subject')->getFieldValue();
} catch (Laminas\Mail\Storage\Exception\InvalidArgumentException $e) {
$subject = '';
}
// secu on subject setting
try {
$subject = $message->getHeader('subject')->getFieldValue();
} catch (Laminas\Mail\Storage\Exception\InvalidArgumentException $e) {
$subject = '';
}

$mail_details = [
'from' => Toolbox::strtolower($sender_email),
'subject' => $subject,
'reply-to' => $reply_to_addr,
'to' => Toolbox::strtolower($to),
'message_id' => $message->getHeader('message_id')->getFieldValue(),
'tos' => $tos,
'ccs' => $ccs,
'date' => $date
];

$mail_details = [
'from' => Toolbox::strtolower($sender->getEmail()),
'subject' => $subject,
'reply-to' => $reply_to_addr,
'to' => Toolbox::strtolower($to->getEmail()),
'message_id' => $message->getHeader('message_id')->getFieldValue(),
'tos' => $tos,
'ccs' => $ccs,
'date' => $date
];

if (isset($message->references)) {
if ($reference = $message->getHeader('references')) {
$mail_details['references'] = $reference->getFieldValue();
}
if (isset($message->references)) {
if ($reference = $message->getHeader('references')) {
$mail_details['references'] = $reference->getFieldValue();
}
}

if (isset($message->in_reply_to)) {
if ($inreplyto = $message->getHeader('in_reply_to')) {
$mail_details['in_reply_to'] = $inreplyto->getFieldValue();
}
if (isset($message->in_reply_to)) {
if ($inreplyto = $message->getHeader('in_reply_to')) {
$mail_details['in_reply_to'] = $inreplyto->getFieldValue();
}
}

//Add additional headers in X-
foreach ($this->getAdditionnalHeaders($message) as $header => $value) {
$mail_details[$header] = $value;
}
//Add additional headers in X-
foreach ($this->getAdditionnalHeaders($message) as $header => $value) {
$mail_details[$header] = $value;
}

return $mail_details;
Expand Down Expand Up @@ -1953,21 +1946,46 @@ function cleanDBonPurge() {
Rule::cleanForItemCriteria($this, '_mailgate');
}

/**
* Get the requester email address.
*
* @param Message $message
*
* @return string|null
*/
private function getRequesterEmail(Message $message): ?string {
$email = null;

if ($this->fields['requester_field'] === self::REQUESTER_FIELD_REPLY_TO) {
// Try to find requester in "reply-to"
$email = $this->getEmailFromHeader($message, 'reply-to');
}

if ($email === null) {
// Fallback on default "from"
$email = $this->getEmailFromHeader($message, 'from');
}

return $email;
}

/**
* Get the requester field
* Get the email address from given header.
*
* @return string
**/
private function getRequesterField() {
switch ($this->fields['requester_field']) {
case self::REQUESTER_FIELD_REPLY_TO:
return "reply-to";

default:
return "from";
* @param Message $message
* @param string $header_name
*
* @return string|null
*/
private function getEmailFromHeader(Message $message, string $header_name): ?string {
if (!$message->getHeaders()->has($header_name)) {
return null;
}

$header = $message->getHeader($header_name);
$address = $header instanceof AbstractAddressList ? $header->getAddressList()->rewind() : null;

return $address instanceof Address ? $address->getEmail() : null;
}


Expand Down Expand Up @@ -1996,10 +2014,9 @@ public function getDecodedContent(\Laminas\Mail\Storage\Part $part) {
case '7bit':
case '8bit':
case 'binary':
default:
// returned verbatim
break;
default:
throw new \UnexpectedValueException("$encoding is not known");
}

$contentType = $part->getHeader('contentType');
Expand Down
34 changes: 34 additions & 0 deletions tests/emails-tests/09-reply-to-tech.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
Delivered-To: REDACTED-EMAIL
Received: by 2002:a50:cfc3:0:0:0:0:0 with SMTP id i3csp1241712edk;
Thu, 9 Jul 2020 00:07:53 -0700 (PDT)
Return-Path: <REDACTED-EMAIL>
Received: from REDACTED-SERVER (REDACTED-SERVER [REDACTED-IP])
by mx.google.com with ESMTPS id ck17si1268608edb.508.2020.07.09.00.07.53
for <REDACTED-EMAIL>
(version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128);
Thu, 09 Jul 2020 00:07:53 -0700 (PDT)
Received: from mail-lf1-f69.google.com ([REDACTED-IP])
by REDACTED-SERVER with ESMTP/TLS/ECDHE-RSA-AES256-GCM-SHA384; 09 Jul 2020 08:07:53 +0100
Received: by mail-lf1-f69.google.com with SMTP id f8so1572484lfh.22
for <REDACTED-EMAIL>; Thu, 09 Jul 2020 00:07:52 -0700 (PDT)
MIME-Version: 1.0
From: Tech (alt address) <alternative.email@glpi-project.org>
Reply-To: Tech Ni Cian <tech@glpi-project.org>
Date: Thu, 9 Jul 2020 08:07:40 +0100
Message-ID: <CAJjtjRxMt1T5Zv1SR6sr+XKK9-U8C4M3cpPGP5fVwvWintxwdQ@mail.gmail.com>
Subject: Re: [GLPI #0038927] Update - Issues with new Windows 10 machine
To: GLPI debug <unittests@glpi-project.org>
Content-Type: multipart/alternative; boundary="000000000000f50df905a9fce128"

--000000000000f50df905a9fce128
Content-Type: text/plain; charset="UTF-8"
This message have reply to header, requester should be get from this header.
--000000000000f50df905a9fce128
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

This message have reply to header, requester should be get from this header.

--000000000000f50df905a9fce128--
85 changes: 85 additions & 0 deletions tests/emails-tests/10-missing-content-transfer-encoding.eml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
From: Normal User <normal@glpi-project.org>
To: GLPI debug <unittests@glpi-project.org>
Subject: Test Email from Outlook
Thread-Topic: Test Email from Outlook
Thread-Index: AdZW2I0jVbjPDha9QGyhAGwBB3ep4A==
Date: Fri, 10 Jul 2020 16:47:41 +0000
Message-ID:
<DM6PR05MB5865518283B3393AA31FAEDFBC650@DM6PR05MB5865.namprd05.prod.outlook.com>
Content-Language: en-US
X-MS-Has-Attach:
X-MS-Exchange-Organization-SCL: -1
X-MS-TNEF-Correlator:
X-MS-Exchange-Organization-RecordReviewCfmType: 0
Content-Type: multipart/alternative;
boundary="_000_DM6PR05MB5865518283B3393AA31FAEDFBC650DM6PR05MB5865namp_"
MIME-Version: 1.0

--_000_DM6PR05MB5865518283B3393AA31FAEDFBC650DM6PR05MB5865namp_
Content-Type: text/plain; charset="us-ascii"
With only text in the body, the email will not be imported and the following error will appear when forcing the retrieval by clicking the 'Get email tickets now' button on the receiver.
'Uncaught Exception UnexpectedValueException: is not known in /var/www/glpi/inc/mailcollector.class.php at line 2002'
--_000_DM6PR05MB5865518283B3393AA31FAEDFBC650DM6PR05MB5865namp_
Content-Type: text/html; charset="us-ascii"

<html xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:w="urn:schemas-microsoft-com:office:word" xmlns:m="http://schemas.microsoft.com/office/2004/12/omml" xmlns="http://www.w3.org/TR/REC-html40">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=us-ascii">
<meta name="Generator" content="Microsoft Word 15 (filtered medium)">
<style><!--
/* Font Definitions */
@font-face
{font-family:"Cambria Math";
panose-1:2 4 5 3 5 4 6 3 2 4;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;}
@font-face
{font-family:"Segoe UI";
panose-1:2 11 5 2 4 2 4 2 2 3;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{margin:0in;
margin-bottom:.0001pt;
font-size:11.0pt;
font-family:"Calibri",sans-serif;}
a:link, span.MsoHyperlink
{mso-style-priority:99;
color:#0563C1;
text-decoration:underline;}
a:visited, span.MsoHyperlinkFollowed
{mso-style-priority:99;
color:#954F72;
text-decoration:underline;}
span.EmailStyle17
{mso-style-type:personal-compose;
font-family:"Calibri",sans-serif;
color:windowtext;}
.MsoChpDefault
{mso-style-type:export-only;
font-family:"Calibri",sans-serif;}
@page WordSection1
{size:8.5in 11.0in;
margin:1.0in 1.0in 1.0in 1.0in;}
div.WordSection1
{page:WordSection1;}
--></style><!--[if gte mso 9]><xml>
<o:shapedefaults v:ext="edit" spidmax="1026" />
</xml><![endif]--><!--[if gte mso 9]><xml>
<o:shapelayout v:ext="edit">
<o:idmap v:ext="edit" data="1" />
</o:shapelayout></xml><![endif]-->
</head>
<body lang="EN-US" link="#0563C1" vlink="#954F72">
<div class="WordSection1">
<p class="MsoNormal">With only text in the body, the email will not be imported and the following error will appear when forcing the retrieval by clicking the &#8216;Get email tickets now&#8217; button on the receiver.<o:p></o:p></p>
<p class="MsoNormal"><o:p>&nbsp;</o:p></p>
<p class="MsoNormal"><span style="font-size:10.5pt;font-family:&quot;Segoe UI&quot;,sans-serif;color:#24292E;background:white">'Uncaught Exception UnexpectedValueException: is not known in /var/www/glpi/inc/mailcollector.class.php at line 2002'</span><o:p></o:p></p>
</div>
</body>
</html>
--_000_DM6PR05MB5865518283B3393AA31FAEDFBC650DM6PR05MB5865namp_--
Loading

0 comments on commit 4e22c46

Please sign in to comment.