Skip to content

Commit

Permalink
Merge pull request #128 from edgardmessias/patch-1
Browse files Browse the repository at this point in the history
Fixed multibyte string encoding
  • Loading branch information
weierophinney authored Feb 12, 2021
2 parents 2cd4c5a + dd51ff2 commit cc5a038
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 17 deletions.
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"laminas/laminas-stdlib": "^2.7 || ^3.0",
"laminas/laminas-validator": "^2.10.2",
"laminas/laminas-zendframework-bridge": "^1.0",
"symfony/polyfill-mbstring": "^1.12.0",
"true/punycode": "^2.1"
},
"require-dev": {
Expand Down
36 changes: 22 additions & 14 deletions src/Header/ContentDisposition.php
Original file line number Diff line number Diff line change
Expand Up @@ -154,26 +154,34 @@ public function getFieldValue($format = HeaderInterface::FORMAT_RAW)
}
} 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);
$fullLength = mb_strlen($value, 'UTF-8');
while ($fullLength > 0) {
$attributePart = $attribute . '*' . $i++ . '="';
$attLen = mb_strlen($attributePart, 'UTF-8');

$subPos = 1;
$valuePart = '';
while ($subPos <= $fullLength) {
$sub = mb_substr($value, 0, $subPos, 'UTF-8');
if ($valueIsEncoded) {
$sub = $this->getEncodedValue($sub);
}
if ($attLen + mb_strlen($sub, 'UTF-8') >= self::MAX_PARAMETER_LENGTH) {
$subPos--;
break;
}
$subPos++;
$valuePart = $sub;
}
$result .= sprintf(';%s%s="%s"', Headers::FOLDING, $attributePart, $valuePart);

$value = mb_substr($value, $subPos, null, 'UTF-8');
$fullLength = mb_strlen($value, 'UTF-8');
$result .= ';' . Headers::FOLDING . $attributePart . $valuePart . '"';
}
}
}
Expand Down
26 changes: 23 additions & 3 deletions test/Header/ContentDispositionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,20 @@ public function setDispositionProvider(): array
// @codingStandardsIgnoreStart
$foldingFieldValue = "attachment;\r\n filename=\"this-test-filename-is-long-enough-to-flow-to-two-lines.txt\"";
$foldingHeaderLine = "Content-Disposition: $foldingFieldValue";
$continuationFieldValue = "attachment;\r\n filename*0=\"this-file-name-is-so-long-that-it-does-not-even\";\r\n filename*1=\"-fit-on-a-whole-line-by-itself-so-we-need-to-sp\";\r\n filename*2=\"lit-it-with-value-continuation.txt\"";
$continuationFieldValue = "attachment;\r\n filename*0=\"this-file-name-is-so-long-that-it-does-not-even-fit-on-a-whole-\";\r\n filename*1=\"line-by-itself-so-we-need-to-split-it-with-value-continuation.t\";\r\n filename*2=\"xt\"";
$continuationHeaderLine = "Content-Disposition: $continuationFieldValue";

$encodedHeaderLine = 'Content-Disposition: attachment; filename="=?UTF-8?Q?=C3=93?="';
$encodedFieldValue = 'attachment; filename="Ó"';

$multibyteFilename = '办公.xlsx';
$multibyteFieldValue = "attachment;\r\n filename=\"=?UTF-8?Q?=E5=8A=9E=E5=85=AC.xlsx?=\"";
$multibyteHeaderLine = "Content-Disposition: $multibyteFieldValue";

$multibyteContinuationFilename = '办公用品预约Apply for office supplies online.xlsx';
$multibyteContinuationFieldValue = "attachment;\r\n filename*0=\"=?UTF-8?Q?=E5=8A=9E=E5=85=AC=E7=94=A8=E5=93=81=E9=A2=84?=\";\r\n filename*1=\"=?UTF-8?Q?=E7=BA=A6Apply=20for=20office=20supplies=20online.x?=\";\r\n filename*2=\"=?UTF-8?Q?lsx?=\"";
$multibyteContinuationHeaderLine = "Content-Disposition: $multibyteContinuationFieldValue";

return [
// Description => [$disposition, $parameters, $fieldValue, toString()]
'inline with no parameters' => ['inline', [], 'inline', 'Content-Disposition: inline'],
Expand All @@ -218,8 +226,20 @@ public function setDispositionProvider(): array
'UTF-8 continuation' => [
'attachment',
['filename' => 'this-file-name-is-so-long-that-it-does-not-even-fit-on-a-whole-line-by-itself-so-we-need-to-split-it-with-value-continuation.also-UTF-8-characters-hērē.txt'],
"attachment;\r\n filename*0=\"this-file-name-is-so-long-that-it-does-not-even-fit-on-a-\";\r\n filename*1=\"whole-line-by-itself-so-we-need-to-split-it-with-value-co\";\r\n filename*2=\"ntinuation.also-UTF-8-characters-hērē.txt\"",
"Content-Disposition: attachment;\r\n filename*0=\"=?UTF-8?Q?this-file-name-is-so-long-that-it-does-not-ev?=\";\r\n filename*1=\"=?UTF-8?Q?en-fit-on-a-whole-line-by-itself-so-we-need-t?=\";\r\n filename*2=\"=?UTF-8?Q?o-split-it-with-value-continuation.also-UTF-8?=\";\r\n filename*3=\"=?UTF-8?Q?-characters-h=C4=93r=C4=93.txt?=\"",
"attachment;\r\n filename*0=\"this-file-name-is-so-long-that-it-does-not-even-fit-on-a-whole-\";\r\n filename*1=\"line-by-itself-so-we-need-to-split-it-with-value-continuation.a\";\r\n filename*2=\"lso-UTF-8-characters-hērē.txt\"",
"Content-Disposition: attachment;\r\n filename*0=\"=?UTF-8?Q?this-file-name-is-so-long-that-it-does-not-even-fit?=\";\r\n filename*1=\"=?UTF-8?Q?-on-a-whole-line-by-itself-so-we-need-to-split-it-w?=\";\r\n filename*2=\"=?UTF-8?Q?ith-value-continuation.also-UTF-8-characters-h?=\";\r\n filename*3=\"=?UTF-8?Q?=C4=93r=C4=93.txt?=\"",
],
'UTF-8 multibyte' => [
'attachment',
['filename' => $multibyteFilename],
"attachment; filename=\"$multibyteFilename\"",
$multibyteHeaderLine,
],
'UTF-8 multibyte continuation' => [
'attachment',
['filename' => $multibyteContinuationFilename],
"attachment;\r\n filename=\"$multibyteContinuationFilename\"",
$multibyteContinuationHeaderLine,
],
];
// @codingStandardsIgnoreEnd
Expand Down

0 comments on commit cc5a038

Please sign in to comment.