mirror of
https://github.com/OpenXE-org/OpenXE.git
synced 2025-02-15 06:00:10 +01:00
315 lines
8.9 KiB
PHP
315 lines
8.9 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @see https://github.com/laminas/laminas-mail for the canonical source repository
|
|
* @copyright https://github.com/laminas/laminas-mail/blob/master/COPYRIGHT.md
|
|
* @license https://github.com/laminas/laminas-mail/blob/master/LICENSE.md New BSD License
|
|
*/
|
|
|
|
namespace Laminas\Mail\Header;
|
|
|
|
use Laminas\Mail\Headers;
|
|
use Laminas\Mime\Mime;
|
|
|
|
class ContentDisposition implements UnstructuredInterface
|
|
{
|
|
/**
|
|
* 78 chars (RFC 2822) - (semicolon + space (Header::FOLDING))
|
|
*
|
|
* @var int
|
|
*/
|
|
// const MAX_PARAMETER_LENGTH = 76; // This is the RECOMMENDATION
|
|
const MAX_PARAMETER_LENGTH = 996; // This is the LIMIT
|
|
|
|
/**
|
|
* @var string
|
|
*/
|
|
protected $disposition = 'inline';
|
|
|
|
/**
|
|
* Header encoding
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $encoding = 'ASCII';
|
|
|
|
/**
|
|
* @var array
|
|
*/
|
|
protected $parameters = [];
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public static function fromString($headerLine)
|
|
{
|
|
list($name, $value) = GenericHeader::splitHeaderLine($headerLine);
|
|
$value = HeaderWrap::mimeDecodeValue($value);
|
|
|
|
// check to ensure proper header type for this factory
|
|
if (strtolower($name) !== 'content-disposition') {
|
|
throw new Exception\InvalidArgumentException('Invalid header line for Content-Disposition string');
|
|
}
|
|
|
|
$value = str_replace(Headers::FOLDING, ' ', $value);
|
|
$parts = explode(';', $value, 2);
|
|
|
|
$header = new static();
|
|
$header->setDisposition($parts[0]);
|
|
|
|
if (isset($parts[1])) {
|
|
$values = ListParser::parse(trim($parts[1]), [';', '=']);
|
|
$length = count($values);
|
|
$continuedValues = [];
|
|
|
|
for ($i = 0; $i < $length; $i += 2) {
|
|
$value = $values[$i + 1];
|
|
$value = trim($value, "'\" \t\n\r\0\x0B");
|
|
$name = trim($values[$i], "'\" \t\n\r\0\x0B");
|
|
|
|
if (strpos($name, '*')) {
|
|
list($name, $count) = explode('*', $name);
|
|
// allow optional count:
|
|
// Content-Disposition: attachment; filename*=UTF-8''%64%61%61%6D%69%2D%6D%C3%B5%72%76%2E%6A%70%67
|
|
if ($count === "") {
|
|
$count = 0;
|
|
}
|
|
|
|
if (! is_numeric($count)) {
|
|
$type = gettype($count);
|
|
$value = var_export($count, 1);
|
|
throw new Exception\InvalidArgumentException(sprintf(
|
|
"Invalid header line for Content-Disposition string".
|
|
" - count expected to be numeric, got %s with value %s",
|
|
$type,
|
|
$value
|
|
));
|
|
}
|
|
if (! isset($continuedValues[$name])) {
|
|
$continuedValues[$name] = [];
|
|
}
|
|
$continuedValues[$name][$count] = $value;
|
|
} else {
|
|
$header->setParameter($name, $value);
|
|
}
|
|
}
|
|
|
|
foreach ($continuedValues as $name => $values) {
|
|
$value = '';
|
|
for ($i = 0; $i < count($values); $i++) {
|
|
if (! isset($values[$i])) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Invalid header line for Content-Disposition string - incomplete continuation'.
|
|
'; HeaderLine: '.$headerLine
|
|
);
|
|
}
|
|
$value .= $values[$i];
|
|
}
|
|
$header->setParameter($name, $value);
|
|
}
|
|
}
|
|
|
|
return $header;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getFieldName()
|
|
{
|
|
return 'Content-Disposition';
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
|
|
{
|
|
$result = $this->disposition;
|
|
if (empty($this->parameters)) {
|
|
return $result;
|
|
}
|
|
|
|
foreach ($this->parameters as $attribute => $value) {
|
|
$valueIsEncoded = false;
|
|
if (HeaderInterface::FORMAT_ENCODED === $format && ! Mime::isPrintable($value)) {
|
|
$value = $this->getEncodedValue($value);
|
|
$valueIsEncoded = true;
|
|
}
|
|
|
|
$line = sprintf('%s="%s"', $attribute, $value);
|
|
|
|
if (strlen($line) < self::MAX_PARAMETER_LENGTH) {
|
|
$lines = explode(Headers::FOLDING, $result);
|
|
|
|
if (count($lines) === 1) {
|
|
$existingLineLength = strlen('Content-Disposition: ' . $result);
|
|
} else {
|
|
$existingLineLength = 1 + strlen($lines[count($lines) - 1]);
|
|
}
|
|
|
|
if ((2 + $existingLineLength + strlen($line)) <= self::MAX_PARAMETER_LENGTH) {
|
|
$result .= '; ' . $line;
|
|
} else {
|
|
$result .= ';' . Headers::FOLDING . $line;
|
|
}
|
|
} else {
|
|
// Use 'continuation' per RFC 2231
|
|
$maxValueLength = strlen($value);
|
|
do {
|
|
$maxValueLength = ceil(0.6 * $maxValueLength);
|
|
} while ($maxValueLength > self::MAX_PARAMETER_LENGTH);
|
|
|
|
if ($valueIsEncoded) {
|
|
$encodedLength = strlen($value);
|
|
$value = HeaderWrap::mimeDecodeValue($value);
|
|
$decodedLength = strlen($value);
|
|
$maxValueLength -= ($encodedLength - $decodedLength);
|
|
}
|
|
|
|
$valueParts = str_split($value, $maxValueLength);
|
|
$i = 0;
|
|
foreach ($valueParts as $valuePart) {
|
|
$attributePart = $attribute . '*' . $i++;
|
|
if ($valueIsEncoded) {
|
|
$valuePart = $this->getEncodedValue($valuePart);
|
|
}
|
|
$result .= sprintf(';%s%s="%s"', Headers::FOLDING, $attributePart, $valuePart);
|
|
}
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @param string $value
|
|
* @return string
|
|
*/
|
|
protected function getEncodedValue($value)
|
|
{
|
|
$configuredEncoding = $this->encoding;
|
|
$this->encoding = 'UTF-8';
|
|
$value = HeaderWrap::wrap($value, $this);
|
|
$this->encoding = $configuredEncoding;
|
|
return $value;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function setEncoding($encoding)
|
|
{
|
|
$this->encoding = $encoding;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function getEncoding()
|
|
{
|
|
return $this->encoding;
|
|
}
|
|
|
|
/**
|
|
* @inheritDoc
|
|
*/
|
|
public function toString()
|
|
{
|
|
return 'Content-Disposition: ' . $this->getFieldValue(HeaderInterface::FORMAT_ENCODED);
|
|
}
|
|
|
|
/**
|
|
* Set the content disposition
|
|
* Expected values include 'inline', 'attachment'
|
|
*
|
|
* @param string $disposition
|
|
* @return ContentDisposition
|
|
*/
|
|
public function setDisposition($disposition)
|
|
{
|
|
$this->disposition = strtolower($disposition);
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Retrieve the content disposition
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getDisposition()
|
|
{
|
|
return $this->disposition;
|
|
}
|
|
|
|
/**
|
|
* Add a parameter pair
|
|
*
|
|
* @param string $name
|
|
* @param string $value
|
|
* @return ContentDisposition
|
|
*/
|
|
public function setParameter($name, $value)
|
|
{
|
|
$name = strtolower($name);
|
|
|
|
if (! HeaderValue::isValid($name)) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Invalid content-disposition parameter name detected'
|
|
);
|
|
}
|
|
// '5' here is for the quotes & equal sign in `name="value"`,
|
|
// and the space & semicolon for line folding
|
|
if ((strlen($name) + 5) >= self::MAX_PARAMETER_LENGTH) {
|
|
throw new Exception\InvalidArgumentException(
|
|
'Invalid content-disposition parameter name detected (too long)'
|
|
);
|
|
}
|
|
|
|
$this->parameters[$name] = $value;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get all parameters
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getParameters()
|
|
{
|
|
return $this->parameters;
|
|
}
|
|
|
|
/**
|
|
* Get a parameter by name
|
|
*
|
|
* @param string $name
|
|
* @return null|string
|
|
*/
|
|
public function getParameter($name)
|
|
{
|
|
$name = strtolower($name);
|
|
if (isset($this->parameters[$name])) {
|
|
return $this->parameters[$name];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Remove a named parameter
|
|
*
|
|
* @param string $name
|
|
* @return bool
|
|
*/
|
|
public function removeParameter($name)
|
|
{
|
|
$name = strtolower($name);
|
|
if (isset($this->parameters[$name])) {
|
|
unset($this->parameters[$name]);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|