Subversion-Projekte lars-tiefland.laravel_shop

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
148 lars 1
<?php
2
 
3
/*
4
 * This file is part of the Symfony package.
5
 *
6
 * (c) Fabien Potencier <fabien@symfony.com>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
 
12
namespace Symfony\Component\Mime\Header;
13
 
14
use Symfony\Component\Mime\Encoder\Rfc2231Encoder;
15
 
16
/**
17
 * @author Chris Corbyn
18
 */
19
final class ParameterizedHeader extends UnstructuredHeader
20
{
21
    /**
22
     * RFC 2231's definition of a token.
23
     *
24
     * @var string
25
     */
26
    public const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';
27
 
28
    private ?Rfc2231Encoder $encoder = null;
29
    private array $parameters = [];
30
 
31
    public function __construct(string $name, string $value, array $parameters = [])
32
    {
33
        parent::__construct($name, $value);
34
 
35
        foreach ($parameters as $k => $v) {
36
            $this->setParameter($k, $v);
37
        }
38
 
39
        if ('content-type' !== strtolower($name)) {
40
            $this->encoder = new Rfc2231Encoder();
41
        }
42
    }
43
 
44
    public function setParameter(string $parameter, ?string $value)
45
    {
46
        $this->setParameters(array_merge($this->getParameters(), [$parameter => $value]));
47
    }
48
 
49
    public function getParameter(string $parameter): string
50
    {
51
        return $this->getParameters()[$parameter] ?? '';
52
    }
53
 
54
    /**
55
     * @param string[] $parameters
56
     */
57
    public function setParameters(array $parameters)
58
    {
59
        $this->parameters = $parameters;
60
    }
61
 
62
    /**
63
     * @return string[]
64
     */
65
    public function getParameters(): array
66
    {
67
        return $this->parameters;
68
    }
69
 
70
    public function getBodyAsString(): string
71
    {
72
        $body = parent::getBodyAsString();
73
        foreach ($this->parameters as $name => $value) {
74
            if (null !== $value) {
75
                $body .= '; '.$this->createParameter($name, $value);
76
            }
77
        }
78
 
79
        return $body;
80
    }
81
 
82
    /**
83
     * Generate a list of all tokens in the final header.
84
     *
85
     * This doesn't need to be overridden in theory, but it is for implementation
86
     * reasons to prevent potential breakage of attributes.
87
     */
88
    protected function toTokens(string $string = null): array
89
    {
90
        $tokens = parent::toTokens(parent::getBodyAsString());
91
 
92
        // Try creating any parameters
93
        foreach ($this->parameters as $name => $value) {
94
            if (null !== $value) {
95
                // Add the semi-colon separator
96
                $tokens[\count($tokens) - 1] .= ';';
97
                $tokens = array_merge($tokens, $this->generateTokenLines(' '.$this->createParameter($name, $value)));
98
            }
99
        }
100
 
101
        return $tokens;
102
    }
103
 
104
    /**
105
     * Render an RFC 2047 compliant header parameter from the $name and $value.
106
     */
107
    private function createParameter(string $name, string $value): string
108
    {
109
        $origValue = $value;
110
 
111
        $encoded = false;
112
        // Allow room for parameter name, indices, "=" and DQUOTEs
113
        $maxValueLength = $this->getMaxLineLength() - \strlen($name.'=*N"";') - 1;
114
        $firstLineOffset = 0;
115
 
116
        // If it's not already a valid parameter value...
117
        if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
118
            // TODO: text, or something else??
119
            // ... and it's not ascii
120
            if (!preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $value)) {
121
                $encoded = true;
122
                // Allow space for the indices, charset and language
123
                $maxValueLength = $this->getMaxLineLength() - \strlen($name.'*N*="";') - 1;
124
                $firstLineOffset = \strlen($this->getCharset()."'".$this->getLanguage()."'");
125
            }
126
 
127
            if (\in_array($name, ['name', 'filename'], true) && 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName()) && preg_match('//u', $value)) {
128
                // WHATWG HTML living standard 4.10.21.8 2 specifies:
129
                // For field names and filenames for file fields, the result of the
130
                // encoding in the previous bullet point must be escaped by replacing
131
                // any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
132
                // and 0x22 (") with `%22`.
133
                // The user agent must not perform any other escapes.
134
                $value = str_replace(['"', "\r", "\n"], ['%22', '%0D', '%0A'], $value);
135
 
136
                if (\strlen($value) <= $maxValueLength) {
137
                    return $name.'="'.$value.'"';
138
                }
139
 
140
                $value = $origValue;
141
            }
142
        }
143
 
144
        // Encode if we need to
145
        if ($encoded || \strlen($value) > $maxValueLength) {
146
            if (null !== $this->encoder) {
147
                $value = $this->encoder->encodeString($origValue, $this->getCharset(), $firstLineOffset, $maxValueLength);
148
            } else {
149
                // We have to go against RFC 2183/2231 in some areas for interoperability
150
                $value = $this->getTokenAsEncodedWord($origValue);
151
                $encoded = false;
152
            }
153
        }
154
 
155
        $valueLines = $this->encoder ? explode("\r\n", $value) : [$value];
156
 
157
        // Need to add indices
158
        if (\count($valueLines) > 1) {
159
            $paramLines = [];
160
            foreach ($valueLines as $i => $line) {
161
                $paramLines[] = $name.'*'.$i.$this->getEndOfParameterValue($line, true, 0 === $i);
162
            }
163
 
164
            return implode(";\r\n ", $paramLines);
165
        } else {
166
            return $name.$this->getEndOfParameterValue($valueLines[0], $encoded, true);
167
        }
168
    }
169
 
170
    /**
171
     * Returns the parameter value from the "=" and beyond.
172
     *
173
     * @param string $value to append
174
     */
175
    private function getEndOfParameterValue(string $value, bool $encoded = false, bool $firstLine = false): string
176
    {
177
        $forceHttpQuoting = 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName());
178
        if ($forceHttpQuoting || !preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
179
            $value = '"'.$value.'"';
180
        }
181
        $prepend = '=';
182
        if ($encoded) {
183
            $prepend = '*=';
184
            if ($firstLine) {
185
                $prepend = '*='.$this->getCharset()."'".$this->getLanguage()."'";
186
            }
187
        }
188
 
189
        return $prepend.$value;
190
    }
191
}