Skip to content

Fix undefined attachment name when headers use "filename*=" format #301

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ Updates should follow the [Keep a CHANGELOG](http://keepachangelog.com/) princip

## [UNRELEASED]
### Fixed
- NaN
-
- Fix undefined attachment name when headers use "filename*=" format

### Added
- NaN

Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"illuminate/pagination": ">=5.0.0"
},
"require-dev": {
"phpunit/phpunit": "~4.0"
"phpunit/phpunit": "^9.5"
},
"suggest": {
"symfony/mime": "Recomended for better extension support"
Expand Down
29 changes: 16 additions & 13 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/autoload.php"
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
bootstrap="vendor/autoload.php"
backupGlobals="false"
backupStaticAttributes="false"
colors="true"
Expand All @@ -8,25 +9,27 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false">
stopOnFailure="false"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd">
<coverage>
<include>
<directory suffix=".php">src/</directory>
</include>
<report>
<clover outputFile="build/logs/clover.xml"/>
<html outputDirectory="build/coverage"/>
<text outputFile="build/coverage.txt"/>
</report>
</coverage>
<testsuites>
<testsuite name="PHP-IMAP Test Suite">
<directory>tests</directory>
</testsuite>
</testsuites>
<filter>
<whitelist>
<directory suffix=".php">src/</directory>
</whitelist>
</filter>
<logging>
<log type="tap" target="build/report.tap"/>
<log type="junit" target="build/report.junit.xml"/>
<log type="coverage-html" target="build/coverage"/>
<log type="coverage-text" target="build/coverage.txt"/>
<log type="coverage-clover" target="build/logs/clover.xml"/>
<junit outputFile="build/report.junit.xml"/>
</logging>
<php>
<env name="APP_ENV" value="testing"/>
</php>
</phpunit>
</phpunit>
35 changes: 34 additions & 1 deletion src/Header.php
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,7 @@ public function rfc822_parse_headers($raw_headers) {
$headers = [];
$imap_headers = [];
if (extension_loaded('imap') && $this->config["rfc822"]) {
$raw_imap_headers = (array)\imap_rfc822_parse_headers($this->raw);
$raw_imap_headers = (array)\imap_rfc822_parse_headers($raw_headers);
foreach ($raw_imap_headers as $key => $values) {
$key = str_replace("-", "_", $key);
$imap_headers[$key] = $values;
Expand Down Expand Up @@ -666,20 +666,53 @@ private function extractHeaderExtensions() {

// Get all potential extensions
$extensions = explode(";", substr($value, $pos + 1));

$previousKey = null;
$previousValue = '';

foreach ($extensions as $extension) {
if (($pos = strpos($extension, "=")) !== false) {
$key = substr($extension, 0, $pos);
$key = trim(rtrim(strtolower($key)));

$matches = [];

if (preg_match('/^(?P<key_name>\w+)\*/', $key, $matches) !== 0) {
$key = $matches['key_name'];
$previousKey = $key;

$value = substr($extension, $pos + 1);
$value = str_replace('"', "", $value);
$previousValue .= trim(rtrim($value));

continue;
}

if (
$previousKey !== null
&& $previousKey !== $key
&& isset($this->attributes[$previousKey]) === false
) {
$this->set($previousKey, $previousValue);

$previousValue = '';
}

if (isset($this->attributes[$key]) === false) {
$value = substr($extension, $pos + 1);
$value = str_replace('"', "", $value);
$value = trim(rtrim($value));

$this->set($key, $value);
}

$previousKey = $key;
}
}

if ($previousValue !== '') {
$this->set($previousKey, $previousValue);
}
}
}
}
Expand Down
75 changes: 75 additions & 0 deletions tests/HeaderTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<?php

namespace Tests;

use Webklex\PHPIMAP\Header;
use PHPUnit\Framework\TestCase;

class HeaderTest extends TestCase
{
public function testRfc822ParseHeaders()
{
$mock = $this->getMockBuilder(Header::class)
->disableOriginalConstructor()
->onlyMethods([])
->getMock();

$config = new \ReflectionProperty($mock, 'config');
$config->setValue($mock, ['rfc822' => true]);

$mockHeader = "Content-Type: text/csv; charset=WINDOWS-1252; name*0=\"TH_Is_a_F ile name example 20221013.c\"; name*1=sv\r\nContent-Transfer-Encoding: quoted-printable\r\nContent-Disposition: attachment; filename*0=\"TH_Is_a_F ile name example 20221013.c\"; filename*1=\"sv\"\r\n";

$expected = new \stdClass();
$expected->content_type = 'text/csv; charset=WINDOWS-1252; name*0="TH_Is_a_F ile name example 20221013.c"; name*1=sv';
$expected->content_transfer_encoding = 'quoted-printable';
$expected->content_disposition = 'attachment; filename*0="TH_Is_a_F ile name example 20221013.c"; filename*1="sv"';

$this->assertEquals($expected, $mock->rfc822_parse_headers($mockHeader));
}

public function testExtractHeaderExtensions()
{
$mock = $this->getMockBuilder(Header::class)
->disableOriginalConstructor()
->onlyMethods([])
->getMock();

$method = new \ReflectionMethod($mock, 'extractHeaderExtensions');
$method->setAccessible(true);

$mockAttributes = [
'content_type' => 'text/csv; charset=WINDOWS-1252; name*0="TH_Is_a_F ile name example 20221013.c"; name*1=sv',
'content_transfer_encoding' => 'quoted-printable',
'content_disposition' => 'attachment; filename*0="TH_Is_a_F ile name example 20221013.c"; filename*1="sv"; attribute_test=attribute_test_value',
];

$attributes = new \ReflectionProperty($mock, 'attributes');
$attributes->setValue($mock, $mockAttributes);

$method->invoke($mock);

$this->assertArrayHasKey('filename', $mock->getAttributes());
$this->assertArrayNotHasKey('filename*0', $mock->getAttributes());
$this->assertEquals('TH_Is_a_F ile name example 20221013.csv', $mock->get('filename'));

$this->assertArrayHasKey('name', $mock->getAttributes());
$this->assertArrayNotHasKey('name*0', $mock->getAttributes());
$this->assertEquals('TH_Is_a_F ile name example 20221013.csv', $mock->get('name'));

$this->assertArrayHasKey('content_type', $mock->getAttributes());
$this->assertEquals('text/csv', $mock->get('content_type'));

$this->assertArrayHasKey('charset', $mock->getAttributes());
$this->assertEquals('WINDOWS-1252', $mock->get('charset'));

$this->assertArrayHasKey('content_transfer_encoding', $mock->getAttributes());
$this->assertEquals('quoted-printable', $mock->get('content_transfer_encoding'));

$this->assertArrayHasKey('content_disposition', $mock->getAttributes());
$this->assertEquals('attachment', $mock->get('content_disposition'));
$this->assertEquals('quoted-printable', $mock->get('content_transfer_encoding'));

$this->assertArrayHasKey('attribute_test', $mock->getAttributes());
$this->assertEquals('attribute_test_value', $mock->get('attribute_test'));
}
}