Skip to content

Commit

Permalink
fixes address containing quotes on the folded line (#63)
Browse files Browse the repository at this point in the history
fixes address containing quotes on the folded line
  • Loading branch information
frederikbosch authored Dec 12, 2019
1 parent 4df12c6 commit 51bee44
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 41 deletions.
14 changes: 5 additions & 9 deletions .appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@ platform:
- x64
clone_folder: c:\projects\php-project-workspace

## Build matrix for lowest and highest possible targets
## Build matrix for single php version
environment:
matrix:
- dependencies: lowest
php_ver_target: 7.3.11
- dependencies: highest
php_ver_target: 7.3.11
- { PHP_VERSION: '7.3.11' }

## Cancel all if one fails
matrix:
Expand All @@ -32,8 +29,9 @@ init:

## Install PHP and composer, and run the appropriate composer command
install:
- ps: Set-Service wuauserv -StartupType Manual
- IF EXIST c:\tools\php (SET PHP=0)
- ps: appveyor-retry cinst --params '""/InstallDir:C:\tools\php""' --ignore-checksums -y php --version ((choco search php --exact --all-versions -r | select-string -pattern $env:php_ver_target | sort { [version]($_ -split '\|' | select -last 1) } -Descending | Select-Object -first 1) -replace '[php|]','')
- choco install -y php --version %PHP_VERSION% --params '""/InstallDir:C:\tools\php""'
- cd c:\tools\php
- IF %PHP%==1 copy php.ini-production php.ini /Y
- IF %PHP%==1 echo date.timezone="UTC" >> php.ini
Expand All @@ -45,9 +43,7 @@ install:
- IF %PHP%==1 echo @php %%~dp0composer.phar %%* > composer.bat
- appveyor-retry appveyor DownloadFile https://getcomposer.org/composer.phar
- cd c:\projects\php-project-workspace
- IF %dependencies%==lowest appveyor-retry composer update --prefer-lowest --no-progress --profile -n
- IF %dependencies%==current appveyor-retry composer install --no-progress --profile
- IF %dependencies%==highest appveyor-retry composer update --no-progress --profile -n
- composer update --no-progress --profile -n
- composer show

## Run the actual test
Expand Down
10 changes: 6 additions & 4 deletions src/Address.php
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@ public function __toString(): string
return (string)$this->address;
}

$encodedName = (string) OptimalEncodedHeaderValue::forPhrase($this->name);
if ($encodedName === $this->name) {
$encodedName = \addcslashes($encodedName, "\0..\37\177\\\"");
$encodedPhrase = OptimalEncodedHeaderValue::forPhrase($this->name);
if ($encodedPhrase->getEncoding() === '7bit' || $encodedPhrase->getEncoding() === '8bit') {
$encodedName = \addslashes((string)$encodedPhrase);

if ($encodedName !== $this->name || \preg_match('/[^A-Za-z0-9!#$%&\'*+\/=?^_`{|}~ -]/', $this->name) === 1) {
if (\preg_match('/[^A-Za-z0-9\s!#$%&\'*+\/=?^_`{|}~\-]/', $this->name) === 1) {
$encodedName = \sprintf('"%s"', $encodedName);
}
} else {
$encodedName = (string)$encodedPhrase;
}

return \sprintf('%s <%s>', $encodedName, $this->address->getPunyCode());
Expand Down
65 changes: 38 additions & 27 deletions src/Header/OptimalEncodedHeaderValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,45 +13,59 @@ final class OptimalEncodedHeaderValue
/**
* @var string
*/
private $value;
private $encoded;

/**
* @var bool
* @var string
*/
private $phrase = false;
private $encoding = '7bit';

/**
* @param string $value
* @param bool $phrase
*/
public function __construct(string $value)
{
$this->value = $value;
}

/**
* @return string
*/
public function __toString(): string
public function __construct(string $value, bool $phrase = false)
{
if ($this->phrase === true) {
$encoded = new OptimalTransferEncodedPhraseStream($this->value, 68, self::FOLDING);
if ($phrase === true) {
$encoded = new OptimalTransferEncodedPhraseStream($value, 68, self::FOLDING);

$encoding = $encoded->getMetadata(['transfer-encoding'])['transfer-encoding'];
$this->encoding = $encoded->getMetadata(['transfer-encoding'])['transfer-encoding'];
} else {
$encoded = new OptimalTransferEncodedTextStream($this->value, 68, self::FOLDING);
$encoded = new OptimalTransferEncodedTextStream($value, 68, self::FOLDING);

$encoding = $encoded->getMetadata(['transfer-encoding'])['transfer-encoding'];
$this->encoding = $encoded->getMetadata(['transfer-encoding'])['transfer-encoding'];
}

if ($encoding === '7bit' || $encoding === '8bit') {
return (string) $encoded;
switch ($this->encoding) {
case '7bit':
case '8bit':
$this->encoded = (string) $encoded;
break;
case 'base64':
$this->encoded = \sprintf('=?%s?B?%s?=', 'UTF-8', (string) $encoded);
break;
case 'quoted-printable':
$this->encoded = \sprintf('=?%s?Q?%s?=', 'UTF-8', (string) $encoded);
break;
default:
throw new \UnexpectedValueException('Unknown encoding ' . $this->encoding);
}
}

if ($encoding === 'base64') {
return \sprintf('=?%s?B?%s?=', 'UTF-8', (string) $encoded);
}
/**
* @return string
*/
public function __toString(): string
{
return $this->encoded;
}

return \sprintf('=?%s?Q?%s?=', 'UTF-8', (string) $encoded);
/**
* @return string
*/
public function getEncoding(): string
{
return $this->encoding;
}

/**
Expand All @@ -60,9 +74,6 @@ public function __toString(): string
*/
public static function forPhrase(string $value): self
{
$encoded = new self($value);
$encoded->value = $value;
$encoded->phrase = true;
return $encoded;
return new self($value, true);
}
}
17 changes: 16 additions & 1 deletion test/Unit/AddressTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ public function it_parses_address_strings(string $addressString, bool $construct

/**
* @test
* @dataProvider provideAddressStrings
*/
public function it_can_be_converted_to_readable_string()
{
Expand All @@ -131,6 +130,22 @@ public function it_can_be_constructed_with_a_multi_byte_name_leading_to_new_line
$this->assertEquals('=?UTF-8?B?0JzQuNGF0LA=?= <[email protected]>', (string)$address);
}

/**
* @test
*/
public function it_quotes_folded_addresses_with_quotes_near_the_end()
{
$address = new Address(
new EmailAddress('[email protected]'),
'Long 7bit name with only normal characters but with quoted characters near the "end"'
);

$this->assertEquals(
"\"Long 7bit name with only normal characters but with quoted\r\n characters near the \\\"end\\\"\" <[email protected]>",
(string)$address
);
}

/**
* @return array
*/
Expand Down

0 comments on commit 51bee44

Please sign in to comment.