Skip to content

Commit

Permalink
N°4281 - "Content Type" and "Content Disposition" RFC 2045-compliant …
Browse files Browse the repository at this point in the history
…for "name" and "filename" attributes.
  • Loading branch information
itomig-martin committed Nov 4, 2022
1 parent 73ead6f commit 3975b08
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 9 deletions.
33 changes: 24 additions & 9 deletions classes/rawemailmessage.class.inc.php
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,21 @@ public function GetMessageId()
*/
public function GetAttachments(&$aAttachments = null, $aPart = null, &$index = 1)
{
// This Regex complies with RFC 2045 regarding the Grammar of Content Type Headers Filenames:
// (1) Allow all chars for the filename, if the filename is quoted with double quotes
// (2) Allow all chars for the filename, if the filename is quoted with single quotes
// (3) If the filename is not quoted, allow only ASCII chars and exclude the following
// chars from the set, referenced as "tspecials" in the RFC:
// <ALL-CTL-CHARS-INCL-DEL>
// <CHAR-SPACE>
// ()<>@,;:\"/[]?=
// To keep the regex as simple as possible, the _allowed_ chars are whitelisted
// with their corresponding hexval.
$sFileNameRegex = <<<REGEX
(("([^"]+)")|('([^']+)')|([\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+))
REGEX
;

static $iAttachmentCount = 0;
if ($aAttachments === null)
{
Expand All @@ -188,17 +203,10 @@ public function GetAttachments(&$aAttachments = null, $aPart = null, &$index = 1
{
$sFileName = '';
$sContentDisposition = $this->GetHeader('content-disposition', $aPart['headers']);
if (($sContentDisposition != '') && (preg_match('/filename="([^"]+)"/', $sContentDisposition, $aMatches)))
if (($sContentDisposition != '') && (preg_match('/filename='.$sFileNameRegex.'/', $sContentDisposition, $aMatches)))
{
$sFileName = $aMatches[1];
}
else
{
if (($sContentDisposition != '') && (preg_match('/filename=([^"]+)/', $sContentDisposition, $aMatches))) // same but without quotes
{
$sFileName = $aMatches[1];
}
}

$bInline = true;
if (stripos($sContentDisposition, 'attachment;') !== false)
Expand All @@ -223,10 +231,17 @@ public function GetAttachments(&$aAttachments = null, $aPart = null, &$index = 1
{
$sType = $aMatches[1];
}
if (empty($sFileName) && preg_match('/name="([^"]+)"/', $sContentType, $aMatches))
if (empty($sFileName) && preg_match('/name='.$sFileNameRegex.'/', $sContentType, $aMatches))
{
$sFileName = $aMatches[1];
}
// Note: Since the RFC2045-compliant regex above has different levels of matches,
// because of the use of capture groups, indexing into the results with
// $aMatches[1] sometimes returns the filename with quotes, but sometimes not.
// For that reason (and for the sake of codesimplicity) we strip all quotes
// from filenames here and will be fine.
$sFileName = str_replace("'", '', $sFileName);
$sFileName = str_replace('"', '', $sFileName);
if (empty($sFileName))
{
// generate a name based on the type of the file...
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
Return-Path: <[email protected]>
Received: from www192.your-server.de
by www192.your-server.de with LMTP
id cANPJDAmYmNaVQEAUXDXKw
(envelope-from <[email protected]>); Wed, 02 Nov 2022 09:11:28 +0100
Envelope-to: [email protected]
Delivery-date: Wed, 02 Nov 2022 09:11:28 +0100
Authentication-Results: www192.your-server.de;
iprev=pass (localhost) smtp.remote-ip=127.0.0.1;
spf=pass smtp.mailfrom=itomig.de;
dkim=pass header.d=itomig.de header.s=default2012 header.a=rsa-sha256;
dmarc=skipped
Received: from localhost ([127.0.0.1] helo=www192.your-server.de)
by www192.your-server.de with esmtps (TLS1.3) tls TLS_AES_256_GCM_SHA384
(Exim 4.94.2)
(envelope-from <[email protected]>)
id 1oq8qO-000NG0-7r
for [email protected]; Wed, 02 Nov 2022 09:11:28 +0100
DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=itomig.de;
s=default2012; h=Subject:From:To:MIME-Version:Date:Message-ID:Content-Type:
Sender:Reply-To:Cc:Content-Transfer-Encoding:Content-ID:Content-Description:
Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID:
In-Reply-To:References; bh=qfsa/c5xPqZ3d+6wNfzUExTW0ZNkPkALI3o0hp2R85A=; b=r5
zbg+7XSo/HBfaFAqqdBD0gDiOIa8ujhqmikWoXps5jf9oMJVJ6af5ynLdfZPbxkpWss5t6os7LhO/
w/ZdvjR8sOpvIX5nkR3DJxnG76Z28p0aFtpBz1OSIXpWe7LmSj8mBwsXBuh6jTLLH2xRGY3gBEIU9
fkOlRbJQ6lU3qhS9Xa9cgSGECPfhIvZzdM/Rb0vdvvERCmrpvr9TylqFb+3RA05pQJBOvG3i/ih7O
5a9RytEsd5rpN1IabPoMV6UJFhKaxPY4hMG4qdgzI2GQZAcoK5kNm/IrUxeHQ8KLy5HigJ1I11e9e
kzqzy6hT0ZKrELFcqh6Y8L6EzgtC+aOg==;
Received: from [95.90.147.54] (helo=[192.168.0.103])
by www192.your-server.de with esmtpsa (TLS1.3) tls TLS_AES_256_GCM_SHA384
(Exim 4.94.2)
(envelope-from <[email protected]>)
id 1oq8qO-000NFs-3t
for [email protected]; Wed, 02 Nov 2022 09:11:28 +0100
Content-Type: multipart/mixed; boundary="------------8PrwbWLJ6Ep94lElB18RfVAR"
Message-ID: <[email protected]>
Date: Wed, 2 Nov 2022 09:11:25 +0100
MIME-Version: 1.0
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101
Thunderbird/102.2.2
Content-Language: en-US
To: [email protected]
From: Martin Raenker <[email protected]>
Subject: test for attachments with unquoted name
X-Authenticated-Sender: [email protected]
X-Virus-Scanned: Clear (ClamAV 0.103.7/26707/Tue Nov 1 21:23:26 2022)
X-local-sign: yes
X-DKIM-Status: pass [(itomig.de) - 127.0.0.1]
Delivered-To: [email protected]

This is a multi-part message in MIME format.
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: text/plain; charset=UTF-8; format=flowed
Content-Transfer-Encoding: 7bit
testbody
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8; name="example_attachment_mail.csv"
Content-Disposition: attachment;
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8; name='example_attachment_mail.csv'
Content-Disposition: attachment;
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8; name=example_attachment_mail.csv
Content-Disposition: attachment;
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8;
Content-Disposition: attachment; filename="example_attachment_mail.csv"
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8;
Content-Disposition: attachment; filename='example_attachment_mail.csv'
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=
--------------8PrwbWLJ6Ep94lElB18RfVAR
Content-Type: application/octet-stream; charset=UTF-8;
Content-Disposition: attachment; filename=example_attachment_mail.csv
Content-Transfer-Encoding: base64
InRlc3QiLCJ0b3N0Igo=

0 comments on commit 3975b08

Please sign in to comment.