Import latest app updates from streamline

An updated set of source files and initialization was provided by streamline to
address issues observed during initial testing. These files have been updated in
order to generate a new set of app images.
This commit is contained in:
2024-04-11 11:16:51 -07:00
parent cb4c6b4c1a
commit 4a89356390
10435 changed files with 107737 additions and 1220538 deletions
@@ -1,288 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Encoder\QpMimeHeaderEncoder;
/**
* An abstract base MIME Header.
*
* @author Chris Corbyn
*/
abstract class AbstractHeader implements HeaderInterface
{
public const PHRASE_PATTERN = '(?:(?:(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?[a-zA-Z0-9!#\$%&\'\*\+\-\/=\?\^_`\{\}\|~]+(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?)|(?:(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?"((?:(?:[ \t]*(?:\r\n))?[ \t])?(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21\x23-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])))*(?:(?:[ \t]*(?:\r\n))?[ \t])?"(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))*(?:(?:(?:(?:[ \t]*(?:\r\n))?[ \t])?(\((?:(?:(?:[ \t]*(?:\r\n))?[ \t])|(?:(?:[\x01-\x08\x0B\x0C\x0E-\x19\x7F]|[\x21-\x27\x2A-\x5B\x5D-\x7E])|(?:\\[\x00-\x08\x0B\x0C\x0E-\x7F])|(?1)))*(?:(?:[ \t]*(?:\r\n))?[ \t])?\)))|(?:(?:[ \t]*(?:\r\n))?[ \t])))?))+?)';
private static QpMimeHeaderEncoder $encoder;
private string $name;
private int $lineLength = 76;
private ?string $lang = null;
private string $charset = 'utf-8';
public function __construct(string $name)
{
$this->name = $name;
}
/**
* @return void
*/
public function setCharset(string $charset)
{
$this->charset = $charset;
}
public function getCharset(): ?string
{
return $this->charset;
}
/**
* Set the language used in this Header.
*
* For example, for US English, 'en-us'.
*
* @return void
*/
public function setLanguage(string $lang)
{
$this->lang = $lang;
}
public function getLanguage(): ?string
{
return $this->lang;
}
public function getName(): string
{
return $this->name;
}
/**
* @return void
*/
public function setMaxLineLength(int $lineLength)
{
$this->lineLength = $lineLength;
}
public function getMaxLineLength(): int
{
return $this->lineLength;
}
public function toString(): string
{
return $this->tokensToString($this->toTokens());
}
/**
* Produces a compliant, formatted RFC 2822 'phrase' based on the string given.
*
* @param string $string as displayed
* @param bool $shorten the first line to make remove for header name
*/
protected function createPhrase(HeaderInterface $header, string $string, string $charset, bool $shorten = false): string
{
// Treat token as exactly what was given
$phraseStr = $string;
// If it's not valid
if (!preg_match('/^'.self::PHRASE_PATTERN.'$/D', $phraseStr)) {
// .. but it is just ascii text, try escaping some characters
// and make it a quoted-string
if (preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $phraseStr)) {
foreach (['\\', '"'] as $char) {
$phraseStr = str_replace($char, '\\'.$char, $phraseStr);
}
$phraseStr = '"'.$phraseStr.'"';
} else {
// ... otherwise it needs encoding
// Determine space remaining on line if first line
if ($shorten) {
$usedLength = \strlen($header->getName().': ');
} else {
$usedLength = 0;
}
$phraseStr = $this->encodeWords($header, $string, $usedLength);
}
} elseif (str_contains($phraseStr, '(')) {
foreach (['\\', '"'] as $char) {
$phraseStr = str_replace($char, '\\'.$char, $phraseStr);
}
$phraseStr = '"'.$phraseStr.'"';
}
return $phraseStr;
}
/**
* Encode needed word tokens within a string of input.
*/
protected function encodeWords(HeaderInterface $header, string $input, int $usedLength = -1): string
{
$value = '';
$tokens = $this->getEncodableWordTokens($input);
foreach ($tokens as $token) {
// See RFC 2822, Sect 2.2 (really 2.2 ??)
if ($this->tokenNeedsEncoding($token)) {
// Don't encode starting WSP
$firstChar = substr($token, 0, 1);
switch ($firstChar) {
case ' ':
case "\t":
$value .= $firstChar;
$token = substr($token, 1);
}
if (-1 == $usedLength) {
$usedLength = \strlen($header->getName().': ') + \strlen($value);
}
$value .= $this->getTokenAsEncodedWord($token, $usedLength);
} else {
$value .= $token;
}
}
return $value;
}
protected function tokenNeedsEncoding(string $token): bool
{
return (bool) preg_match('~[\x00-\x08\x10-\x19\x7F-\xFF\r\n]~', $token);
}
/**
* Splits a string into tokens in blocks of words which can be encoded quickly.
*
* @return string[]
*/
protected function getEncodableWordTokens(string $string): array
{
$tokens = [];
$encodedToken = '';
// Split at all whitespace boundaries
foreach (preg_split('~(?=[\t ])~', $string) as $token) {
if ($this->tokenNeedsEncoding($token)) {
$encodedToken .= $token;
} else {
if ('' !== $encodedToken) {
$tokens[] = $encodedToken;
$encodedToken = '';
}
$tokens[] = $token;
}
}
if ('' !== $encodedToken) {
$tokens[] = $encodedToken;
}
return $tokens;
}
/**
* Get a token as an encoded word for safe insertion into headers.
*/
protected function getTokenAsEncodedWord(string $token, int $firstLineOffset = 0): string
{
self::$encoder ??= new QpMimeHeaderEncoder();
// Adjust $firstLineOffset to account for space needed for syntax
$charsetDecl = $this->charset;
if (null !== $this->lang) {
$charsetDecl .= '*'.$this->lang;
}
$encodingWrapperLength = \strlen('=?'.$charsetDecl.'?'.self::$encoder->getName().'??=');
if ($firstLineOffset >= 75) {
// Does this logic need to be here?
$firstLineOffset = 0;
}
$encodedTextLines = explode("\r\n",
self::$encoder->encodeString($token, $this->charset, $firstLineOffset, 75 - $encodingWrapperLength)
);
if ('iso-2022-jp' !== strtolower($this->charset)) {
// special encoding for iso-2022-jp using mb_encode_mimeheader
foreach ($encodedTextLines as $lineNum => $line) {
$encodedTextLines[$lineNum] = '=?'.$charsetDecl.'?'.self::$encoder->getName().'?'.$line.'?=';
}
}
return implode("\r\n ", $encodedTextLines);
}
/**
* Generates tokens from the given string which include CRLF as individual tokens.
*
* @return string[]
*/
protected function generateTokenLines(string $token): array
{
return preg_split('~(\r\n)~', $token, -1, \PREG_SPLIT_DELIM_CAPTURE);
}
/**
* Generate a list of all tokens in the final header.
*/
protected function toTokens(?string $string = null): array
{
$string ??= $this->getBodyAsString();
$tokens = [];
// Generate atoms; split at all invisible boundaries followed by WSP
foreach (preg_split('~(?=[ \t])~', $string) as $token) {
$newTokens = $this->generateTokenLines($token);
foreach ($newTokens as $newToken) {
$tokens[] = $newToken;
}
}
return $tokens;
}
/**
* Takes an array of tokens which appear in the header and turns them into
* an RFC 2822 compliant string, adding FWSP where needed.
*
* @param string[] $tokens
*/
private function tokensToString(array $tokens): string
{
$lineCount = 0;
$headerLines = [];
$headerLines[] = $this->name.': ';
$currentLine = &$headerLines[$lineCount++];
// Build all tokens back into compliant header
foreach ($tokens as $i => $token) {
// Line longer than specified maximum or token was just a new line
if (("\r\n" === $token)
|| ($i > 0 && \strlen($currentLine.$token) > $this->lineLength)
&& '' !== $currentLine) {
$headerLines[] = '';
$currentLine = &$headerLines[$lineCount++];
}
// Append token to the line
if ("\r\n" !== $token) {
$currentLine .= $token;
}
}
// Implode with FWS (RFC 2822, 2.2.3)
return implode("\r\n", $headerLines);
}
}
@@ -1,62 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
/**
* A Date MIME Header.
*
* @author Chris Corbyn
*/
final class DateHeader extends AbstractHeader
{
private \DateTimeImmutable $dateTime;
public function __construct(string $name, \DateTimeInterface $date)
{
parent::__construct($name);
$this->setDateTime($date);
}
/**
* @param \DateTimeInterface $body
*/
public function setBody(mixed $body): void
{
$this->setDateTime($body);
}
public function getBody(): \DateTimeImmutable
{
return $this->getDateTime();
}
public function getDateTime(): \DateTimeImmutable
{
return $this->dateTime;
}
/**
* Set the date-time of the Date in this Header.
*
* If a DateTime instance is provided, it is converted to DateTimeImmutable.
*/
public function setDateTime(\DateTimeInterface $dateTime): void
{
$this->dateTime = \DateTimeImmutable::createFromInterface($dateTime);
}
public function getBodyAsString(): string
{
return $this->dateTime->format(\DateTimeInterface::RFC2822);
}
}
@@ -1,72 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
/**
* A MIME Header.
*
* @author Chris Corbyn
*/
interface HeaderInterface
{
/**
* Sets the body.
*
* The type depends on the Header concrete class.
*
* @return void
*/
public function setBody(mixed $body);
/**
* Gets the body.
*
* The return type depends on the Header concrete class.
*/
public function getBody(): mixed;
/**
* @return void
*/
public function setCharset(string $charset);
public function getCharset(): ?string;
/**
* @return void
*/
public function setLanguage(string $lang);
public function getLanguage(): ?string;
public function getName(): string;
/**
* @return void
*/
public function setMaxLineLength(int $lineLength);
public function getMaxLineLength(): int;
/**
* Gets this Header rendered as a compliant string.
*/
public function toString(): string;
/**
* Gets the header's body, prepared for folding into a final header value.
*
* This is not necessarily RFC 2822 compliant since folding white space is
* not added at this stage (see {@link toString()} for that).
*/
public function getBodyAsString(): string;
}
@@ -1,316 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\LogicException;
/**
* A collection of headers.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
final class Headers
{
private const UNIQUE_HEADERS = [
'date', 'from', 'sender', 'reply-to', 'to', 'cc', 'bcc',
'message-id', 'in-reply-to', 'references', 'subject',
];
private const HEADER_CLASS_MAP = [
'date' => DateHeader::class,
'from' => MailboxListHeader::class,
'sender' => MailboxHeader::class,
'reply-to' => MailboxListHeader::class,
'to' => MailboxListHeader::class,
'cc' => MailboxListHeader::class,
'bcc' => MailboxListHeader::class,
'message-id' => IdentificationHeader::class,
'in-reply-to' => [UnstructuredHeader::class, IdentificationHeader::class], // `In-Reply-To` and `References` are less strict than RFC 2822 (3.6.4) to allow users entering the original email's ...
'references' => [UnstructuredHeader::class, IdentificationHeader::class], // ... `Message-ID`, even if that is no valid `msg-id`
'return-path' => PathHeader::class,
];
/**
* @var HeaderInterface[][]
*/
private array $headers = [];
private int $lineLength = 76;
public function __construct(HeaderInterface ...$headers)
{
foreach ($headers as $header) {
$this->add($header);
}
}
public function __clone()
{
foreach ($this->headers as $name => $collection) {
foreach ($collection as $i => $header) {
$this->headers[$name][$i] = clone $header;
}
}
}
public function setMaxLineLength(int $lineLength): void
{
$this->lineLength = $lineLength;
foreach ($this->all() as $header) {
$header->setMaxLineLength($lineLength);
}
}
public function getMaxLineLength(): int
{
return $this->lineLength;
}
/**
* @param array<Address|string> $addresses
*
* @return $this
*/
public function addMailboxListHeader(string $name, array $addresses): static
{
return $this->add(new MailboxListHeader($name, Address::createArray($addresses)));
}
/**
* @return $this
*/
public function addMailboxHeader(string $name, Address|string $address): static
{
return $this->add(new MailboxHeader($name, Address::create($address)));
}
/**
* @return $this
*/
public function addIdHeader(string $name, string|array $ids): static
{
return $this->add(new IdentificationHeader($name, $ids));
}
/**
* @return $this
*/
public function addPathHeader(string $name, Address|string $path): static
{
return $this->add(new PathHeader($name, $path instanceof Address ? $path : new Address($path)));
}
/**
* @return $this
*/
public function addDateHeader(string $name, \DateTimeInterface $dateTime): static
{
return $this->add(new DateHeader($name, $dateTime));
}
/**
* @return $this
*/
public function addTextHeader(string $name, string $value): static
{
return $this->add(new UnstructuredHeader($name, $value));
}
/**
* @return $this
*/
public function addParameterizedHeader(string $name, string $value, array $params = []): static
{
return $this->add(new ParameterizedHeader($name, $value, $params));
}
/**
* @return $this
*/
public function addHeader(string $name, mixed $argument, array $more = []): static
{
$headerClass = self::HEADER_CLASS_MAP[strtolower($name)] ?? UnstructuredHeader::class;
if (\is_array($headerClass)) {
$headerClass = $headerClass[0];
}
$parts = explode('\\', $headerClass);
$method = 'add'.ucfirst(array_pop($parts));
if ('addUnstructuredHeader' === $method) {
$method = 'addTextHeader';
} elseif ('addIdentificationHeader' === $method) {
$method = 'addIdHeader';
} elseif ('addMailboxListHeader' === $method && !\is_array($argument)) {
$argument = [$argument];
}
return $this->$method($name, $argument, $more);
}
public function has(string $name): bool
{
return isset($this->headers[strtolower($name)]);
}
/**
* @return $this
*/
public function add(HeaderInterface $header): static
{
self::checkHeaderClass($header);
$header->setMaxLineLength($this->lineLength);
$name = strtolower($header->getName());
if (\in_array($name, self::UNIQUE_HEADERS, true) && isset($this->headers[$name]) && \count($this->headers[$name]) > 0) {
throw new LogicException(sprintf('Impossible to set header "%s" as it\'s already defined and must be unique.', $header->getName()));
}
$this->headers[$name][] = $header;
return $this;
}
public function get(string $name): ?HeaderInterface
{
$name = strtolower($name);
if (!isset($this->headers[$name])) {
return null;
}
$values = array_values($this->headers[$name]);
return array_shift($values);
}
public function all(?string $name = null): iterable
{
if (null === $name) {
foreach ($this->headers as $name => $collection) {
foreach ($collection as $header) {
yield $name => $header;
}
}
} elseif (isset($this->headers[strtolower($name)])) {
foreach ($this->headers[strtolower($name)] as $header) {
yield $header;
}
}
}
public function getNames(): array
{
return array_keys($this->headers);
}
public function remove(string $name): void
{
unset($this->headers[strtolower($name)]);
}
public static function isUniqueHeader(string $name): bool
{
return \in_array(strtolower($name), self::UNIQUE_HEADERS, true);
}
/**
* @throws LogicException if the header name and class are not compatible
*/
public static function checkHeaderClass(HeaderInterface $header): void
{
$name = strtolower($header->getName());
$headerClasses = self::HEADER_CLASS_MAP[$name] ?? [];
if (!\is_array($headerClasses)) {
$headerClasses = [$headerClasses];
}
if (!$headerClasses) {
return;
}
foreach ($headerClasses as $c) {
if ($header instanceof $c) {
return;
}
}
throw new LogicException(sprintf('The "%s" header must be an instance of "%s" (got "%s").', $header->getName(), implode('" or "', $headerClasses), get_debug_type($header)));
}
public function toString(): string
{
$string = '';
foreach ($this->toArray() as $str) {
$string .= $str."\r\n";
}
return $string;
}
public function toArray(): array
{
$arr = [];
foreach ($this->all() as $header) {
if ('' !== $header->getBodyAsString()) {
$arr[] = $header->toString();
}
}
return $arr;
}
public function getHeaderBody(string $name): mixed
{
return $this->has($name) ? $this->get($name)->getBody() : null;
}
/**
* @internal
*/
public function setHeaderBody(string $type, string $name, mixed $body): void
{
if ($this->has($name)) {
$this->get($name)->setBody($body);
} else {
$this->{'add'.$type.'Header'}($name, $body);
}
}
public function getHeaderParameter(string $name, string $parameter): ?string
{
if (!$this->has($name)) {
return null;
}
$header = $this->get($name);
if (!$header instanceof ParameterizedHeader) {
throw new LogicException(sprintf('Unable to get parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class));
}
return $header->getParameter($parameter);
}
/**
* @internal
*/
public function setHeaderParameter(string $name, string $parameter, ?string $value): void
{
if (!$this->has($name)) {
throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not defined.', $parameter, $name));
}
$header = $this->get($name);
if (!$header instanceof ParameterizedHeader) {
throw new LogicException(sprintf('Unable to set parameter "%s" on header "%s" as the header is not of class "%s".', $parameter, $name, ParameterizedHeader::class));
}
$header->setParameter($parameter, $value);
}
}
@@ -1,107 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;
/**
* An ID MIME Header for something like Message-ID or Content-ID (one or more addresses).
*
* @author Chris Corbyn
*/
final class IdentificationHeader extends AbstractHeader
{
private array $ids = [];
private array $idsAsAddresses = [];
public function __construct(string $name, string|array $ids)
{
parent::__construct($name);
$this->setId($ids);
}
/**
* @param string|string[] $body a string ID or an array of IDs
*
* @throws RfcComplianceException
*/
public function setBody(mixed $body): void
{
$this->setId($body);
}
public function getBody(): array
{
return $this->getIds();
}
/**
* Set the ID used in the value of this header.
*
* @param string|string[] $id
*
* @throws RfcComplianceException
*/
public function setId(string|array $id): void
{
$this->setIds(\is_array($id) ? $id : [$id]);
}
/**
* Get the ID used in the value of this Header.
*
* If multiple IDs are set only the first is returned.
*/
public function getId(): ?string
{
return $this->ids[0] ?? null;
}
/**
* Set a collection of IDs to use in the value of this Header.
*
* @param string[] $ids
*
* @throws RfcComplianceException
*/
public function setIds(array $ids): void
{
$this->ids = [];
$this->idsAsAddresses = [];
foreach ($ids as $id) {
$this->idsAsAddresses[] = new Address($id);
$this->ids[] = $id;
}
}
/**
* Get the list of IDs used in this Header.
*
* @return string[]
*/
public function getIds(): array
{
return $this->ids;
}
public function getBodyAsString(): string
{
$addrs = [];
foreach ($this->idsAsAddresses as $address) {
$addrs[] = '<'.$address->toString().'>';
}
return implode(' ', $addrs);
}
}
@@ -1,85 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;
/**
* A Mailbox MIME Header for something like Sender (one named address).
*
* @author Fabien Potencier <fabien@symfony.com>
*/
final class MailboxHeader extends AbstractHeader
{
private Address $address;
public function __construct(string $name, Address $address)
{
parent::__construct($name);
$this->setAddress($address);
}
/**
* @param Address $body
*
* @throws RfcComplianceException
*/
public function setBody(mixed $body): void
{
$this->setAddress($body);
}
/**
* @throws RfcComplianceException
*/
public function getBody(): Address
{
return $this->getAddress();
}
/**
* @throws RfcComplianceException
*/
public function setAddress(Address $address): void
{
$this->address = $address;
}
public function getAddress(): Address
{
return $this->address;
}
public function getBodyAsString(): string
{
$str = $this->address->getEncodedAddress();
if ($name = $this->address->getName()) {
$str = $this->createPhrase($this, $name, $this->getCharset(), true).' <'.$str.'>';
}
return $str;
}
/**
* Redefine the encoding requirements for an address.
*
* All "specials" must be encoded as the full header value will not be quoted
*
* @see RFC 2822 3.2.1
*/
protected function tokenNeedsEncoding(string $token): bool
{
return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token);
}
}
@@ -1,136 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;
/**
* A Mailbox list MIME Header for something like From, To, Cc, and Bcc (one or more named addresses).
*
* @author Chris Corbyn
*/
final class MailboxListHeader extends AbstractHeader
{
private array $addresses = [];
/**
* @param Address[] $addresses
*/
public function __construct(string $name, array $addresses)
{
parent::__construct($name);
$this->setAddresses($addresses);
}
/**
* @param Address[] $body
*
* @throws RfcComplianceException
*/
public function setBody(mixed $body): void
{
$this->setAddresses($body);
}
/**
* @return Address[]
*
* @throws RfcComplianceException
*/
public function getBody(): array
{
return $this->getAddresses();
}
/**
* Sets a list of addresses to be shown in this Header.
*
* @param Address[] $addresses
*
* @throws RfcComplianceException
*/
public function setAddresses(array $addresses): void
{
$this->addresses = [];
$this->addAddresses($addresses);
}
/**
* Sets a list of addresses to be shown in this Header.
*
* @param Address[] $addresses
*
* @throws RfcComplianceException
*/
public function addAddresses(array $addresses): void
{
foreach ($addresses as $address) {
$this->addAddress($address);
}
}
/**
* @throws RfcComplianceException
*/
public function addAddress(Address $address): void
{
$this->addresses[] = $address;
}
/**
* @return Address[]
*/
public function getAddresses(): array
{
return $this->addresses;
}
/**
* Gets the full mailbox list of this Header as an array of valid RFC 2822 strings.
*
* @return string[]
*
* @throws RfcComplianceException
*/
public function getAddressStrings(): array
{
$strings = [];
foreach ($this->addresses as $address) {
$str = $address->getEncodedAddress();
if ($name = $address->getName()) {
$str = $this->createPhrase($this, $name, $this->getCharset(), !$strings).' <'.$str.'>';
}
$strings[] = $str;
}
return $strings;
}
public function getBodyAsString(): string
{
return implode(', ', $this->getAddressStrings());
}
/**
* Redefine the encoding requirements for addresses.
*
* All "specials" must be encoded as the full header value will not be quoted
*
* @see RFC 2822 3.2.1
*/
protected function tokenNeedsEncoding(string $token): bool
{
return preg_match('/[()<>\[\]:;@\,."]/', $token) || parent::tokenNeedsEncoding($token);
}
}
@@ -1,191 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Encoder\Rfc2231Encoder;
/**
* @author Chris Corbyn
*/
final class ParameterizedHeader extends UnstructuredHeader
{
/**
* RFC 2231's definition of a token.
*
* @var string
*/
public const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';
private ?Rfc2231Encoder $encoder = null;
private array $parameters = [];
public function __construct(string $name, string $value, array $parameters = [])
{
parent::__construct($name, $value);
foreach ($parameters as $k => $v) {
$this->setParameter($k, $v);
}
if ('content-type' !== strtolower($name)) {
$this->encoder = new Rfc2231Encoder();
}
}
public function setParameter(string $parameter, ?string $value): void
{
$this->setParameters(array_merge($this->getParameters(), [$parameter => $value]));
}
public function getParameter(string $parameter): string
{
return $this->getParameters()[$parameter] ?? '';
}
/**
* @param string[] $parameters
*/
public function setParameters(array $parameters): void
{
$this->parameters = $parameters;
}
/**
* @return string[]
*/
public function getParameters(): array
{
return $this->parameters;
}
public function getBodyAsString(): string
{
$body = parent::getBodyAsString();
foreach ($this->parameters as $name => $value) {
if (null !== $value) {
$body .= '; '.$this->createParameter($name, $value);
}
}
return $body;
}
/**
* Generate a list of all tokens in the final header.
*
* This doesn't need to be overridden in theory, but it is for implementation
* reasons to prevent potential breakage of attributes.
*/
protected function toTokens(?string $string = null): array
{
$tokens = parent::toTokens(parent::getBodyAsString());
// Try creating any parameters
foreach ($this->parameters as $name => $value) {
if (null !== $value) {
// Add the semi-colon separator
$tokens[\count($tokens) - 1] .= ';';
$tokens = array_merge($tokens, $this->generateTokenLines(' '.$this->createParameter($name, $value)));
}
}
return $tokens;
}
/**
* Render an RFC 2047 compliant header parameter from the $name and $value.
*/
private function createParameter(string $name, string $value): string
{
$origValue = $value;
$encoded = false;
// Allow room for parameter name, indices, "=" and DQUOTEs
$maxValueLength = $this->getMaxLineLength() - \strlen($name.'=*N"";') - 1;
$firstLineOffset = 0;
// If it's not already a valid parameter value...
if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
// TODO: text, or something else??
// ... and it's not ascii
if (!preg_match('/^[\x00-\x08\x0B\x0C\x0E-\x7F]*$/D', $value)) {
$encoded = true;
// Allow space for the indices, charset and language
$maxValueLength = $this->getMaxLineLength() - \strlen($name.'*N*="";') - 1;
$firstLineOffset = \strlen($this->getCharset()."'".$this->getLanguage()."'");
}
if (\in_array($name, ['name', 'filename'], true) && 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName()) && preg_match('//u', $value)) {
// WHATWG HTML living standard 4.10.21.8 2 specifies:
// For field names and filenames for file fields, the result of the
// encoding in the previous bullet point must be escaped by replacing
// any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
// and 0x22 (") with `%22`.
// The user agent must not perform any other escapes.
$value = str_replace(['"', "\r", "\n"], ['%22', '%0D', '%0A'], $value);
if (\strlen($value) <= $maxValueLength) {
return $name.'="'.$value.'"';
}
$value = $origValue;
}
}
// Encode if we need to
if ($encoded || \strlen($value) > $maxValueLength) {
if (null !== $this->encoder) {
$value = $this->encoder->encodeString($origValue, $this->getCharset(), $firstLineOffset, $maxValueLength);
} else {
// We have to go against RFC 2183/2231 in some areas for interoperability
$value = $this->getTokenAsEncodedWord($origValue);
$encoded = false;
}
}
$valueLines = $this->encoder ? explode("\r\n", $value) : [$value];
// Need to add indices
if (\count($valueLines) > 1) {
$paramLines = [];
foreach ($valueLines as $i => $line) {
$paramLines[] = $name.'*'.$i.$this->getEndOfParameterValue($line, true, 0 === $i);
}
return implode(";\r\n ", $paramLines);
} else {
return $name.$this->getEndOfParameterValue($valueLines[0], $encoded, true);
}
}
/**
* Returns the parameter value from the "=" and beyond.
*
* @param string $value to append
*/
private function getEndOfParameterValue(string $value, bool $encoded = false, bool $firstLine = false): string
{
$forceHttpQuoting = 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName());
if ($forceHttpQuoting || !preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
$value = '"'.$value.'"';
}
$prepend = '=';
if ($encoded) {
$prepend = '*=';
if ($firstLine) {
$prepend = '*='.$this->getCharset()."'".$this->getLanguage()."'";
}
}
return $prepend.$value;
}
}
@@ -1,62 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Exception\RfcComplianceException;
/**
* A Path Header, such a Return-Path (one address).
*
* @author Chris Corbyn
*/
final class PathHeader extends AbstractHeader
{
private Address $address;
public function __construct(string $name, Address $address)
{
parent::__construct($name);
$this->setAddress($address);
}
/**
* @param Address $body
*
* @throws RfcComplianceException
*/
public function setBody(mixed $body): void
{
$this->setAddress($body);
}
public function getBody(): Address
{
return $this->getAddress();
}
public function setAddress(Address $address): void
{
$this->address = $address;
}
public function getAddress(): Address
{
return $this->address;
}
public function getBodyAsString(): string
{
return '<'.$this->address->toString().'>';
}
}
@@ -1,70 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Mime\Header;
/**
* A Simple MIME Header.
*
* @author Chris Corbyn
*/
class UnstructuredHeader extends AbstractHeader
{
private string $value;
public function __construct(string $name, string $value)
{
parent::__construct($name);
$this->setValue($value);
}
/**
* @param string $body
*
* @return void
*/
public function setBody(mixed $body)
{
$this->setValue($body);
}
public function getBody(): string
{
return $this->getValue();
}
/**
* Get the (unencoded) value of this header.
*/
public function getValue(): string
{
return $this->value;
}
/**
* Set the (unencoded) value of this header.
*
* @return void
*/
public function setValue(string $value)
{
$this->value = $value;
}
/**
* Get the value of this header prepared for rendering.
*/
public function getBodyAsString(): string
{
return $this->encodeWords($this, $this->value);
}
}