Skip to content

Commit

Permalink
[HttpFoundation] implemented RFC6266 (Content-Disposition header)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabpot committed Sep 4, 2011
1 parent e7b2d2d commit dccd2d5
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG-2.1.md
Expand Up @@ -22,6 +22,7 @@ To get the diff between two versions, go to https://github.com/symfony/symfony/c

* added support for the PATCH method in Request
* removed the ContentTypeMimeTypeGuesser class as it is deprecated and never used on PHP 5.3
* added ResponseHeaderBag::makeDisposition() (implements RFC 6266)

### Translation

Expand Down
44 changes: 44 additions & 0 deletions src/Symfony/Component/HttpFoundation/ResponseHeaderBag.php
Expand Up @@ -200,6 +200,50 @@ public function clearCookie($name, $path = null, $domain = null)
$this->setCookie(new Cookie($name, null, 1, $path, $domain));
}

/**
* Generates a HTTP Content-Disposition field-value.
*
* @param string $disposition One of "inline" or "attachment"
* @param string $filename A unicode string
* @param string $filenameFallback A string containing only ASCII characters that
* is semantically equivalent to $filename. If the filename is already ASCII,
* it can be omitted, or just copied from $filename
*
* @return string A string suitable for use as a Content-Disposition field-value.
*
* @throws \InvalidArgumentException
* @see RFC 6266
*/
public function makeDisposition($disposition, $filename, $filenameFallback = '')
{
if (!$filenameFallback) {
$filenameFallback = $filename;
}

// filenameFallback is not ASCII.
if (!preg_match('/^[\x20-\x7e]*$/', $filenameFallback)) {
throw new \InvalidArgumentException('The filename fallback must only contain ASCII characters.');
}

// percent characters aren't safe in fallback.
if (false !== strpos($filenameFallback, '%')) {
throw new \InvalidArgumentException('The filename fallback cannot contain the "%" character.');
}

// path separators aren't allowed in either.

This comment has been minimized.

Copy link
@Matzz

Matzz Sep 8, 2011

preg_match('#[/\]#', $filename.$filenameFallback) is little bit faster

if (preg_match('#[/\\\\]#', $filename) || preg_match('#[/\\\\]#', $filenameFallback)) {
throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
}

This comment has been minimized.

Copy link
@Matzz

Matzz Sep 8, 2011

Replacing '' is unnecessary because \ char is validated in line 234

$output = sprintf('%s; filename="%s"', $disposition, str_replace(array('\\', '"'), array('\\\\', '\\"'), $filenameFallback));

if ($filename != $filenameFallback) {

This comment has been minimized.

Copy link
@Matzz

Matzz Sep 8, 2011

urlencode escapes "'", '(', ')', '*' so str_replace is redundant

$output .= sprintf("; filename*=utf-8''%s", str_replace(array("'", '(', ')', '*'), array('%27', '%28', '%29', '%2A'), urlencode($filename)));
}

return $output;
}

/**
* Returns the calculated value of the cache-control header.
*
Expand Down
Expand Up @@ -118,4 +118,49 @@ public function testRemoveCookie()
$cookies = $bag->getCookies(ResponseHeaderBag::COOKIES_ARRAY);
$this->assertFalse(isset($cookies['foo.bar']));
}

/**
* @dataProvider provideMakeDisposition
*/
public function testMakeDisposition($disposition, $filename, $filenameFallback, $expected)
{
$headers = new ResponseHeaderBag();

$this->assertEquals($expected, $headers->makeDisposition($disposition, $filename, $filenameFallback));
}

public function provideMakeDisposition()
{
return array(
array('attachment', 'foo.html', 'foo.html', 'attachment; filename="foo.html"'),
array('attachment', 'foo.html', '', 'attachment; filename="foo.html"'),
array('attachment', 'foo bar.html', '', 'attachment; filename="foo bar.html"'),
array('attachment', 'foo "bar".html', '', 'attachment; filename="foo \\"bar\\".html"'),
array('attachment', 'foo%20bar.html', 'foo bar.html', 'attachment; filename="foo bar.html"; filename*=utf-8\'\'foo%2520bar.html'),
array('attachment', 'föö.html', 'foo.html', 'attachment; filename="foo.html"; filename*=utf-8\'\'f%C3%B6%C3%B6.html'),
);
}

/**
* @dataProvider provideMakeDispositionFail
* @expectedException \InvalidArgumentException
*/
public function testMakeDispositionFail($disposition, $filename)
{
$headers = new ResponseHeaderBag();

$headers->makeDisposition($disposition, $filename);
}

public function provideMakeDispositionFail()
{
return array(
array('attachment', 'foo%20bar.html'),
array('attachment', 'foo/bar.html'),
array('attachment', '/foo.html'),
array('attachment', 'foo\bar.html'),
array('attachment', '\foo.html'),
array('attachment', 'föö.html'),
);
}
}

3 comments on commit dccd2d5

@Seldaek
Copy link
Member

@Seldaek Seldaek commented on dccd2d5 Sep 4, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cool, didn't know about filename*=utf-8.

@jonathaningram
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Should makeDisposition also check for invalid arguments for "disposition" - i.e. if not exactly "attachment" or "inline" throw invalid argument exception.
  2. Could two constants DISPOSITION_ATTACHMENT and DISPOSITION_INLINE be added instead of relying on the strings? Helps with autocomplete, and also ensures you don't accidentally type "Inline" or something.

Happy to make issues if you like.

@jalliot
Copy link
Contributor

@jalliot jalliot commented on dccd2d5 Sep 8, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jonathaningram You're right, see #2132

Please sign in to comment.