Skip to content
This repository has been archived by the owner on Jan 30, 2020. It is now read-only.

Commit

Permalink
Merge branch 'hotfix/26' into develop
Browse files Browse the repository at this point in the history
Forward port #26
  • Loading branch information
weierophinney committed Sep 9, 2015
2 parents 5c22013 + 3343664 commit b095152
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 95 deletions.
28 changes: 22 additions & 6 deletions src/Header/ContentType.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@
namespace Zend\Mail\Header;

use Zend\Mail\Headers;
use Zend\Mime\Mime;

class ContentType implements HeaderInterface
class ContentType implements UnstructuredInterface
{
/**
* @var string
*/
protected $type;

/**
* Header encoding
*
* @var string
*/
protected $encoding = 'ASCII';

/**
* @var array
*/
Expand Down Expand Up @@ -66,6 +74,12 @@ public function getFieldValue($format = HeaderInterface::FORMAT_RAW)

$values = [$prepared];
foreach ($this->parameters as $attribute => $value) {
if (HeaderInterface::FORMAT_ENCODED === $format && !Mime::isPrintable($value)) {
$this->encoding = 'UTF-8';
$value = HeaderWrap::wrap($value, $this);
$this->encoding = 'ASCII';
}

$values[] = sprintf('%s="%s"', $attribute, $value);
}

Expand All @@ -74,18 +88,18 @@ public function getFieldValue($format = HeaderInterface::FORMAT_RAW)

public function setEncoding($encoding)
{
// This header must be always in US-ASCII
$this->encoding = $encoding;
return $this;
}

public function getEncoding()
{
return 'ASCII';
return $this->encoding;
}

public function toString()
{
return 'Content-Type: ' . $this->getFieldValue();
return 'Content-Type: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
}

/**
Expand Down Expand Up @@ -135,8 +149,10 @@ public function addParameter($name, $value)
if (! HeaderValue::isValid($name)) {
throw new Exception\InvalidArgumentException('Invalid content-type parameter name detected');
}
if (! HeaderValue::isValid($value)) {
throw new Exception\InvalidArgumentException('Invalid content-type parameter value detected');
if (! HeaderWrap::canBeEncoded($value)) {
throw new Exception\InvalidArgumentException(
'Parameter value must be composed of printable US-ASCII or UTF-8 characters.'
);
}

$this->parameters[$name] = $value;
Expand Down
177 changes: 88 additions & 89 deletions test/Header/ContentTypeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,37 +10,21 @@
namespace ZendTest\Mail\Header;

use Zend\Mail\Header\ContentType;
use Zend\Mail\Header\Exception\InvalidArgumentException;
use Zend\Mail\Header\HeaderInterface;
use Zend\Mail\Header\UnstructuredInterface;

/**
* @group Zend_Mail
*/
class ContentTypeTest extends \PHPUnit_Framework_TestCase
{
public function testContentTypeFromStringCreatesValidContentTypeHeader()
public function testImplementsHeaderInterface()
{
$contentTypeHeader = ContentType::fromString('Content-Type: xxx/yyy');
$this->assertInstanceOf('Zend\Mail\Header\HeaderInterface', $contentTypeHeader);
$this->assertInstanceOf('Zend\Mail\Header\ContentType', $contentTypeHeader);
}

public function testContentTypeGetFieldNameReturnsHeaderName()
{
$contentTypeHeader = new ContentType();
$this->assertEquals('Content-Type', $contentTypeHeader->getFieldName());
}
$header = new ContentType();

public function testContentTypeGetFieldValueReturnsProperValue()
{
$contentTypeHeader = new ContentType();
$contentTypeHeader->setType('foo/bar');
$this->assertEquals('foo/bar', $contentTypeHeader->getFieldValue());
}

public function testContentTypeToStringReturnsHeaderFormattedString()
{
$contentTypeHeader = new ContentType();
$contentTypeHeader->setType('foo/bar');
$this->assertEquals("Content-Type: foo/bar", $contentTypeHeader->toString());
$this->assertInstanceOf(UnstructuredInterface::class, $header);
$this->assertInstanceOf(HeaderInterface::class, $header);
}

/**
Expand All @@ -55,80 +39,53 @@ public function testTrailingSemiColonFromString()
$this->assertEquals(['boundary' => 'Apple-Mail=_1B852F10-F9C6-463D-AADD-CD503A5428DD'], $params);
}

public function testProvidingParametersIntroducesHeaderFolding()
{
$header = new ContentType();
$header->setType('application/x-unit-test');
$header->addParameter('charset', 'us-ascii');
$string = $header->toString();

$this->assertContains("Content-Type: application/x-unit-test;", $string);
$this->assertContains(";\r\n charset=\"us-ascii\"", $string);
}

public function testExtractsExtraInformationFromContentType()
{
$contentTypeHeader = ContentType::fromString(
'Content-Type: multipart/alternative; boundary="Apple-Mail=_1B852F10-F9C6-463D-AADD-CD503A5428DD"'
);
$params = $contentTypeHeader->getParameters();
$this->assertEquals($params, ['boundary' => 'Apple-Mail=_1B852F10-F9C6-463D-AADD-CD503A5428DD']);
}

public function testExtractsExtraInformationWithoutBeingConfusedByTrailingSemicolon()
{
$header = ContentType::fromString('Content-Type: application/pdf;name="foo.pdf";');
$this->assertEquals($header->getParameters(), ['name' => 'foo.pdf']);
}

/**
* @group #2728
*
* Tests setting different MIME types
* @dataProvider setTypeProvider
*/
public function testSetContentType()
public function testFromString($type, $parameters, $fieldValue, $expectedToString)
{
$header = new ContentType();

$header->setType('application/vnd.ms-excel');
$this->assertEquals('Content-Type: application/vnd.ms-excel', $header->toString());

$header->setType('application/rss+xml');
$this->assertEquals('Content-Type: application/rss+xml', $header->toString());

$header->setType('video/mp4');
$this->assertEquals('Content-Type: video/mp4', $header->toString());

$header->setType('message/rfc822');
$this->assertEquals('Content-Type: message/rfc822', $header->toString());
$header = ContentType::fromString($expectedToString);

$this->assertInstanceOf(ContentType::class, $header);
$this->assertEquals('Content-Type', $header->getFieldName(), 'getFieldName() value not match');
$this->assertEquals($type, $header->getType(), 'getType() value not match');
$this->assertEquals($fieldValue, $header->getFieldValue(), 'getFieldValue() value not match');
$this->assertEquals($parameters, $header->getParameters(), 'getParameters() value not match');
$this->assertEquals($expectedToString, $header->toString(), 'toString() value not match');
}

/**
* @group ZF2015-04
* @dataProvider setTypeProvider
*/
public function testFromStringRaisesExceptionForInvalidName()
public function testSetType($type, $parameters, $fieldValue, $expectedToString)
{
$this->setExpectedException('Zend\Mail\Header\Exception\InvalidArgumentException', 'header name');
$header = ContentType::fromString('Content-Type' . chr(32) . ': text/html');
}
$header = new ContentType();

public function headerLines()
{
return [
'newline' => ["Content-Type: text/html;\nlevel=1"],
'cr-lf' => ["Content-Type: text/html\r\n;level=1",],
'multiline' => ["Content-Type: text/html;\r\nlevel=1\r\nq=0.1"],
];
$header->setType($type);
foreach ($parameters as $name => $value) {
$header->addParameter($name, $value);
}

$this->assertEquals('Content-Type', $header->getFieldName(), 'getFieldName() value not match');
$this->assertEquals($type, $header->getType(), 'getType() value not match');
$this->assertEquals($fieldValue, $header->getFieldValue(), 'getFieldValue() value not match');
$this->assertEquals($parameters, $header->getParameters(), 'getParameters() value not match');
$this->assertEquals($expectedToString, $header->toString(), 'toString() value not match');
}

/**
* @dataProvider headerLines
* @group ZF2015-04
* @dataProvider invalidHeaderLinesProvider
*/
public function testFromStringRaisesExceptionForNonFoldingMultilineValues($headerLine)
public function testFromStringThrowException($headerLine, $expectedException, $exceptionMessage)
{
$this->setExpectedException('Zend\Mail\Header\Exception\InvalidArgumentException', 'header value');
$header = ContentType::fromString($headerLine);
$this->setExpectedException($expectedException, $exceptionMessage);
ContentType::fromString($headerLine);
}

/**
Expand All @@ -142,24 +99,66 @@ public function testFromStringHandlesContinuations()
}

/**
* @group ZF2015-04
* @dataProvider invalidParametersProvider
*/
public function testAddParameterRaisesInvalidArgumentExceptionForInvalidParameterName()
public function testAddParameterThrowException($paramName, $paramValue, $expectedException, $exceptionMessage)
{
$header = new ContentType();
$header->setType('text/html');
$this->setExpectedException('Zend\Mail\Header\Exception\InvalidArgumentException', 'parameter name');
$header->addParameter("b\r\na\rr\n", "baz");

$this->setExpectedException($expectedException, $exceptionMessage);
$header->addParameter($paramName, $paramValue);
}

/**
* @group ZF2015-04
*/
public function testAddParameterRaisesInvalidArgumentExceptionForInvalidParameterValue()
public function setTypeProvider()
{
$header = new ContentType();
$header->setType('text/html');
$this->setExpectedException('Zend\Mail\Header\Exception\InvalidArgumentException', 'parameter value');
$header->addParameter('foo', "\nbar\r\nbaz\r");
$foldingHeaderLine = "Content-Type: foo/baz;\r\n charset=\"us-ascii\"";
$foldingFieldValue = "foo/baz;\r\n charset=\"us-ascii\"";

$encodedHeaderLine = "Content-Type: foo/baz;\r\n name=\"=?UTF-8?Q?=C3=93?=\"";
$encodedFieldValue = "foo/baz;\r\n name=\"Ó\"";

// @codingStandardsIgnoreStart
return [
// Description => [$type, $parameters, $fieldValue, toString()]
// @group #2728
'foo/a.b-c' => ['foo/a.b-c', [], 'foo/a.b-c', 'Content-Type: foo/a.b-c'],
'foo/a+b' => ['foo/a+b' , [], 'foo/a+b' , 'Content-Type: foo/a+b'],
'foo/baz' => ['foo/baz' , [], 'foo/baz' , 'Content-Type: foo/baz'],
'parameter use header folding' => ['foo/baz' , ['charset' => 'us-ascii'], $foldingFieldValue, $foldingHeaderLine],
'encoded characters' => ['foo/baz' , ['name' => 'Ó'], $encodedFieldValue, $encodedHeaderLine],
];
// @codingStandardsIgnoreEnd
}

public function invalidParametersProvider()
{
$invalidArgumentException = InvalidArgumentException::class;

// @codingStandardsIgnoreStart
return [
// Description => [param name, param value, expected exception, exception message contain]

// @group ZF2015-04
'invalid name' => ["b\r\na\rr\n", 'baz', $invalidArgumentException, 'parameter name'],
];
// @codingStandardsIgnoreEnd
}

public function invalidHeaderLinesProvider()
{
$invalidArgumentException = InvalidArgumentException::class;

// @codingStandardsIgnoreStart
return [
// Description => [header line, expected exception, exception message contain]

// @group ZF2015-04
'invalid name' => ['Content-Type' . chr(32) . ': text/html', $invalidArgumentException, 'header name'],
'newline' => ["Content-Type: text/html;\nlevel=1", $invalidArgumentException, 'header value'],
'cr-lf' => ["Content-Type: text/html\r\n;level=1", $invalidArgumentException, 'header value'],
'multiline' => ["Content-Type: text/html;\r\nlevel=1\r\nq=0.1", $invalidArgumentException, 'header value'],
];
// @codingStandardsIgnoreEnd
}
}

0 comments on commit b095152

Please sign in to comment.