+ */
+class Stream extends File
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function getSize()
+ {
+ return false;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/File/UploadedFile.php b/lam/lib/3rdParty/composer/symfony/http-foundation/File/UploadedFile.php
new file mode 100644
index 00000000..3a258e29
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/File/UploadedFile.php
@@ -0,0 +1,281 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\File;
+
+use Symfony\Component\HttpFoundation\File\Exception\CannotWriteFileException;
+use Symfony\Component\HttpFoundation\File\Exception\ExtensionFileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileException;
+use Symfony\Component\HttpFoundation\File\Exception\FileNotFoundException;
+use Symfony\Component\HttpFoundation\File\Exception\FormSizeFileException;
+use Symfony\Component\HttpFoundation\File\Exception\IniSizeFileException;
+use Symfony\Component\HttpFoundation\File\Exception\NoFileException;
+use Symfony\Component\HttpFoundation\File\Exception\NoTmpDirFileException;
+use Symfony\Component\HttpFoundation\File\Exception\PartialFileException;
+use Symfony\Component\Mime\MimeTypes;
+
+/**
+ * A file uploaded through a form.
+ *
+ * @author Bernhard Schussek
+ * @author Florian Eckerstorfer
+ * @author Fabien Potencier
+ */
+class UploadedFile extends File
+{
+ private $test = false;
+ private $originalName;
+ private $mimeType;
+ private $error;
+
+ /**
+ * Accepts the information of the uploaded file as provided by the PHP global $_FILES.
+ *
+ * The file object is only created when the uploaded file is valid (i.e. when the
+ * isValid() method returns true). Otherwise the only methods that could be called
+ * on an UploadedFile instance are:
+ *
+ * * getClientOriginalName,
+ * * getClientMimeType,
+ * * isValid,
+ * * getError.
+ *
+ * Calling any other method on an non-valid instance will cause an unpredictable result.
+ *
+ * @param string $path The full temporary path to the file
+ * @param string $originalName The original file name of the uploaded file
+ * @param string|null $mimeType The type of the file as provided by PHP; null defaults to application/octet-stream
+ * @param int|null $error The error constant of the upload (one of PHP's UPLOAD_ERR_XXX constants); null defaults to UPLOAD_ERR_OK
+ * @param bool $test Whether the test mode is active
+ * Local files are used in test mode hence the code should not enforce HTTP uploads
+ *
+ * @throws FileException If file_uploads is disabled
+ * @throws FileNotFoundException If the file does not exist
+ */
+ public function __construct(string $path, string $originalName, string $mimeType = null, int $error = null, bool $test = false)
+ {
+ $this->originalName = $this->getName($originalName);
+ $this->mimeType = $mimeType ?: 'application/octet-stream';
+ $this->error = $error ?: UPLOAD_ERR_OK;
+ $this->test = $test;
+
+ parent::__construct($path, UPLOAD_ERR_OK === $this->error);
+ }
+
+ /**
+ * Returns the original file name.
+ *
+ * It is extracted from the request from which the file has been uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string|null The original name
+ */
+ public function getClientOriginalName()
+ {
+ return $this->originalName;
+ }
+
+ /**
+ * Returns the original file extension.
+ *
+ * It is extracted from the original file name that was uploaded.
+ * Then it should not be considered as a safe value.
+ *
+ * @return string The extension
+ */
+ public function getClientOriginalExtension()
+ {
+ return pathinfo($this->originalName, PATHINFO_EXTENSION);
+ }
+
+ /**
+ * Returns the file mime type.
+ *
+ * The client mime type is extracted from the request from which the file
+ * was uploaded, so it should not be considered as a safe value.
+ *
+ * For a trusted mime type, use getMimeType() instead (which guesses the mime
+ * type based on the file content).
+ *
+ * @return string|null The mime type
+ *
+ * @see getMimeType()
+ */
+ public function getClientMimeType()
+ {
+ return $this->mimeType;
+ }
+
+ /**
+ * Returns the extension based on the client mime type.
+ *
+ * If the mime type is unknown, returns null.
+ *
+ * This method uses the mime type as guessed by getClientMimeType()
+ * to guess the file extension. As such, the extension returned
+ * by this method cannot be trusted.
+ *
+ * For a trusted extension, use guessExtension() instead (which guesses
+ * the extension based on the guessed mime type for the file).
+ *
+ * @return string|null The guessed extension or null if it cannot be guessed
+ *
+ * @see guessExtension()
+ * @see getClientMimeType()
+ */
+ public function guessClientExtension()
+ {
+ return MimeTypes::getDefault()->getExtensions($this->getClientMimeType())[0] ?? null;
+ }
+
+ /**
+ * Returns the upload error.
+ *
+ * If the upload was successful, the constant UPLOAD_ERR_OK is returned.
+ * Otherwise one of the other UPLOAD_ERR_XXX constants is returned.
+ *
+ * @return int The upload error
+ */
+ public function getError()
+ {
+ return $this->error;
+ }
+
+ /**
+ * Returns whether the file was uploaded successfully.
+ *
+ * @return bool True if the file has been uploaded with HTTP and no error occurred
+ */
+ public function isValid()
+ {
+ $isOk = UPLOAD_ERR_OK === $this->error;
+
+ return $this->test ? $isOk : $isOk && is_uploaded_file($this->getPathname());
+ }
+
+ /**
+ * Moves the file to a new location.
+ *
+ * @return File A File object representing the new file
+ *
+ * @throws FileException if, for any reason, the file could not have been moved
+ */
+ public function move(string $directory, string $name = null)
+ {
+ if ($this->isValid()) {
+ if ($this->test) {
+ return parent::move($directory, $name);
+ }
+
+ $target = $this->getTargetFile($directory, $name);
+
+ set_error_handler(function ($type, $msg) use (&$error) { $error = $msg; });
+ $moved = move_uploaded_file($this->getPathname(), $target);
+ restore_error_handler();
+ if (!$moved) {
+ throw new FileException(sprintf('Could not move the file "%s" to "%s" (%s)', $this->getPathname(), $target, strip_tags($error)));
+ }
+
+ @chmod($target, 0666 & ~umask());
+
+ return $target;
+ }
+
+ switch ($this->error) {
+ case UPLOAD_ERR_INI_SIZE:
+ throw new IniSizeFileException($this->getErrorMessage());
+ case UPLOAD_ERR_FORM_SIZE:
+ throw new FormSizeFileException($this->getErrorMessage());
+ case UPLOAD_ERR_PARTIAL:
+ throw new PartialFileException($this->getErrorMessage());
+ case UPLOAD_ERR_NO_FILE:
+ throw new NoFileException($this->getErrorMessage());
+ case UPLOAD_ERR_CANT_WRITE:
+ throw new CannotWriteFileException($this->getErrorMessage());
+ case UPLOAD_ERR_NO_TMP_DIR:
+ throw new NoTmpDirFileException($this->getErrorMessage());
+ case UPLOAD_ERR_EXTENSION:
+ throw new ExtensionFileException($this->getErrorMessage());
+ }
+
+ throw new FileException($this->getErrorMessage());
+ }
+
+ /**
+ * Returns the maximum size of an uploaded file as configured in php.ini.
+ *
+ * @return int The maximum size of an uploaded file in bytes
+ */
+ public static function getMaxFilesize()
+ {
+ $sizePostMax = self::parseFilesize(ini_get('post_max_size'));
+ $sizeUploadMax = self::parseFilesize(ini_get('upload_max_filesize'));
+
+ return min($sizePostMax ?: PHP_INT_MAX, $sizeUploadMax ?: PHP_INT_MAX);
+ }
+
+ /**
+ * Returns the given size from an ini value in bytes.
+ */
+ private static function parseFilesize($size): int
+ {
+ if ('' === $size) {
+ return 0;
+ }
+
+ $size = strtolower($size);
+
+ $max = ltrim($size, '+');
+ if (0 === strpos($max, '0x')) {
+ $max = \intval($max, 16);
+ } elseif (0 === strpos($max, '0')) {
+ $max = \intval($max, 8);
+ } else {
+ $max = (int) $max;
+ }
+
+ switch (substr($size, -1)) {
+ case 't': $max *= 1024;
+ // no break
+ case 'g': $max *= 1024;
+ // no break
+ case 'm': $max *= 1024;
+ // no break
+ case 'k': $max *= 1024;
+ }
+
+ return $max;
+ }
+
+ /**
+ * Returns an informative upload error message.
+ *
+ * @return string The error message regarding the specified error code
+ */
+ public function getErrorMessage()
+ {
+ static $errors = [
+ UPLOAD_ERR_INI_SIZE => 'The file "%s" exceeds your upload_max_filesize ini directive (limit is %d KiB).',
+ UPLOAD_ERR_FORM_SIZE => 'The file "%s" exceeds the upload limit defined in your form.',
+ UPLOAD_ERR_PARTIAL => 'The file "%s" was only partially uploaded.',
+ UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
+ UPLOAD_ERR_CANT_WRITE => 'The file "%s" could not be written on disk.',
+ UPLOAD_ERR_NO_TMP_DIR => 'File could not be uploaded: missing temporary directory.',
+ UPLOAD_ERR_EXTENSION => 'File upload was stopped by a PHP extension.',
+ ];
+
+ $errorCode = $this->error;
+ $maxFilesize = UPLOAD_ERR_INI_SIZE === $errorCode ? self::getMaxFilesize() / 1024 : 0;
+ $message = isset($errors[$errorCode]) ? $errors[$errorCode] : 'The file "%s" was not uploaded due to an unknown error.';
+
+ return sprintf($message, $this->getClientOriginalName(), $maxFilesize);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/FileBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/FileBag.php
new file mode 100644
index 00000000..5edd372b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/FileBag.php
@@ -0,0 +1,142 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\File\UploadedFile;
+
+/**
+ * FileBag is a container for uploaded files.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ */
+class FileBag extends ParameterBag
+{
+ private static $fileKeys = ['error', 'name', 'size', 'tmp_name', 'type'];
+
+ /**
+ * @param array|UploadedFile[] $parameters An array of HTTP files
+ */
+ public function __construct(array $parameters = [])
+ {
+ $this->replace($parameters);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $files = [])
+ {
+ $this->parameters = [];
+ $this->add($files);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $key, $value)
+ {
+ if (!\is_array($value) && !$value instanceof UploadedFile) {
+ throw new \InvalidArgumentException('An uploaded file must be an array or an instance of UploadedFile.');
+ }
+
+ parent::set($key, $this->convertFileInformation($value));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(array $files = [])
+ {
+ foreach ($files as $key => $file) {
+ $this->set($key, $file);
+ }
+ }
+
+ /**
+ * Converts uploaded files to UploadedFile instances.
+ *
+ * @param array|UploadedFile $file A (multi-dimensional) array of uploaded file information
+ *
+ * @return UploadedFile[]|UploadedFile|null A (multi-dimensional) array of UploadedFile instances
+ */
+ protected function convertFileInformation($file)
+ {
+ if ($file instanceof UploadedFile) {
+ return $file;
+ }
+
+ if (\is_array($file)) {
+ $file = $this->fixPhpFilesArray($file);
+ $keys = array_keys($file);
+ sort($keys);
+
+ if ($keys == self::$fileKeys) {
+ if (UPLOAD_ERR_NO_FILE == $file['error']) {
+ $file = null;
+ } else {
+ $file = new UploadedFile($file['tmp_name'], $file['name'], $file['type'], $file['error'], false);
+ }
+ } else {
+ $file = array_map([$this, 'convertFileInformation'], $file);
+ if (array_keys($keys) === $keys) {
+ $file = array_filter($file);
+ }
+ }
+ }
+
+ return $file;
+ }
+
+ /**
+ * Fixes a malformed PHP $_FILES array.
+ *
+ * PHP has a bug that the format of the $_FILES array differs, depending on
+ * whether the uploaded file fields had normal field names or array-like
+ * field names ("normal" vs. "parent[child]").
+ *
+ * This method fixes the array to look like the "normal" $_FILES array.
+ *
+ * It's safe to pass an already converted array, in which case this method
+ * just returns the original array unmodified.
+ *
+ * @param array $data
+ *
+ * @return array
+ */
+ protected function fixPhpFilesArray($data)
+ {
+ $keys = array_keys($data);
+ sort($keys);
+
+ if (self::$fileKeys != $keys || !isset($data['name']) || !\is_array($data['name'])) {
+ return $data;
+ }
+
+ $files = $data;
+ foreach (self::$fileKeys as $k) {
+ unset($files[$k]);
+ }
+
+ foreach ($data['name'] as $key => $name) {
+ $files[$key] = $this->fixPhpFilesArray([
+ 'error' => $data['error'][$key],
+ 'name' => $name,
+ 'type' => $data['type'][$key],
+ 'tmp_name' => $data['tmp_name'][$key],
+ 'size' => $data['size'][$key],
+ ]);
+ }
+
+ return $files;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderBag.php
new file mode 100644
index 00000000..9c7273a7
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderBag.php
@@ -0,0 +1,288 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * HeaderBag is a container for HTTP headers.
+ *
+ * @author Fabien Potencier
+ */
+class HeaderBag implements \IteratorAggregate, \Countable
+{
+ protected const UPPER = '_ABCDEFGHIJKLMNOPQRSTUVWXYZ';
+ protected const LOWER = '-abcdefghijklmnopqrstuvwxyz';
+
+ protected $headers = [];
+ protected $cacheControl = [];
+
+ public function __construct(array $headers = [])
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns the headers as a string.
+ *
+ * @return string The headers
+ */
+ public function __toString()
+ {
+ if (!$headers = $this->all()) {
+ return '';
+ }
+
+ ksort($headers);
+ $max = max(array_map('strlen', array_keys($headers))) + 1;
+ $content = '';
+ foreach ($headers as $name => $values) {
+ $name = ucwords($name, '-');
+ foreach ($values as $value) {
+ $content .= sprintf("%-{$max}s %s\r\n", $name.':', $value);
+ }
+ }
+
+ return $content;
+ }
+
+ /**
+ * Returns the headers.
+ *
+ * @param string|null $key The name of the headers to return or null to get them all
+ *
+ * @return array An array of headers
+ */
+ public function all(string $key = null)
+ {
+ if (null !== $key) {
+ return $this->headers[strtr($key, self::UPPER, self::LOWER)] ?? [];
+ }
+
+ return $this->headers;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->all());
+ }
+
+ /**
+ * Replaces the current HTTP headers by a new set.
+ */
+ public function replace(array $headers = [])
+ {
+ $this->headers = [];
+ $this->add($headers);
+ }
+
+ /**
+ * Adds new headers the current HTTP headers set.
+ */
+ public function add(array $headers)
+ {
+ foreach ($headers as $key => $values) {
+ $this->set($key, $values);
+ }
+ }
+
+ /**
+ * Returns a header value by name.
+ *
+ * @return string|null The first header value or default value
+ */
+ public function get(string $key, string $default = null)
+ {
+ $headers = $this->all($key);
+
+ if (!$headers) {
+ return $default;
+ }
+
+ if (null === $headers[0]) {
+ return null;
+ }
+
+ return (string) $headers[0];
+ }
+
+ /**
+ * Sets a header by name.
+ *
+ * @param string|string[] $values The value or an array of values
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ */
+ public function set(string $key, $values, bool $replace = true)
+ {
+ $key = strtr($key, self::UPPER, self::LOWER);
+
+ if (\is_array($values)) {
+ $values = array_values($values);
+
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = $values;
+ } else {
+ $this->headers[$key] = array_merge($this->headers[$key], $values);
+ }
+ } else {
+ if (true === $replace || !isset($this->headers[$key])) {
+ $this->headers[$key] = [$values];
+ } else {
+ $this->headers[$key][] = $values;
+ }
+ }
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = $this->parseCacheControl(implode(', ', $this->headers[$key]));
+ }
+ }
+
+ /**
+ * Returns true if the HTTP header is defined.
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has(string $key)
+ {
+ return \array_key_exists(strtr($key, self::UPPER, self::LOWER), $this->all());
+ }
+
+ /**
+ * Returns true if the given HTTP header contains the given value.
+ *
+ * @return bool true if the value is contained in the header, false otherwise
+ */
+ public function contains(string $key, string $value)
+ {
+ return \in_array($value, $this->all($key));
+ }
+
+ /**
+ * Removes a header.
+ */
+ public function remove(string $key)
+ {
+ $key = strtr($key, self::UPPER, self::LOWER);
+
+ unset($this->headers[$key]);
+
+ if ('cache-control' === $key) {
+ $this->cacheControl = [];
+ }
+ }
+
+ /**
+ * Returns the HTTP header value converted to a date.
+ *
+ * @return \DateTimeInterface|null The parsed DateTime or the default value if the header does not exist
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ */
+ public function getDate(string $key, \DateTime $default = null)
+ {
+ if (null === $value = $this->get($key)) {
+ return $default;
+ }
+
+ if (false === $date = \DateTime::createFromFormat(DATE_RFC2822, $value)) {
+ throw new \RuntimeException(sprintf('The %s HTTP header is not parseable (%s).', $key, $value));
+ }
+
+ return $date;
+ }
+
+ /**
+ * Adds a custom Cache-Control directive.
+ *
+ * @param mixed $value The Cache-Control directive value
+ */
+ public function addCacheControlDirective(string $key, $value = true)
+ {
+ $this->cacheControl[$key] = $value;
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns true if the Cache-Control directive is defined.
+ *
+ * @return bool true if the directive exists, false otherwise
+ */
+ public function hasCacheControlDirective(string $key)
+ {
+ return \array_key_exists($key, $this->cacheControl);
+ }
+
+ /**
+ * Returns a Cache-Control directive value by name.
+ *
+ * @return mixed|null The directive value if defined, null otherwise
+ */
+ public function getCacheControlDirective(string $key)
+ {
+ return \array_key_exists($key, $this->cacheControl) ? $this->cacheControl[$key] : null;
+ }
+
+ /**
+ * Removes a Cache-Control directive.
+ */
+ public function removeCacheControlDirective(string $key)
+ {
+ unset($this->cacheControl[$key]);
+
+ $this->set('Cache-Control', $this->getCacheControlHeader());
+ }
+
+ /**
+ * Returns an iterator for headers.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->headers);
+ }
+
+ /**
+ * Returns the number of headers.
+ *
+ * @return int The number of headers
+ */
+ public function count()
+ {
+ return \count($this->headers);
+ }
+
+ protected function getCacheControlHeader()
+ {
+ ksort($this->cacheControl);
+
+ return HeaderUtils::toString($this->cacheControl, ',');
+ }
+
+ /**
+ * Parses a Cache-Control HTTP header.
+ *
+ * @return array An array representing the attribute values
+ */
+ protected function parseCacheControl(string $header)
+ {
+ $parts = HeaderUtils::split($header, ',=');
+
+ return HeaderUtils::combine($parts);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderUtils.php b/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderUtils.php
new file mode 100644
index 00000000..5866e3b2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/HeaderUtils.php
@@ -0,0 +1,224 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * HTTP header utility functions.
+ *
+ * @author Christian Schmidt
+ */
+class HeaderUtils
+{
+ public const DISPOSITION_ATTACHMENT = 'attachment';
+ public const DISPOSITION_INLINE = 'inline';
+
+ /**
+ * This class should not be instantiated.
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Splits an HTTP header by one or more separators.
+ *
+ * Example:
+ *
+ * HeaderUtils::split("da, en-gb;q=0.8", ",;")
+ * // => ['da'], ['en-gb', 'q=0.8']]
+ *
+ * @param string $separators List of characters to split on, ordered by
+ * precedence, e.g. ",", ";=", or ",;="
+ *
+ * @return array Nested array with as many levels as there are characters in
+ * $separators
+ */
+ public static function split(string $header, string $separators): array
+ {
+ $quotedSeparators = preg_quote($separators, '/');
+
+ preg_match_all('
+ /
+ (?!\s)
+ (?:
+ # quoted-string
+ "(?:[^"\\\\]|\\\\.)*(?:"|\\\\|$)
+ |
+ # token
+ [^"'.$quotedSeparators.']+
+ )+
+ (?['.$quotedSeparators.'])
+ \s*
+ /x', trim($header), $matches, PREG_SET_ORDER);
+
+ return self::groupParts($matches, $separators);
+ }
+
+ /**
+ * Combines an array of arrays into one associative array.
+ *
+ * Each of the nested arrays should have one or two elements. The first
+ * value will be used as the keys in the associative array, and the second
+ * will be used as the values, or true if the nested array only contains one
+ * element. Array keys are lowercased.
+ *
+ * Example:
+ *
+ * HeaderUtils::combine([["foo", "abc"], ["bar"]])
+ * // => ["foo" => "abc", "bar" => true]
+ */
+ public static function combine(array $parts): array
+ {
+ $assoc = [];
+ foreach ($parts as $part) {
+ $name = strtolower($part[0]);
+ $value = $part[1] ?? true;
+ $assoc[$name] = $value;
+ }
+
+ return $assoc;
+ }
+
+ /**
+ * Joins an associative array into a string for use in an HTTP header.
+ *
+ * The key and value of each entry are joined with "=", and all entries
+ * are joined with the specified separator and an additional space (for
+ * readability). Values are quoted if necessary.
+ *
+ * Example:
+ *
+ * HeaderUtils::toString(["foo" => "abc", "bar" => true, "baz" => "a b c"], ",")
+ * // => 'foo=abc, bar, baz="a b c"'
+ */
+ public static function toString(array $assoc, string $separator): string
+ {
+ $parts = [];
+ foreach ($assoc as $name => $value) {
+ if (true === $value) {
+ $parts[] = $name;
+ } else {
+ $parts[] = $name.'='.self::quote($value);
+ }
+ }
+
+ return implode($separator.' ', $parts);
+ }
+
+ /**
+ * Encodes a string as a quoted string, if necessary.
+ *
+ * If a string contains characters not allowed by the "token" construct in
+ * the HTTP specification, it is backslash-escaped and enclosed in quotes
+ * to match the "quoted-string" construct.
+ */
+ public static function quote(string $s): string
+ {
+ if (preg_match('/^[a-z0-9!#$%&\'*.^_`|~-]+$/i', $s)) {
+ return $s;
+ }
+
+ return '"'.addcslashes($s, '"\\"').'"';
+ }
+
+ /**
+ * Decodes a quoted string.
+ *
+ * If passed an unquoted string that matches the "token" construct (as
+ * defined in the HTTP specification), it is passed through verbatimly.
+ */
+ public static function unquote(string $s): string
+ {
+ return preg_replace('/\\\\(.)|"/', '$1', $s);
+ }
+
+ /**
+ * 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 static function makeDisposition(string $disposition, string $filename, string $filenameFallback = ''): string
+ {
+ if (!\in_array($disposition, [self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE])) {
+ throw new \InvalidArgumentException(sprintf('The disposition must be either "%s" or "%s".', self::DISPOSITION_ATTACHMENT, self::DISPOSITION_INLINE));
+ }
+
+ 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.
+ if (false !== strpos($filename, '/') || false !== strpos($filename, '\\') || false !== strpos($filenameFallback, '/') || false !== strpos($filenameFallback, '\\')) {
+ throw new \InvalidArgumentException('The filename and the fallback cannot contain the "/" and "\\" characters.');
+ }
+
+ $params = ['filename' => $filenameFallback];
+ if ($filename !== $filenameFallback) {
+ $params['filename*'] = "utf-8''".rawurlencode($filename);
+ }
+
+ return $disposition.'; '.self::toString($params, ';');
+ }
+
+ private static function groupParts(array $matches, string $separators): array
+ {
+ $separator = $separators[0];
+ $partSeparators = substr($separators, 1);
+
+ $i = 0;
+ $partMatches = [];
+ foreach ($matches as $match) {
+ if (isset($match['separator']) && $match['separator'] === $separator) {
+ ++$i;
+ } else {
+ $partMatches[$i][] = $match;
+ }
+ }
+
+ $parts = [];
+ if ($partSeparators) {
+ foreach ($partMatches as $matches) {
+ $parts[] = self::groupParts($matches, $partSeparators);
+ }
+ } else {
+ foreach ($partMatches as $matches) {
+ $parts[] = self::unquote($matches[0][0]);
+ }
+ }
+
+ return $parts;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/IpUtils.php b/lam/lib/3rdParty/composer/symfony/http-foundation/IpUtils.php
new file mode 100644
index 00000000..80c5a950
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/IpUtils.php
@@ -0,0 +1,185 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Http utility functions.
+ *
+ * @author Fabien Potencier
+ */
+class IpUtils
+{
+ private static $checkedIps = [];
+
+ /**
+ * This class should not be instantiated.
+ */
+ private function __construct()
+ {
+ }
+
+ /**
+ * Checks if an IPv4 or IPv6 address is contained in the list of given IPs or subnets.
+ *
+ * @param string|array $ips List of IPs or subnets (can be a string if only a single one)
+ *
+ * @return bool Whether the IP is valid
+ */
+ public static function checkIp(?string $requestIp, $ips)
+ {
+ if (!\is_array($ips)) {
+ $ips = [$ips];
+ }
+
+ $method = substr_count($requestIp, ':') > 1 ? 'checkIp6' : 'checkIp4';
+
+ foreach ($ips as $ip) {
+ if (self::$method($requestIp, $ip)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Compares two IPv4 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @param string $ip IPv4 address or subnet in CIDR notation
+ *
+ * @return bool Whether the request IP matches the IP, or whether the request IP is within the CIDR subnet
+ */
+ public static function checkIp4(?string $requestIp, string $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!filter_var($requestIp, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return self::$checkedIps[$cacheKey] = filter_var($address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
+ }
+
+ if ($netmask < 0 || $netmask > 32) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 32;
+ }
+
+ if (false === ip2long($address)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ return self::$checkedIps[$cacheKey] = 0 === substr_compare(sprintf('%032b', ip2long($requestIp)), sprintf('%032b', ip2long($address)), 0, $netmask);
+ }
+
+ /**
+ * Compares two IPv6 addresses.
+ * In case a subnet is given, it checks if it contains the request IP.
+ *
+ * @author David Soria Parra
+ *
+ * @see https://github.com/dsp/v6tools
+ *
+ * @param string $ip IPv6 address or subnet in CIDR notation
+ *
+ * @return bool Whether the IP is valid
+ *
+ * @throws \RuntimeException When IPV6 support is not enabled
+ */
+ public static function checkIp6(?string $requestIp, string $ip)
+ {
+ $cacheKey = $requestIp.'-'.$ip;
+ if (isset(self::$checkedIps[$cacheKey])) {
+ return self::$checkedIps[$cacheKey];
+ }
+
+ if (!((\extension_loaded('sockets') && \defined('AF_INET6')) || @inet_pton('::1'))) {
+ throw new \RuntimeException('Unable to check Ipv6. Check that PHP was not compiled with option "disable-ipv6".');
+ }
+
+ if (false !== strpos($ip, '/')) {
+ list($address, $netmask) = explode('/', $ip, 2);
+
+ if ('0' === $netmask) {
+ return (bool) unpack('n*', @inet_pton($address));
+ }
+
+ if ($netmask < 1 || $netmask > 128) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ } else {
+ $address = $ip;
+ $netmask = 128;
+ }
+
+ $bytesAddr = unpack('n*', @inet_pton($address));
+ $bytesTest = unpack('n*', @inet_pton($requestIp));
+
+ if (!$bytesAddr || !$bytesTest) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+
+ for ($i = 1, $ceil = ceil($netmask / 16); $i <= $ceil; ++$i) {
+ $left = $netmask - 16 * ($i - 1);
+ $left = ($left <= 16) ? $left : 16;
+ $mask = ~(0xffff >> $left) & 0xffff;
+ if (($bytesAddr[$i] & $mask) != ($bytesTest[$i] & $mask)) {
+ return self::$checkedIps[$cacheKey] = false;
+ }
+ }
+
+ return self::$checkedIps[$cacheKey] = true;
+ }
+
+ /**
+ * Anonymizes an IP/IPv6.
+ *
+ * Removes the last byte for v4 and the last 8 bytes for v6 IPs
+ */
+ public static function anonymize(string $ip): string
+ {
+ $wrappedIPv6 = false;
+ if ('[' === substr($ip, 0, 1) && ']' === substr($ip, -1, 1)) {
+ $wrappedIPv6 = true;
+ $ip = substr($ip, 1, -1);
+ }
+
+ $packedAddress = inet_pton($ip);
+ if (4 === \strlen($packedAddress)) {
+ $mask = '255.255.255.0';
+ } elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff:ffff'))) {
+ $mask = '::ffff:ffff:ff00';
+ } elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff'))) {
+ $mask = '::ffff:ff00';
+ } else {
+ $mask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
+ }
+ $ip = inet_ntop($packedAddress & inet_pton($mask));
+
+ if ($wrappedIPv6) {
+ $ip = '['.$ip.']';
+ }
+
+ return $ip;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/JsonResponse.php b/lam/lib/3rdParty/composer/symfony/http-foundation/JsonResponse.php
new file mode 100644
index 00000000..8489bc0b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/JsonResponse.php
@@ -0,0 +1,215 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response in JSON format.
+ *
+ * Note that this class does not force the returned JSON content to be an
+ * object. It is however recommended that you do return an object as it
+ * protects yourself against XSSI and JSON-JavaScript Hijacking.
+ *
+ * @see https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/AJAX_Security_Cheat_Sheet.md#always-return-json-with-an-object-on-the-outside
+ *
+ * @author Igor Wiedler
+ */
+class JsonResponse extends Response
+{
+ protected $data;
+ protected $callback;
+
+ // Encode <, >, ', &, and " characters in the JSON, making it also safe to be embedded into HTML.
+ // 15 === JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
+ const DEFAULT_ENCODING_OPTIONS = 15;
+
+ protected $encodingOptions = self::DEFAULT_ENCODING_OPTIONS;
+
+ /**
+ * @param mixed $data The response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ * @param bool $json If the data is already a JSON string
+ */
+ public function __construct($data = null, int $status = 200, array $headers = [], bool $json = false)
+ {
+ parent::__construct('', $status, $headers);
+
+ if (null === $data) {
+ $data = new \ArrayObject();
+ }
+
+ $json ? $this->setJson($data) : $this->setData($data);
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return JsonResponse::create(['key' => 'value'])
+ * ->setSharedMaxAge(300);
+ *
+ * @param mixed $data The JSON response data
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function create($data = null, int $status = 200, array $headers = [])
+ {
+ return new static($data, $status, $headers);
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return JsonResponse::fromJsonString('{"key": "value"}')
+ * ->setSharedMaxAge(300);
+ *
+ * @param string|null $data The JSON response string
+ * @param int $status The response status code
+ * @param array $headers An array of response headers
+ *
+ * @return static
+ */
+ public static function fromJsonString(string $data = null, int $status = 200, array $headers = [])
+ {
+ return new static($data, $status, $headers, true);
+ }
+
+ /**
+ * Sets the JSONP callback.
+ *
+ * @param string|null $callback The JSONP callback or null to use none
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the callback name is not valid
+ */
+ public function setCallback(string $callback = null)
+ {
+ if (null !== $callback) {
+ // partially taken from https://geekality.net/2011/08/03/valid-javascript-identifier/
+ // partially taken from https://github.com/willdurand/JsonpCallbackValidator
+ // JsonpCallbackValidator is released under the MIT License. See https://github.com/willdurand/JsonpCallbackValidator/blob/v1.1.0/LICENSE for details.
+ // (c) William Durand
+ $pattern = '/^[$_\p{L}][$_\p{L}\p{Mn}\p{Mc}\p{Nd}\p{Pc}\x{200C}\x{200D}]*(?:\[(?:"(?:\\\.|[^"\\\])*"|\'(?:\\\.|[^\'\\\])*\'|\d+)\])*?$/u';
+ $reserved = [
+ 'break', 'do', 'instanceof', 'typeof', 'case', 'else', 'new', 'var', 'catch', 'finally', 'return', 'void', 'continue', 'for', 'switch', 'while',
+ 'debugger', 'function', 'this', 'with', 'default', 'if', 'throw', 'delete', 'in', 'try', 'class', 'enum', 'extends', 'super', 'const', 'export',
+ 'import', 'implements', 'let', 'private', 'public', 'yield', 'interface', 'package', 'protected', 'static', 'null', 'true', 'false',
+ ];
+ $parts = explode('.', $callback);
+ foreach ($parts as $part) {
+ if (!preg_match($pattern, $part) || \in_array($part, $reserved, true)) {
+ throw new \InvalidArgumentException('The callback name is not valid.');
+ }
+ }
+ }
+
+ $this->callback = $callback;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets a raw string containing a JSON document to be sent.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setJson(string $json)
+ {
+ $this->data = $json;
+
+ return $this->update();
+ }
+
+ /**
+ * Sets the data to be sent as JSON.
+ *
+ * @param mixed $data
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setData($data = [])
+ {
+ try {
+ $data = json_encode($data, $this->encodingOptions);
+ } catch (\Exception $e) {
+ if ('Exception' === \get_class($e) && 0 === strpos($e->getMessage(), 'Failed calling ')) {
+ throw $e->getPrevious() ?: $e;
+ }
+ throw $e;
+ }
+
+ if (\PHP_VERSION_ID >= 70300 && (JSON_THROW_ON_ERROR & $this->encodingOptions)) {
+ return $this->setJson($data);
+ }
+
+ if (JSON_ERROR_NONE !== json_last_error()) {
+ throw new \InvalidArgumentException(json_last_error_msg());
+ }
+
+ return $this->setJson($data);
+ }
+
+ /**
+ * Returns options used while encoding data to JSON.
+ *
+ * @return int
+ */
+ public function getEncodingOptions()
+ {
+ return $this->encodingOptions;
+ }
+
+ /**
+ * Sets options used while encoding data to JSON.
+ *
+ * @return $this
+ */
+ public function setEncodingOptions(int $encodingOptions)
+ {
+ $this->encodingOptions = $encodingOptions;
+
+ return $this->setData(json_decode($this->data));
+ }
+
+ /**
+ * Updates the content and headers according to the JSON data and callback.
+ *
+ * @return $this
+ */
+ protected function update()
+ {
+ if (null !== $this->callback) {
+ // Not using application/javascript for compatibility reasons with older browsers.
+ $this->headers->set('Content-Type', 'text/javascript');
+
+ return $this->setContent(sprintf('/**/%s(%s);', $this->callback, $this->data));
+ }
+
+ // Only set the header when there is none or when it equals 'text/javascript' (from a previous update with callback)
+ // in order to not overwrite a custom definition.
+ if (!$this->headers->has('Content-Type') || 'text/javascript' === $this->headers->get('Content-Type')) {
+ $this->headers->set('Content-Type', 'application/json');
+ }
+
+ return $this->setContent($this->data);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/LICENSE b/lam/lib/3rdParty/composer/symfony/http-foundation/LICENSE
new file mode 100644
index 00000000..a677f437
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2004-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/ParameterBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/ParameterBag.php
new file mode 100644
index 00000000..212149c7
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/ParameterBag.php
@@ -0,0 +1,205 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ParameterBag is a container for key/value pairs.
+ *
+ * @author Fabien Potencier
+ */
+class ParameterBag implements \IteratorAggregate, \Countable
+{
+ /**
+ * Parameter storage.
+ */
+ protected $parameters;
+
+ public function __construct(array $parameters = [])
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Returns the parameters.
+ *
+ * @return array An array of parameters
+ */
+ public function all()
+ {
+ return $this->parameters;
+ }
+
+ /**
+ * Returns the parameter keys.
+ *
+ * @return array An array of parameter keys
+ */
+ public function keys()
+ {
+ return array_keys($this->parameters);
+ }
+
+ /**
+ * Replaces the current parameters by a new set.
+ */
+ public function replace(array $parameters = [])
+ {
+ $this->parameters = $parameters;
+ }
+
+ /**
+ * Adds parameters.
+ */
+ public function add(array $parameters = [])
+ {
+ $this->parameters = array_replace($this->parameters, $parameters);
+ }
+
+ /**
+ * Returns a parameter by name.
+ *
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get(string $key, $default = null)
+ {
+ return \array_key_exists($key, $this->parameters) ? $this->parameters[$key] : $default;
+ }
+
+ /**
+ * Sets a parameter by name.
+ *
+ * @param mixed $value The value
+ */
+ public function set(string $key, $value)
+ {
+ $this->parameters[$key] = $value;
+ }
+
+ /**
+ * Returns true if the parameter is defined.
+ *
+ * @return bool true if the parameter exists, false otherwise
+ */
+ public function has(string $key)
+ {
+ return \array_key_exists($key, $this->parameters);
+ }
+
+ /**
+ * Removes a parameter.
+ */
+ public function remove(string $key)
+ {
+ unset($this->parameters[$key]);
+ }
+
+ /**
+ * Returns the alphabetic characters of the parameter value.
+ *
+ * @return string The filtered value
+ */
+ public function getAlpha(string $key, string $default = '')
+ {
+ return preg_replace('/[^[:alpha:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the alphabetic characters and digits of the parameter value.
+ *
+ * @return string The filtered value
+ */
+ public function getAlnum(string $key, string $default = '')
+ {
+ return preg_replace('/[^[:alnum:]]/', '', $this->get($key, $default));
+ }
+
+ /**
+ * Returns the digits of the parameter value.
+ *
+ * @return string The filtered value
+ */
+ public function getDigits(string $key, string $default = '')
+ {
+ // we need to remove - and + because they're allowed in the filter
+ return str_replace(['-', '+'], '', $this->filter($key, $default, FILTER_SANITIZE_NUMBER_INT));
+ }
+
+ /**
+ * Returns the parameter value converted to integer.
+ *
+ * @return int The filtered value
+ */
+ public function getInt(string $key, int $default = 0)
+ {
+ return (int) $this->get($key, $default);
+ }
+
+ /**
+ * Returns the parameter value converted to boolean.
+ *
+ * @return bool The filtered value
+ */
+ public function getBoolean(string $key, bool $default = false)
+ {
+ return $this->filter($key, $default, FILTER_VALIDATE_BOOLEAN);
+ }
+
+ /**
+ * Filter key.
+ *
+ * @param mixed $default Default = null
+ * @param int $filter FILTER_* constant
+ * @param mixed $options Filter options
+ *
+ * @see https://php.net/filter-var
+ *
+ * @return mixed
+ */
+ public function filter(string $key, $default = null, int $filter = FILTER_DEFAULT, $options = [])
+ {
+ $value = $this->get($key, $default);
+
+ // Always turn $options into an array - this allows filter_var option shortcuts.
+ if (!\is_array($options) && $options) {
+ $options = ['flags' => $options];
+ }
+
+ // Add a convenience check for arrays.
+ if (\is_array($value) && !isset($options['flags'])) {
+ $options['flags'] = FILTER_REQUIRE_ARRAY;
+ }
+
+ return filter_var($value, $filter, $options);
+ }
+
+ /**
+ * Returns an iterator for parameters.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->parameters);
+ }
+
+ /**
+ * Returns the number of parameters.
+ *
+ * @return int The number of parameters
+ */
+ public function count()
+ {
+ return \count($this->parameters);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/README.md b/lam/lib/3rdParty/composer/symfony/http-foundation/README.md
new file mode 100644
index 00000000..8907f0b9
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/README.md
@@ -0,0 +1,14 @@
+HttpFoundation Component
+========================
+
+The HttpFoundation component defines an object-oriented layer for the HTTP
+specification.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/http_foundation/index.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/RedirectResponse.php b/lam/lib/3rdParty/composer/symfony/http-foundation/RedirectResponse.php
new file mode 100644
index 00000000..13da56a7
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/RedirectResponse.php
@@ -0,0 +1,105 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RedirectResponse represents an HTTP response doing a redirect.
+ *
+ * @author Fabien Potencier
+ */
+class RedirectResponse extends Response
+{
+ protected $targetUrl;
+
+ /**
+ * Creates a redirect response so that it conforms to the rules defined for a redirect status code.
+ *
+ * @param string $url The URL to redirect to. The URL should be a full URL, with schema etc.,
+ * but practically every browser redirects on paths only as well
+ * @param int $status The status code (302 by default)
+ * @param array $headers The headers (Location is always set to the given URL)
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @see https://tools.ietf.org/html/rfc2616#section-10.3
+ */
+ public function __construct(string $url, int $status = 302, array $headers = [])
+ {
+ parent::__construct('', $status, $headers);
+
+ $this->setTargetUrl($url);
+
+ if (!$this->isRedirect()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code is not a redirect ("%s" given).', $status));
+ }
+
+ if (301 == $status && !\array_key_exists('cache-control', array_change_key_case($headers, \CASE_LOWER))) {
+ $this->headers->remove('cache-control');
+ }
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * @param string $url The URL to redirect to
+ *
+ * @return static
+ */
+ public static function create($url = '', int $status = 302, array $headers = [])
+ {
+ return new static($url, $status, $headers);
+ }
+
+ /**
+ * Returns the target URL.
+ *
+ * @return string target URL
+ */
+ public function getTargetUrl()
+ {
+ return $this->targetUrl;
+ }
+
+ /**
+ * Sets the redirect target of this response.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setTargetUrl(string $url)
+ {
+ if ('' === $url) {
+ throw new \InvalidArgumentException('Cannot redirect to an empty URL.');
+ }
+
+ $this->targetUrl = $url;
+
+ $this->setContent(
+ sprintf('
+
+
+
+
+
+ Redirecting to %1$s
+
+
+ Redirecting to %1$s.
+
+', htmlspecialchars($url, ENT_QUOTES, 'UTF-8')));
+
+ $this->headers->set('Location', $url);
+
+ return $this;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Request.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Request.php
new file mode 100644
index 00000000..1ab74d7e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Request.php
@@ -0,0 +1,2060 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\HttpFoundation\Exception\ConflictingHeadersException;
+use Symfony\Component\HttpFoundation\Exception\SuspiciousOperationException;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+
+/**
+ * Request represents an HTTP request.
+ *
+ * The methods dealing with URL accept / return a raw path (% encoded):
+ * * getBasePath
+ * * getBaseUrl
+ * * getPathInfo
+ * * getRequestUri
+ * * getUri
+ * * getUriForPath
+ *
+ * @author Fabien Potencier
+ */
+class Request
+{
+ const HEADER_FORWARDED = 0b00001; // When using RFC 7239
+ const HEADER_X_FORWARDED_FOR = 0b00010;
+ const HEADER_X_FORWARDED_HOST = 0b00100;
+ const HEADER_X_FORWARDED_PROTO = 0b01000;
+ const HEADER_X_FORWARDED_PORT = 0b10000;
+ const HEADER_X_FORWARDED_ALL = 0b11110; // All "X-Forwarded-*" headers
+ const HEADER_X_FORWARDED_AWS_ELB = 0b11010; // AWS ELB doesn't send X-Forwarded-Host
+
+ const METHOD_HEAD = 'HEAD';
+ const METHOD_GET = 'GET';
+ const METHOD_POST = 'POST';
+ const METHOD_PUT = 'PUT';
+ const METHOD_PATCH = 'PATCH';
+ const METHOD_DELETE = 'DELETE';
+ const METHOD_PURGE = 'PURGE';
+ const METHOD_OPTIONS = 'OPTIONS';
+ const METHOD_TRACE = 'TRACE';
+ const METHOD_CONNECT = 'CONNECT';
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedProxies = [];
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHostPatterns = [];
+
+ /**
+ * @var string[]
+ */
+ protected static $trustedHosts = [];
+
+ protected static $httpMethodParameterOverride = false;
+
+ /**
+ * Custom parameters.
+ *
+ * @var ParameterBag
+ */
+ public $attributes;
+
+ /**
+ * Request body parameters ($_POST).
+ *
+ * @var ParameterBag
+ */
+ public $request;
+
+ /**
+ * Query string parameters ($_GET).
+ *
+ * @var ParameterBag
+ */
+ public $query;
+
+ /**
+ * Server and execution environment parameters ($_SERVER).
+ *
+ * @var ServerBag
+ */
+ public $server;
+
+ /**
+ * Uploaded files ($_FILES).
+ *
+ * @var FileBag
+ */
+ public $files;
+
+ /**
+ * Cookies ($_COOKIE).
+ *
+ * @var ParameterBag
+ */
+ public $cookies;
+
+ /**
+ * Headers (taken from the $_SERVER).
+ *
+ * @var HeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string|resource|false|null
+ */
+ protected $content;
+
+ /**
+ * @var array
+ */
+ protected $languages;
+
+ /**
+ * @var array
+ */
+ protected $charsets;
+
+ /**
+ * @var array
+ */
+ protected $encodings;
+
+ /**
+ * @var array
+ */
+ protected $acceptableContentTypes;
+
+ /**
+ * @var string
+ */
+ protected $pathInfo;
+
+ /**
+ * @var string
+ */
+ protected $requestUri;
+
+ /**
+ * @var string
+ */
+ protected $baseUrl;
+
+ /**
+ * @var string
+ */
+ protected $basePath;
+
+ /**
+ * @var string
+ */
+ protected $method;
+
+ /**
+ * @var string
+ */
+ protected $format;
+
+ /**
+ * @var SessionInterface
+ */
+ protected $session;
+
+ /**
+ * @var string
+ */
+ protected $locale;
+
+ /**
+ * @var string
+ */
+ protected $defaultLocale = 'en';
+
+ /**
+ * @var array
+ */
+ protected static $formats;
+
+ protected static $requestFactory;
+
+ /**
+ * @var string|null
+ */
+ private $preferredFormat;
+ private $isHostValid = true;
+ private $isForwardedValid = true;
+
+ private static $trustedHeaderSet = -1;
+
+ private static $forwardedParams = [
+ self::HEADER_X_FORWARDED_FOR => 'for',
+ self::HEADER_X_FORWARDED_HOST => 'host',
+ self::HEADER_X_FORWARDED_PROTO => 'proto',
+ self::HEADER_X_FORWARDED_PORT => 'host',
+ ];
+
+ /**
+ * Names for headers that can be trusted when
+ * using trusted proxies.
+ *
+ * The FORWARDED header is the standard as of rfc7239.
+ *
+ * The other headers are non-standard, but widely used
+ * by popular reverse proxies (like Apache mod_proxy or Amazon EC2).
+ */
+ private static $trustedHeaders = [
+ self::HEADER_FORWARDED => 'FORWARDED',
+ self::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
+ self::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
+ self::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
+ self::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
+ ];
+
+ /**
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function __construct(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+ {
+ $this->initialize($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets the parameters for this request.
+ *
+ * This method also re-initializes all properties.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ * @param string|resource|null $content The raw body data
+ */
+ public function initialize(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+ {
+ $this->request = new ParameterBag($request);
+ $this->query = new ParameterBag($query);
+ $this->attributes = new ParameterBag($attributes);
+ $this->cookies = new ParameterBag($cookies);
+ $this->files = new FileBag($files);
+ $this->server = new ServerBag($server);
+ $this->headers = new HeaderBag($this->server->getHeaders());
+
+ $this->content = $content;
+ $this->languages = null;
+ $this->charsets = null;
+ $this->encodings = null;
+ $this->acceptableContentTypes = null;
+ $this->pathInfo = null;
+ $this->requestUri = null;
+ $this->baseUrl = null;
+ $this->basePath = null;
+ $this->method = null;
+ $this->format = null;
+ }
+
+ /**
+ * Creates a new request with values from PHP's super globals.
+ *
+ * @return static
+ */
+ public static function createFromGlobals()
+ {
+ $request = self::createRequestFromFactory($_GET, $_POST, [], $_COOKIE, $_FILES, $_SERVER);
+
+ if (0 === strpos($request->headers->get('CONTENT_TYPE'), 'application/x-www-form-urlencoded')
+ && \in_array(strtoupper($request->server->get('REQUEST_METHOD', 'GET')), ['PUT', 'DELETE', 'PATCH'])
+ ) {
+ parse_str($request->getContent(), $data);
+ $request->request = new ParameterBag($data);
+ }
+
+ return $request;
+ }
+
+ /**
+ * Creates a Request based on a given URI and configuration.
+ *
+ * The information contained in the URI always take precedence
+ * over the other information (server and parameters).
+ *
+ * @param string $uri The URI
+ * @param string $method The HTTP method
+ * @param array $parameters The query (GET) or request (POST) parameters
+ * @param array $cookies The request cookies ($_COOKIE)
+ * @param array $files The request files ($_FILES)
+ * @param array $server The server parameters ($_SERVER)
+ * @param string|resource|null $content The raw body data
+ *
+ * @return static
+ */
+ public static function create(string $uri, string $method = 'GET', array $parameters = [], array $cookies = [], array $files = [], array $server = [], $content = null)
+ {
+ $server = array_replace([
+ 'SERVER_NAME' => 'localhost',
+ 'SERVER_PORT' => 80,
+ 'HTTP_HOST' => 'localhost',
+ 'HTTP_USER_AGENT' => 'Symfony',
+ 'HTTP_ACCEPT' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
+ 'HTTP_ACCEPT_LANGUAGE' => 'en-us,en;q=0.5',
+ 'HTTP_ACCEPT_CHARSET' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
+ 'REMOTE_ADDR' => '127.0.0.1',
+ 'SCRIPT_NAME' => '',
+ 'SCRIPT_FILENAME' => '',
+ 'SERVER_PROTOCOL' => 'HTTP/1.1',
+ 'REQUEST_TIME' => time(),
+ ], $server);
+
+ $server['PATH_INFO'] = '';
+ $server['REQUEST_METHOD'] = strtoupper($method);
+
+ $components = parse_url($uri);
+ if (isset($components['host'])) {
+ $server['SERVER_NAME'] = $components['host'];
+ $server['HTTP_HOST'] = $components['host'];
+ }
+
+ if (isset($components['scheme'])) {
+ if ('https' === $components['scheme']) {
+ $server['HTTPS'] = 'on';
+ $server['SERVER_PORT'] = 443;
+ } else {
+ unset($server['HTTPS']);
+ $server['SERVER_PORT'] = 80;
+ }
+ }
+
+ if (isset($components['port'])) {
+ $server['SERVER_PORT'] = $components['port'];
+ $server['HTTP_HOST'] .= ':'.$components['port'];
+ }
+
+ if (isset($components['user'])) {
+ $server['PHP_AUTH_USER'] = $components['user'];
+ }
+
+ if (isset($components['pass'])) {
+ $server['PHP_AUTH_PW'] = $components['pass'];
+ }
+
+ if (!isset($components['path'])) {
+ $components['path'] = '/';
+ }
+
+ switch (strtoupper($method)) {
+ case 'POST':
+ case 'PUT':
+ case 'DELETE':
+ if (!isset($server['CONTENT_TYPE'])) {
+ $server['CONTENT_TYPE'] = 'application/x-www-form-urlencoded';
+ }
+ // no break
+ case 'PATCH':
+ $request = $parameters;
+ $query = [];
+ break;
+ default:
+ $request = [];
+ $query = $parameters;
+ break;
+ }
+
+ $queryString = '';
+ if (isset($components['query'])) {
+ parse_str(html_entity_decode($components['query']), $qs);
+
+ if ($query) {
+ $query = array_replace($qs, $query);
+ $queryString = http_build_query($query, '', '&');
+ } else {
+ $query = $qs;
+ $queryString = $components['query'];
+ }
+ } elseif ($query) {
+ $queryString = http_build_query($query, '', '&');
+ }
+
+ $server['REQUEST_URI'] = $components['path'].('' !== $queryString ? '?'.$queryString : '');
+ $server['QUERY_STRING'] = $queryString;
+
+ return self::createRequestFromFactory($query, $request, [], $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Sets a callable able to create a Request instance.
+ *
+ * This is mainly useful when you need to override the Request class
+ * to keep BC with an existing system. It should not be used for any
+ * other purpose.
+ */
+ public static function setFactory(?callable $callable)
+ {
+ self::$requestFactory = $callable;
+ }
+
+ /**
+ * Clones a request and overrides some of its parameters.
+ *
+ * @param array $query The GET parameters
+ * @param array $request The POST parameters
+ * @param array $attributes The request attributes (parameters parsed from the PATH_INFO, ...)
+ * @param array $cookies The COOKIE parameters
+ * @param array $files The FILES parameters
+ * @param array $server The SERVER parameters
+ *
+ * @return static
+ */
+ public function duplicate(array $query = null, array $request = null, array $attributes = null, array $cookies = null, array $files = null, array $server = null)
+ {
+ $dup = clone $this;
+ if (null !== $query) {
+ $dup->query = new ParameterBag($query);
+ }
+ if (null !== $request) {
+ $dup->request = new ParameterBag($request);
+ }
+ if (null !== $attributes) {
+ $dup->attributes = new ParameterBag($attributes);
+ }
+ if (null !== $cookies) {
+ $dup->cookies = new ParameterBag($cookies);
+ }
+ if (null !== $files) {
+ $dup->files = new FileBag($files);
+ }
+ if (null !== $server) {
+ $dup->server = new ServerBag($server);
+ $dup->headers = new HeaderBag($dup->server->getHeaders());
+ }
+ $dup->languages = null;
+ $dup->charsets = null;
+ $dup->encodings = null;
+ $dup->acceptableContentTypes = null;
+ $dup->pathInfo = null;
+ $dup->requestUri = null;
+ $dup->baseUrl = null;
+ $dup->basePath = null;
+ $dup->method = null;
+ $dup->format = null;
+
+ if (!$dup->get('_format') && $this->get('_format')) {
+ $dup->attributes->set('_format', $this->get('_format'));
+ }
+
+ if (!$dup->getRequestFormat(null)) {
+ $dup->setRequestFormat($this->getRequestFormat(null));
+ }
+
+ return $dup;
+ }
+
+ /**
+ * Clones the current request.
+ *
+ * Note that the session is not cloned as duplicated requests
+ * are most of the time sub-requests of the main one.
+ */
+ public function __clone()
+ {
+ $this->query = clone $this->query;
+ $this->request = clone $this->request;
+ $this->attributes = clone $this->attributes;
+ $this->cookies = clone $this->cookies;
+ $this->files = clone $this->files;
+ $this->server = clone $this->server;
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Returns the request as a string.
+ *
+ * @return string The request
+ */
+ public function __toString()
+ {
+ try {
+ $content = $this->getContent();
+ } catch (\LogicException $e) {
+ if (\PHP_VERSION_ID >= 70400) {
+ throw $e;
+ }
+
+ return trigger_error($e, E_USER_ERROR);
+ }
+
+ $cookieHeader = '';
+ $cookies = [];
+
+ foreach ($this->cookies as $k => $v) {
+ $cookies[] = $k.'='.$v;
+ }
+
+ if (!empty($cookies)) {
+ $cookieHeader = 'Cookie: '.implode('; ', $cookies)."\r\n";
+ }
+
+ return
+ sprintf('%s %s %s', $this->getMethod(), $this->getRequestUri(), $this->server->get('SERVER_PROTOCOL'))."\r\n".
+ $this->headers.
+ $cookieHeader."\r\n".
+ $content;
+ }
+
+ /**
+ * Overrides the PHP global variables according to this request instance.
+ *
+ * It overrides $_GET, $_POST, $_REQUEST, $_SERVER, $_COOKIE.
+ * $_FILES is never overridden, see rfc1867
+ */
+ public function overrideGlobals()
+ {
+ $this->server->set('QUERY_STRING', static::normalizeQueryString(http_build_query($this->query->all(), '', '&')));
+
+ $_GET = $this->query->all();
+ $_POST = $this->request->all();
+ $_SERVER = $this->server->all();
+ $_COOKIE = $this->cookies->all();
+
+ foreach ($this->headers->all() as $key => $value) {
+ $key = strtoupper(str_replace('-', '_', $key));
+ if (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
+ $_SERVER[$key] = implode(', ', $value);
+ } else {
+ $_SERVER['HTTP_'.$key] = implode(', ', $value);
+ }
+ }
+
+ $request = ['g' => $_GET, 'p' => $_POST, 'c' => $_COOKIE];
+
+ $requestOrder = ini_get('request_order') ?: ini_get('variables_order');
+ $requestOrder = preg_replace('#[^cgp]#', '', strtolower($requestOrder)) ?: 'gp';
+
+ $_REQUEST = [[]];
+
+ foreach (str_split($requestOrder) as $order) {
+ $_REQUEST[] = $request[$order];
+ }
+
+ $_REQUEST = array_merge(...$_REQUEST);
+ }
+
+ /**
+ * Sets a list of trusted proxies.
+ *
+ * You should only list the reverse proxies that you manage directly.
+ *
+ * @param array $proxies A list of trusted proxies, the string 'REMOTE_ADDR' will be replaced with $_SERVER['REMOTE_ADDR']
+ * @param int $trustedHeaderSet A bit field of Request::HEADER_*, to set which headers to trust from your proxies
+ *
+ * @throws \InvalidArgumentException When $trustedHeaderSet is invalid
+ */
+ public static function setTrustedProxies(array $proxies, int $trustedHeaderSet)
+ {
+ self::$trustedProxies = array_reduce($proxies, function ($proxies, $proxy) {
+ if ('REMOTE_ADDR' !== $proxy) {
+ $proxies[] = $proxy;
+ } elseif (isset($_SERVER['REMOTE_ADDR'])) {
+ $proxies[] = $_SERVER['REMOTE_ADDR'];
+ }
+
+ return $proxies;
+ }, []);
+ self::$trustedHeaderSet = $trustedHeaderSet;
+ }
+
+ /**
+ * Gets the list of trusted proxies.
+ *
+ * @return array An array of trusted proxies
+ */
+ public static function getTrustedProxies()
+ {
+ return self::$trustedProxies;
+ }
+
+ /**
+ * Gets the set of trusted headers from trusted proxies.
+ *
+ * @return int A bit field of Request::HEADER_* that defines which headers are trusted from your proxies
+ */
+ public static function getTrustedHeaderSet()
+ {
+ return self::$trustedHeaderSet;
+ }
+
+ /**
+ * Sets a list of trusted host patterns.
+ *
+ * You should only list the hosts you manage using regexs.
+ *
+ * @param array $hostPatterns A list of trusted host patterns
+ */
+ public static function setTrustedHosts(array $hostPatterns)
+ {
+ self::$trustedHostPatterns = array_map(function ($hostPattern) {
+ return sprintf('{%s}i', $hostPattern);
+ }, $hostPatterns);
+ // we need to reset trusted hosts on trusted host patterns change
+ self::$trustedHosts = [];
+ }
+
+ /**
+ * Gets the list of trusted host patterns.
+ *
+ * @return array An array of trusted host patterns
+ */
+ public static function getTrustedHosts()
+ {
+ return self::$trustedHostPatterns;
+ }
+
+ /**
+ * Normalizes a query string.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized,
+ * have consistent escaping and unneeded delimiters are removed.
+ *
+ * @return string A normalized query string for the Request
+ */
+ public static function normalizeQueryString(?string $qs)
+ {
+ if ('' === ($qs ?? '')) {
+ return '';
+ }
+
+ parse_str($qs, $qs);
+ ksort($qs);
+
+ return http_build_query($qs, '', '&', PHP_QUERY_RFC3986);
+ }
+
+ /**
+ * Enables support for the _method request parameter to determine the intended HTTP method.
+ *
+ * Be warned that enabling this feature might lead to CSRF issues in your code.
+ * Check that you are using CSRF tokens when required.
+ * If the HTTP method parameter override is enabled, an html-form with method "POST" can be altered
+ * and used to send a "PUT" or "DELETE" request via the _method request parameter.
+ * If these methods are not protected against CSRF, this presents a possible vulnerability.
+ *
+ * The HTTP method can only be overridden when the real HTTP method is POST.
+ */
+ public static function enableHttpMethodParameterOverride()
+ {
+ self::$httpMethodParameterOverride = true;
+ }
+
+ /**
+ * Checks whether support for the _method request parameter is enabled.
+ *
+ * @return bool True when the _method request parameter is enabled, false otherwise
+ */
+ public static function getHttpMethodParameterOverride()
+ {
+ return self::$httpMethodParameterOverride;
+ }
+
+ /**
+ * Gets a "parameter" value from any bag.
+ *
+ * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
+ * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
+ * public property instead (attributes, query, request).
+ *
+ * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
+ *
+ * @param mixed $default The default value if the parameter key does not exist
+ *
+ * @return mixed
+ */
+ public function get(string $key, $default = null)
+ {
+ if ($this !== $result = $this->attributes->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->query->get($key, $this)) {
+ return $result;
+ }
+
+ if ($this !== $result = $this->request->get($key, $this)) {
+ return $result;
+ }
+
+ return $default;
+ }
+
+ /**
+ * Gets the Session.
+ *
+ * @return SessionInterface The session
+ */
+ public function getSession()
+ {
+ $session = $this->session;
+ if (!$session instanceof SessionInterface && null !== $session) {
+ $this->setSession($session = $session());
+ }
+
+ if (null === $session) {
+ throw new \BadMethodCallException('Session has not been set.');
+ }
+
+ return $session;
+ }
+
+ /**
+ * Whether the request contains a Session which was started in one of the
+ * previous requests.
+ *
+ * @return bool
+ */
+ public function hasPreviousSession()
+ {
+ // the check for $this->session avoids malicious users trying to fake a session cookie with proper name
+ return $this->hasSession() && $this->cookies->has($this->getSession()->getName());
+ }
+
+ /**
+ * Whether the request contains a Session object.
+ *
+ * This method does not give any information about the state of the session object,
+ * like whether the session is started or not. It is just a way to check if this Request
+ * is associated with a Session instance.
+ *
+ * @return bool true when the Request contains a Session object, false otherwise
+ */
+ public function hasSession()
+ {
+ return null !== $this->session;
+ }
+
+ public function setSession(SessionInterface $session)
+ {
+ $this->session = $session;
+ }
+
+ /**
+ * @internal
+ */
+ public function setSessionFactory(callable $factory)
+ {
+ $this->session = $factory;
+ }
+
+ /**
+ * Returns the client IP addresses.
+ *
+ * In the returned array the most trusted IP address is first, and the
+ * least trusted one last. The "real" client IP address is the last one,
+ * but this is also the least trusted one. Trusted proxies are stripped.
+ *
+ * Use this method carefully; you should use getClientIp() instead.
+ *
+ * @return array The client IP addresses
+ *
+ * @see getClientIp()
+ */
+ public function getClientIps()
+ {
+ $ip = $this->server->get('REMOTE_ADDR');
+
+ if (!$this->isFromTrustedProxy()) {
+ return [$ip];
+ }
+
+ return $this->getTrustedValues(self::HEADER_X_FORWARDED_FOR, $ip) ?: [$ip];
+ }
+
+ /**
+ * Returns the client IP address.
+ *
+ * This method can read the client IP address from the "X-Forwarded-For" header
+ * when trusted proxies were set via "setTrustedProxies()". The "X-Forwarded-For"
+ * header value is a comma+space separated list of IP addresses, the left-most
+ * being the original client, and each successive proxy that passed the request
+ * adding the IP address where it received the request from.
+ *
+ * If your reverse proxy uses a different header name than "X-Forwarded-For",
+ * ("Client-Ip" for instance), configure it via the $trustedHeaderSet
+ * argument of the Request::setTrustedProxies() method instead.
+ *
+ * @return string|null The client IP address
+ *
+ * @see getClientIps()
+ * @see https://wikipedia.org/wiki/X-Forwarded-For
+ */
+ public function getClientIp()
+ {
+ $ipAddresses = $this->getClientIps();
+
+ return $ipAddresses[0];
+ }
+
+ /**
+ * Returns current script name.
+ *
+ * @return string
+ */
+ public function getScriptName()
+ {
+ return $this->server->get('SCRIPT_NAME', $this->server->get('ORIG_SCRIPT_NAME', ''));
+ }
+
+ /**
+ * Returns the path being requested relative to the executed script.
+ *
+ * The path info always starts with a /.
+ *
+ * Suppose this request is instantiated from /mysite on localhost:
+ *
+ * * http://localhost/mysite returns an empty string
+ * * http://localhost/mysite/about returns '/about'
+ * * http://localhost/mysite/enco%20ded returns '/enco%20ded'
+ * * http://localhost/mysite/about?var=1 returns '/about'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getPathInfo()
+ {
+ if (null === $this->pathInfo) {
+ $this->pathInfo = $this->preparePathInfo();
+ }
+
+ return $this->pathInfo;
+ }
+
+ /**
+ * Returns the root path from which this request is executed.
+ *
+ * Suppose that an index.php file instantiates this request object:
+ *
+ * * http://localhost/index.php returns an empty string
+ * * http://localhost/index.php/page returns an empty string
+ * * http://localhost/web/index.php returns '/web'
+ * * http://localhost/we%20b/index.php returns '/we%20b'
+ *
+ * @return string The raw path (i.e. not urldecoded)
+ */
+ public function getBasePath()
+ {
+ if (null === $this->basePath) {
+ $this->basePath = $this->prepareBasePath();
+ }
+
+ return $this->basePath;
+ }
+
+ /**
+ * Returns the root URL from which this request is executed.
+ *
+ * The base URL never ends with a /.
+ *
+ * This is similar to getBasePath(), except that it also includes the
+ * script filename (e.g. index.php) if one exists.
+ *
+ * @return string The raw URL (i.e. not urldecoded)
+ */
+ public function getBaseUrl()
+ {
+ if (null === $this->baseUrl) {
+ $this->baseUrl = $this->prepareBaseUrl();
+ }
+
+ return $this->baseUrl;
+ }
+
+ /**
+ * Gets the request's scheme.
+ *
+ * @return string
+ */
+ public function getScheme()
+ {
+ return $this->isSecure() ? 'https' : 'http';
+ }
+
+ /**
+ * Returns the port on which the request is made.
+ *
+ * This method can read the client port from the "X-Forwarded-Port" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Port" header must contain the client port.
+ *
+ * @return int|string can be a string if fetched from the server bag
+ */
+ public function getPort()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_PORT)) {
+ $host = $host[0];
+ } elseif ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ return $this->server->get('SERVER_PORT');
+ }
+
+ if ('[' === $host[0]) {
+ $pos = strpos($host, ':', strrpos($host, ']'));
+ } else {
+ $pos = strrpos($host, ':');
+ }
+
+ if (false !== $pos && $port = substr($host, $pos + 1)) {
+ return (int) $port;
+ }
+
+ return 'https' === $this->getScheme() ? 443 : 80;
+ }
+
+ /**
+ * Returns the user.
+ *
+ * @return string|null
+ */
+ public function getUser()
+ {
+ return $this->headers->get('PHP_AUTH_USER');
+ }
+
+ /**
+ * Returns the password.
+ *
+ * @return string|null
+ */
+ public function getPassword()
+ {
+ return $this->headers->get('PHP_AUTH_PW');
+ }
+
+ /**
+ * Gets the user info.
+ *
+ * @return string A user name and, optionally, scheme-specific information about how to gain authorization to access the server
+ */
+ public function getUserInfo()
+ {
+ $userinfo = $this->getUser();
+
+ $pass = $this->getPassword();
+ if ('' != $pass) {
+ $userinfo .= ":$pass";
+ }
+
+ return $userinfo;
+ }
+
+ /**
+ * Returns the HTTP host being requested.
+ *
+ * The port name will be appended to the host if it's non-standard.
+ *
+ * @return string
+ */
+ public function getHttpHost()
+ {
+ $scheme = $this->getScheme();
+ $port = $this->getPort();
+
+ if (('http' == $scheme && 80 == $port) || ('https' == $scheme && 443 == $port)) {
+ return $this->getHost();
+ }
+
+ return $this->getHost().':'.$port;
+ }
+
+ /**
+ * Returns the requested URI (path and query string).
+ *
+ * @return string The raw URI (i.e. not URI decoded)
+ */
+ public function getRequestUri()
+ {
+ if (null === $this->requestUri) {
+ $this->requestUri = $this->prepareRequestUri();
+ }
+
+ return $this->requestUri;
+ }
+
+ /**
+ * Gets the scheme and HTTP host.
+ *
+ * If the URL was called with basic authentication, the user
+ * and the password are not added to the generated string.
+ *
+ * @return string The scheme and HTTP host
+ */
+ public function getSchemeAndHttpHost()
+ {
+ return $this->getScheme().'://'.$this->getHttpHost();
+ }
+
+ /**
+ * Generates a normalized URI (URL) for the Request.
+ *
+ * @return string A normalized URI (URL) for the Request
+ *
+ * @see getQueryString()
+ */
+ public function getUri()
+ {
+ if (null !== $qs = $this->getQueryString()) {
+ $qs = '?'.$qs;
+ }
+
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$this->getPathInfo().$qs;
+ }
+
+ /**
+ * Generates a normalized URI for the given path.
+ *
+ * @param string $path A path to use instead of the current one
+ *
+ * @return string The normalized URI for the path
+ */
+ public function getUriForPath(string $path)
+ {
+ return $this->getSchemeAndHttpHost().$this->getBaseUrl().$path;
+ }
+
+ /**
+ * Returns the path as relative reference from the current Request path.
+ *
+ * Only the URIs path component (no schema, host etc.) is relevant and must be given.
+ * Both paths must be absolute and not contain relative parts.
+ * Relative URLs from one resource to another are useful when generating self-contained downloadable document archives.
+ * Furthermore, they can be used to reduce the link size in documents.
+ *
+ * Example target paths, given a base path of "/a/b/c/d":
+ * - "/a/b/c/d" -> ""
+ * - "/a/b/c/" -> "./"
+ * - "/a/b/" -> "../"
+ * - "/a/b/c/other" -> "other"
+ * - "/a/x/y" -> "../../x/y"
+ *
+ * @return string The relative target path
+ */
+ public function getRelativeUriForPath(string $path)
+ {
+ // be sure that we are dealing with an absolute path
+ if (!isset($path[0]) || '/' !== $path[0]) {
+ return $path;
+ }
+
+ if ($path === $basePath = $this->getPathInfo()) {
+ return '';
+ }
+
+ $sourceDirs = explode('/', isset($basePath[0]) && '/' === $basePath[0] ? substr($basePath, 1) : $basePath);
+ $targetDirs = explode('/', substr($path, 1));
+ array_pop($sourceDirs);
+ $targetFile = array_pop($targetDirs);
+
+ foreach ($sourceDirs as $i => $dir) {
+ if (isset($targetDirs[$i]) && $dir === $targetDirs[$i]) {
+ unset($sourceDirs[$i], $targetDirs[$i]);
+ } else {
+ break;
+ }
+ }
+
+ $targetDirs[] = $targetFile;
+ $path = str_repeat('../', \count($sourceDirs)).implode('/', $targetDirs);
+
+ // A reference to the same base directory or an empty subdirectory must be prefixed with "./".
+ // This also applies to a segment with a colon character (e.g., "file:colon") that cannot be used
+ // as the first segment of a relative-path reference, as it would be mistaken for a scheme name
+ // (see https://tools.ietf.org/html/rfc3986#section-4.2).
+ return !isset($path[0]) || '/' === $path[0]
+ || false !== ($colonPos = strpos($path, ':')) && ($colonPos < ($slashPos = strpos($path, '/')) || false === $slashPos)
+ ? "./$path" : $path;
+ }
+
+ /**
+ * Generates the normalized query string for the Request.
+ *
+ * It builds a normalized query string, where keys/value pairs are alphabetized
+ * and have consistent escaping.
+ *
+ * @return string|null A normalized query string for the Request
+ */
+ public function getQueryString()
+ {
+ $qs = static::normalizeQueryString($this->server->get('QUERY_STRING'));
+
+ return '' === $qs ? null : $qs;
+ }
+
+ /**
+ * Checks whether the request is secure or not.
+ *
+ * This method can read the client protocol from the "X-Forwarded-Proto" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Proto" header must contain the protocol: "https" or "http".
+ *
+ * @return bool
+ */
+ public function isSecure()
+ {
+ if ($this->isFromTrustedProxy() && $proto = $this->getTrustedValues(self::HEADER_X_FORWARDED_PROTO)) {
+ return \in_array(strtolower($proto[0]), ['https', 'on', 'ssl', '1'], true);
+ }
+
+ $https = $this->server->get('HTTPS');
+
+ return !empty($https) && 'off' !== strtolower($https);
+ }
+
+ /**
+ * Returns the host name.
+ *
+ * This method can read the client host name from the "X-Forwarded-Host" header
+ * when trusted proxies were set via "setTrustedProxies()".
+ *
+ * The "X-Forwarded-Host" header must contain the client host name.
+ *
+ * @return string
+ *
+ * @throws SuspiciousOperationException when the host name is invalid or not trusted
+ */
+ public function getHost()
+ {
+ if ($this->isFromTrustedProxy() && $host = $this->getTrustedValues(self::HEADER_X_FORWARDED_HOST)) {
+ $host = $host[0];
+ } elseif (!$host = $this->headers->get('HOST')) {
+ if (!$host = $this->server->get('SERVER_NAME')) {
+ $host = $this->server->get('SERVER_ADDR', '');
+ }
+ }
+
+ // trim and remove port number from host
+ // host is lowercase as per RFC 952/2181
+ $host = strtolower(preg_replace('/:\d+$/', '', trim($host)));
+
+ // as the host can come from the user (HTTP_HOST and depending on the configuration, SERVER_NAME too can come from the user)
+ // check that it does not contain forbidden characters (see RFC 952 and RFC 2181)
+ // use preg_replace() instead of preg_match() to prevent DoS attacks with long host names
+ if ($host && '' !== preg_replace('/(?:^\[)?[a-zA-Z0-9-:\]_]+\.?/', '', $host)) {
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Invalid Host "%s".', $host));
+ }
+
+ if (\count(self::$trustedHostPatterns) > 0) {
+ // to avoid host header injection attacks, you should provide a list of trusted host patterns
+
+ if (\in_array($host, self::$trustedHosts)) {
+ return $host;
+ }
+
+ foreach (self::$trustedHostPatterns as $pattern) {
+ if (preg_match($pattern, $host)) {
+ self::$trustedHosts[] = $host;
+
+ return $host;
+ }
+ }
+
+ if (!$this->isHostValid) {
+ return '';
+ }
+ $this->isHostValid = false;
+
+ throw new SuspiciousOperationException(sprintf('Untrusted Host "%s".', $host));
+ }
+
+ return $host;
+ }
+
+ /**
+ * Sets the request method.
+ */
+ public function setMethod(string $method)
+ {
+ $this->method = null;
+ $this->server->set('REQUEST_METHOD', $method);
+ }
+
+ /**
+ * Gets the request "intended" method.
+ *
+ * If the X-HTTP-Method-Override header is set, and if the method is a POST,
+ * then it is used to determine the "real" intended HTTP method.
+ *
+ * The _method request parameter can also be used to determine the HTTP method,
+ * but only if enableHttpMethodParameterOverride() has been called.
+ *
+ * The method is always an uppercased string.
+ *
+ * @return string The request method
+ *
+ * @see getRealMethod()
+ */
+ public function getMethod()
+ {
+ if (null !== $this->method) {
+ return $this->method;
+ }
+
+ $this->method = strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+
+ if ('POST' !== $this->method) {
+ return $this->method;
+ }
+
+ $method = $this->headers->get('X-HTTP-METHOD-OVERRIDE');
+
+ if (!$method && self::$httpMethodParameterOverride) {
+ $method = $this->request->get('_method', $this->query->get('_method', 'POST'));
+ }
+
+ if (!\is_string($method)) {
+ return $this->method;
+ }
+
+ $method = strtoupper($method);
+
+ if (\in_array($method, ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'PATCH', 'PURGE', 'TRACE'], true)) {
+ return $this->method = $method;
+ }
+
+ if (!preg_match('/^[A-Z]++$/D', $method)) {
+ throw new SuspiciousOperationException(sprintf('Invalid method override "%s".', $method));
+ }
+
+ return $this->method = $method;
+ }
+
+ /**
+ * Gets the "real" request method.
+ *
+ * @return string The request method
+ *
+ * @see getMethod()
+ */
+ public function getRealMethod()
+ {
+ return strtoupper($this->server->get('REQUEST_METHOD', 'GET'));
+ }
+
+ /**
+ * Gets the mime type associated with the format.
+ *
+ * @return string|null The associated mime type (null if not found)
+ */
+ public function getMimeType(string $format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format][0] : null;
+ }
+
+ /**
+ * Gets the mime types associated with the format.
+ *
+ * @return array The associated mime types
+ */
+ public static function getMimeTypes(string $format)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ return isset(static::$formats[$format]) ? static::$formats[$format] : [];
+ }
+
+ /**
+ * Gets the format associated with the mime type.
+ *
+ * @return string|null The format (null if not found)
+ */
+ public function getFormat(?string $mimeType)
+ {
+ $canonicalMimeType = null;
+ if (false !== $pos = strpos($mimeType, ';')) {
+ $canonicalMimeType = trim(substr($mimeType, 0, $pos));
+ }
+
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ foreach (static::$formats as $format => $mimeTypes) {
+ if (\in_array($mimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ if (null !== $canonicalMimeType && \in_array($canonicalMimeType, (array) $mimeTypes)) {
+ return $format;
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Associates a format with mime types.
+ *
+ * @param string|array $mimeTypes The associated mime types (the preferred one must be the first as it will be used as the content type)
+ */
+ public function setFormat(?string $format, $mimeTypes)
+ {
+ if (null === static::$formats) {
+ static::initializeFormats();
+ }
+
+ static::$formats[$format] = \is_array($mimeTypes) ? $mimeTypes : [$mimeTypes];
+ }
+
+ /**
+ * Gets the request format.
+ *
+ * Here is the process to determine the format:
+ *
+ * * format defined by the user (with setRequestFormat())
+ * * _format request attribute
+ * * $default
+ *
+ * @see getPreferredFormat
+ *
+ * @return string|null The request format
+ */
+ public function getRequestFormat(?string $default = 'html')
+ {
+ if (null === $this->format) {
+ $this->format = $this->attributes->get('_format');
+ }
+
+ return null === $this->format ? $default : $this->format;
+ }
+
+ /**
+ * Sets the request format.
+ */
+ public function setRequestFormat(?string $format)
+ {
+ $this->format = $format;
+ }
+
+ /**
+ * Gets the format associated with the request.
+ *
+ * @return string|null The format (null if no content type is present)
+ */
+ public function getContentType()
+ {
+ return $this->getFormat($this->headers->get('CONTENT_TYPE'));
+ }
+
+ /**
+ * Sets the default locale.
+ */
+ public function setDefaultLocale(string $locale)
+ {
+ $this->defaultLocale = $locale;
+
+ if (null === $this->locale) {
+ $this->setPhpDefaultLocale($locale);
+ }
+ }
+
+ /**
+ * Get the default locale.
+ *
+ * @return string
+ */
+ public function getDefaultLocale()
+ {
+ return $this->defaultLocale;
+ }
+
+ /**
+ * Sets the locale.
+ */
+ public function setLocale(string $locale)
+ {
+ $this->setPhpDefaultLocale($this->locale = $locale);
+ }
+
+ /**
+ * Get the locale.
+ *
+ * @return string
+ */
+ public function getLocale()
+ {
+ return null === $this->locale ? $this->defaultLocale : $this->locale;
+ }
+
+ /**
+ * Checks if the request method is of specified type.
+ *
+ * @param string $method Uppercase request method (GET, POST etc)
+ *
+ * @return bool
+ */
+ public function isMethod(string $method)
+ {
+ return $this->getMethod() === strtoupper($method);
+ }
+
+ /**
+ * Checks whether or not the method is safe.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.1
+ *
+ * @return bool
+ */
+ public function isMethodSafe()
+ {
+ return \in_array($this->getMethod(), ['GET', 'HEAD', 'OPTIONS', 'TRACE']);
+ }
+
+ /**
+ * Checks whether or not the method is idempotent.
+ *
+ * @return bool
+ */
+ public function isMethodIdempotent()
+ {
+ return \in_array($this->getMethod(), ['HEAD', 'GET', 'PUT', 'DELETE', 'TRACE', 'OPTIONS', 'PURGE']);
+ }
+
+ /**
+ * Checks whether the method is cacheable or not.
+ *
+ * @see https://tools.ietf.org/html/rfc7231#section-4.2.3
+ *
+ * @return bool True for GET and HEAD, false otherwise
+ */
+ public function isMethodCacheable()
+ {
+ return \in_array($this->getMethod(), ['GET', 'HEAD']);
+ }
+
+ /**
+ * Returns the protocol version.
+ *
+ * If the application is behind a proxy, the protocol version used in the
+ * requests between the client and the proxy and between the proxy and the
+ * server might be different. This returns the former (from the "Via" header)
+ * if the proxy is trusted (see "setTrustedProxies()"), otherwise it returns
+ * the latter (from the "SERVER_PROTOCOL" server parameter).
+ *
+ * @return string
+ */
+ public function getProtocolVersion()
+ {
+ if ($this->isFromTrustedProxy()) {
+ preg_match('~^(HTTP/)?([1-9]\.[0-9]) ~', $this->headers->get('Via'), $matches);
+
+ if ($matches) {
+ return 'HTTP/'.$matches[2];
+ }
+ }
+
+ return $this->server->get('SERVER_PROTOCOL');
+ }
+
+ /**
+ * Returns the request body content.
+ *
+ * @param bool $asResource If true, a resource will be returned
+ *
+ * @return string|resource The request body content or a resource to read the body stream
+ *
+ * @throws \LogicException
+ */
+ public function getContent(bool $asResource = false)
+ {
+ $currentContentIsResource = \is_resource($this->content);
+
+ if (true === $asResource) {
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return $this->content;
+ }
+
+ // Content passed in parameter (test)
+ if (\is_string($this->content)) {
+ $resource = fopen('php://temp', 'r+');
+ fwrite($resource, $this->content);
+ rewind($resource);
+
+ return $resource;
+ }
+
+ $this->content = false;
+
+ return fopen('php://input', 'rb');
+ }
+
+ if ($currentContentIsResource) {
+ rewind($this->content);
+
+ return stream_get_contents($this->content);
+ }
+
+ if (null === $this->content || false === $this->content) {
+ $this->content = file_get_contents('php://input');
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * Gets the Etags.
+ *
+ * @return array The entity tags
+ */
+ public function getETags()
+ {
+ return preg_split('/\s*,\s*/', $this->headers->get('if_none_match'), null, PREG_SPLIT_NO_EMPTY);
+ }
+
+ /**
+ * @return bool
+ */
+ public function isNoCache()
+ {
+ return $this->headers->hasCacheControlDirective('no-cache') || 'no-cache' == $this->headers->get('Pragma');
+ }
+
+ /**
+ * Gets the preferred format for the response by inspecting, in the following order:
+ * * the request format set using setRequestFormat
+ * * the values of the Accept HTTP header
+ * * the content type of the body of the request.
+ */
+ public function getPreferredFormat(?string $default = 'html'): ?string
+ {
+ if (null !== $this->preferredFormat) {
+ return $this->preferredFormat;
+ }
+
+ $preferredFormat = null;
+ foreach ($this->getAcceptableContentTypes() as $contentType) {
+ if ($preferredFormat = $this->getFormat($contentType)) {
+ break;
+ }
+ }
+
+ $this->preferredFormat = $this->getRequestFormat($preferredFormat ?: $this->getContentType());
+
+ return $this->preferredFormat ?: $default;
+ }
+
+ /**
+ * Returns the preferred language.
+ *
+ * @param string[] $locales An array of ordered available locales
+ *
+ * @return string|null The preferred locale
+ */
+ public function getPreferredLanguage(array $locales = null)
+ {
+ $preferredLanguages = $this->getLanguages();
+
+ if (empty($locales)) {
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : null;
+ }
+
+ if (!$preferredLanguages) {
+ return $locales[0];
+ }
+
+ $extendedPreferredLanguages = [];
+ foreach ($preferredLanguages as $language) {
+ $extendedPreferredLanguages[] = $language;
+ if (false !== $position = strpos($language, '_')) {
+ $superLanguage = substr($language, 0, $position);
+ if (!\in_array($superLanguage, $preferredLanguages)) {
+ $extendedPreferredLanguages[] = $superLanguage;
+ }
+ }
+ }
+
+ $preferredLanguages = array_values(array_intersect($extendedPreferredLanguages, $locales));
+
+ return isset($preferredLanguages[0]) ? $preferredLanguages[0] : $locales[0];
+ }
+
+ /**
+ * Gets a list of languages acceptable by the client browser.
+ *
+ * @return array Languages ordered in the user browser preferences
+ */
+ public function getLanguages()
+ {
+ if (null !== $this->languages) {
+ return $this->languages;
+ }
+
+ $languages = AcceptHeader::fromString($this->headers->get('Accept-Language'))->all();
+ $this->languages = [];
+ foreach ($languages as $lang => $acceptHeaderItem) {
+ if (false !== strpos($lang, '-')) {
+ $codes = explode('-', $lang);
+ if ('i' === $codes[0]) {
+ // Language not listed in ISO 639 that are not variants
+ // of any listed language, which can be registered with the
+ // i-prefix, such as i-cherokee
+ if (\count($codes) > 1) {
+ $lang = $codes[1];
+ }
+ } else {
+ for ($i = 0, $max = \count($codes); $i < $max; ++$i) {
+ if (0 === $i) {
+ $lang = strtolower($codes[0]);
+ } else {
+ $lang .= '_'.strtoupper($codes[$i]);
+ }
+ }
+ }
+ }
+
+ $this->languages[] = $lang;
+ }
+
+ return $this->languages;
+ }
+
+ /**
+ * Gets a list of charsets acceptable by the client browser.
+ *
+ * @return array List of charsets in preferable order
+ */
+ public function getCharsets()
+ {
+ if (null !== $this->charsets) {
+ return $this->charsets;
+ }
+
+ return $this->charsets = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Charset'))->all());
+ }
+
+ /**
+ * Gets a list of encodings acceptable by the client browser.
+ *
+ * @return array List of encodings in preferable order
+ */
+ public function getEncodings()
+ {
+ if (null !== $this->encodings) {
+ return $this->encodings;
+ }
+
+ return $this->encodings = array_keys(AcceptHeader::fromString($this->headers->get('Accept-Encoding'))->all());
+ }
+
+ /**
+ * Gets a list of content types acceptable by the client browser.
+ *
+ * @return array List of content types in preferable order
+ */
+ public function getAcceptableContentTypes()
+ {
+ if (null !== $this->acceptableContentTypes) {
+ return $this->acceptableContentTypes;
+ }
+
+ return $this->acceptableContentTypes = array_keys(AcceptHeader::fromString($this->headers->get('Accept'))->all());
+ }
+
+ /**
+ * Returns true if the request is a XMLHttpRequest.
+ *
+ * It works if your JavaScript library sets an X-Requested-With HTTP header.
+ * It is known to work with common JavaScript frameworks:
+ *
+ * @see https://wikipedia.org/wiki/List_of_Ajax_frameworks#JavaScript
+ *
+ * @return bool true if the request is an XMLHttpRequest, false otherwise
+ */
+ public function isXmlHttpRequest()
+ {
+ return 'XMLHttpRequest' == $this->headers->get('X-Requested-With');
+ }
+
+ /*
+ * The following methods are derived from code of the Zend Framework (1.10dev - 2010-01-24)
+ *
+ * Code subject to the new BSD license (https://framework.zend.com/license).
+ *
+ * Copyright (c) 2005-2010 Zend Technologies USA Inc. (https://www.zend.com/)
+ */
+
+ protected function prepareRequestUri()
+ {
+ $requestUri = '';
+
+ if ('1' == $this->server->get('IIS_WasUrlRewritten') && '' != $this->server->get('UNENCODED_URL')) {
+ // IIS7 with URL Rewrite: make sure we get the unencoded URL (double slash problem)
+ $requestUri = $this->server->get('UNENCODED_URL');
+ $this->server->remove('UNENCODED_URL');
+ $this->server->remove('IIS_WasUrlRewritten');
+ } elseif ($this->server->has('REQUEST_URI')) {
+ $requestUri = $this->server->get('REQUEST_URI');
+
+ if ('' !== $requestUri && '/' === $requestUri[0]) {
+ // To only use path and query remove the fragment.
+ if (false !== $pos = strpos($requestUri, '#')) {
+ $requestUri = substr($requestUri, 0, $pos);
+ }
+ } else {
+ // HTTP proxy reqs setup request URI with scheme and host [and port] + the URL path,
+ // only use URL path.
+ $uriComponents = parse_url($requestUri);
+
+ if (isset($uriComponents['path'])) {
+ $requestUri = $uriComponents['path'];
+ }
+
+ if (isset($uriComponents['query'])) {
+ $requestUri .= '?'.$uriComponents['query'];
+ }
+ }
+ } elseif ($this->server->has('ORIG_PATH_INFO')) {
+ // IIS 5.0, PHP as CGI
+ $requestUri = $this->server->get('ORIG_PATH_INFO');
+ if ('' != $this->server->get('QUERY_STRING')) {
+ $requestUri .= '?'.$this->server->get('QUERY_STRING');
+ }
+ $this->server->remove('ORIG_PATH_INFO');
+ }
+
+ // normalize the request URI to ease creating sub-requests from this request
+ $this->server->set('REQUEST_URI', $requestUri);
+
+ return $requestUri;
+ }
+
+ /**
+ * Prepares the base URL.
+ *
+ * @return string
+ */
+ protected function prepareBaseUrl()
+ {
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+
+ if (basename($this->server->get('SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('SCRIPT_NAME');
+ } elseif (basename($this->server->get('PHP_SELF')) === $filename) {
+ $baseUrl = $this->server->get('PHP_SELF');
+ } elseif (basename($this->server->get('ORIG_SCRIPT_NAME')) === $filename) {
+ $baseUrl = $this->server->get('ORIG_SCRIPT_NAME'); // 1and1 shared hosting compatibility
+ } else {
+ // Backtrack up the script_filename to find the portion matching
+ // php_self
+ $path = $this->server->get('PHP_SELF', '');
+ $file = $this->server->get('SCRIPT_FILENAME', '');
+ $segs = explode('/', trim($file, '/'));
+ $segs = array_reverse($segs);
+ $index = 0;
+ $last = \count($segs);
+ $baseUrl = '';
+ do {
+ $seg = $segs[$index];
+ $baseUrl = '/'.$seg.$baseUrl;
+ ++$index;
+ } while ($last > $index && (false !== $pos = strpos($path, $baseUrl)) && 0 != $pos);
+ }
+
+ // Does the baseUrl have anything in common with the request_uri?
+ $requestUri = $this->getRequestUri();
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, $baseUrl)) {
+ // full $baseUrl matches
+ return $prefix;
+ }
+
+ if ($baseUrl && null !== $prefix = $this->getUrlencodedPrefix($requestUri, rtrim(\dirname($baseUrl), '/'.\DIRECTORY_SEPARATOR).'/')) {
+ // directory portion of $baseUrl matches
+ return rtrim($prefix, '/'.\DIRECTORY_SEPARATOR);
+ }
+
+ $truncatedRequestUri = $requestUri;
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $truncatedRequestUri = substr($requestUri, 0, $pos);
+ }
+
+ $basename = basename($baseUrl);
+ if (empty($basename) || !strpos(rawurldecode($truncatedRequestUri), $basename)) {
+ // no match whatsoever; set it blank
+ return '';
+ }
+
+ // If using mod_rewrite or ISAPI_Rewrite strip the script filename
+ // out of baseUrl. $pos !== 0 makes sure it is not matching a value
+ // from PATH_INFO or QUERY_STRING
+ if (\strlen($requestUri) >= \strlen($baseUrl) && (false !== $pos = strpos($requestUri, $baseUrl)) && 0 !== $pos) {
+ $baseUrl = substr($requestUri, 0, $pos + \strlen($baseUrl));
+ }
+
+ return rtrim($baseUrl, '/'.\DIRECTORY_SEPARATOR);
+ }
+
+ /**
+ * Prepares the base path.
+ *
+ * @return string base path
+ */
+ protected function prepareBasePath()
+ {
+ $baseUrl = $this->getBaseUrl();
+ if (empty($baseUrl)) {
+ return '';
+ }
+
+ $filename = basename($this->server->get('SCRIPT_FILENAME'));
+ if (basename($baseUrl) === $filename) {
+ $basePath = \dirname($baseUrl);
+ } else {
+ $basePath = $baseUrl;
+ }
+
+ if ('\\' === \DIRECTORY_SEPARATOR) {
+ $basePath = str_replace('\\', '/', $basePath);
+ }
+
+ return rtrim($basePath, '/');
+ }
+
+ /**
+ * Prepares the path info.
+ *
+ * @return string path info
+ */
+ protected function preparePathInfo()
+ {
+ if (null === ($requestUri = $this->getRequestUri())) {
+ return '/';
+ }
+
+ // Remove the query string from REQUEST_URI
+ if (false !== $pos = strpos($requestUri, '?')) {
+ $requestUri = substr($requestUri, 0, $pos);
+ }
+ if ('' !== $requestUri && '/' !== $requestUri[0]) {
+ $requestUri = '/'.$requestUri;
+ }
+
+ if (null === ($baseUrl = $this->getBaseUrl())) {
+ return $requestUri;
+ }
+
+ $pathInfo = substr($requestUri, \strlen($baseUrl));
+ if (false === $pathInfo || '' === $pathInfo) {
+ // If substr() returns false then PATH_INFO is set to an empty string
+ return '/';
+ }
+
+ return (string) $pathInfo;
+ }
+
+ /**
+ * Initializes HTTP request formats.
+ */
+ protected static function initializeFormats()
+ {
+ static::$formats = [
+ 'html' => ['text/html', 'application/xhtml+xml'],
+ 'txt' => ['text/plain'],
+ 'js' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
+ 'css' => ['text/css'],
+ 'json' => ['application/json', 'application/x-json'],
+ 'jsonld' => ['application/ld+json'],
+ 'xml' => ['text/xml', 'application/xml', 'application/x-xml'],
+ 'rdf' => ['application/rdf+xml'],
+ 'atom' => ['application/atom+xml'],
+ 'rss' => ['application/rss+xml'],
+ 'form' => ['application/x-www-form-urlencoded'],
+ ];
+ }
+
+ private function setPhpDefaultLocale(string $locale): void
+ {
+ // if either the class Locale doesn't exist, or an exception is thrown when
+ // setting the default locale, the intl module is not installed, and
+ // the call can be ignored:
+ try {
+ if (class_exists('Locale', false)) {
+ \Locale::setDefault($locale);
+ }
+ } catch (\Exception $e) {
+ }
+ }
+
+ /**
+ * Returns the prefix as encoded in the string when the string starts with
+ * the given prefix, null otherwise.
+ */
+ private function getUrlencodedPrefix(string $string, string $prefix): ?string
+ {
+ if (0 !== strpos(rawurldecode($string), $prefix)) {
+ return null;
+ }
+
+ $len = \strlen($prefix);
+
+ if (preg_match(sprintf('#^(%%[[:xdigit:]]{2}|.){%d}#', $len), $string, $match)) {
+ return $match[0];
+ }
+
+ return null;
+ }
+
+ private static function createRequestFromFactory(array $query = [], array $request = [], array $attributes = [], array $cookies = [], array $files = [], array $server = [], $content = null): self
+ {
+ if (self::$requestFactory) {
+ $request = (self::$requestFactory)($query, $request, $attributes, $cookies, $files, $server, $content);
+
+ if (!$request instanceof self) {
+ throw new \LogicException('The Request factory must return an instance of Symfony\Component\HttpFoundation\Request.');
+ }
+
+ return $request;
+ }
+
+ return new static($query, $request, $attributes, $cookies, $files, $server, $content);
+ }
+
+ /**
+ * Indicates whether this request originated from a trusted proxy.
+ *
+ * This can be useful to determine whether or not to trust the
+ * contents of a proxy-specific header.
+ *
+ * @return bool true if the request came from a trusted proxy, false otherwise
+ */
+ public function isFromTrustedProxy()
+ {
+ return self::$trustedProxies && IpUtils::checkIp($this->server->get('REMOTE_ADDR'), self::$trustedProxies);
+ }
+
+ private function getTrustedValues(int $type, string $ip = null): array
+ {
+ $clientValues = [];
+ $forwardedValues = [];
+
+ if ((self::$trustedHeaderSet & $type) && $this->headers->has(self::$trustedHeaders[$type])) {
+ foreach (explode(',', $this->headers->get(self::$trustedHeaders[$type])) as $v) {
+ $clientValues[] = (self::HEADER_X_FORWARDED_PORT === $type ? '0.0.0.0:' : '').trim($v);
+ }
+ }
+
+ if ((self::$trustedHeaderSet & self::HEADER_FORWARDED) && $this->headers->has(self::$trustedHeaders[self::HEADER_FORWARDED])) {
+ $forwarded = $this->headers->get(self::$trustedHeaders[self::HEADER_FORWARDED]);
+ $parts = HeaderUtils::split($forwarded, ',;=');
+ $forwardedValues = [];
+ $param = self::$forwardedParams[$type];
+ foreach ($parts as $subParts) {
+ if (null === $v = HeaderUtils::combine($subParts)[$param] ?? null) {
+ continue;
+ }
+ if (self::HEADER_X_FORWARDED_PORT === $type) {
+ if (']' === substr($v, -1) || false === $v = strrchr($v, ':')) {
+ $v = $this->isSecure() ? ':443' : ':80';
+ }
+ $v = '0.0.0.0'.$v;
+ }
+ $forwardedValues[] = $v;
+ }
+ }
+
+ if (null !== $ip) {
+ $clientValues = $this->normalizeAndFilterClientIps($clientValues, $ip);
+ $forwardedValues = $this->normalizeAndFilterClientIps($forwardedValues, $ip);
+ }
+
+ if ($forwardedValues === $clientValues || !$clientValues) {
+ return $forwardedValues;
+ }
+
+ if (!$forwardedValues) {
+ return $clientValues;
+ }
+
+ if (!$this->isForwardedValid) {
+ return null !== $ip ? ['0.0.0.0', $ip] : [];
+ }
+ $this->isForwardedValid = false;
+
+ throw new ConflictingHeadersException(sprintf('The request has both a trusted "%s" header and a trusted "%s" header, conflicting with each other. You should either configure your proxy to remove one of them, or configure your project to distrust the offending one.', self::$trustedHeaders[self::HEADER_FORWARDED], self::$trustedHeaders[$type]));
+ }
+
+ private function normalizeAndFilterClientIps(array $clientIps, string $ip): array
+ {
+ if (!$clientIps) {
+ return [];
+ }
+ $clientIps[] = $ip; // Complete the IP chain with the IP the request actually came from
+ $firstTrustedIp = null;
+
+ foreach ($clientIps as $key => $clientIp) {
+ if (strpos($clientIp, '.')) {
+ // Strip :port from IPv4 addresses. This is allowed in Forwarded
+ // and may occur in X-Forwarded-For.
+ $i = strpos($clientIp, ':');
+ if ($i) {
+ $clientIps[$key] = $clientIp = substr($clientIp, 0, $i);
+ }
+ } elseif (0 === strpos($clientIp, '[')) {
+ // Strip brackets and :port from IPv6 addresses.
+ $i = strpos($clientIp, ']', 1);
+ $clientIps[$key] = $clientIp = substr($clientIp, 1, $i - 1);
+ }
+
+ if (!filter_var($clientIp, FILTER_VALIDATE_IP)) {
+ unset($clientIps[$key]);
+
+ continue;
+ }
+
+ if (IpUtils::checkIp($clientIp, self::$trustedProxies)) {
+ unset($clientIps[$key]);
+
+ // Fallback to this when the client IP falls into the range of trusted proxies
+ if (null === $firstTrustedIp) {
+ $firstTrustedIp = $clientIp;
+ }
+ }
+ }
+
+ // Now the IP chain contains only untrusted proxies and the client IP
+ return $clientIps ? array_reverse($clientIps) : [$firstTrustedIp];
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcher.php b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcher.php
new file mode 100644
index 00000000..c32c5cdc
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcher.php
@@ -0,0 +1,188 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcher compares a pre-defined set of checks against a Request instance.
+ *
+ * @author Fabien Potencier
+ */
+class RequestMatcher implements RequestMatcherInterface
+{
+ /**
+ * @var string|null
+ */
+ private $path;
+
+ /**
+ * @var string|null
+ */
+ private $host;
+
+ /**
+ * @var int|null
+ */
+ private $port;
+
+ /**
+ * @var string[]
+ */
+ private $methods = [];
+
+ /**
+ * @var string[]
+ */
+ private $ips = [];
+
+ /**
+ * @var array
+ */
+ private $attributes = [];
+
+ /**
+ * @var string[]
+ */
+ private $schemes = [];
+
+ /**
+ * @param string|string[]|null $methods
+ * @param string|string[]|null $ips
+ * @param string|string[]|null $schemes
+ */
+ public function __construct(string $path = null, string $host = null, $methods = null, $ips = null, array $attributes = [], $schemes = null, int $port = null)
+ {
+ $this->matchPath($path);
+ $this->matchHost($host);
+ $this->matchMethod($methods);
+ $this->matchIps($ips);
+ $this->matchScheme($schemes);
+ $this->matchPort($port);
+
+ foreach ($attributes as $k => $v) {
+ $this->matchAttribute($k, $v);
+ }
+ }
+
+ /**
+ * Adds a check for the HTTP scheme.
+ *
+ * @param string|string[]|null $scheme An HTTP scheme or an array of HTTP schemes
+ */
+ public function matchScheme($scheme)
+ {
+ $this->schemes = null !== $scheme ? array_map('strtolower', (array) $scheme) : [];
+ }
+
+ /**
+ * Adds a check for the URL host name.
+ */
+ public function matchHost(?string $regexp)
+ {
+ $this->host = $regexp;
+ }
+
+ /**
+ * Adds a check for the the URL port.
+ *
+ * @param int|null $port The port number to connect to
+ */
+ public function matchPort(?int $port)
+ {
+ $this->port = $port;
+ }
+
+ /**
+ * Adds a check for the URL path info.
+ */
+ public function matchPath(?string $regexp)
+ {
+ $this->path = $regexp;
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string $ip A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIp(string $ip)
+ {
+ $this->matchIps($ip);
+ }
+
+ /**
+ * Adds a check for the client IP.
+ *
+ * @param string|string[]|null $ips A specific IP address or a range specified using IP/netmask like 192.168.1.0/24
+ */
+ public function matchIps($ips)
+ {
+ $this->ips = null !== $ips ? (array) $ips : [];
+ }
+
+ /**
+ * Adds a check for the HTTP method.
+ *
+ * @param string|string[]|null $method An HTTP method or an array of HTTP methods
+ */
+ public function matchMethod($method)
+ {
+ $this->methods = null !== $method ? array_map('strtoupper', (array) $method) : [];
+ }
+
+ /**
+ * Adds a check for request attribute.
+ */
+ public function matchAttribute(string $key, string $regexp)
+ {
+ $this->attributes[$key] = $regexp;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function matches(Request $request)
+ {
+ if ($this->schemes && !\in_array($request->getScheme(), $this->schemes, true)) {
+ return false;
+ }
+
+ if ($this->methods && !\in_array($request->getMethod(), $this->methods, true)) {
+ return false;
+ }
+
+ foreach ($this->attributes as $key => $pattern) {
+ if (!preg_match('{'.$pattern.'}', $request->attributes->get($key))) {
+ return false;
+ }
+ }
+
+ if (null !== $this->path && !preg_match('{'.$this->path.'}', rawurldecode($request->getPathInfo()))) {
+ return false;
+ }
+
+ if (null !== $this->host && !preg_match('{'.$this->host.'}i', $request->getHost())) {
+ return false;
+ }
+
+ if (null !== $this->port && 0 < $this->port && $request->getPort() !== $this->port) {
+ return false;
+ }
+
+ if (IpUtils::checkIp($request->getClientIp(), $this->ips)) {
+ return true;
+ }
+
+ // Note to future implementors: add additional checks above the
+ // foreach above or else your check might not be run!
+ return 0 === \count($this->ips);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcherInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcherInterface.php
new file mode 100644
index 00000000..c26db3e6
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestMatcherInterface.php
@@ -0,0 +1,27 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * RequestMatcherInterface is an interface for strategies to match a Request.
+ *
+ * @author Fabien Potencier
+ */
+interface RequestMatcherInterface
+{
+ /**
+ * Decides whether the rule(s) implemented by the strategy matches the supplied request.
+ *
+ * @return bool true if the request matches, false otherwise
+ */
+ public function matches(Request $request);
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/RequestStack.php b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestStack.php
new file mode 100644
index 00000000..244a77d6
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/RequestStack.php
@@ -0,0 +1,103 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Request stack that controls the lifecycle of requests.
+ *
+ * @author Benjamin Eberlei
+ */
+class RequestStack
+{
+ /**
+ * @var Request[]
+ */
+ private $requests = [];
+
+ /**
+ * Pushes a Request on the stack.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ */
+ public function push(Request $request)
+ {
+ $this->requests[] = $request;
+ }
+
+ /**
+ * Pops the current request from the stack.
+ *
+ * This operation lets the current request go out of scope.
+ *
+ * This method should generally not be called directly as the stack
+ * management should be taken care of by the application itself.
+ *
+ * @return Request|null
+ */
+ public function pop()
+ {
+ if (!$this->requests) {
+ return null;
+ }
+
+ return array_pop($this->requests);
+ }
+
+ /**
+ * @return Request|null
+ */
+ public function getCurrentRequest()
+ {
+ return end($this->requests) ?: null;
+ }
+
+ /**
+ * Gets the master Request.
+ *
+ * Be warned that making your code aware of the master request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * @return Request|null
+ */
+ public function getMasterRequest()
+ {
+ if (!$this->requests) {
+ return null;
+ }
+
+ return $this->requests[0];
+ }
+
+ /**
+ * Returns the parent request of the current.
+ *
+ * Be warned that making your code aware of the parent request
+ * might make it un-compatible with other features of your framework
+ * like ESI support.
+ *
+ * If current Request is the master request, it returns null.
+ *
+ * @return Request|null
+ */
+ public function getParentRequest()
+ {
+ $pos = \count($this->requests) - 2;
+
+ if (!isset($this->requests[$pos])) {
+ return null;
+ }
+
+ return $this->requests[$pos];
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Response.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Response.php
new file mode 100644
index 00000000..9122b13c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Response.php
@@ -0,0 +1,1224 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * Response represents an HTTP response.
+ *
+ * @author Fabien Potencier
+ */
+class Response
+{
+ const HTTP_CONTINUE = 100;
+ const HTTP_SWITCHING_PROTOCOLS = 101;
+ const HTTP_PROCESSING = 102; // RFC2518
+ const HTTP_EARLY_HINTS = 103; // RFC8297
+ const HTTP_OK = 200;
+ const HTTP_CREATED = 201;
+ const HTTP_ACCEPTED = 202;
+ const HTTP_NON_AUTHORITATIVE_INFORMATION = 203;
+ const HTTP_NO_CONTENT = 204;
+ const HTTP_RESET_CONTENT = 205;
+ const HTTP_PARTIAL_CONTENT = 206;
+ const HTTP_MULTI_STATUS = 207; // RFC4918
+ const HTTP_ALREADY_REPORTED = 208; // RFC5842
+ const HTTP_IM_USED = 226; // RFC3229
+ const HTTP_MULTIPLE_CHOICES = 300;
+ const HTTP_MOVED_PERMANENTLY = 301;
+ const HTTP_FOUND = 302;
+ const HTTP_SEE_OTHER = 303;
+ const HTTP_NOT_MODIFIED = 304;
+ const HTTP_USE_PROXY = 305;
+ const HTTP_RESERVED = 306;
+ const HTTP_TEMPORARY_REDIRECT = 307;
+ const HTTP_PERMANENTLY_REDIRECT = 308; // RFC7238
+ const HTTP_BAD_REQUEST = 400;
+ const HTTP_UNAUTHORIZED = 401;
+ const HTTP_PAYMENT_REQUIRED = 402;
+ const HTTP_FORBIDDEN = 403;
+ const HTTP_NOT_FOUND = 404;
+ const HTTP_METHOD_NOT_ALLOWED = 405;
+ const HTTP_NOT_ACCEPTABLE = 406;
+ const HTTP_PROXY_AUTHENTICATION_REQUIRED = 407;
+ const HTTP_REQUEST_TIMEOUT = 408;
+ const HTTP_CONFLICT = 409;
+ const HTTP_GONE = 410;
+ const HTTP_LENGTH_REQUIRED = 411;
+ const HTTP_PRECONDITION_FAILED = 412;
+ const HTTP_REQUEST_ENTITY_TOO_LARGE = 413;
+ const HTTP_REQUEST_URI_TOO_LONG = 414;
+ const HTTP_UNSUPPORTED_MEDIA_TYPE = 415;
+ const HTTP_REQUESTED_RANGE_NOT_SATISFIABLE = 416;
+ const HTTP_EXPECTATION_FAILED = 417;
+ const HTTP_I_AM_A_TEAPOT = 418; // RFC2324
+ const HTTP_MISDIRECTED_REQUEST = 421; // RFC7540
+ const HTTP_UNPROCESSABLE_ENTITY = 422; // RFC4918
+ const HTTP_LOCKED = 423; // RFC4918
+ const HTTP_FAILED_DEPENDENCY = 424; // RFC4918
+ const HTTP_TOO_EARLY = 425; // RFC-ietf-httpbis-replay-04
+ const HTTP_UPGRADE_REQUIRED = 426; // RFC2817
+ const HTTP_PRECONDITION_REQUIRED = 428; // RFC6585
+ const HTTP_TOO_MANY_REQUESTS = 429; // RFC6585
+ const HTTP_REQUEST_HEADER_FIELDS_TOO_LARGE = 431; // RFC6585
+ const HTTP_UNAVAILABLE_FOR_LEGAL_REASONS = 451;
+ const HTTP_INTERNAL_SERVER_ERROR = 500;
+ const HTTP_NOT_IMPLEMENTED = 501;
+ const HTTP_BAD_GATEWAY = 502;
+ const HTTP_SERVICE_UNAVAILABLE = 503;
+ const HTTP_GATEWAY_TIMEOUT = 504;
+ const HTTP_VERSION_NOT_SUPPORTED = 505;
+ const HTTP_VARIANT_ALSO_NEGOTIATES_EXPERIMENTAL = 506; // RFC2295
+ const HTTP_INSUFFICIENT_STORAGE = 507; // RFC4918
+ const HTTP_LOOP_DETECTED = 508; // RFC5842
+ const HTTP_NOT_EXTENDED = 510; // RFC2774
+ const HTTP_NETWORK_AUTHENTICATION_REQUIRED = 511; // RFC6585
+
+ /**
+ * @var ResponseHeaderBag
+ */
+ public $headers;
+
+ /**
+ * @var string
+ */
+ protected $content;
+
+ /**
+ * @var string
+ */
+ protected $version;
+
+ /**
+ * @var int
+ */
+ protected $statusCode;
+
+ /**
+ * @var string
+ */
+ protected $statusText;
+
+ /**
+ * @var string
+ */
+ protected $charset;
+
+ /**
+ * Status codes translation table.
+ *
+ * The list of codes is complete according to the
+ * {@link https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml Hypertext Transfer Protocol (HTTP) Status Code Registry}
+ * (last updated 2016-03-01).
+ *
+ * Unless otherwise noted, the status code is defined in RFC2616.
+ *
+ * @var array
+ */
+ public static $statusTexts = [
+ 100 => 'Continue',
+ 101 => 'Switching Protocols',
+ 102 => 'Processing', // RFC2518
+ 103 => 'Early Hints',
+ 200 => 'OK',
+ 201 => 'Created',
+ 202 => 'Accepted',
+ 203 => 'Non-Authoritative Information',
+ 204 => 'No Content',
+ 205 => 'Reset Content',
+ 206 => 'Partial Content',
+ 207 => 'Multi-Status', // RFC4918
+ 208 => 'Already Reported', // RFC5842
+ 226 => 'IM Used', // RFC3229
+ 300 => 'Multiple Choices',
+ 301 => 'Moved Permanently',
+ 302 => 'Found',
+ 303 => 'See Other',
+ 304 => 'Not Modified',
+ 305 => 'Use Proxy',
+ 307 => 'Temporary Redirect',
+ 308 => 'Permanent Redirect', // RFC7238
+ 400 => 'Bad Request',
+ 401 => 'Unauthorized',
+ 402 => 'Payment Required',
+ 403 => 'Forbidden',
+ 404 => 'Not Found',
+ 405 => 'Method Not Allowed',
+ 406 => 'Not Acceptable',
+ 407 => 'Proxy Authentication Required',
+ 408 => 'Request Timeout',
+ 409 => 'Conflict',
+ 410 => 'Gone',
+ 411 => 'Length Required',
+ 412 => 'Precondition Failed',
+ 413 => 'Payload Too Large',
+ 414 => 'URI Too Long',
+ 415 => 'Unsupported Media Type',
+ 416 => 'Range Not Satisfiable',
+ 417 => 'Expectation Failed',
+ 418 => 'I\'m a teapot', // RFC2324
+ 421 => 'Misdirected Request', // RFC7540
+ 422 => 'Unprocessable Entity', // RFC4918
+ 423 => 'Locked', // RFC4918
+ 424 => 'Failed Dependency', // RFC4918
+ 425 => 'Too Early', // RFC-ietf-httpbis-replay-04
+ 426 => 'Upgrade Required', // RFC2817
+ 428 => 'Precondition Required', // RFC6585
+ 429 => 'Too Many Requests', // RFC6585
+ 431 => 'Request Header Fields Too Large', // RFC6585
+ 451 => 'Unavailable For Legal Reasons', // RFC7725
+ 500 => 'Internal Server Error',
+ 501 => 'Not Implemented',
+ 502 => 'Bad Gateway',
+ 503 => 'Service Unavailable',
+ 504 => 'Gateway Timeout',
+ 505 => 'HTTP Version Not Supported',
+ 506 => 'Variant Also Negotiates', // RFC2295
+ 507 => 'Insufficient Storage', // RFC4918
+ 508 => 'Loop Detected', // RFC5842
+ 510 => 'Not Extended', // RFC2774
+ 511 => 'Network Authentication Required', // RFC6585
+ ];
+
+ /**
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ */
+ public function __construct(?string $content = '', int $status = 200, array $headers = [])
+ {
+ $this->headers = new ResponseHeaderBag($headers);
+ $this->setContent($content);
+ $this->setStatusCode($status);
+ $this->setProtocolVersion('1.0');
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * Example:
+ *
+ * return Response::create($body, 200)
+ * ->setSharedMaxAge(300);
+ *
+ * @return static
+ */
+ public static function create(?string $content = '', int $status = 200, array $headers = [])
+ {
+ return new static($content, $status, $headers);
+ }
+
+ /**
+ * Returns the Response as an HTTP string.
+ *
+ * The string representation of the Response is the same as the
+ * one that will be sent to the client only if the prepare() method
+ * has been called before.
+ *
+ * @return string The Response as an HTTP string
+ *
+ * @see prepare()
+ */
+ public function __toString()
+ {
+ return
+ sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText)."\r\n".
+ $this->headers."\r\n".
+ $this->getContent();
+ }
+
+ /**
+ * Clones the current Response instance.
+ */
+ public function __clone()
+ {
+ $this->headers = clone $this->headers;
+ }
+
+ /**
+ * Prepares the Response before it is sent to the client.
+ *
+ * This method tweaks the Response to ensure that it is
+ * compliant with RFC 2616. Most of the changes are based on
+ * the Request that is "associated" with this Response.
+ *
+ * @return $this
+ */
+ public function prepare(Request $request)
+ {
+ $headers = $this->headers;
+
+ if ($this->isInformational() || $this->isEmpty()) {
+ $this->setContent(null);
+ $headers->remove('Content-Type');
+ $headers->remove('Content-Length');
+ } else {
+ // Content-type based on the Request
+ if (!$headers->has('Content-Type')) {
+ $format = $request->getPreferredFormat();
+ if (null !== $format && $mimeType = $request->getMimeType($format)) {
+ $headers->set('Content-Type', $mimeType);
+ }
+ }
+
+ // Fix Content-Type
+ $charset = $this->charset ?: 'UTF-8';
+ if (!$headers->has('Content-Type')) {
+ $headers->set('Content-Type', 'text/html; charset='.$charset);
+ } elseif (0 === stripos($headers->get('Content-Type'), 'text/') && false === stripos($headers->get('Content-Type'), 'charset')) {
+ // add the charset
+ $headers->set('Content-Type', $headers->get('Content-Type').'; charset='.$charset);
+ }
+
+ // Fix Content-Length
+ if ($headers->has('Transfer-Encoding')) {
+ $headers->remove('Content-Length');
+ }
+
+ if ($request->isMethod('HEAD')) {
+ // cf. RFC2616 14.13
+ $length = $headers->get('Content-Length');
+ $this->setContent(null);
+ if ($length) {
+ $headers->set('Content-Length', $length);
+ }
+ }
+ }
+
+ // Fix protocol
+ if ('HTTP/1.0' != $request->server->get('SERVER_PROTOCOL')) {
+ $this->setProtocolVersion('1.1');
+ }
+
+ // Check if we need to send extra expire info headers
+ if ('1.0' == $this->getProtocolVersion() && false !== strpos($headers->get('Cache-Control'), 'no-cache')) {
+ $headers->set('pragma', 'no-cache');
+ $headers->set('expires', -1);
+ }
+
+ $this->ensureIEOverSSLCompatibility($request);
+
+ if ($request->isSecure()) {
+ foreach ($headers->getCookies() as $cookie) {
+ $cookie->setSecureDefault(true);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers.
+ *
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ // headers have already been sent by the developer
+ if (headers_sent()) {
+ return $this;
+ }
+
+ // headers
+ foreach ($this->headers->allPreserveCaseWithoutCookies() as $name => $values) {
+ $replace = 0 === strcasecmp($name, 'Content-Type');
+ foreach ($values as $value) {
+ header($name.': '.$value, $replace, $this->statusCode);
+ }
+ }
+
+ // cookies
+ foreach ($this->headers->getCookies() as $cookie) {
+ header('Set-Cookie: '.$cookie, false, $this->statusCode);
+ }
+
+ // status
+ header(sprintf('HTTP/%s %s %s', $this->version, $this->statusCode, $this->statusText), true, $this->statusCode);
+
+ return $this;
+ }
+
+ /**
+ * Sends content for the current web response.
+ *
+ * @return $this
+ */
+ public function sendContent()
+ {
+ echo $this->content;
+
+ return $this;
+ }
+
+ /**
+ * Sends HTTP headers and content.
+ *
+ * @return $this
+ */
+ public function send()
+ {
+ $this->sendHeaders();
+ $this->sendContent();
+
+ if (\function_exists('fastcgi_finish_request')) {
+ fastcgi_finish_request();
+ } elseif (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true)) {
+ static::closeOutputBuffers(0, true);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response content.
+ *
+ * @return $this
+ *
+ * @throws \UnexpectedValueException
+ */
+ public function setContent(?string $content)
+ {
+ $this->content = $content ?? '';
+
+ return $this;
+ }
+
+ /**
+ * Gets the current response content.
+ *
+ * @return string|false
+ */
+ public function getContent()
+ {
+ return $this->content;
+ }
+
+ /**
+ * Sets the HTTP protocol version (1.0 or 1.1).
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setProtocolVersion(string $version): object
+ {
+ $this->version = $version;
+
+ return $this;
+ }
+
+ /**
+ * Gets the HTTP protocol version.
+ *
+ * @final
+ */
+ public function getProtocolVersion(): string
+ {
+ return $this->version;
+ }
+
+ /**
+ * Sets the response status code.
+ *
+ * If the status text is null it will be automatically populated for the known
+ * status codes and left empty otherwise.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException When the HTTP status code is not valid
+ *
+ * @final
+ */
+ public function setStatusCode(int $code, $text = null): object
+ {
+ $this->statusCode = $code;
+ if ($this->isInvalid()) {
+ throw new \InvalidArgumentException(sprintf('The HTTP status code "%s" is not valid.', $code));
+ }
+
+ if (null === $text) {
+ $this->statusText = isset(self::$statusTexts[$code]) ? self::$statusTexts[$code] : 'unknown status';
+
+ return $this;
+ }
+
+ if (false === $text) {
+ $this->statusText = '';
+
+ return $this;
+ }
+
+ $this->statusText = $text;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the status code for the current web response.
+ *
+ * @final
+ */
+ public function getStatusCode(): int
+ {
+ return $this->statusCode;
+ }
+
+ /**
+ * Sets the response charset.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setCharset(string $charset): object
+ {
+ $this->charset = $charset;
+
+ return $this;
+ }
+
+ /**
+ * Retrieves the response charset.
+ *
+ * @final
+ */
+ public function getCharset(): ?string
+ {
+ return $this->charset;
+ }
+
+ /**
+ * Returns true if the response may safely be kept in a shared (surrogate) cache.
+ *
+ * Responses marked "private" with an explicit Cache-Control directive are
+ * considered uncacheable.
+ *
+ * Responses with neither a freshness lifetime (Expires, max-age) nor cache
+ * validator (Last-Modified, ETag) are considered uncacheable because there is
+ * no way to tell when or how to remove them from the cache.
+ *
+ * Note that RFC 7231 and RFC 7234 possibly allow for a more permissive implementation,
+ * for example "status codes that are defined as cacheable by default [...]
+ * can be reused by a cache with heuristic expiration unless otherwise indicated"
+ * (https://tools.ietf.org/html/rfc7231#section-6.1)
+ *
+ * @final
+ */
+ public function isCacheable(): bool
+ {
+ if (!\in_array($this->statusCode, [200, 203, 300, 301, 302, 404, 410])) {
+ return false;
+ }
+
+ if ($this->headers->hasCacheControlDirective('no-store') || $this->headers->getCacheControlDirective('private')) {
+ return false;
+ }
+
+ return $this->isValidateable() || $this->isFresh();
+ }
+
+ /**
+ * Returns true if the response is "fresh".
+ *
+ * Fresh responses may be served from cache without any interaction with the
+ * origin. A response is considered fresh when it includes a Cache-Control/max-age
+ * indicator or Expires header and the calculated age is less than the freshness lifetime.
+ *
+ * @final
+ */
+ public function isFresh(): bool
+ {
+ return $this->getTtl() > 0;
+ }
+
+ /**
+ * Returns true if the response includes headers that can be used to validate
+ * the response with the origin server using a conditional GET request.
+ *
+ * @final
+ */
+ public function isValidateable(): bool
+ {
+ return $this->headers->has('Last-Modified') || $this->headers->has('ETag');
+ }
+
+ /**
+ * Marks the response as "private".
+ *
+ * It makes the response ineligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setPrivate(): object
+ {
+ $this->headers->removeCacheControlDirective('public');
+ $this->headers->addCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "public".
+ *
+ * It makes the response eligible for serving other clients.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setPublic(): object
+ {
+ $this->headers->addCacheControlDirective('public');
+ $this->headers->removeCacheControlDirective('private');
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as "immutable".
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setImmutable(bool $immutable = true): object
+ {
+ if ($immutable) {
+ $this->headers->addCacheControlDirective('immutable');
+ } else {
+ $this->headers->removeCacheControlDirective('immutable');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response is marked as "immutable".
+ *
+ * @final
+ */
+ public function isImmutable(): bool
+ {
+ return $this->headers->hasCacheControlDirective('immutable');
+ }
+
+ /**
+ * Returns true if the response must be revalidated by caches.
+ *
+ * This method indicates that the response must not be served stale by a
+ * cache in any circumstance without first revalidating with the origin.
+ * When present, the TTL of the response should not be overridden to be
+ * greater than the value provided by the origin.
+ *
+ * @final
+ */
+ public function mustRevalidate(): bool
+ {
+ return $this->headers->hasCacheControlDirective('must-revalidate') || $this->headers->hasCacheControlDirective('proxy-revalidate');
+ }
+
+ /**
+ * Returns the Date header as a DateTime instance.
+ *
+ * @throws \RuntimeException When the header is not parseable
+ *
+ * @final
+ */
+ public function getDate(): ?\DateTimeInterface
+ {
+ return $this->headers->getDate('Date');
+ }
+
+ /**
+ * Sets the Date header.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setDate(\DateTimeInterface $date): object
+ {
+ if ($date instanceof \DateTime) {
+ $date = \DateTimeImmutable::createFromMutable($date);
+ }
+
+ $date = $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Date', $date->format('D, d M Y H:i:s').' GMT');
+
+ return $this;
+ }
+
+ /**
+ * Returns the age of the response in seconds.
+ *
+ * @final
+ */
+ public function getAge(): int
+ {
+ if (null !== $age = $this->headers->get('Age')) {
+ return (int) $age;
+ }
+
+ return max(time() - (int) $this->getDate()->format('U'), 0);
+ }
+
+ /**
+ * Marks the response stale by setting the Age header to be equal to the maximum age of the response.
+ *
+ * @return $this
+ */
+ public function expire()
+ {
+ if ($this->isFresh()) {
+ $this->headers->set('Age', $this->getMaxAge());
+ $this->headers->remove('Expires');
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns the value of the Expires header as a DateTime instance.
+ *
+ * @final
+ */
+ public function getExpires(): ?\DateTimeInterface
+ {
+ try {
+ return $this->headers->getDate('Expires');
+ } catch (\RuntimeException $e) {
+ // according to RFC 2616 invalid date formats (e.g. "0" and "-1") must be treated as in the past
+ return \DateTime::createFromFormat('U', time() - 172800);
+ }
+ }
+
+ /**
+ * Sets the Expires HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setExpires(\DateTimeInterface $date = null): object
+ {
+ if (null === $date) {
+ $this->headers->remove('Expires');
+
+ return $this;
+ }
+
+ if ($date instanceof \DateTime) {
+ $date = \DateTimeImmutable::createFromMutable($date);
+ }
+
+ $date = $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Expires', $date->format('D, d M Y H:i:s').' GMT');
+
+ return $this;
+ }
+
+ /**
+ * Returns the number of seconds after the time specified in the response's Date
+ * header when the response should no longer be considered fresh.
+ *
+ * First, it checks for a s-maxage directive, then a max-age directive, and then it falls
+ * back on an expires header. It returns null when no maximum age can be established.
+ *
+ * @final
+ */
+ public function getMaxAge(): ?int
+ {
+ if ($this->headers->hasCacheControlDirective('s-maxage')) {
+ return (int) $this->headers->getCacheControlDirective('s-maxage');
+ }
+
+ if ($this->headers->hasCacheControlDirective('max-age')) {
+ return (int) $this->headers->getCacheControlDirective('max-age');
+ }
+
+ if (null !== $this->getExpires()) {
+ return (int) $this->getExpires()->format('U') - (int) $this->getDate()->format('U');
+ }
+
+ return null;
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh.
+ *
+ * This methods sets the Cache-Control max-age directive.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setMaxAge(int $value): object
+ {
+ $this->headers->addCacheControlDirective('max-age', $value);
+
+ return $this;
+ }
+
+ /**
+ * Sets the number of seconds after which the response should no longer be considered fresh by shared caches.
+ *
+ * This methods sets the Cache-Control s-maxage directive.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setSharedMaxAge(int $value): object
+ {
+ $this->setPublic();
+ $this->headers->addCacheControlDirective('s-maxage', $value);
+
+ return $this;
+ }
+
+ /**
+ * Returns the response's time-to-live in seconds.
+ *
+ * It returns null when no freshness information is present in the response.
+ *
+ * When the responses TTL is <= 0, the response may not be served from cache without first
+ * revalidating with the origin.
+ *
+ * @final
+ */
+ public function getTtl(): ?int
+ {
+ $maxAge = $this->getMaxAge();
+
+ return null !== $maxAge ? $maxAge - $this->getAge() : null;
+ }
+
+ /**
+ * Sets the response's time-to-live for shared caches in seconds.
+ *
+ * This method adjusts the Cache-Control/s-maxage directive.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setTtl(int $seconds): object
+ {
+ $this->setSharedMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's time-to-live for private/client caches in seconds.
+ *
+ * This method adjusts the Cache-Control/max-age directive.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setClientTtl(int $seconds): object
+ {
+ $this->setMaxAge($this->getAge() + $seconds);
+
+ return $this;
+ }
+
+ /**
+ * Returns the Last-Modified HTTP header as a DateTime instance.
+ *
+ * @throws \RuntimeException When the HTTP header is not parseable
+ *
+ * @final
+ */
+ public function getLastModified(): ?\DateTimeInterface
+ {
+ return $this->headers->getDate('Last-Modified');
+ }
+
+ /**
+ * Sets the Last-Modified HTTP header with a DateTime instance.
+ *
+ * Passing null as value will remove the header.
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setLastModified(\DateTimeInterface $date = null): object
+ {
+ if (null === $date) {
+ $this->headers->remove('Last-Modified');
+
+ return $this;
+ }
+
+ if ($date instanceof \DateTime) {
+ $date = \DateTimeImmutable::createFromMutable($date);
+ }
+
+ $date = $date->setTimezone(new \DateTimeZone('UTC'));
+ $this->headers->set('Last-Modified', $date->format('D, d M Y H:i:s').' GMT');
+
+ return $this;
+ }
+
+ /**
+ * Returns the literal value of the ETag HTTP header.
+ *
+ * @final
+ */
+ public function getEtag(): ?string
+ {
+ return $this->headers->get('ETag');
+ }
+
+ /**
+ * Sets the ETag value.
+ *
+ * @param string|null $etag The ETag unique identifier or null to remove the header
+ * @param bool $weak Whether you want a weak ETag or not
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setEtag(string $etag = null, bool $weak = false): object
+ {
+ if (null === $etag) {
+ $this->headers->remove('Etag');
+ } else {
+ if (0 !== strpos($etag, '"')) {
+ $etag = '"'.$etag.'"';
+ }
+
+ $this->headers->set('ETag', (true === $weak ? 'W/' : '').$etag);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sets the response's cache headers (validation and/or expiration).
+ *
+ * Available options are: etag, last_modified, max_age, s_maxage, private, public and immutable.
+ *
+ * @return $this
+ *
+ * @throws \InvalidArgumentException
+ *
+ * @final
+ */
+ public function setCache(array $options): object
+ {
+ if ($diff = array_diff(array_keys($options), ['etag', 'last_modified', 'max_age', 's_maxage', 'private', 'public', 'immutable'])) {
+ throw new \InvalidArgumentException(sprintf('Response does not support the following options: "%s".', implode('", "', $diff)));
+ }
+
+ if (isset($options['etag'])) {
+ $this->setEtag($options['etag']);
+ }
+
+ if (isset($options['last_modified'])) {
+ $this->setLastModified($options['last_modified']);
+ }
+
+ if (isset($options['max_age'])) {
+ $this->setMaxAge($options['max_age']);
+ }
+
+ if (isset($options['s_maxage'])) {
+ $this->setSharedMaxAge($options['s_maxage']);
+ }
+
+ if (isset($options['public'])) {
+ if ($options['public']) {
+ $this->setPublic();
+ } else {
+ $this->setPrivate();
+ }
+ }
+
+ if (isset($options['private'])) {
+ if ($options['private']) {
+ $this->setPrivate();
+ } else {
+ $this->setPublic();
+ }
+ }
+
+ if (isset($options['immutable'])) {
+ $this->setImmutable((bool) $options['immutable']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Modifies the response so that it conforms to the rules defined for a 304 status code.
+ *
+ * This sets the status, removes the body, and discards any headers
+ * that MUST NOT be included in 304 responses.
+ *
+ * @return $this
+ *
+ * @see https://tools.ietf.org/html/rfc2616#section-10.3.5
+ *
+ * @final
+ */
+ public function setNotModified(): object
+ {
+ $this->setStatusCode(304);
+ $this->setContent(null);
+
+ // remove headers that MUST NOT be included with 304 Not Modified responses
+ foreach (['Allow', 'Content-Encoding', 'Content-Language', 'Content-Length', 'Content-MD5', 'Content-Type', 'Last-Modified'] as $header) {
+ $this->headers->remove($header);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Returns true if the response includes a Vary header.
+ *
+ * @final
+ */
+ public function hasVary(): bool
+ {
+ return null !== $this->headers->get('Vary');
+ }
+
+ /**
+ * Returns an array of header names given in the Vary header.
+ *
+ * @final
+ */
+ public function getVary(): array
+ {
+ if (!$vary = $this->headers->all('Vary')) {
+ return [];
+ }
+
+ $ret = [];
+ foreach ($vary as $item) {
+ $ret = array_merge($ret, preg_split('/[\s,]+/', $item));
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Sets the Vary header.
+ *
+ * @param string|array $headers
+ * @param bool $replace Whether to replace the actual value or not (true by default)
+ *
+ * @return $this
+ *
+ * @final
+ */
+ public function setVary($headers, bool $replace = true): object
+ {
+ $this->headers->set('Vary', $headers, $replace);
+
+ return $this;
+ }
+
+ /**
+ * Determines if the Response validators (ETag, Last-Modified) match
+ * a conditional value specified in the Request.
+ *
+ * If the Response is not modified, it sets the status code to 304 and
+ * removes the actual content by calling the setNotModified() method.
+ *
+ * @return bool true if the Response validators match the Request, false otherwise
+ *
+ * @final
+ */
+ public function isNotModified(Request $request): bool
+ {
+ if (!$request->isMethodCacheable()) {
+ return false;
+ }
+
+ $notModified = false;
+ $lastModified = $this->headers->get('Last-Modified');
+ $modifiedSince = $request->headers->get('If-Modified-Since');
+
+ if ($etags = $request->getETags()) {
+ $notModified = \in_array($this->getEtag(), $etags) || \in_array('*', $etags);
+ }
+
+ if ($modifiedSince && $lastModified) {
+ $notModified = strtotime($modifiedSince) >= strtotime($lastModified) && (!$etags || $notModified);
+ }
+
+ if ($notModified) {
+ $this->setNotModified();
+ }
+
+ return $notModified;
+ }
+
+ /**
+ * Is response invalid?
+ *
+ * @see https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+ *
+ * @final
+ */
+ public function isInvalid(): bool
+ {
+ return $this->statusCode < 100 || $this->statusCode >= 600;
+ }
+
+ /**
+ * Is response informative?
+ *
+ * @final
+ */
+ public function isInformational(): bool
+ {
+ return $this->statusCode >= 100 && $this->statusCode < 200;
+ }
+
+ /**
+ * Is response successful?
+ *
+ * @final
+ */
+ public function isSuccessful(): bool
+ {
+ return $this->statusCode >= 200 && $this->statusCode < 300;
+ }
+
+ /**
+ * Is the response a redirect?
+ *
+ * @final
+ */
+ public function isRedirection(): bool
+ {
+ return $this->statusCode >= 300 && $this->statusCode < 400;
+ }
+
+ /**
+ * Is there a client error?
+ *
+ * @final
+ */
+ public function isClientError(): bool
+ {
+ return $this->statusCode >= 400 && $this->statusCode < 500;
+ }
+
+ /**
+ * Was there a server side error?
+ *
+ * @final
+ */
+ public function isServerError(): bool
+ {
+ return $this->statusCode >= 500 && $this->statusCode < 600;
+ }
+
+ /**
+ * Is the response OK?
+ *
+ * @final
+ */
+ public function isOk(): bool
+ {
+ return 200 === $this->statusCode;
+ }
+
+ /**
+ * Is the response forbidden?
+ *
+ * @final
+ */
+ public function isForbidden(): bool
+ {
+ return 403 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a not found error?
+ *
+ * @final
+ */
+ public function isNotFound(): bool
+ {
+ return 404 === $this->statusCode;
+ }
+
+ /**
+ * Is the response a redirect of some form?
+ *
+ * @final
+ */
+ public function isRedirect(string $location = null): bool
+ {
+ return \in_array($this->statusCode, [201, 301, 302, 303, 307, 308]) && (null === $location ?: $location == $this->headers->get('Location'));
+ }
+
+ /**
+ * Is the response empty?
+ *
+ * @final
+ */
+ public function isEmpty(): bool
+ {
+ return \in_array($this->statusCode, [204, 304]);
+ }
+
+ /**
+ * Cleans or flushes output buffers up to target level.
+ *
+ * Resulting level can be greater than target level if a non-removable buffer has been encountered.
+ *
+ * @final
+ */
+ public static function closeOutputBuffers(int $targetLevel, bool $flush): void
+ {
+ $status = ob_get_status(true);
+ $level = \count($status);
+ $flags = PHP_OUTPUT_HANDLER_REMOVABLE | ($flush ? PHP_OUTPUT_HANDLER_FLUSHABLE : PHP_OUTPUT_HANDLER_CLEANABLE);
+
+ while ($level-- > $targetLevel && ($s = $status[$level]) && (!isset($s['del']) ? !isset($s['flags']) || ($s['flags'] & $flags) === $flags : $s['del'])) {
+ if ($flush) {
+ ob_end_flush();
+ } else {
+ ob_end_clean();
+ }
+ }
+ }
+
+ /**
+ * Checks if we need to remove Cache-Control for SSL encrypted downloads when using IE < 9.
+ *
+ * @see http://support.microsoft.com/kb/323308
+ *
+ * @final
+ */
+ protected function ensureIEOverSSLCompatibility(Request $request): void
+ {
+ if (false !== stripos($this->headers->get('Content-Disposition'), 'attachment') && 1 == preg_match('/MSIE (.*?);/i', $request->server->get('HTTP_USER_AGENT'), $match) && true === $request->isSecure()) {
+ if ((int) preg_replace('/(MSIE )(.*?);/', '$2', $match[0]) < 9) {
+ $this->headers->remove('Cache-Control');
+ }
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/ResponseHeaderBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/ResponseHeaderBag.php
new file mode 100644
index 00000000..de1e2a16
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/ResponseHeaderBag.php
@@ -0,0 +1,293 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ResponseHeaderBag is a container for Response HTTP headers.
+ *
+ * @author Fabien Potencier
+ */
+class ResponseHeaderBag extends HeaderBag
+{
+ const COOKIES_FLAT = 'flat';
+ const COOKIES_ARRAY = 'array';
+
+ const DISPOSITION_ATTACHMENT = 'attachment';
+ const DISPOSITION_INLINE = 'inline';
+
+ protected $computedCacheControl = [];
+ protected $cookies = [];
+ protected $headerNames = [];
+
+ public function __construct(array $headers = [])
+ {
+ parent::__construct($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ /* RFC2616 - 14.18 says all Responses need to have a Date */
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * Returns the headers, with original capitalizations.
+ *
+ * @return array An array of headers
+ */
+ public function allPreserveCase()
+ {
+ $headers = [];
+ foreach ($this->all() as $name => $value) {
+ $headers[$this->headerNames[$name] ?? $name] = $value;
+ }
+
+ return $headers;
+ }
+
+ public function allPreserveCaseWithoutCookies()
+ {
+ $headers = $this->allPreserveCase();
+ if (isset($this->headerNames['set-cookie'])) {
+ unset($headers[$this->headerNames['set-cookie']]);
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $headers = [])
+ {
+ $this->headerNames = [];
+
+ parent::replace($headers);
+
+ if (!isset($this->headers['cache-control'])) {
+ $this->set('Cache-Control', '');
+ }
+
+ if (!isset($this->headers['date'])) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all(string $key = null)
+ {
+ $headers = parent::all();
+
+ if (null !== $key) {
+ $key = strtr($key, self::UPPER, self::LOWER);
+
+ return 'set-cookie' !== $key ? $headers[$key] ?? [] : array_map('strval', $this->getCookies());
+ }
+
+ foreach ($this->getCookies() as $cookie) {
+ $headers['set-cookie'][] = (string) $cookie;
+ }
+
+ return $headers;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $key, $values, bool $replace = true)
+ {
+ $uniqueKey = strtr($key, self::UPPER, self::LOWER);
+
+ if ('set-cookie' === $uniqueKey) {
+ if ($replace) {
+ $this->cookies = [];
+ }
+ foreach ((array) $values as $cookie) {
+ $this->setCookie(Cookie::fromString($cookie));
+ }
+ $this->headerNames[$uniqueKey] = $key;
+
+ return;
+ }
+
+ $this->headerNames[$uniqueKey] = $key;
+
+ parent::set($key, $values, $replace);
+
+ // ensure the cache-control header has sensible defaults
+ if (\in_array($uniqueKey, ['cache-control', 'etag', 'last-modified', 'expires'], true) && '' !== $computed = $this->computeCacheControlValue()) {
+ $this->headers['cache-control'] = [$computed];
+ $this->headerNames['cache-control'] = 'Cache-Control';
+ $this->computedCacheControl = $this->parseCacheControl($computed);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(string $key)
+ {
+ $uniqueKey = strtr($key, self::UPPER, self::LOWER);
+ unset($this->headerNames[$uniqueKey]);
+
+ if ('set-cookie' === $uniqueKey) {
+ $this->cookies = [];
+
+ return;
+ }
+
+ parent::remove($key);
+
+ if ('cache-control' === $uniqueKey) {
+ $this->computedCacheControl = [];
+ }
+
+ if ('date' === $uniqueKey) {
+ $this->initDate();
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function hasCacheControlDirective(string $key)
+ {
+ return \array_key_exists($key, $this->computedCacheControl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getCacheControlDirective(string $key)
+ {
+ return \array_key_exists($key, $this->computedCacheControl) ? $this->computedCacheControl[$key] : null;
+ }
+
+ public function setCookie(Cookie $cookie)
+ {
+ $this->cookies[$cookie->getDomain()][$cookie->getPath()][$cookie->getName()] = $cookie;
+ $this->headerNames['set-cookie'] = 'Set-Cookie';
+ }
+
+ /**
+ * Removes a cookie from the array, but does not unset it in the browser.
+ */
+ public function removeCookie(string $name, ?string $path = '/', string $domain = null)
+ {
+ if (null === $path) {
+ $path = '/';
+ }
+
+ unset($this->cookies[$domain][$path][$name]);
+
+ if (empty($this->cookies[$domain][$path])) {
+ unset($this->cookies[$domain][$path]);
+
+ if (empty($this->cookies[$domain])) {
+ unset($this->cookies[$domain]);
+ }
+ }
+
+ if (empty($this->cookies)) {
+ unset($this->headerNames['set-cookie']);
+ }
+ }
+
+ /**
+ * Returns an array with all cookies.
+ *
+ * @return Cookie[]
+ *
+ * @throws \InvalidArgumentException When the $format is invalid
+ */
+ public function getCookies(string $format = self::COOKIES_FLAT)
+ {
+ if (!\in_array($format, [self::COOKIES_FLAT, self::COOKIES_ARRAY])) {
+ throw new \InvalidArgumentException(sprintf('Format "%s" invalid (%s).', $format, implode(', ', [self::COOKIES_FLAT, self::COOKIES_ARRAY])));
+ }
+
+ if (self::COOKIES_ARRAY === $format) {
+ return $this->cookies;
+ }
+
+ $flattenedCookies = [];
+ foreach ($this->cookies as $path) {
+ foreach ($path as $cookies) {
+ foreach ($cookies as $cookie) {
+ $flattenedCookies[] = $cookie;
+ }
+ }
+ }
+
+ return $flattenedCookies;
+ }
+
+ /**
+ * Clears a cookie in the browser.
+ */
+ public function clearCookie(string $name, ?string $path = '/', string $domain = null, bool $secure = false, bool $httpOnly = true)
+ {
+ $this->setCookie(new Cookie($name, null, 1, $path, $domain, $secure, $httpOnly, false, null));
+ }
+
+ /**
+ * @see HeaderUtils::makeDisposition()
+ */
+ public function makeDisposition(string $disposition, string $filename, string $filenameFallback = '')
+ {
+ return HeaderUtils::makeDisposition($disposition, $filename, $filenameFallback);
+ }
+
+ /**
+ * Returns the calculated value of the cache-control header.
+ *
+ * This considers several other headers and calculates or modifies the
+ * cache-control header to a sensible, conservative value.
+ *
+ * @return string
+ */
+ protected function computeCacheControlValue()
+ {
+ if (!$this->cacheControl && !$this->has('ETag') && !$this->has('Last-Modified') && !$this->has('Expires')) {
+ return 'no-cache, private';
+ }
+
+ if (!$this->cacheControl) {
+ // conservative by default
+ return 'private, must-revalidate';
+ }
+
+ $header = $this->getCacheControlHeader();
+ if (isset($this->cacheControl['public']) || isset($this->cacheControl['private'])) {
+ return $header;
+ }
+
+ // public if s-maxage is defined, private otherwise
+ if (!isset($this->cacheControl['s-maxage'])) {
+ return $header.', private';
+ }
+
+ return $header;
+ }
+
+ private function initDate(): void
+ {
+ $now = \DateTime::createFromFormat('U', time());
+ $now->setTimezone(new \DateTimeZone('UTC'));
+ $this->set('Date', $now->format('D, d M Y H:i:s').' GMT');
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/ServerBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/ServerBag.php
new file mode 100644
index 00000000..25da35ec
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/ServerBag.php
@@ -0,0 +1,99 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * ServerBag is a container for HTTP headers from the $_SERVER variable.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ * @author Robert Kiss
+ */
+class ServerBag extends ParameterBag
+{
+ /**
+ * Gets the HTTP headers.
+ *
+ * @return array
+ */
+ public function getHeaders()
+ {
+ $headers = [];
+ foreach ($this->parameters as $key => $value) {
+ if (0 === strpos($key, 'HTTP_')) {
+ $headers[substr($key, 5)] = $value;
+ } elseif (\in_array($key, ['CONTENT_TYPE', 'CONTENT_LENGTH', 'CONTENT_MD5'], true)) {
+ $headers[$key] = $value;
+ }
+ }
+
+ if (isset($this->parameters['PHP_AUTH_USER'])) {
+ $headers['PHP_AUTH_USER'] = $this->parameters['PHP_AUTH_USER'];
+ $headers['PHP_AUTH_PW'] = isset($this->parameters['PHP_AUTH_PW']) ? $this->parameters['PHP_AUTH_PW'] : '';
+ } else {
+ /*
+ * php-cgi under Apache does not pass HTTP Basic user/pass to PHP by default
+ * For this workaround to work, add these lines to your .htaccess file:
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ *
+ * A sample .htaccess file:
+ * RewriteEngine On
+ * RewriteCond %{HTTP:Authorization} ^(.+)$
+ * RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
+ * RewriteCond %{REQUEST_FILENAME} !-f
+ * RewriteRule ^(.*)$ app.php [QSA,L]
+ */
+
+ $authorizationHeader = null;
+ if (isset($this->parameters['HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['HTTP_AUTHORIZATION'];
+ } elseif (isset($this->parameters['REDIRECT_HTTP_AUTHORIZATION'])) {
+ $authorizationHeader = $this->parameters['REDIRECT_HTTP_AUTHORIZATION'];
+ }
+
+ if (null !== $authorizationHeader) {
+ if (0 === stripos($authorizationHeader, 'basic ')) {
+ // Decode AUTHORIZATION header into PHP_AUTH_USER and PHP_AUTH_PW when authorization header is basic
+ $exploded = explode(':', base64_decode(substr($authorizationHeader, 6)), 2);
+ if (2 == \count($exploded)) {
+ list($headers['PHP_AUTH_USER'], $headers['PHP_AUTH_PW']) = $exploded;
+ }
+ } elseif (empty($this->parameters['PHP_AUTH_DIGEST']) && (0 === stripos($authorizationHeader, 'digest '))) {
+ // In some circumstances PHP_AUTH_DIGEST needs to be set
+ $headers['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ $this->parameters['PHP_AUTH_DIGEST'] = $authorizationHeader;
+ } elseif (0 === stripos($authorizationHeader, 'bearer ')) {
+ /*
+ * XXX: Since there is no PHP_AUTH_BEARER in PHP predefined variables,
+ * I'll just set $headers['AUTHORIZATION'] here.
+ * https://php.net/reserved.variables.server
+ */
+ $headers['AUTHORIZATION'] = $authorizationHeader;
+ }
+ }
+ }
+
+ if (isset($headers['AUTHORIZATION'])) {
+ return $headers;
+ }
+
+ // PHP_AUTH_USER/PHP_AUTH_PW
+ if (isset($headers['PHP_AUTH_USER'])) {
+ $headers['AUTHORIZATION'] = 'Basic '.base64_encode($headers['PHP_AUTH_USER'].':'.$headers['PHP_AUTH_PW']);
+ } elseif (isset($headers['PHP_AUTH_DIGEST'])) {
+ $headers['AUTHORIZATION'] = $headers['PHP_AUTH_DIGEST'];
+ }
+
+ return $headers;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBag.php
new file mode 100644
index 00000000..aad6b610
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBag.php
@@ -0,0 +1,148 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class relates to session attribute storage.
+ */
+class AttributeBag implements AttributeBagInterface, \IteratorAggregate, \Countable
+{
+ private $name = 'attributes';
+ private $storageKey;
+
+ protected $attributes = [];
+
+ /**
+ * @param string $storageKey The key used to store attributes in the session
+ */
+ public function __construct(string $storageKey = '_sf2_attributes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$attributes)
+ {
+ $this->attributes = &$attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has(string $name)
+ {
+ return \array_key_exists($name, $this->attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get(string $name, $default = null)
+ {
+ return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $name, $value)
+ {
+ $this->attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->attributes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->attributes = [];
+ foreach ($attributes as $key => $value) {
+ $this->set($key, $value);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(string $name)
+ {
+ $retval = null;
+ if (\array_key_exists($name, $this->attributes)) {
+ $retval = $this->attributes[$name];
+ unset($this->attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $return = $this->attributes;
+ $this->attributes = [];
+
+ return $return;
+ }
+
+ /**
+ * Returns an iterator for attributes.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->attributes);
+ }
+
+ /**
+ * Returns the number of attributes.
+ *
+ * @return int The number of attributes
+ */
+ public function count()
+ {
+ return \count($this->attributes);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
new file mode 100644
index 00000000..7017b717
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/AttributeBagInterface.php
@@ -0,0 +1,61 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Attributes store.
+ *
+ * @author Drak
+ */
+interface AttributeBagInterface extends SessionBagInterface
+{
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @return bool true if the attribute is defined, false otherwise
+ */
+ public function has(string $name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param mixed $default The default value if not found
+ *
+ * @return mixed
+ */
+ public function get(string $name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param mixed $value
+ */
+ public function set(string $name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array
+ */
+ public function all();
+
+ public function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @return mixed The removed value or null when it does not exist
+ */
+ public function remove(string $name);
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
new file mode 100644
index 00000000..7e752dda
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Attribute/NamespacedAttributeBag.php
@@ -0,0 +1,157 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Attribute;
+
+/**
+ * This class provides structured storage of session attributes using
+ * a name spacing character in the key.
+ *
+ * @author Drak
+ */
+class NamespacedAttributeBag extends AttributeBag
+{
+ private $namespaceCharacter;
+
+ /**
+ * @param string $storageKey Session storage key
+ * @param string $namespaceCharacter Namespace character to use in keys
+ */
+ public function __construct(string $storageKey = '_sf2_attributes', string $namespaceCharacter = '/')
+ {
+ $this->namespaceCharacter = $namespaceCharacter;
+ parent::__construct($storageKey);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has(string $name)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return false;
+ }
+
+ return \array_key_exists($name, $attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get(string $name, $default = null)
+ {
+ // reference mismatch: if fixed, re-introduced in array_key_exists; keep as it is
+ $attributes = $this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+
+ if (null === $attributes) {
+ return $default;
+ }
+
+ return \array_key_exists($name, $attributes) ? $attributes[$name] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $name, $value)
+ {
+ $attributes = &$this->resolveAttributePath($name, true);
+ $name = $this->resolveKey($name);
+ $attributes[$name] = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(string $name)
+ {
+ $retval = null;
+ $attributes = &$this->resolveAttributePath($name);
+ $name = $this->resolveKey($name);
+ if (null !== $attributes && \array_key_exists($name, $attributes)) {
+ $retval = $attributes[$name];
+ unset($attributes[$name]);
+ }
+
+ return $retval;
+ }
+
+ /**
+ * Resolves a path in attributes property and returns it as a reference.
+ *
+ * This method allows structured namespacing of session attributes.
+ *
+ * @param string $name Key name
+ * @param bool $writeContext Write context, default false
+ *
+ * @return array|null
+ */
+ protected function &resolveAttributePath(string $name, bool $writeContext = false)
+ {
+ $array = &$this->attributes;
+ $name = (0 === strpos($name, $this->namespaceCharacter)) ? substr($name, 1) : $name;
+
+ // Check if there is anything to do, else return
+ if (!$name) {
+ return $array;
+ }
+
+ $parts = explode($this->namespaceCharacter, $name);
+ if (\count($parts) < 2) {
+ if (!$writeContext) {
+ return $array;
+ }
+
+ $array[$parts[0]] = [];
+
+ return $array;
+ }
+
+ unset($parts[\count($parts) - 1]);
+
+ foreach ($parts as $part) {
+ if (null !== $array && !\array_key_exists($part, $array)) {
+ if (!$writeContext) {
+ $null = null;
+
+ return $null;
+ }
+
+ $array[$part] = [];
+ }
+
+ $array = &$array[$part];
+ }
+
+ return $array;
+ }
+
+ /**
+ * Resolves the key from the name.
+ *
+ * This is the last part in a dot separated string.
+ *
+ * @return string
+ */
+ protected function resolveKey(string $name)
+ {
+ if (false !== $pos = strrpos($name, $this->namespaceCharacter)) {
+ $name = substr($name, $pos + 1);
+ }
+
+ return $name;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
new file mode 100644
index 00000000..2707d64e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/AutoExpireFlashBag.php
@@ -0,0 +1,161 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * AutoExpireFlashBag flash message container.
+ *
+ * @author Drak
+ */
+class AutoExpireFlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = ['display' => [], 'new' => []];
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct(string $storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+
+ // The logic: messages from the last request will be stored in new, so we move them to previous
+ // This request we will show what is in 'display'. What is placed into 'new' this time round will
+ // be moved to display next time round.
+ $this->flashes['display'] = \array_key_exists('new', $this->flashes) ? $this->flashes['new'] : [];
+ $this->flashes['new'] = [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(string $type, $message)
+ {
+ $this->flashes['new'][$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek(string $type, array $default = [])
+ {
+ return $this->has($type) ? $this->flashes['display'][$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return \array_key_exists('display', $this->flashes) ? (array) $this->flashes['display'] : [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get(string $type, array $default = [])
+ {
+ $return = $default;
+
+ if (!$this->has($type)) {
+ return $return;
+ }
+
+ if (isset($this->flashes['display'][$type])) {
+ $return = $this->flashes['display'][$type];
+ unset($this->flashes['display'][$type]);
+ }
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->flashes['display'];
+ $this->flashes['display'] = [];
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes['new'] = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $type, $messages)
+ {
+ $this->flashes['new'][$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has(string $type)
+ {
+ return \array_key_exists($type, $this->flashes['display']) && $this->flashes['display'][$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes['display']);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBag.php
new file mode 100644
index 00000000..88df7508
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBag.php
@@ -0,0 +1,152 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+/**
+ * FlashBag flash message container.
+ *
+ * @author Drak
+ */
+class FlashBag implements FlashBagInterface
+{
+ private $name = 'flashes';
+ private $flashes = [];
+ private $storageKey;
+
+ /**
+ * @param string $storageKey The key used to store flashes in the session
+ */
+ public function __construct(string $storageKey = '_symfony_flashes')
+ {
+ $this->storageKey = $storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ public function setName(string $name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$flashes)
+ {
+ $this->flashes = &$flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function add(string $type, $message)
+ {
+ $this->flashes[$type][] = $message;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peek(string $type, array $default = [])
+ {
+ return $this->has($type) ? $this->flashes[$type] : $default;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function peekAll()
+ {
+ return $this->flashes;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get(string $type, array $default = [])
+ {
+ if (!$this->has($type)) {
+ return $default;
+ }
+
+ $return = $this->flashes[$type];
+
+ unset($this->flashes[$type]);
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ $return = $this->peekAll();
+ $this->flashes = [];
+
+ return $return;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $type, $messages)
+ {
+ $this->flashes[$type] = (array) $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setAll(array $messages)
+ {
+ $this->flashes = $messages;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has(string $type)
+ {
+ return \array_key_exists($type, $this->flashes) && $this->flashes[$type];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function keys()
+ {
+ return array_keys($this->flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->all();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBagInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBagInterface.php
new file mode 100644
index 00000000..8713e71d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Flash/FlashBagInterface.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Flash;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * FlashBagInterface.
+ *
+ * @author Drak
+ */
+interface FlashBagInterface extends SessionBagInterface
+{
+ /**
+ * Adds a flash message for the given type.
+ *
+ * @param mixed $message
+ */
+ public function add(string $type, $message);
+
+ /**
+ * Registers one or more messages for a given type.
+ *
+ * @param string|array $messages
+ */
+ public function set(string $type, $messages);
+
+ /**
+ * Gets flash messages for a given type.
+ *
+ * @param string $type Message category type
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function peek(string $type, array $default = []);
+
+ /**
+ * Gets all flash messages.
+ *
+ * @return array
+ */
+ public function peekAll();
+
+ /**
+ * Gets and clears flash from the stack.
+ *
+ * @param array $default Default value if $type does not exist
+ *
+ * @return array
+ */
+ public function get(string $type, array $default = []);
+
+ /**
+ * Gets and clears flashes from the stack.
+ *
+ * @return array
+ */
+ public function all();
+
+ /**
+ * Sets all flash messages.
+ */
+ public function setAll(array $messages);
+
+ /**
+ * Has flash messages for a given type?
+ *
+ * @return bool
+ */
+ public function has(string $type);
+
+ /**
+ * Returns a list of all defined types.
+ *
+ * @return array
+ */
+ public function keys();
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Session.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Session.php
new file mode 100644
index 00000000..89071b86
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Session.php
@@ -0,0 +1,268 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
+use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBag;
+use Symfony\Component\HttpFoundation\Session\Flash\FlashBagInterface;
+use Symfony\Component\HttpFoundation\Session\Storage\NativeSessionStorage;
+use Symfony\Component\HttpFoundation\Session\Storage\SessionStorageInterface;
+
+/**
+ * @author Fabien Potencier
+ * @author Drak
+ */
+class Session implements SessionInterface, \IteratorAggregate, \Countable
+{
+ protected $storage;
+
+ private $flashName;
+ private $attributeName;
+ private $data = [];
+ private $usageIndex = 0;
+
+ public function __construct(SessionStorageInterface $storage = null, AttributeBagInterface $attributes = null, FlashBagInterface $flashes = null)
+ {
+ $this->storage = $storage ?: new NativeSessionStorage();
+
+ $attributes = $attributes ?: new AttributeBag();
+ $this->attributeName = $attributes->getName();
+ $this->registerBag($attributes);
+
+ $flashes = $flashes ?: new FlashBag();
+ $this->flashName = $flashes->getName();
+ $this->registerBag($flashes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ return $this->storage->start();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function has(string $name)
+ {
+ return $this->getAttributeBag()->has($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function get(string $name, $default = null)
+ {
+ return $this->getAttributeBag()->get($name, $default);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function set(string $name, $value)
+ {
+ $this->getAttributeBag()->set($name, $value);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function all()
+ {
+ return $this->getAttributeBag()->all();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function replace(array $attributes)
+ {
+ $this->getAttributeBag()->replace($attributes);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function remove(string $name)
+ {
+ return $this->getAttributeBag()->remove($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ $this->getAttributeBag()->clear();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->storage->isStarted();
+ }
+
+ /**
+ * Returns an iterator for attributes.
+ *
+ * @return \ArrayIterator An \ArrayIterator instance
+ */
+ public function getIterator()
+ {
+ return new \ArrayIterator($this->getAttributeBag()->all());
+ }
+
+ /**
+ * Returns the number of attributes.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ return \count($this->getAttributeBag()->all());
+ }
+
+ public function &getUsageIndex(): int
+ {
+ return $this->usageIndex;
+ }
+
+ /**
+ * @internal
+ */
+ public function isEmpty(): bool
+ {
+ if ($this->isStarted()) {
+ ++$this->usageIndex;
+ }
+ foreach ($this->data as &$data) {
+ if (!empty($data)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function invalidate(int $lifetime = null)
+ {
+ $this->storage->clear();
+
+ return $this->migrate(true, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function migrate(bool $destroy = false, int $lifetime = null)
+ {
+ return $this->storage->regenerate($destroy, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ $this->storage->save();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->storage->getId();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId(string $id)
+ {
+ if ($this->storage->getId() !== $id) {
+ $this->storage->setId($id);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->storage->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName(string $name)
+ {
+ $this->storage->setName($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMetadataBag()
+ {
+ ++$this->usageIndex;
+
+ return $this->storage->getMetadataBag();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->storage->registerBag(new SessionBagProxy($bag, $this->data, $this->usageIndex));
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag(string $name)
+ {
+ $bag = $this->storage->getBag($name);
+
+ return method_exists($bag, 'getBag') ? $bag->getBag() : $bag;
+ }
+
+ /**
+ * Gets the flashbag interface.
+ *
+ * @return FlashBagInterface
+ */
+ public function getFlashBag()
+ {
+ return $this->getBag($this->flashName);
+ }
+
+ /**
+ * Gets the attributebag interface.
+ *
+ * Note that this method was added to help with IDE autocompletion.
+ */
+ private function getAttributeBag(): AttributeBagInterface
+ {
+ return $this->getBag($this->attributeName);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagInterface.php
new file mode 100644
index 00000000..8e37d06d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagInterface.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * Session Bag store.
+ *
+ * @author Drak
+ */
+interface SessionBagInterface
+{
+ /**
+ * Gets this bag's name.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Initializes the Bag.
+ */
+ public function initialize(array &$array);
+
+ /**
+ * Gets the storage key for this bag.
+ *
+ * @return string
+ */
+ public function getStorageKey();
+
+ /**
+ * Clears out data from bag.
+ *
+ * @return mixed Whatever data was contained
+ */
+ public function clear();
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagProxy.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagProxy.php
new file mode 100644
index 00000000..0ae8231e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionBagProxy.php
@@ -0,0 +1,83 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * @author Nicolas Grekas
+ *
+ * @internal
+ */
+final class SessionBagProxy implements SessionBagInterface
+{
+ private $bag;
+ private $data;
+ private $usageIndex;
+
+ public function __construct(SessionBagInterface $bag, array &$data, ?int &$usageIndex)
+ {
+ $this->bag = $bag;
+ $this->data = &$data;
+ $this->usageIndex = &$usageIndex;
+ }
+
+ public function getBag(): SessionBagInterface
+ {
+ ++$this->usageIndex;
+
+ return $this->bag;
+ }
+
+ public function isEmpty(): bool
+ {
+ if (!isset($this->data[$this->bag->getStorageKey()])) {
+ return true;
+ }
+ ++$this->usageIndex;
+
+ return empty($this->data[$this->bag->getStorageKey()]);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName(): string
+ {
+ return $this->bag->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$array): void
+ {
+ ++$this->usageIndex;
+ $this->data[$this->bag->getStorageKey()] = &$array;
+
+ $this->bag->initialize($array);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey(): string
+ {
+ return $this->bag->getStorageKey();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ return $this->bag->clear();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionInterface.php
new file mode 100644
index 00000000..b2f09fd0
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionInterface.php
@@ -0,0 +1,166 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+use Symfony\Component\HttpFoundation\Session\Storage\MetadataBag;
+
+/**
+ * Interface for the session.
+ *
+ * @author Drak
+ */
+interface SessionInterface
+{
+ /**
+ * Starts the session storage.
+ *
+ * @return bool
+ *
+ * @throws \RuntimeException if session fails to start
+ */
+ public function start();
+
+ /**
+ * Returns the session ID.
+ *
+ * @return string
+ */
+ public function getId();
+
+ /**
+ * Sets the session ID.
+ */
+ public function setId(string $id);
+
+ /**
+ * Returns the session name.
+ *
+ * @return string
+ */
+ public function getName();
+
+ /**
+ * Sets the session name.
+ */
+ public function setName(string $name);
+
+ /**
+ * Invalidates the current session.
+ *
+ * Clears all session attributes and flashes and regenerates the
+ * session and deletes the old session from persistence.
+ *
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool
+ */
+ public function invalidate(int $lifetime = null);
+
+ /**
+ * Migrates the current session to a new session id while maintaining all
+ * session attributes.
+ *
+ * @param bool $destroy Whether to delete the old session or leave it to garbage collection
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool
+ */
+ public function migrate(bool $destroy = false, int $lifetime = null);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * This method is generally not required for real sessions as
+ * the session will be automatically saved at the end of
+ * code execution.
+ */
+ public function save();
+
+ /**
+ * Checks if an attribute is defined.
+ *
+ * @return bool
+ */
+ public function has(string $name);
+
+ /**
+ * Returns an attribute.
+ *
+ * @param mixed $default The default value if not found
+ *
+ * @return mixed
+ */
+ public function get(string $name, $default = null);
+
+ /**
+ * Sets an attribute.
+ *
+ * @param mixed $value
+ */
+ public function set(string $name, $value);
+
+ /**
+ * Returns attributes.
+ *
+ * @return array
+ */
+ public function all();
+
+ /**
+ * Sets attributes.
+ */
+ public function replace(array $attributes);
+
+ /**
+ * Removes an attribute.
+ *
+ * @return mixed The removed value or null when it does not exist
+ */
+ public function remove(string $name);
+
+ /**
+ * Clears all attributes.
+ */
+ public function clear();
+
+ /**
+ * Checks if the session was started.
+ *
+ * @return bool
+ */
+ public function isStarted();
+
+ /**
+ * Registers a SessionBagInterface with the session.
+ */
+ public function registerBag(SessionBagInterface $bag);
+
+ /**
+ * Gets a bag instance by name.
+ *
+ * @return SessionBagInterface
+ */
+ public function getBag(string $name);
+
+ /**
+ * Gets session meta.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag();
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionUtils.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionUtils.php
new file mode 100644
index 00000000..b5bce4a8
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/SessionUtils.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session;
+
+/**
+ * Session utility functions.
+ *
+ * @author Nicolas Grekas
+ * @author Rémon van de Kamp
+ *
+ * @internal
+ */
+final class SessionUtils
+{
+ /**
+ * Finds the session header amongst the headers that are to be sent, removes it, and returns
+ * it so the caller can process it further.
+ */
+ public static function popSessionCookie(string $sessionName, string $sessionId): ?string
+ {
+ $sessionCookie = null;
+ $sessionCookiePrefix = sprintf(' %s=', urlencode($sessionName));
+ $sessionCookieWithId = sprintf('%s%s;', $sessionCookiePrefix, urlencode($sessionId));
+ $otherCookies = [];
+ foreach (headers_list() as $h) {
+ if (0 !== stripos($h, 'Set-Cookie:')) {
+ continue;
+ }
+ if (11 === strpos($h, $sessionCookiePrefix, 11)) {
+ $sessionCookie = $h;
+
+ if (11 !== strpos($h, $sessionCookieWithId, 11)) {
+ $otherCookies[] = $h;
+ }
+ } else {
+ $otherCookies[] = $h;
+ }
+ }
+ if (null === $sessionCookie) {
+ return null;
+ }
+
+ header_remove('Set-Cookie');
+ foreach ($otherCookies as $h) {
+ header($h, false);
+ }
+
+ return $sessionCookie;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
new file mode 100644
index 00000000..0618035b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/AbstractSessionHandler.php
@@ -0,0 +1,141 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+use Symfony\Component\HttpFoundation\Session\SessionUtils;
+
+/**
+ * This abstract session handler provides a generic implementation
+ * of the PHP 7.0 SessionUpdateTimestampHandlerInterface,
+ * enabling strict and lazy session handling.
+ *
+ * @author Nicolas Grekas
+ */
+abstract class AbstractSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+ private $sessionName;
+ private $prefetchId;
+ private $prefetchData;
+ private $newSessionId;
+ private $igbinaryEmptyData;
+
+ /**
+ * @return bool
+ */
+ public function open($savePath, $sessionName)
+ {
+ $this->sessionName = $sessionName;
+ if (!headers_sent() && !ini_get('session.cache_limiter') && '0' !== ini_get('session.cache_limiter')) {
+ header(sprintf('Cache-Control: max-age=%d, private, must-revalidate', 60 * (int) ini_get('session.cache_expire')));
+ }
+
+ return true;
+ }
+
+ /**
+ * @return string
+ */
+ abstract protected function doRead(string $sessionId);
+
+ /**
+ * @return bool
+ */
+ abstract protected function doWrite(string $sessionId, string $data);
+
+ /**
+ * @return bool
+ */
+ abstract protected function doDestroy(string $sessionId);
+
+ /**
+ * @return bool
+ */
+ public function validateId($sessionId)
+ {
+ $this->prefetchData = $this->read($sessionId);
+ $this->prefetchId = $sessionId;
+
+ return '' !== $this->prefetchData;
+ }
+
+ /**
+ * @return string
+ */
+ public function read($sessionId)
+ {
+ if (null !== $this->prefetchId) {
+ $prefetchId = $this->prefetchId;
+ $prefetchData = $this->prefetchData;
+ $this->prefetchId = $this->prefetchData = null;
+
+ if ($prefetchId === $sessionId || '' === $prefetchData) {
+ $this->newSessionId = '' === $prefetchData ? $sessionId : null;
+
+ return $prefetchData;
+ }
+ }
+
+ $data = $this->doRead($sessionId);
+ $this->newSessionId = '' === $data ? $sessionId : null;
+
+ return $data;
+ }
+
+ /**
+ * @return bool
+ */
+ public function write($sessionId, $data)
+ {
+ if (null === $this->igbinaryEmptyData) {
+ // see https://github.com/igbinary/igbinary/issues/146
+ $this->igbinaryEmptyData = \function_exists('igbinary_serialize') ? igbinary_serialize([]) : '';
+ }
+ if ('' === $data || $this->igbinaryEmptyData === $data) {
+ return $this->destroy($sessionId);
+ }
+ $this->newSessionId = null;
+
+ return $this->doWrite($sessionId, $data);
+ }
+
+ /**
+ * @return bool
+ */
+ public function destroy($sessionId)
+ {
+ if (!headers_sent() && filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN)) {
+ if (!$this->sessionName) {
+ throw new \LogicException(sprintf('Session name cannot be empty, did you forget to call "parent::open()" in "%s"?.', \get_class($this)));
+ }
+ $cookie = SessionUtils::popSessionCookie($this->sessionName, $sessionId);
+
+ /*
+ * We send an invalidation Set-Cookie header (zero lifetime)
+ * when either the session was started or a cookie with
+ * the session name was sent by the client (in which case
+ * we know it's invalid as a valid session cookie would've
+ * started the session).
+ */
+ if (null === $cookie || isset($_COOKIE[$this->sessionName])) {
+ if (\PHP_VERSION_ID < 70300) {
+ setcookie($this->sessionName, '', 0, ini_get('session.cookie_path'), ini_get('session.cookie_domain'), filter_var(ini_get('session.cookie_secure'), FILTER_VALIDATE_BOOLEAN), filter_var(ini_get('session.cookie_httponly'), FILTER_VALIDATE_BOOLEAN));
+ } else {
+ $params = session_get_cookie_params();
+ unset($params['lifetime']);
+ setcookie($this->sessionName, '', $params);
+ }
+ }
+ }
+
+ return $this->newSessionId === $sessionId || $this->doDestroy($sessionId);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
new file mode 100644
index 00000000..8896964e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MemcachedSessionHandler.php
@@ -0,0 +1,119 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Memcached based session storage handler based on the Memcached class
+ * provided by the PHP memcached extension.
+ *
+ * @see https://php.net/memcached
+ *
+ * @author Drak
+ */
+class MemcachedSessionHandler extends AbstractSessionHandler
+{
+ private $memcached;
+
+ /**
+ * @var int Time to live in seconds
+ */
+ private $ttl;
+
+ /**
+ * @var string Key prefix for shared environments
+ */
+ private $prefix;
+
+ /**
+ * Constructor.
+ *
+ * List of available options:
+ * * prefix: The prefix to use for the memcached keys in order to avoid collision
+ * * expiretime: The time to live in seconds.
+ *
+ * @throws \InvalidArgumentException When unsupported options are passed
+ */
+ public function __construct(\Memcached $memcached, array $options = [])
+ {
+ $this->memcached = $memcached;
+
+ if ($diff = array_diff(array_keys($options), ['prefix', 'expiretime'])) {
+ throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
+ }
+
+ $this->ttl = isset($options['expiretime']) ? (int) $options['expiretime'] : 86400;
+ $this->prefix = isset($options['prefix']) ? $options['prefix'] : 'sf2s';
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ return $this->memcached->quit();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead(string $sessionId)
+ {
+ return $this->memcached->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $this->memcached->touch($this->prefix.$sessionId, time() + $this->ttl);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data)
+ {
+ return $this->memcached->set($this->prefix.$sessionId, $data, time() + $this->ttl);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId)
+ {
+ $result = $this->memcached->delete($this->prefix.$sessionId);
+
+ return $result || \Memcached::RES_NOTFOUND == $this->memcached->getResultCode();
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ // not required here because memcached will auto expire the records anyhow.
+ return true;
+ }
+
+ /**
+ * Return a Memcached instance.
+ *
+ * @return \Memcached
+ */
+ protected function getMemcached()
+ {
+ return $this->memcached;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php
new file mode 100644
index 00000000..c6b16d11
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MigratingSessionHandler.php
@@ -0,0 +1,124 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Migrating session handler for migrating from one handler to another. It reads
+ * from the current handler and writes both the current and new ones.
+ *
+ * It ignores errors from the new handler.
+ *
+ * @author Ross Motley
+ * @author Oliver Radwell
+ */
+class MigratingSessionHandler implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+ private $currentHandler;
+ private $writeOnlyHandler;
+
+ public function __construct(\SessionHandlerInterface $currentHandler, \SessionHandlerInterface $writeOnlyHandler)
+ {
+ if (!$currentHandler instanceof \SessionUpdateTimestampHandlerInterface) {
+ $currentHandler = new StrictSessionHandler($currentHandler);
+ }
+ if (!$writeOnlyHandler instanceof \SessionUpdateTimestampHandlerInterface) {
+ $writeOnlyHandler = new StrictSessionHandler($writeOnlyHandler);
+ }
+
+ $this->currentHandler = $currentHandler;
+ $this->writeOnlyHandler = $writeOnlyHandler;
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ $result = $this->currentHandler->close();
+ $this->writeOnlyHandler->close();
+
+ return $result;
+ }
+
+ /**
+ * @return bool
+ */
+ public function destroy($sessionId)
+ {
+ $result = $this->currentHandler->destroy($sessionId);
+ $this->writeOnlyHandler->destroy($sessionId);
+
+ return $result;
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ $result = $this->currentHandler->gc($maxlifetime);
+ $this->writeOnlyHandler->gc($maxlifetime);
+
+ return $result;
+ }
+
+ /**
+ * @return bool
+ */
+ public function open($savePath, $sessionName)
+ {
+ $result = $this->currentHandler->open($savePath, $sessionName);
+ $this->writeOnlyHandler->open($savePath, $sessionName);
+
+ return $result;
+ }
+
+ /**
+ * @return string
+ */
+ public function read($sessionId)
+ {
+ // No reading from new handler until switch-over
+ return $this->currentHandler->read($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function write($sessionId, $sessionData)
+ {
+ $result = $this->currentHandler->write($sessionId, $sessionData);
+ $this->writeOnlyHandler->write($sessionId, $sessionData);
+
+ return $result;
+ }
+
+ /**
+ * @return bool
+ */
+ public function validateId($sessionId)
+ {
+ // No reading from new handler until switch-over
+ return $this->currentHandler->validateId($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $sessionData)
+ {
+ $result = $this->currentHandler->updateTimestamp($sessionId, $sessionData);
+ $this->writeOnlyHandler->updateTimestamp($sessionId, $sessionData);
+
+ return $result;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
new file mode 100644
index 00000000..a6889e40
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/MongoDbSessionHandler.php
@@ -0,0 +1,187 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using the mongodb/mongodb package and MongoDB driver extension.
+ *
+ * @author Markus Bachmann
+ *
+ * @see https://packagist.org/packages/mongodb/mongodb
+ * @see https://php.net/mongodb
+ */
+class MongoDbSessionHandler extends AbstractSessionHandler
+{
+ private $mongo;
+
+ /**
+ * @var \MongoDB\Collection
+ */
+ private $collection;
+
+ /**
+ * @var array
+ */
+ private $options;
+
+ /**
+ * Constructor.
+ *
+ * List of available options:
+ * * database: The name of the database [required]
+ * * collection: The name of the collection [required]
+ * * id_field: The field name for storing the session id [default: _id]
+ * * data_field: The field name for storing the session data [default: data]
+ * * time_field: The field name for storing the timestamp [default: time]
+ * * expiry_field: The field name for storing the expiry-timestamp [default: expires_at].
+ *
+ * It is strongly recommended to put an index on the `expiry_field` for
+ * garbage-collection. Alternatively it's possible to automatically expire
+ * the sessions in the database as described below:
+ *
+ * A TTL collections can be used on MongoDB 2.2+ to cleanup expired sessions
+ * automatically. Such an index can for example look like this:
+ *
+ * db..ensureIndex(
+ * { "": 1 },
+ * { "expireAfterSeconds": 0 }
+ * )
+ *
+ * More details on: https://docs.mongodb.org/manual/tutorial/expire-data/
+ *
+ * If you use such an index, you can drop `gc_probability` to 0 since
+ * no garbage-collection is required.
+ *
+ * @throws \InvalidArgumentException When "database" or "collection" not provided
+ */
+ public function __construct(\MongoDB\Client $mongo, array $options)
+ {
+ if (!isset($options['database']) || !isset($options['collection'])) {
+ throw new \InvalidArgumentException('You must provide the "database" and "collection" option for MongoDBSessionHandler');
+ }
+
+ $this->mongo = $mongo;
+
+ $this->options = array_merge([
+ 'id_field' => '_id',
+ 'data_field' => 'data',
+ 'time_field' => 'time',
+ 'expiry_field' => 'expires_at',
+ ], $options);
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId)
+ {
+ $this->getCollection()->deleteOne([
+ $this->options['id_field'] => $sessionId,
+ ]);
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ $this->getCollection()->deleteMany([
+ $this->options['expiry_field'] => ['$lt' => new \MongoDB\BSON\UTCDateTime()],
+ ]);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data)
+ {
+ $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
+
+ $fields = [
+ $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
+ $this->options['expiry_field'] => $expiry,
+ $this->options['data_field'] => new \MongoDB\BSON\Binary($data, \MongoDB\BSON\Binary::TYPE_OLD_BINARY),
+ ];
+
+ $this->getCollection()->updateOne(
+ [$this->options['id_field'] => $sessionId],
+ ['$set' => $fields],
+ ['upsert' => true]
+ );
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $expiry = new \MongoDB\BSON\UTCDateTime((time() + (int) ini_get('session.gc_maxlifetime')) * 1000);
+
+ $this->getCollection()->updateOne(
+ [$this->options['id_field'] => $sessionId],
+ ['$set' => [
+ $this->options['time_field'] => new \MongoDB\BSON\UTCDateTime(),
+ $this->options['expiry_field'] => $expiry,
+ ]]
+ );
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead(string $sessionId)
+ {
+ $dbData = $this->getCollection()->findOne([
+ $this->options['id_field'] => $sessionId,
+ $this->options['expiry_field'] => ['$gte' => new \MongoDB\BSON\UTCDateTime()],
+ ]);
+
+ if (null === $dbData) {
+ return '';
+ }
+
+ return $dbData[$this->options['data_field']]->getData();
+ }
+
+ private function getCollection(): \MongoDB\Collection
+ {
+ if (null === $this->collection) {
+ $this->collection = $this->mongo->selectCollection($this->options['database'], $this->options['collection']);
+ }
+
+ return $this->collection;
+ }
+
+ /**
+ * @return \MongoDB\Client
+ */
+ protected function getMongo()
+ {
+ return $this->mongo;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
new file mode 100644
index 00000000..bdfc9d81
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NativeFileSessionHandler.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Native session handler using PHP's built in file storage.
+ *
+ * @author Drak
+ */
+class NativeFileSessionHandler extends \SessionHandler
+{
+ /**
+ * @param string $savePath Path of directory to save session files
+ * Default null will leave setting as defined by PHP.
+ * '/path', 'N;/path', or 'N;octal-mode;/path
+ *
+ * @see https://php.net/session.configuration#ini.session.save-path for further details.
+ *
+ * @throws \InvalidArgumentException On invalid $savePath
+ * @throws \RuntimeException When failing to create the save directory
+ */
+ public function __construct(string $savePath = null)
+ {
+ if (null === $savePath) {
+ $savePath = ini_get('session.save_path');
+ }
+
+ $baseDir = $savePath;
+
+ if ($count = substr_count($savePath, ';')) {
+ if ($count > 2) {
+ throw new \InvalidArgumentException(sprintf('Invalid argument $savePath \'%s\'', $savePath));
+ }
+
+ // characters after last ';' are the path
+ $baseDir = ltrim(strrchr($savePath, ';'), ';');
+ }
+
+ if ($baseDir && !is_dir($baseDir) && !@mkdir($baseDir, 0777, true) && !is_dir($baseDir)) {
+ throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $baseDir));
+ }
+
+ ini_set('session.save_path', $savePath);
+ ini_set('session.save_handler', 'files');
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
new file mode 100644
index 00000000..aa0e595c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/NullSessionHandler.php
@@ -0,0 +1,76 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Can be used in unit testing or in a situations where persisted sessions are not desired.
+ *
+ * @author Drak
+ */
+class NullSessionHandler extends AbstractSessionHandler
+{
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function validateId($sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead(string $sessionId)
+ {
+ return '';
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data)
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId)
+ {
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ return true;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
new file mode 100644
index 00000000..52f8334a
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/PdoSessionHandler.php
@@ -0,0 +1,899 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Session handler using a PDO connection to read and write data.
+ *
+ * It works with MySQL, PostgreSQL, Oracle, SQL Server and SQLite and implements
+ * different locking strategies to handle concurrent access to the same session.
+ * Locking is necessary to prevent loss of data due to race conditions and to keep
+ * the session data consistent between read() and write(). With locking, requests
+ * for the same session will wait until the other one finished writing. For this
+ * reason it's best practice to close a session as early as possible to improve
+ * concurrency. PHPs internal files session handler also implements locking.
+ *
+ * Attention: Since SQLite does not support row level locks but locks the whole database,
+ * it means only one session can be accessed at a time. Even different sessions would wait
+ * for another to finish. So saving session in SQLite should only be considered for
+ * development or prototypes.
+ *
+ * Session data is a binary string that can contain non-printable characters like the null byte.
+ * For this reason it must be saved in a binary column in the database like BLOB in MySQL.
+ * Saving it in a character column could corrupt the data. You can use createTable()
+ * to initialize a correctly defined table.
+ *
+ * @see https://php.net/sessionhandlerinterface
+ *
+ * @author Fabien Potencier
+ * @author Michael Williams
+ * @author Tobias Schultze
+ */
+class PdoSessionHandler extends AbstractSessionHandler
+{
+ /**
+ * No locking is done. This means sessions are prone to loss of data due to
+ * race conditions of concurrent requests to the same session. The last session
+ * write will win in this case. It might be useful when you implement your own
+ * logic to deal with this like an optimistic approach.
+ */
+ const LOCK_NONE = 0;
+
+ /**
+ * Creates an application-level lock on a session. The disadvantage is that the
+ * lock is not enforced by the database and thus other, unaware parts of the
+ * application could still concurrently modify the session. The advantage is it
+ * does not require a transaction.
+ * This mode is not available for SQLite and not yet implemented for oci and sqlsrv.
+ */
+ const LOCK_ADVISORY = 1;
+
+ /**
+ * Issues a real row lock. Since it uses a transaction between opening and
+ * closing a session, you have to be careful when you use same database connection
+ * that you also use for your application logic. This mode is the default because
+ * it's the only reliable solution across DBMSs.
+ */
+ const LOCK_TRANSACTIONAL = 2;
+
+ private const MAX_LIFETIME = 315576000;
+
+ /**
+ * @var \PDO|null PDO instance or null when not connected yet
+ */
+ private $pdo;
+
+ /**
+ * @var string|false|null DSN string or null for session.save_path or false when lazy connection disabled
+ */
+ private $dsn = false;
+
+ /**
+ * @var string Database driver
+ */
+ private $driver;
+
+ /**
+ * @var string Table name
+ */
+ private $table = 'sessions';
+
+ /**
+ * @var string Column for session id
+ */
+ private $idCol = 'sess_id';
+
+ /**
+ * @var string Column for session data
+ */
+ private $dataCol = 'sess_data';
+
+ /**
+ * @var string Column for lifetime
+ */
+ private $lifetimeCol = 'sess_lifetime';
+
+ /**
+ * @var string Column for timestamp
+ */
+ private $timeCol = 'sess_time';
+
+ /**
+ * @var string Username when lazy-connect
+ */
+ private $username = '';
+
+ /**
+ * @var string Password when lazy-connect
+ */
+ private $password = '';
+
+ /**
+ * @var array Connection options when lazy-connect
+ */
+ private $connectionOptions = [];
+
+ /**
+ * @var int The strategy for locking, see constants
+ */
+ private $lockMode = self::LOCK_TRANSACTIONAL;
+
+ /**
+ * It's an array to support multiple reads before closing which is manual, non-standard usage.
+ *
+ * @var \PDOStatement[] An array of statements to release advisory locks
+ */
+ private $unlockStatements = [];
+
+ /**
+ * @var bool True when the current session exists but expired according to session.gc_maxlifetime
+ */
+ private $sessionExpired = false;
+
+ /**
+ * @var bool Whether a transaction is active
+ */
+ private $inTransaction = false;
+
+ /**
+ * @var bool Whether gc() has been called
+ */
+ private $gcCalled = false;
+
+ /**
+ * You can either pass an existing database connection as PDO instance or
+ * pass a DSN string that will be used to lazy-connect to the database
+ * when the session is actually used. Furthermore it's possible to pass null
+ * which will then use the session.save_path ini setting as PDO DSN parameter.
+ *
+ * List of available options:
+ * * db_table: The name of the table [default: sessions]
+ * * db_id_col: The column where to store the session id [default: sess_id]
+ * * db_data_col: The column where to store the session data [default: sess_data]
+ * * db_lifetime_col: The column where to store the lifetime [default: sess_lifetime]
+ * * db_time_col: The column where to store the timestamp [default: sess_time]
+ * * db_username: The username when lazy-connect [default: '']
+ * * db_password: The password when lazy-connect [default: '']
+ * * db_connection_options: An array of driver-specific connection options [default: []]
+ * * lock_mode: The strategy for locking, see constants [default: LOCK_TRANSACTIONAL]
+ *
+ * @param \PDO|string|null $pdoOrDsn A \PDO instance or DSN string or URL string or null
+ *
+ * @throws \InvalidArgumentException When PDO error mode is not PDO::ERRMODE_EXCEPTION
+ */
+ public function __construct($pdoOrDsn = null, array $options = [])
+ {
+ if ($pdoOrDsn instanceof \PDO) {
+ if (\PDO::ERRMODE_EXCEPTION !== $pdoOrDsn->getAttribute(\PDO::ATTR_ERRMODE)) {
+ throw new \InvalidArgumentException(sprintf('"%s" requires PDO error mode attribute be set to throw Exceptions (i.e. $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION))', __CLASS__));
+ }
+
+ $this->pdo = $pdoOrDsn;
+ $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+ } elseif (\is_string($pdoOrDsn) && false !== strpos($pdoOrDsn, '://')) {
+ $this->dsn = $this->buildDsnFromUrl($pdoOrDsn);
+ } else {
+ $this->dsn = $pdoOrDsn;
+ }
+
+ $this->table = isset($options['db_table']) ? $options['db_table'] : $this->table;
+ $this->idCol = isset($options['db_id_col']) ? $options['db_id_col'] : $this->idCol;
+ $this->dataCol = isset($options['db_data_col']) ? $options['db_data_col'] : $this->dataCol;
+ $this->lifetimeCol = isset($options['db_lifetime_col']) ? $options['db_lifetime_col'] : $this->lifetimeCol;
+ $this->timeCol = isset($options['db_time_col']) ? $options['db_time_col'] : $this->timeCol;
+ $this->username = isset($options['db_username']) ? $options['db_username'] : $this->username;
+ $this->password = isset($options['db_password']) ? $options['db_password'] : $this->password;
+ $this->connectionOptions = isset($options['db_connection_options']) ? $options['db_connection_options'] : $this->connectionOptions;
+ $this->lockMode = isset($options['lock_mode']) ? $options['lock_mode'] : $this->lockMode;
+ }
+
+ /**
+ * Creates the table to store sessions which can be called once for setup.
+ *
+ * Session ID is saved in a column of maximum length 128 because that is enough even
+ * for a 512 bit configured session.hash_function like Whirlpool. Session data is
+ * saved in a BLOB. One could also use a shorter inlined varbinary column
+ * if one was sure the data fits into it.
+ *
+ * @throws \PDOException When the table already exists
+ * @throws \DomainException When an unsupported PDO driver is used
+ */
+ public function createTable()
+ {
+ // connect if we are not yet
+ $this->getConnection();
+
+ switch ($this->driver) {
+ case 'mysql':
+ // We use varbinary for the ID column because it prevents unwanted conversions:
+ // - character set conversions between server and client
+ // - trailing space removal
+ // - case-insensitivity
+ // - language processing like é == e
+ $sql = "CREATE TABLE $this->table ($this->idCol VARBINARY(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER UNSIGNED NOT NULL, $this->timeCol INTEGER UNSIGNED NOT NULL) COLLATE utf8_bin, ENGINE = InnoDB";
+ break;
+ case 'sqlite':
+ $sql = "CREATE TABLE $this->table ($this->idCol TEXT NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'pgsql':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol BYTEA NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'oci':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR2(128) NOT NULL PRIMARY KEY, $this->dataCol BLOB NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ case 'sqlsrv':
+ $sql = "CREATE TABLE $this->table ($this->idCol VARCHAR(128) NOT NULL PRIMARY KEY, $this->dataCol VARBINARY(MAX) NOT NULL, $this->lifetimeCol INTEGER NOT NULL, $this->timeCol INTEGER NOT NULL)";
+ break;
+ default:
+ throw new \DomainException(sprintf('Creating the session table is currently not implemented for PDO driver "%s".', $this->driver));
+ }
+
+ try {
+ $this->pdo->exec($sql);
+ $this->pdo->exec("CREATE INDEX EXPIRY ON $this->table ($this->lifetimeCol)");
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+
+ /**
+ * Returns true when the current session exists but expired according to session.gc_maxlifetime.
+ *
+ * Can be used to distinguish between a new session and one that expired due to inactivity.
+ *
+ * @return bool Whether current session expired
+ */
+ public function isSessionExpired()
+ {
+ return $this->sessionExpired;
+ }
+
+ /**
+ * @return bool
+ */
+ public function open($savePath, $sessionName)
+ {
+ $this->sessionExpired = false;
+
+ if (null === $this->pdo) {
+ $this->connect($this->dsn ?: $savePath);
+ }
+
+ return parent::open($savePath, $sessionName);
+ }
+
+ /**
+ * @return string
+ */
+ public function read($sessionId)
+ {
+ try {
+ return parent::read($sessionId);
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ // We delay gc() to close() so that it is executed outside the transactional and blocking read-write process.
+ // This way, pruning expired sessions does not block them from being started while the current session is used.
+ $this->gcCalled = true;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId)
+ {
+ // delete the record associated with this id
+ $sql = "DELETE FROM $this->table WHERE $this->idCol = :id";
+
+ try {
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->execute();
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data)
+ {
+ $maxlifetime = (int) ini_get('session.gc_maxlifetime');
+
+ try {
+ // We use a single MERGE SQL query when supported by the database.
+ $mergeStmt = $this->getMergeStatement($sessionId, $data, $maxlifetime);
+ if (null !== $mergeStmt) {
+ $mergeStmt->execute();
+
+ return true;
+ }
+
+ $updateStmt = $this->getUpdateStatement($sessionId, $data, $maxlifetime);
+ $updateStmt->execute();
+
+ // When MERGE is not supported, like in Postgres < 9.5, we have to use this approach that can result in
+ // duplicate key errors when the same session is written simultaneously (given the LOCK_NONE behavior).
+ // We can just catch such an error and re-execute the update. This is similar to a serializable
+ // transaction with retry logic on serialization failures but without the overhead and without possible
+ // false positives due to longer gap locking.
+ if (!$updateStmt->rowCount()) {
+ try {
+ $insertStmt = $this->getInsertStatement($sessionId, $data, $maxlifetime);
+ $insertStmt->execute();
+ } catch (\PDOException $e) {
+ // Handle integrity violation SQLSTATE 23000 (or a subclass like 23505 in Postgres) for duplicate keys
+ if (0 === strpos($e->getCode(), '23')) {
+ $updateStmt->execute();
+ } else {
+ throw $e;
+ }
+ }
+ }
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ $expiry = time() + (int) ini_get('session.gc_maxlifetime');
+
+ try {
+ $updateStmt = $this->pdo->prepare(
+ "UPDATE $this->table SET $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id"
+ );
+ $updateStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $updateStmt->bindParam(':expiry', $expiry, \PDO::PARAM_INT);
+ $updateStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $updateStmt->execute();
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ $this->commit();
+
+ while ($unlockStmt = array_shift($this->unlockStatements)) {
+ $unlockStmt->execute();
+ }
+
+ if ($this->gcCalled) {
+ $this->gcCalled = false;
+
+ // delete the session records that have expired
+ $sql = "DELETE FROM $this->table WHERE $this->lifetimeCol < :time AND $this->lifetimeCol > :min";
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
+ $stmt->execute();
+ // to be removed in 6.0
+ if ('mysql' === $this->driver) {
+ $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol + $this->timeCol < :time";
+ } else {
+ $legacySql = "DELETE FROM $this->table WHERE $this->lifetimeCol <= :min AND $this->lifetimeCol < :time - $this->timeCol";
+ }
+
+ $stmt = $this->pdo->prepare($legacySql);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ $stmt->bindValue(':min', self::MAX_LIFETIME, \PDO::PARAM_INT);
+ $stmt->execute();
+ }
+
+ if (false !== $this->dsn) {
+ $this->pdo = null; // only close lazy-connection
+ }
+
+ return true;
+ }
+
+ /**
+ * Lazy-connects to the database.
+ */
+ private function connect(string $dsn): void
+ {
+ $this->pdo = new \PDO($dsn, $this->username, $this->password, $this->connectionOptions);
+ $this->pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
+ $this->driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME);
+ }
+
+ /**
+ * Builds a PDO DSN from a URL-like connection string.
+ *
+ * @todo implement missing support for oci DSN (which look totally different from other PDO ones)
+ */
+ private function buildDsnFromUrl(string $dsnOrUrl): string
+ {
+ // (pdo_)?sqlite3?:///... => (pdo_)?sqlite3?://localhost/... or else the URL will be invalid
+ $url = preg_replace('#^((?:pdo_)?sqlite3?):///#', '$1://localhost/', $dsnOrUrl);
+
+ $params = parse_url($url);
+
+ if (false === $params) {
+ return $dsnOrUrl; // If the URL is not valid, let's assume it might be a DSN already.
+ }
+
+ $params = array_map('rawurldecode', $params);
+
+ // Override the default username and password. Values passed through options will still win over these in the constructor.
+ if (isset($params['user'])) {
+ $this->username = $params['user'];
+ }
+
+ if (isset($params['pass'])) {
+ $this->password = $params['pass'];
+ }
+
+ if (!isset($params['scheme'])) {
+ throw new \InvalidArgumentException('URLs without scheme are not supported to configure the PdoSessionHandler');
+ }
+
+ $driverAliasMap = [
+ 'mssql' => 'sqlsrv',
+ 'mysql2' => 'mysql', // Amazon RDS, for some weird reason
+ 'postgres' => 'pgsql',
+ 'postgresql' => 'pgsql',
+ 'sqlite3' => 'sqlite',
+ ];
+
+ $driver = isset($driverAliasMap[$params['scheme']]) ? $driverAliasMap[$params['scheme']] : $params['scheme'];
+
+ // Doctrine DBAL supports passing its internal pdo_* driver names directly too (allowing both dashes and underscores). This allows supporting the same here.
+ if (0 === strpos($driver, 'pdo_') || 0 === strpos($driver, 'pdo-')) {
+ $driver = substr($driver, 4);
+ }
+
+ switch ($driver) {
+ case 'mysql':
+ case 'pgsql':
+ $dsn = $driver.':';
+
+ if (isset($params['host']) && '' !== $params['host']) {
+ $dsn .= 'host='.$params['host'].';';
+ }
+
+ if (isset($params['port']) && '' !== $params['port']) {
+ $dsn .= 'port='.$params['port'].';';
+ }
+
+ if (isset($params['path'])) {
+ $dbName = substr($params['path'], 1); // Remove the leading slash
+ $dsn .= 'dbname='.$dbName.';';
+ }
+
+ return $dsn;
+
+ case 'sqlite':
+ return 'sqlite:'.substr($params['path'], 1);
+
+ case 'sqlsrv':
+ $dsn = 'sqlsrv:server=';
+
+ if (isset($params['host'])) {
+ $dsn .= $params['host'];
+ }
+
+ if (isset($params['port']) && '' !== $params['port']) {
+ $dsn .= ','.$params['port'];
+ }
+
+ if (isset($params['path'])) {
+ $dbName = substr($params['path'], 1); // Remove the leading slash
+ $dsn .= ';Database='.$dbName;
+ }
+
+ return $dsn;
+
+ default:
+ throw new \InvalidArgumentException(sprintf('The scheme "%s" is not supported by the PdoSessionHandler URL configuration. Pass a PDO DSN directly.', $params['scheme']));
+ }
+ }
+
+ /**
+ * Helper method to begin a transaction.
+ *
+ * Since SQLite does not support row level locks, we have to acquire a reserved lock
+ * on the database immediately. Because of https://bugs.php.net/42766 we have to create
+ * such a transaction manually which also means we cannot use PDO::commit or
+ * PDO::rollback or PDO::inTransaction for SQLite.
+ *
+ * Also MySQLs default isolation, REPEATABLE READ, causes deadlock for different sessions
+ * due to https://percona.com/blog/2013/12/12/one-more-innodb-gap-lock-to-avoid/ .
+ * So we change it to READ COMMITTED.
+ */
+ private function beginTransaction(): void
+ {
+ if (!$this->inTransaction) {
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('BEGIN IMMEDIATE TRANSACTION');
+ } else {
+ if ('mysql' === $this->driver) {
+ $this->pdo->exec('SET TRANSACTION ISOLATION LEVEL READ COMMITTED');
+ }
+ $this->pdo->beginTransaction();
+ }
+ $this->inTransaction = true;
+ }
+ }
+
+ /**
+ * Helper method to commit a transaction.
+ */
+ private function commit(): void
+ {
+ if ($this->inTransaction) {
+ try {
+ // commit read-write transaction which also releases the lock
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('COMMIT');
+ } else {
+ $this->pdo->commit();
+ }
+ $this->inTransaction = false;
+ } catch (\PDOException $e) {
+ $this->rollback();
+
+ throw $e;
+ }
+ }
+ }
+
+ /**
+ * Helper method to rollback a transaction.
+ */
+ private function rollback(): void
+ {
+ // We only need to rollback if we are in a transaction. Otherwise the resulting
+ // error would hide the real problem why rollback was called. We might not be
+ // in a transaction when not using the transactional locking behavior or when
+ // two callbacks (e.g. destroy and write) are invoked that both fail.
+ if ($this->inTransaction) {
+ if ('sqlite' === $this->driver) {
+ $this->pdo->exec('ROLLBACK');
+ } else {
+ $this->pdo->rollBack();
+ }
+ $this->inTransaction = false;
+ }
+ }
+
+ /**
+ * Reads the session data in respect to the different locking strategies.
+ *
+ * We need to make sure we do not return session data that is already considered garbage according
+ * to the session.gc_maxlifetime setting because gc() is called after read() and only sometimes.
+ *
+ * @return string
+ */
+ protected function doRead(string $sessionId)
+ {
+ if (self::LOCK_ADVISORY === $this->lockMode) {
+ $this->unlockStatements[] = $this->doAdvisoryLock($sessionId);
+ }
+
+ $selectSql = $this->getSelectSql();
+ $selectStmt = $this->pdo->prepare($selectSql);
+ $selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $insertStmt = null;
+
+ do {
+ $selectStmt->execute();
+ $sessionRows = $selectStmt->fetchAll(\PDO::FETCH_NUM);
+
+ if ($sessionRows) {
+ $expiry = (int) $sessionRows[0][1];
+ if ($expiry <= self::MAX_LIFETIME) {
+ $expiry += $sessionRows[0][2];
+ }
+
+ if ($expiry < time()) {
+ $this->sessionExpired = true;
+
+ return '';
+ }
+
+ return \is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
+ }
+
+ if (null !== $insertStmt) {
+ $this->rollback();
+ throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
+ }
+
+ if (!filter_var(ini_get('session.use_strict_mode'), FILTER_VALIDATE_BOOLEAN) && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
+ // In strict mode, session fixation is not possible: new sessions always start with a unique
+ // random id, so that concurrency is not possible and this code path can be skipped.
+ // Exclusive-reading of non-existent rows does not block, so we need to do an insert to block
+ // until other connections to the session are committed.
+ try {
+ $insertStmt = $this->getInsertStatement($sessionId, '', 0);
+ $insertStmt->execute();
+ } catch (\PDOException $e) {
+ // Catch duplicate key error because other connection created the session already.
+ // It would only not be the case when the other connection destroyed the session.
+ if (0 === strpos($e->getCode(), '23')) {
+ // Retrieve finished session data written by concurrent connection by restarting the loop.
+ // We have to start a new transaction as a failed query will mark the current transaction as
+ // aborted in PostgreSQL and disallow further queries within it.
+ $this->rollback();
+ $this->beginTransaction();
+ continue;
+ }
+
+ throw $e;
+ }
+ }
+
+ return '';
+ } while (true);
+ }
+
+ /**
+ * Executes an application-level lock on the database.
+ *
+ * @return \PDOStatement The statement that needs to be executed later to release the lock
+ *
+ * @throws \DomainException When an unsupported PDO driver is used
+ *
+ * @todo implement missing advisory locks
+ * - for oci using DBMS_LOCK.REQUEST
+ * - for sqlsrv using sp_getapplock with LockOwner = Session
+ */
+ private function doAdvisoryLock(string $sessionId): \PDOStatement
+ {
+ switch ($this->driver) {
+ case 'mysql':
+ // MySQL 5.7.5 and later enforces a maximum length on lock names of 64 characters. Previously, no limit was enforced.
+ $lockId = substr($sessionId, 0, 64);
+ // should we handle the return value? 0 on timeout, null on error
+ // we use a timeout of 50 seconds which is also the default for innodb_lock_wait_timeout
+ $stmt = $this->pdo->prepare('SELECT GET_LOCK(:key, 50)');
+ $stmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('DO RELEASE_LOCK(:key)');
+ $releaseStmt->bindValue(':key', $lockId, \PDO::PARAM_STR);
+
+ return $releaseStmt;
+ case 'pgsql':
+ // Obtaining an exclusive session level advisory lock requires an integer key.
+ // When session.sid_bits_per_character > 4, the session id can contain non-hex-characters.
+ // So we cannot just use hexdec().
+ if (4 === \PHP_INT_SIZE) {
+ $sessionInt1 = $this->convertStringToInt($sessionId);
+ $sessionInt2 = $this->convertStringToInt(substr($sessionId, 4, 4));
+
+ $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key1, :key2)');
+ $stmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+ $stmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key1, :key2)');
+ $releaseStmt->bindValue(':key1', $sessionInt1, \PDO::PARAM_INT);
+ $releaseStmt->bindValue(':key2', $sessionInt2, \PDO::PARAM_INT);
+ } else {
+ $sessionBigInt = $this->convertStringToInt($sessionId);
+
+ $stmt = $this->pdo->prepare('SELECT pg_advisory_lock(:key)');
+ $stmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+ $stmt->execute();
+
+ $releaseStmt = $this->pdo->prepare('SELECT pg_advisory_unlock(:key)');
+ $releaseStmt->bindValue(':key', $sessionBigInt, \PDO::PARAM_INT);
+ }
+
+ return $releaseStmt;
+ case 'sqlite':
+ throw new \DomainException('SQLite does not support advisory locks.');
+ default:
+ throw new \DomainException(sprintf('Advisory locks are currently not implemented for PDO driver "%s".', $this->driver));
+ }
+ }
+
+ /**
+ * Encodes the first 4 (when PHP_INT_SIZE == 4) or 8 characters of the string as an integer.
+ *
+ * Keep in mind, PHP integers are signed.
+ */
+ private function convertStringToInt(string $string): int
+ {
+ if (4 === \PHP_INT_SIZE) {
+ return (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
+ }
+
+ $int1 = (\ord($string[7]) << 24) + (\ord($string[6]) << 16) + (\ord($string[5]) << 8) + \ord($string[4]);
+ $int2 = (\ord($string[3]) << 24) + (\ord($string[2]) << 16) + (\ord($string[1]) << 8) + \ord($string[0]);
+
+ return $int2 + ($int1 << 32);
+ }
+
+ /**
+ * Return a locking or nonlocking SQL query to read session information.
+ *
+ * @throws \DomainException When an unsupported PDO driver is used
+ */
+ private function getSelectSql(): string
+ {
+ if (self::LOCK_TRANSACTIONAL === $this->lockMode) {
+ $this->beginTransaction();
+
+ // selecting the time column should be removed in 6.0
+ switch ($this->driver) {
+ case 'mysql':
+ case 'oci':
+ case 'pgsql':
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id FOR UPDATE";
+ case 'sqlsrv':
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WITH (UPDLOCK, ROWLOCK) WHERE $this->idCol = :id";
+ case 'sqlite':
+ // we already locked when starting transaction
+ break;
+ default:
+ throw new \DomainException(sprintf('Transactional locks are currently not implemented for PDO driver "%s".', $this->driver));
+ }
+ }
+
+ return "SELECT $this->dataCol, $this->lifetimeCol, $this->timeCol FROM $this->table WHERE $this->idCol = :id";
+ }
+
+ /**
+ * Returns an insert statement supported by the database for writing session data.
+ */
+ private function getInsertStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
+ {
+ switch ($this->driver) {
+ case 'oci':
+ $data = fopen('php://memory', 'r+');
+ fwrite($data, $sessionData);
+ rewind($data);
+ $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, EMPTY_BLOB(), :expiry, :time) RETURNING $this->dataCol into :data";
+ break;
+ default:
+ $data = $sessionData;
+ $sql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
+ break;
+ }
+
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+ return $stmt;
+ }
+
+ /**
+ * Returns an update statement supported by the database for writing session data.
+ */
+ private function getUpdateStatement(string $sessionId, string $sessionData, int $maxlifetime): \PDOStatement
+ {
+ switch ($this->driver) {
+ case 'oci':
+ $data = fopen('php://memory', 'r+');
+ fwrite($data, $sessionData);
+ rewind($data);
+ $sql = "UPDATE $this->table SET $this->dataCol = EMPTY_BLOB(), $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id RETURNING $this->dataCol into :data";
+ break;
+ default:
+ $data = $sessionData;
+ $sql = "UPDATE $this->table SET $this->dataCol = :data, $this->lifetimeCol = :expiry, $this->timeCol = :time WHERE $this->idCol = :id";
+ break;
+ }
+
+ $stmt = $this->pdo->prepare($sql);
+ $stmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $stmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $stmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
+ $stmt->bindValue(':time', time(), \PDO::PARAM_INT);
+
+ return $stmt;
+ }
+
+ /**
+ * Returns a merge/upsert (i.e. insert or update) statement when supported by the database for writing session data.
+ */
+ private function getMergeStatement(string $sessionId, string $data, int $maxlifetime): ?\PDOStatement
+ {
+ switch (true) {
+ case 'mysql' === $this->driver:
+ $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
+ "ON DUPLICATE KEY UPDATE $this->dataCol = VALUES($this->dataCol), $this->lifetimeCol = VALUES($this->lifetimeCol), $this->timeCol = VALUES($this->timeCol)";
+ break;
+ case 'sqlsrv' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '10', '>='):
+ // MERGE is only available since SQL Server 2008 and must be terminated by semicolon
+ // It also requires HOLDLOCK according to https://weblogs.sqlteam.com/dang/2009/01/31/upsert-race-condition-with-merge/
+ $mergeSql = "MERGE INTO $this->table WITH (HOLDLOCK) USING (SELECT 1 AS dummy) AS src ON ($this->idCol = ?) ".
+ "WHEN NOT MATCHED THEN INSERT ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (?, ?, ?, ?) ".
+ "WHEN MATCHED THEN UPDATE SET $this->dataCol = ?, $this->lifetimeCol = ?, $this->timeCol = ?;";
+ break;
+ case 'sqlite' === $this->driver:
+ $mergeSql = "INSERT OR REPLACE INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time)";
+ break;
+ case 'pgsql' === $this->driver && version_compare($this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION), '9.5', '>='):
+ $mergeSql = "INSERT INTO $this->table ($this->idCol, $this->dataCol, $this->lifetimeCol, $this->timeCol) VALUES (:id, :data, :expiry, :time) ".
+ "ON CONFLICT ($this->idCol) DO UPDATE SET ($this->dataCol, $this->lifetimeCol, $this->timeCol) = (EXCLUDED.$this->dataCol, EXCLUDED.$this->lifetimeCol, EXCLUDED.$this->timeCol)";
+ break;
+ default:
+ // MERGE is not supported with LOBs: https://oracle.com/technetwork/articles/fuecks-lobs-095315.html
+ return null;
+ }
+
+ $mergeStmt = $this->pdo->prepare($mergeSql);
+
+ if ('sqlsrv' === $this->driver) {
+ $mergeStmt->bindParam(1, $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(2, $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(3, $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindValue(4, time() + $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(4, time(), \PDO::PARAM_INT);
+ $mergeStmt->bindParam(5, $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindValue(6, time() + $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(6, time(), \PDO::PARAM_INT);
+ } else {
+ $mergeStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
+ $mergeStmt->bindParam(':data', $data, \PDO::PARAM_LOB);
+ $mergeStmt->bindValue(':expiry', time() + $maxlifetime, \PDO::PARAM_INT);
+ $mergeStmt->bindValue(':time', time(), \PDO::PARAM_INT);
+ }
+
+ return $mergeStmt;
+ }
+
+ /**
+ * Return a PDO instance.
+ *
+ * @return \PDO
+ */
+ protected function getConnection()
+ {
+ if (null === $this->pdo) {
+ $this->connect($this->dsn ?: ini_get('session.save_path'));
+ }
+
+ return $this->pdo;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php
new file mode 100644
index 00000000..678fb028
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/RedisSessionHandler.php
@@ -0,0 +1,120 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+use Predis\Response\ErrorInterface;
+use Symfony\Component\Cache\Traits\RedisClusterProxy;
+use Symfony\Component\Cache\Traits\RedisProxy;
+
+/**
+ * Redis based session storage handler based on the Redis class
+ * provided by the PHP redis extension.
+ *
+ * @author Dalibor Karlović
+ */
+class RedisSessionHandler extends AbstractSessionHandler
+{
+ private $redis;
+
+ /**
+ * @var string Key prefix for shared environments
+ */
+ private $prefix;
+
+ /**
+ * @var int Time to live in seconds
+ */
+ private $ttl;
+
+ /**
+ * List of available options:
+ * * prefix: The prefix to use for the keys in order to avoid collision on the Redis server
+ * * ttl: The time to live in seconds.
+ *
+ * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy $redis
+ *
+ * @throws \InvalidArgumentException When unsupported client or options are passed
+ */
+ public function __construct($redis, array $options = [])
+ {
+ if (
+ !$redis instanceof \Redis &&
+ !$redis instanceof \RedisArray &&
+ !$redis instanceof \RedisCluster &&
+ !$redis instanceof \Predis\ClientInterface &&
+ !$redis instanceof RedisProxy &&
+ !$redis instanceof RedisClusterProxy
+ ) {
+ throw new \InvalidArgumentException(sprintf('%s() expects parameter 1 to be Redis, RedisArray, RedisCluster or Predis\ClientInterface, %s given', __METHOD__, \is_object($redis) ? \get_class($redis) : \gettype($redis)));
+ }
+
+ if ($diff = array_diff(array_keys($options), ['prefix', 'ttl'])) {
+ throw new \InvalidArgumentException(sprintf('The following options are not supported "%s"', implode(', ', $diff)));
+ }
+
+ $this->redis = $redis;
+ $this->prefix = $options['prefix'] ?? 'sf_s';
+ $this->ttl = $options['ttl'] ?? (int) ini_get('session.gc_maxlifetime');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead(string $sessionId): string
+ {
+ return $this->redis->get($this->prefix.$sessionId) ?: '';
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data): bool
+ {
+ $result = $this->redis->setEx($this->prefix.$sessionId, $this->ttl, $data);
+
+ return $result && !$result instanceof ErrorInterface;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId): bool
+ {
+ $this->redis->del($this->prefix.$sessionId);
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function close(): bool
+ {
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function gc($maxlifetime): bool
+ {
+ return true;
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return (bool) $this->redis->expire($this->prefix.$sessionId, $this->ttl);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
new file mode 100644
index 00000000..f4feeac0
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/SessionHandlerFactory.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+use Doctrine\DBAL\DriverManager;
+use Symfony\Component\Cache\Adapter\AbstractAdapter;
+use Symfony\Component\Cache\Traits\RedisClusterProxy;
+use Symfony\Component\Cache\Traits\RedisProxy;
+
+/**
+ * @author Nicolas Grekas
+ */
+class SessionHandlerFactory
+{
+ /**
+ * @param \Redis|\RedisArray|\RedisCluster|\Predis\ClientInterface|RedisProxy|RedisClusterProxy|\Memcached|\PDO|string $connection Connection or DSN
+ */
+ public static function createHandler($connection): AbstractSessionHandler
+ {
+ if (!\is_string($connection) && !\is_object($connection)) {
+ throw new \TypeError(sprintf('Argument 1 passed to %s() must be a string or a connection object, %s given.', __METHOD__, \gettype($connection)));
+ }
+
+ switch (true) {
+ case $connection instanceof \Redis:
+ case $connection instanceof \RedisArray:
+ case $connection instanceof \RedisCluster:
+ case $connection instanceof \Predis\ClientInterface:
+ case $connection instanceof RedisProxy:
+ case $connection instanceof RedisClusterProxy:
+ return new RedisSessionHandler($connection);
+
+ case $connection instanceof \Memcached:
+ return new MemcachedSessionHandler($connection);
+
+ case $connection instanceof \PDO:
+ return new PdoSessionHandler($connection);
+
+ case !\is_string($connection):
+ throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', \get_class($connection)));
+ case 0 === strpos($connection, 'file://'):
+ return new StrictSessionHandler(new NativeFileSessionHandler(substr($connection, 7)));
+
+ case 0 === strpos($connection, 'redis://'):
+ case 0 === strpos($connection, 'rediss://'):
+ case 0 === strpos($connection, 'memcached://'):
+ if (!class_exists(AbstractAdapter::class)) {
+ throw new InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require symfony/cache".', $connection));
+ }
+ $handlerClass = 0 === strpos($connection, 'memcached://') ? MemcachedSessionHandler::class : RedisSessionHandler::class;
+ $connection = AbstractAdapter::createConnection($connection, ['lazy' => true]);
+
+ return new $handlerClass($connection);
+
+ case 0 === strpos($connection, 'pdo_oci://'):
+ if (!class_exists(DriverManager::class)) {
+ throw new \InvalidArgumentException(sprintf('Unsupported DSN "%s". Try running "composer require doctrine/dbal".', $connection));
+ }
+ $connection = DriverManager::getConnection(['url' => $connection])->getWrappedConnection();
+ // no break;
+
+ case 0 === strpos($connection, 'mssql://'):
+ case 0 === strpos($connection, 'mysql://'):
+ case 0 === strpos($connection, 'mysql2://'):
+ case 0 === strpos($connection, 'pgsql://'):
+ case 0 === strpos($connection, 'postgres://'):
+ case 0 === strpos($connection, 'postgresql://'):
+ case 0 === strpos($connection, 'sqlsrv://'):
+ case 0 === strpos($connection, 'sqlite://'):
+ case 0 === strpos($connection, 'sqlite3://'):
+ return new PdoSessionHandler($connection);
+ }
+
+ throw new \InvalidArgumentException(sprintf('Unsupported Connection: %s.', $connection));
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
new file mode 100644
index 00000000..4292a3b2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Handler/StrictSessionHandler.php
@@ -0,0 +1,103 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Handler;
+
+/**
+ * Adds basic `SessionUpdateTimestampHandlerInterface` behaviors to another `SessionHandlerInterface`.
+ *
+ * @author Nicolas Grekas
+ */
+class StrictSessionHandler extends AbstractSessionHandler
+{
+ private $handler;
+ private $doDestroy;
+
+ public function __construct(\SessionHandlerInterface $handler)
+ {
+ if ($handler instanceof \SessionUpdateTimestampHandlerInterface) {
+ throw new \LogicException(sprintf('"%s" is already an instance of "SessionUpdateTimestampHandlerInterface", you cannot wrap it with "%s".', \get_class($handler), self::class));
+ }
+
+ $this->handler = $handler;
+ }
+
+ /**
+ * @return bool
+ */
+ public function open($savePath, $sessionName)
+ {
+ parent::open($savePath, $sessionName);
+
+ return $this->handler->open($savePath, $sessionName);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doRead(string $sessionId)
+ {
+ return $this->handler->read($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return $this->write($sessionId, $data);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doWrite(string $sessionId, string $data)
+ {
+ return $this->handler->write($sessionId, $data);
+ }
+
+ /**
+ * @return bool
+ */
+ public function destroy($sessionId)
+ {
+ $this->doDestroy = true;
+ $destroyed = parent::destroy($sessionId);
+
+ return $this->doDestroy ? $this->doDestroy($sessionId) : $destroyed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ protected function doDestroy(string $sessionId)
+ {
+ $this->doDestroy = false;
+
+ return $this->handler->destroy($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ return $this->handler->close();
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ return $this->handler->gc($maxlifetime);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MetadataBag.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MetadataBag.php
new file mode 100644
index 00000000..c79ee023
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MetadataBag.php
@@ -0,0 +1,166 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * Metadata container.
+ *
+ * Adds metadata to the session.
+ *
+ * @author Drak
+ */
+class MetadataBag implements SessionBagInterface
+{
+ const CREATED = 'c';
+ const UPDATED = 'u';
+ const LIFETIME = 'l';
+
+ /**
+ * @var string
+ */
+ private $name = '__metadata';
+
+ /**
+ * @var string
+ */
+ private $storageKey;
+
+ /**
+ * @var array
+ */
+ protected $meta = [self::CREATED => 0, self::UPDATED => 0, self::LIFETIME => 0];
+
+ /**
+ * Unix timestamp.
+ *
+ * @var int
+ */
+ private $lastUsed;
+
+ /**
+ * @var int
+ */
+ private $updateThreshold;
+
+ /**
+ * @param string $storageKey The key used to store bag in the session
+ * @param int $updateThreshold The time to wait between two UPDATED updates
+ */
+ public function __construct(string $storageKey = '_sf2_meta', int $updateThreshold = 0)
+ {
+ $this->storageKey = $storageKey;
+ $this->updateThreshold = $updateThreshold;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function initialize(array &$array)
+ {
+ $this->meta = &$array;
+
+ if (isset($array[self::CREATED])) {
+ $this->lastUsed = $this->meta[self::UPDATED];
+
+ $timeStamp = time();
+ if ($timeStamp - $array[self::UPDATED] >= $this->updateThreshold) {
+ $this->meta[self::UPDATED] = $timeStamp;
+ }
+ } else {
+ $this->stampCreated();
+ }
+ }
+
+ /**
+ * Gets the lifetime that the session cookie was set with.
+ *
+ * @return int
+ */
+ public function getLifetime()
+ {
+ return $this->meta[self::LIFETIME];
+ }
+
+ /**
+ * Stamps a new session's metadata.
+ *
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ */
+ public function stampNew(int $lifetime = null)
+ {
+ $this->stampCreated($lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getStorageKey()
+ {
+ return $this->storageKey;
+ }
+
+ /**
+ * Gets the created timestamp metadata.
+ *
+ * @return int Unix timestamp
+ */
+ public function getCreated()
+ {
+ return $this->meta[self::CREATED];
+ }
+
+ /**
+ * Gets the last used metadata.
+ *
+ * @return int Unix timestamp
+ */
+ public function getLastUsed()
+ {
+ return $this->lastUsed;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // nothing to do
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * Sets name.
+ */
+ public function setName(string $name)
+ {
+ $this->name = $name;
+ }
+
+ private function stampCreated(int $lifetime = null): void
+ {
+ $timeStamp = time();
+ $this->meta[self::CREATED] = $this->meta[self::UPDATED] = $this->lastUsed = $timeStamp;
+ $this->meta[self::LIFETIME] = (null === $lifetime) ? ini_get('session.cookie_lifetime') : $lifetime;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
new file mode 100644
index 00000000..4662bd86
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockArraySessionStorage.php
@@ -0,0 +1,252 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * MockArraySessionStorage mocks the session for unit tests.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle.
+ *
+ * When doing functional testing, you should use MockFileSessionStorage instead.
+ *
+ * @author Fabien Potencier
+ * @author Bulat Shakirzyanov
+ * @author Drak
+ */
+class MockArraySessionStorage implements SessionStorageInterface
+{
+ /**
+ * @var string
+ */
+ protected $id = '';
+
+ /**
+ * @var string
+ */
+ protected $name;
+
+ /**
+ * @var bool
+ */
+ protected $started = false;
+
+ /**
+ * @var bool
+ */
+ protected $closed = false;
+
+ /**
+ * @var array
+ */
+ protected $data = [];
+
+ /**
+ * @var MetadataBag
+ */
+ protected $metadataBag;
+
+ /**
+ * @var array|SessionBagInterface[]
+ */
+ protected $bags = [];
+
+ public function __construct(string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
+ {
+ $this->name = $name;
+ $this->setMetadataBag($metaBag);
+ }
+
+ public function setSessionData(array $array)
+ {
+ $this->data = $array;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (empty($this->id)) {
+ $this->id = $this->generateId();
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate(bool $destroy = false, int $lifetime = null)
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ $this->metadataBag->stampNew($lifetime);
+ $this->id = $this->generateId();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId(string $id)
+ {
+ if ($this->started) {
+ throw new \LogicException('Cannot set session ID after the session has started.');
+ }
+
+ $this->id = $id;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName(string $name)
+ {
+ $this->name = $name;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ if (!$this->started || $this->closed) {
+ throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+ }
+ // nothing to do since we don't persist the session data
+ $this->closed = false;
+ $this->started = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $this->data = [];
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ $this->bags[$bag->getName()] = $bag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag(string $name)
+ {
+ if (!isset($this->bags[$name])) {
+ throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+ }
+
+ if (!$this->started) {
+ $this->start();
+ }
+
+ return $this->bags[$name];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->started;
+ }
+
+ public function setMetadataBag(MetadataBag $bag = null)
+ {
+ if (null === $bag) {
+ $bag = new MetadataBag();
+ }
+
+ $this->metadataBag = $bag;
+ }
+
+ /**
+ * Gets the MetadataBag.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag()
+ {
+ return $this->metadataBag;
+ }
+
+ /**
+ * Generates a session ID.
+ *
+ * This doesn't need to be particularly cryptographically secure since this is just
+ * a mock.
+ *
+ * @return string
+ */
+ protected function generateId()
+ {
+ return hash('sha256', uniqid('ss_mock_', true));
+ }
+
+ protected function loadSession()
+ {
+ $bags = array_merge($this->bags, [$this->metadataBag]);
+
+ foreach ($bags as $bag) {
+ $key = $bag->getStorageKey();
+ $this->data[$key] = isset($this->data[$key]) ? $this->data[$key] : [];
+ $bag->initialize($this->data[$key]);
+ }
+
+ $this->started = true;
+ $this->closed = false;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
new file mode 100644
index 00000000..019a6348
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/MockFileSessionStorage.php
@@ -0,0 +1,148 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+/**
+ * MockFileSessionStorage is used to mock sessions for
+ * functional testing when done in a single PHP process.
+ *
+ * No PHP session is actually started since a session can be initialized
+ * and shutdown only once per PHP execution cycle and this class does
+ * not pollute any session related globals, including session_*() functions
+ * or session.* PHP ini directives.
+ *
+ * @author Drak
+ */
+class MockFileSessionStorage extends MockArraySessionStorage
+{
+ private $savePath;
+
+ /**
+ * @param string $savePath Path of directory to save session files
+ */
+ public function __construct(string $savePath = null, string $name = 'MOCKSESSID', MetadataBag $metaBag = null)
+ {
+ if (null === $savePath) {
+ $savePath = sys_get_temp_dir();
+ }
+
+ if (!is_dir($savePath) && !@mkdir($savePath, 0777, true) && !is_dir($savePath)) {
+ throw new \RuntimeException(sprintf('Session Storage was not able to create directory "%s"', $savePath));
+ }
+
+ $this->savePath = $savePath;
+
+ parent::__construct($name, $metaBag);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (!$this->id) {
+ $this->id = $this->generateId();
+ }
+
+ $this->read();
+
+ $this->started = true;
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate(bool $destroy = false, int $lifetime = null)
+ {
+ if (!$this->started) {
+ $this->start();
+ }
+
+ if ($destroy) {
+ $this->destroy();
+ }
+
+ return parent::regenerate($destroy, $lifetime);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ if (!$this->started) {
+ throw new \RuntimeException('Trying to save a session that was not started yet or was already closed');
+ }
+
+ $data = $this->data;
+
+ foreach ($this->bags as $bag) {
+ if (empty($data[$key = $bag->getStorageKey()])) {
+ unset($data[$key]);
+ }
+ }
+ if ([$key = $this->metadataBag->getStorageKey()] === array_keys($data)) {
+ unset($data[$key]);
+ }
+
+ try {
+ if ($data) {
+ file_put_contents($this->getFilePath(), serialize($data));
+ } else {
+ $this->destroy();
+ }
+ } finally {
+ $this->data = $data;
+ }
+
+ // this is needed for Silex, where the session object is re-used across requests
+ // in functional tests. In Symfony, the container is rebooted, so we don't have
+ // this issue
+ $this->started = false;
+ }
+
+ /**
+ * Deletes a session from persistent storage.
+ * Deliberately leaves session data in memory intact.
+ */
+ private function destroy(): void
+ {
+ if (is_file($this->getFilePath())) {
+ unlink($this->getFilePath());
+ }
+ }
+
+ /**
+ * Calculate path to file.
+ */
+ private function getFilePath(): string
+ {
+ return $this->savePath.'/'.$this->id.'.mocksess';
+ }
+
+ /**
+ * Reads session from storage and loads session.
+ */
+ private function read(): void
+ {
+ $filePath = $this->getFilePath();
+ $this->data = is_readable($filePath) && is_file($filePath) ? unserialize(file_get_contents($filePath)) : [];
+
+ $this->loadSession();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/NativeSessionStorage.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
new file mode 100644
index 00000000..d3b44c85
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/NativeSessionStorage.php
@@ -0,0 +1,466 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+use Symfony\Component\HttpFoundation\Session\SessionUtils;
+use Symfony\Component\HttpFoundation\Session\Storage\Handler\StrictSessionHandler;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\SessionHandlerProxy;
+
+/**
+ * This provides a base class for session attribute storage.
+ *
+ * @author Drak
+ */
+class NativeSessionStorage implements SessionStorageInterface
+{
+ /**
+ * @var SessionBagInterface[]
+ */
+ protected $bags = [];
+
+ /**
+ * @var bool
+ */
+ protected $started = false;
+
+ /**
+ * @var bool
+ */
+ protected $closed = false;
+
+ /**
+ * @var AbstractProxy|\SessionHandlerInterface
+ */
+ protected $saveHandler;
+
+ /**
+ * @var MetadataBag
+ */
+ protected $metadataBag;
+
+ /**
+ * @var string|null
+ */
+ private $emulateSameSite;
+
+ /**
+ * Depending on how you want the storage driver to behave you probably
+ * want to override this constructor entirely.
+ *
+ * List of options for $options array with their defaults.
+ *
+ * @see https://php.net/session.configuration for options
+ * but we omit 'session.' from the beginning of the keys for convenience.
+ *
+ * ("auto_start", is not supported as it tells PHP to start a session before
+ * PHP starts to execute user-land code. Setting during runtime has no effect).
+ *
+ * cache_limiter, "" (use "0" to prevent headers from being sent entirely).
+ * cache_expire, "0"
+ * cookie_domain, ""
+ * cookie_httponly, ""
+ * cookie_lifetime, "0"
+ * cookie_path, "/"
+ * cookie_secure, ""
+ * cookie_samesite, null
+ * gc_divisor, "100"
+ * gc_maxlifetime, "1440"
+ * gc_probability, "1"
+ * lazy_write, "1"
+ * name, "PHPSESSID"
+ * referer_check, ""
+ * serialize_handler, "php"
+ * use_strict_mode, "0"
+ * use_cookies, "1"
+ * use_only_cookies, "1"
+ * use_trans_sid, "0"
+ * upload_progress.enabled, "1"
+ * upload_progress.cleanup, "1"
+ * upload_progress.prefix, "upload_progress_"
+ * upload_progress.name, "PHP_SESSION_UPLOAD_PROGRESS"
+ * upload_progress.freq, "1%"
+ * upload_progress.min-freq, "1"
+ * url_rewriter.tags, "a=href,area=href,frame=src,form=,fieldset="
+ * sid_length, "32"
+ * sid_bits_per_character, "5"
+ * trans_sid_hosts, $_SERVER['HTTP_HOST']
+ * trans_sid_tags, "a=href,area=href,frame=src,form="
+ *
+ * @param AbstractProxy|\SessionHandlerInterface|null $handler
+ */
+ public function __construct(array $options = [], $handler = null, MetadataBag $metaBag = null)
+ {
+ if (!\extension_loaded('session')) {
+ throw new \LogicException('PHP extension "session" is required.');
+ }
+
+ $options += [
+ 'cache_limiter' => '',
+ 'cache_expire' => 0,
+ 'use_cookies' => 1,
+ 'lazy_write' => 1,
+ 'use_strict_mode' => 1,
+ ];
+
+ session_register_shutdown();
+
+ $this->setMetadataBag($metaBag);
+ $this->setOptions($options);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * Gets the save handler instance.
+ *
+ * @return AbstractProxy|\SessionHandlerInterface
+ */
+ public function getSaveHandler()
+ {
+ return $this->saveHandler;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ if (\PHP_SESSION_ACTIVE === session_status()) {
+ throw new \RuntimeException('Failed to start the session: already started by PHP.');
+ }
+
+ if (filter_var(ini_get('session.use_cookies'), FILTER_VALIDATE_BOOLEAN) && headers_sent($file, $line)) {
+ throw new \RuntimeException(sprintf('Failed to start the session because headers have already been sent by "%s" at line %d.', $file, $line));
+ }
+
+ // ok to try and start the session
+ if (!session_start()) {
+ throw new \RuntimeException('Failed to start the session');
+ }
+
+ if (null !== $this->emulateSameSite) {
+ $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
+ if (null !== $originalCookie) {
+ header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
+ }
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getId()
+ {
+ return $this->saveHandler->getId();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setId(string $id)
+ {
+ $this->saveHandler->setId($id);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getName()
+ {
+ return $this->saveHandler->getName();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function setName(string $name)
+ {
+ $this->saveHandler->setName($name);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function regenerate(bool $destroy = false, int $lifetime = null)
+ {
+ // Cannot regenerate the session ID for non-active sessions.
+ if (\PHP_SESSION_ACTIVE !== session_status()) {
+ return false;
+ }
+
+ if (headers_sent()) {
+ return false;
+ }
+
+ if (null !== $lifetime) {
+ ini_set('session.cookie_lifetime', $lifetime);
+ }
+
+ if ($destroy) {
+ $this->metadataBag->stampNew();
+ }
+
+ $isRegenerated = session_regenerate_id($destroy);
+
+ // The reference to $_SESSION in session bags is lost in PHP7 and we need to re-create it.
+ // @see https://bugs.php.net/70013
+ $this->loadSession();
+
+ if (null !== $this->emulateSameSite) {
+ $originalCookie = SessionUtils::popSessionCookie(session_name(), session_id());
+ if (null !== $originalCookie) {
+ header(sprintf('%s; SameSite=%s', $originalCookie, $this->emulateSameSite), false);
+ }
+ }
+
+ return $isRegenerated;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function save()
+ {
+ // Store a copy so we can restore the bags in case the session was not left empty
+ $session = $_SESSION;
+
+ foreach ($this->bags as $bag) {
+ if (empty($_SESSION[$key = $bag->getStorageKey()])) {
+ unset($_SESSION[$key]);
+ }
+ }
+ if ([$key = $this->metadataBag->getStorageKey()] === array_keys($_SESSION)) {
+ unset($_SESSION[$key]);
+ }
+
+ // Register error handler to add information about the current save handler
+ $previousHandler = set_error_handler(function ($type, $msg, $file, $line) use (&$previousHandler) {
+ if (E_WARNING === $type && 0 === strpos($msg, 'session_write_close():')) {
+ $handler = $this->saveHandler instanceof SessionHandlerProxy ? $this->saveHandler->getHandler() : $this->saveHandler;
+ $msg = sprintf('session_write_close(): Failed to write session data with "%s" handler', \get_class($handler));
+ }
+
+ return $previousHandler ? $previousHandler($type, $msg, $file, $line) : false;
+ });
+
+ try {
+ session_write_close();
+ } finally {
+ restore_error_handler();
+
+ // Restore only if not empty
+ if ($_SESSION) {
+ $_SESSION = $session;
+ }
+ }
+
+ $this->closed = true;
+ $this->started = false;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // clear out the session
+ $_SESSION = [];
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function registerBag(SessionBagInterface $bag)
+ {
+ if ($this->started) {
+ throw new \LogicException('Cannot register a bag when the session is already started.');
+ }
+
+ $this->bags[$bag->getName()] = $bag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getBag(string $name)
+ {
+ if (!isset($this->bags[$name])) {
+ throw new \InvalidArgumentException(sprintf('The SessionBagInterface %s is not registered.', $name));
+ }
+
+ if (!$this->started && $this->saveHandler->isActive()) {
+ $this->loadSession();
+ } elseif (!$this->started) {
+ $this->start();
+ }
+
+ return $this->bags[$name];
+ }
+
+ public function setMetadataBag(MetadataBag $metaBag = null)
+ {
+ if (null === $metaBag) {
+ $metaBag = new MetadataBag();
+ }
+
+ $this->metadataBag = $metaBag;
+ }
+
+ /**
+ * Gets the MetadataBag.
+ *
+ * @return MetadataBag
+ */
+ public function getMetadataBag()
+ {
+ return $this->metadataBag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isStarted()
+ {
+ return $this->started;
+ }
+
+ /**
+ * Sets session.* ini variables.
+ *
+ * For convenience we omit 'session.' from the beginning of the keys.
+ * Explicitly ignores other ini keys.
+ *
+ * @param array $options Session ini directives [key => value]
+ *
+ * @see https://php.net/session.configuration
+ */
+ public function setOptions(array $options)
+ {
+ if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+ return;
+ }
+
+ $validOptions = array_flip([
+ 'cache_expire', 'cache_limiter', 'cookie_domain', 'cookie_httponly',
+ 'cookie_lifetime', 'cookie_path', 'cookie_secure', 'cookie_samesite',
+ 'gc_divisor', 'gc_maxlifetime', 'gc_probability',
+ 'lazy_write', 'name', 'referer_check',
+ 'serialize_handler', 'use_strict_mode', 'use_cookies',
+ 'use_only_cookies', 'use_trans_sid', 'upload_progress.enabled',
+ 'upload_progress.cleanup', 'upload_progress.prefix', 'upload_progress.name',
+ 'upload_progress.freq', 'upload_progress.min_freq', 'url_rewriter.tags',
+ 'sid_length', 'sid_bits_per_character', 'trans_sid_hosts', 'trans_sid_tags',
+ ]);
+
+ foreach ($options as $key => $value) {
+ if (isset($validOptions[$key])) {
+ if ('cookie_samesite' === $key && \PHP_VERSION_ID < 70300) {
+ // PHP < 7.3 does not support same_site cookies. We will emulate it in
+ // the start() method instead.
+ $this->emulateSameSite = $value;
+ continue;
+ }
+ ini_set('url_rewriter.tags' !== $key ? 'session.'.$key : $key, $value);
+ }
+ }
+ }
+
+ /**
+ * Registers session save handler as a PHP session handler.
+ *
+ * To use internal PHP session save handlers, override this method using ini_set with
+ * session.save_handler and session.save_path e.g.
+ *
+ * ini_set('session.save_handler', 'files');
+ * ini_set('session.save_path', '/tmp');
+ *
+ * or pass in a \SessionHandler instance which configures session.save_handler in the
+ * constructor, for a template see NativeFileSessionHandler or use handlers in
+ * composer package drak/native-session
+ *
+ * @see https://php.net/session-set-save-handler
+ * @see https://php.net/sessionhandlerinterface
+ * @see https://php.net/sessionhandler
+ * @see https://github.com/zikula/NativeSession
+ *
+ * @param AbstractProxy|\SessionHandlerInterface|null $saveHandler
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function setSaveHandler($saveHandler = null)
+ {
+ if (!$saveHandler instanceof AbstractProxy &&
+ !$saveHandler instanceof \SessionHandlerInterface &&
+ null !== $saveHandler) {
+ throw new \InvalidArgumentException('Must be instance of AbstractProxy; implement \SessionHandlerInterface; or be null.');
+ }
+
+ // Wrap $saveHandler in proxy and prevent double wrapping of proxy
+ if (!$saveHandler instanceof AbstractProxy && $saveHandler instanceof \SessionHandlerInterface) {
+ $saveHandler = new SessionHandlerProxy($saveHandler);
+ } elseif (!$saveHandler instanceof AbstractProxy) {
+ $saveHandler = new SessionHandlerProxy(new StrictSessionHandler(new \SessionHandler()));
+ }
+ $this->saveHandler = $saveHandler;
+
+ if (headers_sent() || \PHP_SESSION_ACTIVE === session_status()) {
+ return;
+ }
+
+ if ($this->saveHandler instanceof SessionHandlerProxy) {
+ session_set_save_handler($this->saveHandler, false);
+ }
+ }
+
+ /**
+ * Load the session with attributes.
+ *
+ * After starting the session, PHP retrieves the session from whatever handlers
+ * are set to (either PHP's internal, or a custom save handler set with session_set_save_handler()).
+ * PHP takes the return value from the read() handler, unserializes it
+ * and populates $_SESSION with the result automatically.
+ */
+ protected function loadSession(array &$session = null)
+ {
+ if (null === $session) {
+ $session = &$_SESSION;
+ }
+
+ $bags = array_merge($this->bags, [$this->metadataBag]);
+
+ foreach ($bags as $bag) {
+ $key = $bag->getStorageKey();
+ $session[$key] = isset($session[$key]) && \is_array($session[$key]) ? $session[$key] : [];
+ $bag->initialize($session[$key]);
+ }
+
+ $this->started = true;
+ $this->closed = false;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
new file mode 100644
index 00000000..72dbef13
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/PhpBridgeSessionStorage.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\Storage\Proxy\AbstractProxy;
+
+/**
+ * Allows session to be started by PHP and managed by Symfony.
+ *
+ * @author Drak
+ */
+class PhpBridgeSessionStorage extends NativeSessionStorage
+{
+ /**
+ * @param AbstractProxy|\SessionHandlerInterface|null $handler
+ */
+ public function __construct($handler = null, MetadataBag $metaBag = null)
+ {
+ if (!\extension_loaded('session')) {
+ throw new \LogicException('PHP extension "session" is required.');
+ }
+
+ $this->setMetadataBag($metaBag);
+ $this->setSaveHandler($handler);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function start()
+ {
+ if ($this->started) {
+ return true;
+ }
+
+ $this->loadSession();
+
+ return true;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function clear()
+ {
+ // clear out the bags and nothing else that may be set
+ // since the purpose of this driver is to share a handler
+ foreach ($this->bags as $bag) {
+ $bag->clear();
+ }
+
+ // reconnect the bags to the session
+ $this->loadSession();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
new file mode 100644
index 00000000..ded79d64
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/AbstractProxy.php
@@ -0,0 +1,118 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak
+ */
+abstract class AbstractProxy
+{
+ /**
+ * Flag if handler wraps an internal PHP session handler (using \SessionHandler).
+ *
+ * @var bool
+ */
+ protected $wrapper = false;
+
+ /**
+ * @var string
+ */
+ protected $saveHandlerName;
+
+ /**
+ * Gets the session.save_handler name.
+ *
+ * @return string|null
+ */
+ public function getSaveHandlerName()
+ {
+ return $this->saveHandlerName;
+ }
+
+ /**
+ * Is this proxy handler and instance of \SessionHandlerInterface.
+ *
+ * @return bool
+ */
+ public function isSessionHandlerInterface()
+ {
+ return $this instanceof \SessionHandlerInterface;
+ }
+
+ /**
+ * Returns true if this handler wraps an internal PHP session save handler using \SessionHandler.
+ *
+ * @return bool
+ */
+ public function isWrapper()
+ {
+ return $this->wrapper;
+ }
+
+ /**
+ * Has a session started?
+ *
+ * @return bool
+ */
+ public function isActive()
+ {
+ return \PHP_SESSION_ACTIVE === session_status();
+ }
+
+ /**
+ * Gets the session ID.
+ *
+ * @return string
+ */
+ public function getId()
+ {
+ return session_id();
+ }
+
+ /**
+ * Sets the session ID.
+ *
+ * @throws \LogicException
+ */
+ public function setId(string $id)
+ {
+ if ($this->isActive()) {
+ throw new \LogicException('Cannot change the ID of an active session');
+ }
+
+ session_id($id);
+ }
+
+ /**
+ * Gets the session name.
+ *
+ * @return string
+ */
+ public function getName()
+ {
+ return session_name();
+ }
+
+ /**
+ * Sets the session name.
+ *
+ * @throws \LogicException
+ */
+ public function setName(string $name)
+ {
+ if ($this->isActive()) {
+ throw new \LogicException('Cannot change the name of an active session');
+ }
+
+ session_name($name);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
new file mode 100644
index 00000000..de4f550b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/Proxy/SessionHandlerProxy.php
@@ -0,0 +1,101 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage\Proxy;
+
+/**
+ * @author Drak
+ */
+class SessionHandlerProxy extends AbstractProxy implements \SessionHandlerInterface, \SessionUpdateTimestampHandlerInterface
+{
+ protected $handler;
+
+ public function __construct(\SessionHandlerInterface $handler)
+ {
+ $this->handler = $handler;
+ $this->wrapper = ($handler instanceof \SessionHandler);
+ $this->saveHandlerName = $this->wrapper ? ini_get('session.save_handler') : 'user';
+ }
+
+ /**
+ * @return \SessionHandlerInterface
+ */
+ public function getHandler()
+ {
+ return $this->handler;
+ }
+
+ // \SessionHandlerInterface
+
+ /**
+ * @return bool
+ */
+ public function open($savePath, $sessionName)
+ {
+ return (bool) $this->handler->open($savePath, $sessionName);
+ }
+
+ /**
+ * @return bool
+ */
+ public function close()
+ {
+ return (bool) $this->handler->close();
+ }
+
+ /**
+ * @return string
+ */
+ public function read($sessionId)
+ {
+ return (string) $this->handler->read($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function write($sessionId, $data)
+ {
+ return (bool) $this->handler->write($sessionId, $data);
+ }
+
+ /**
+ * @return bool
+ */
+ public function destroy($sessionId)
+ {
+ return (bool) $this->handler->destroy($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function gc($maxlifetime)
+ {
+ return (bool) $this->handler->gc($maxlifetime);
+ }
+
+ /**
+ * @return bool
+ */
+ public function validateId($sessionId)
+ {
+ return !$this->handler instanceof \SessionUpdateTimestampHandlerInterface || $this->handler->validateId($sessionId);
+ }
+
+ /**
+ * @return bool
+ */
+ public function updateTimestamp($sessionId, $data)
+ {
+ return $this->handler instanceof \SessionUpdateTimestampHandlerInterface ? $this->handler->updateTimestamp($sessionId, $data) : $this->write($sessionId, $data);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/SessionStorageInterface.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
new file mode 100644
index 00000000..eb8e8ff2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Session/Storage/SessionStorageInterface.php
@@ -0,0 +1,131 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Session\Storage;
+
+use Symfony\Component\HttpFoundation\Session\SessionBagInterface;
+
+/**
+ * StorageInterface.
+ *
+ * @author Fabien Potencier
+ * @author Drak
+ */
+interface SessionStorageInterface
+{
+ /**
+ * Starts the session.
+ *
+ * @return bool True if started
+ *
+ * @throws \RuntimeException if something goes wrong starting the session
+ */
+ public function start();
+
+ /**
+ * Checks if the session is started.
+ *
+ * @return bool True if started, false otherwise
+ */
+ public function isStarted();
+
+ /**
+ * Returns the session ID.
+ *
+ * @return string The session ID or empty
+ */
+ public function getId();
+
+ /**
+ * Sets the session ID.
+ */
+ public function setId(string $id);
+
+ /**
+ * Returns the session name.
+ *
+ * @return string The session name
+ */
+ public function getName();
+
+ /**
+ * Sets the session name.
+ */
+ public function setName(string $name);
+
+ /**
+ * Regenerates id that represents this storage.
+ *
+ * This method must invoke session_regenerate_id($destroy) unless
+ * this interface is used for a storage object designed for unit
+ * or functional testing where a real PHP session would interfere
+ * with testing.
+ *
+ * Note regenerate+destroy should not clear the session data in memory
+ * only delete the session data from persistent storage.
+ *
+ * Care: When regenerating the session ID no locking is involved in PHP's
+ * session design. See https://bugs.php.net/61470 for a discussion.
+ * So you must make sure the regenerated session is saved BEFORE sending the
+ * headers with the new ID. Symfony's HttpKernel offers a listener for this.
+ * See Symfony\Component\HttpKernel\EventListener\SaveSessionListener.
+ * Otherwise session data could get lost again for concurrent requests with the
+ * new ID. One result could be that you get logged out after just logging in.
+ *
+ * @param bool $destroy Destroy session when regenerating?
+ * @param int $lifetime Sets the cookie lifetime for the session cookie. A null value
+ * will leave the system settings unchanged, 0 sets the cookie
+ * to expire with browser session. Time is in seconds, and is
+ * not a Unix timestamp.
+ *
+ * @return bool True if session regenerated, false if error
+ *
+ * @throws \RuntimeException If an error occurs while regenerating this storage
+ */
+ public function regenerate(bool $destroy = false, int $lifetime = null);
+
+ /**
+ * Force the session to be saved and closed.
+ *
+ * This method must invoke session_write_close() unless this interface is
+ * used for a storage object design for unit or functional testing where
+ * a real PHP session would interfere with testing, in which case
+ * it should actually persist the session data if required.
+ *
+ * @throws \RuntimeException if the session is saved without being started, or if the session
+ * is already closed
+ */
+ public function save();
+
+ /**
+ * Clear all session data in memory.
+ */
+ public function clear();
+
+ /**
+ * Gets a SessionBagInterface by name.
+ *
+ * @return SessionBagInterface
+ *
+ * @throws \InvalidArgumentException If the bag does not exist
+ */
+ public function getBag(string $name);
+
+ /**
+ * Registers a SessionBagInterface for use.
+ */
+ public function registerBag(SessionBagInterface $bag);
+
+ /**
+ * @return MetadataBag
+ */
+ public function getMetadataBag();
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/StreamedResponse.php b/lam/lib/3rdParty/composer/symfony/http-foundation/StreamedResponse.php
new file mode 100644
index 00000000..65ec2d98
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/StreamedResponse.php
@@ -0,0 +1,135 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+/**
+ * StreamedResponse represents a streamed HTTP response.
+ *
+ * A StreamedResponse uses a callback for its content.
+ *
+ * The callback should use the standard PHP functions like echo
+ * to stream the response back to the client. The flush() function
+ * can also be used if needed.
+ *
+ * @see flush()
+ *
+ * @author Fabien Potencier
+ */
+class StreamedResponse extends Response
+{
+ protected $callback;
+ protected $streamed;
+ private $headersSent;
+
+ public function __construct(callable $callback = null, int $status = 200, array $headers = [])
+ {
+ parent::__construct(null, $status, $headers);
+
+ if (null !== $callback) {
+ $this->setCallback($callback);
+ }
+ $this->streamed = false;
+ $this->headersSent = false;
+ }
+
+ /**
+ * Factory method for chainability.
+ *
+ * @param callable|null $callback A valid PHP callback or null to set it later
+ *
+ * @return static
+ */
+ public static function create($callback = null, int $status = 200, array $headers = [])
+ {
+ return new static($callback, $status, $headers);
+ }
+
+ /**
+ * Sets the PHP callback associated with this Response.
+ *
+ * @return $this
+ */
+ public function setCallback(callable $callback)
+ {
+ $this->callback = $callback;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This method only sends the headers once.
+ *
+ * @return $this
+ */
+ public function sendHeaders()
+ {
+ if ($this->headersSent) {
+ return $this;
+ }
+
+ $this->headersSent = true;
+
+ return parent::sendHeaders();
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * This method only sends the content once.
+ *
+ * @return $this
+ */
+ public function sendContent()
+ {
+ if ($this->streamed) {
+ return $this;
+ }
+
+ $this->streamed = true;
+
+ if (null === $this->callback) {
+ throw new \LogicException('The Response callback must not be null.');
+ }
+
+ ($this->callback)();
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @throws \LogicException when the content is not null
+ *
+ * @return $this
+ */
+ public function setContent(?string $content)
+ {
+ if (null !== $content) {
+ throw new \LogicException('The content cannot be set on a StreamedResponse instance.');
+ }
+
+ $this->streamed = true;
+
+ return $this;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getContent()
+ {
+ return false;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php
new file mode 100644
index 00000000..cb216ea1
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/RequestAttributeValueSame.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Request;
+
+final class RequestAttributeValueSame extends Constraint
+{
+ private $name;
+ private $value;
+
+ public function __construct(string $name, string $value)
+ {
+ $this->name = $name;
+ $this->value = $value;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has attribute "%s" with value "%s"', $this->name, $this->value);
+ }
+
+ /**
+ * @param Request $request
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($request): bool
+ {
+ return $this->value === $request->attributes->get($this->name);
+ }
+
+ /**
+ * @param Request $request
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($request): string
+ {
+ return 'the Request '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php
new file mode 100644
index 00000000..554e1a16
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseCookieValueSame.php
@@ -0,0 +1,85 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseCookieValueSame extends Constraint
+{
+ private $name;
+ private $value;
+ private $path;
+ private $domain;
+
+ public function __construct(string $name, string $value, string $path = '/', string $domain = null)
+ {
+ $this->name = $name;
+ $this->value = $value;
+ $this->path = $path;
+ $this->domain = $domain;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ $str = sprintf('has cookie "%s"', $this->name);
+ if ('/' !== $this->path) {
+ $str .= sprintf(' with path "%s"', $this->path);
+ }
+ if ($this->domain) {
+ $str .= sprintf(' for domain "%s"', $this->domain);
+ }
+ $str .= sprintf(' with value "%s"', $this->value);
+
+ return $str;
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ $cookie = $this->getCookie($response);
+ if (!$cookie) {
+ return false;
+ }
+
+ return $this->value === $cookie->getValue();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+
+ protected function getCookie(Response $response): ?Cookie
+ {
+ $cookies = $response->headers->getCookies();
+
+ $filteredCookies = array_filter($cookies, function (Cookie $cookie) {
+ return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain;
+ });
+
+ return reset($filteredCookies) ?: null;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php
new file mode 100644
index 00000000..eae9e271
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasCookie.php
@@ -0,0 +1,77 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Cookie;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseHasCookie extends Constraint
+{
+ private $name;
+ private $path;
+ private $domain;
+
+ public function __construct(string $name, string $path = '/', string $domain = null)
+ {
+ $this->name = $name;
+ $this->path = $path;
+ $this->domain = $domain;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ $str = sprintf('has cookie "%s"', $this->name);
+ if ('/' !== $this->path) {
+ $str .= sprintf(' with path "%s"', $this->path);
+ }
+ if ($this->domain) {
+ $str .= sprintf(' for domain "%s"', $this->domain);
+ }
+
+ return $str;
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return null !== $this->getCookie($response);
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+
+ private function getCookie(Response $response): ?Cookie
+ {
+ $cookies = $response->headers->getCookies();
+
+ $filteredCookies = array_filter($cookies, function (Cookie $cookie) {
+ return $cookie->getName() === $this->name && $cookie->getPath() === $this->path && $cookie->getDomain() === $this->domain;
+ });
+
+ return reset($filteredCookies) ?: null;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php
new file mode 100644
index 00000000..68ad8273
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHasHeader.php
@@ -0,0 +1,53 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseHasHeader extends Constraint
+{
+ private $headerName;
+
+ public function __construct(string $headerName)
+ {
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has header "%s"', $this->headerName);
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return $response->headers->has($this->headerName);
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php
new file mode 100644
index 00000000..a27d0c73
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseHeaderSame.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseHeaderSame extends Constraint
+{
+ private $headerName;
+ private $expectedValue;
+
+ public function __construct(string $headerName, string $expectedValue)
+ {
+ $this->headerName = $headerName;
+ $this->expectedValue = $expectedValue;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue);
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return $this->expectedValue === $response->headers->get($this->headerName, null);
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php
new file mode 100644
index 00000000..8c4b883f
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsRedirected.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseIsRedirected extends Constraint
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return 'is redirected';
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return $response->isRedirect();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function additionalFailureDescription($response): string
+ {
+ return (string) $response;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php
new file mode 100644
index 00000000..9c665589
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseIsSuccessful.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseIsSuccessful extends Constraint
+{
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return 'is successful';
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return $response->isSuccessful();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function additionalFailureDescription($response): string
+ {
+ return (string) $response;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php
new file mode 100644
index 00000000..72bb000b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/Test/Constraint/ResponseStatusCodeSame.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\HttpFoundation\Response;
+
+final class ResponseStatusCodeSame extends Constraint
+{
+ private $statusCode;
+
+ public function __construct(int $statusCode)
+ {
+ $this->statusCode = $statusCode;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return 'status code is '.$this->statusCode;
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($response): bool
+ {
+ return $this->statusCode === $response->getStatusCode();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($response): string
+ {
+ return 'the Response '.$this->toString();
+ }
+
+ /**
+ * @param Response $response
+ *
+ * {@inheritdoc}
+ */
+ protected function additionalFailureDescription($response): string
+ {
+ return (string) $response;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/UrlHelper.php b/lam/lib/3rdParty/composer/symfony/http-foundation/UrlHelper.php
new file mode 100644
index 00000000..f114c0a9
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/UrlHelper.php
@@ -0,0 +1,102 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\HttpFoundation;
+
+use Symfony\Component\Routing\RequestContext;
+
+/**
+ * A helper service for manipulating URLs within and outside the request scope.
+ *
+ * @author Valentin Udaltsov
+ */
+final class UrlHelper
+{
+ private $requestStack;
+ private $requestContext;
+
+ public function __construct(RequestStack $requestStack, RequestContext $requestContext = null)
+ {
+ $this->requestStack = $requestStack;
+ $this->requestContext = $requestContext;
+ }
+
+ public function getAbsoluteUrl(string $path): string
+ {
+ if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
+ return $path;
+ }
+
+ if (null === $request = $this->requestStack->getMasterRequest()) {
+ return $this->getAbsoluteUrlFromContext($path);
+ }
+
+ if ('#' === $path[0]) {
+ $path = $request->getRequestUri().$path;
+ } elseif ('?' === $path[0]) {
+ $path = $request->getPathInfo().$path;
+ }
+
+ if (!$path || '/' !== $path[0]) {
+ $prefix = $request->getPathInfo();
+ $last = \strlen($prefix) - 1;
+ if ($last !== $pos = strrpos($prefix, '/')) {
+ $prefix = substr($prefix, 0, $pos).'/';
+ }
+
+ return $request->getUriForPath($prefix.$path);
+ }
+
+ return $request->getSchemeAndHttpHost().$path;
+ }
+
+ public function getRelativePath(string $path): string
+ {
+ if (false !== strpos($path, '://') || '//' === substr($path, 0, 2)) {
+ return $path;
+ }
+
+ if (null === $request = $this->requestStack->getMasterRequest()) {
+ return $path;
+ }
+
+ return $request->getRelativeUriForPath($path);
+ }
+
+ private function getAbsoluteUrlFromContext(string $path): string
+ {
+ if (null === $this->requestContext || '' === $host = $this->requestContext->getHost()) {
+ return $path;
+ }
+
+ $scheme = $this->requestContext->getScheme();
+ $port = '';
+
+ if ('http' === $scheme && 80 !== $this->requestContext->getHttpPort()) {
+ $port = ':'.$this->requestContext->getHttpPort();
+ } elseif ('https' === $scheme && 443 !== $this->requestContext->getHttpsPort()) {
+ $port = ':'.$this->requestContext->getHttpsPort();
+ }
+
+ if ('#' === $path[0]) {
+ $queryString = $this->requestContext->getQueryString();
+ $path = $this->requestContext->getPathInfo().($queryString ? '?'.$queryString : '').$path;
+ } elseif ('?' === $path[0]) {
+ $path = $this->requestContext->getPathInfo().$path;
+ }
+
+ if ('/' !== $path[0]) {
+ $path = rtrim($this->requestContext->getBaseUrl(), '/').'/'.$path;
+ }
+
+ return $scheme.'://'.$host.$port.$path;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/http-foundation/composer.json b/lam/lib/3rdParty/composer/symfony/http-foundation/composer.json
new file mode 100644
index 00000000..fcec68c3
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/http-foundation/composer.json
@@ -0,0 +1,39 @@
+{
+ "name": "symfony/http-foundation",
+ "type": "library",
+ "description": "Symfony HttpFoundation Component",
+ "keywords": [],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.2.5",
+ "symfony/mime": "^4.4|^5.0",
+ "symfony/polyfill-mbstring": "~1.1"
+ },
+ "require-dev": {
+ "predis/predis": "~1.0",
+ "symfony/expression-language": "^4.4|^5.0"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\HttpFoundation\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/.gitattributes b/lam/lib/3rdParty/composer/symfony/mime/.gitattributes
new file mode 100644
index 00000000..ebb92870
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/.gitattributes
@@ -0,0 +1,3 @@
+/Tests export-ignore
+/phpunit.xml.dist export-ignore
+/.gitignore export-ignore
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Address.php b/lam/lib/3rdParty/composer/symfony/mime/Address.php
new file mode 100644
index 00000000..b0dcbd08
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Address.php
@@ -0,0 +1,125 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Egulias\EmailValidator\EmailValidator;
+use Egulias\EmailValidator\Validation\RFCValidation;
+use Symfony\Component\Mime\Encoder\IdnAddressEncoder;
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Exception\LogicException;
+use Symfony\Component\Mime\Exception\RfcComplianceException;
+
+/**
+ * @author Fabien Potencier
+ */
+final class Address
+{
+ /**
+ * A regex that matches a structure like 'Name '.
+ * It matches anything between the first < and last > as email address.
+ * This allows to use a single string to construct an Address, which can be convenient to use in
+ * config, and allows to have more readable config.
+ * This does not try to cover all edge cases for address.
+ */
+ private const FROM_STRING_PATTERN = '~(?[^<]*)<(?.*)>[^>]*~';
+
+ private static $validator;
+ private static $encoder;
+
+ private $address;
+ private $name;
+
+ public function __construct(string $address, string $name = '')
+ {
+ if (!class_exists(EmailValidator::class)) {
+ throw new LogicException(sprintf('The "%s" class cannot be used as it needs "%s"; try running "composer require egulias/email-validator".', __CLASS__, EmailValidator::class));
+ }
+
+ if (null === self::$validator) {
+ self::$validator = new EmailValidator();
+ }
+
+ $this->address = trim($address);
+ $this->name = trim(str_replace(["\n", "\r"], '', $name));
+
+ if (!self::$validator->isValid($this->address, new RFCValidation())) {
+ throw new RfcComplianceException(sprintf('Email "%s" does not comply with addr-spec of RFC 2822.', $address));
+ }
+ }
+
+ public function getAddress(): string
+ {
+ return $this->address;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ public function getEncodedAddress(): string
+ {
+ if (null === self::$encoder) {
+ self::$encoder = new IdnAddressEncoder();
+ }
+
+ return self::$encoder->encodeString($this->address);
+ }
+
+ public function toString(): string
+ {
+ return ($n = $this->getName()) ? $n.' <'.$this->getEncodedAddress().'>' : $this->getEncodedAddress();
+ }
+
+ /**
+ * @param Address|string $address
+ */
+ public static function create($address): self
+ {
+ if ($address instanceof self) {
+ return $address;
+ }
+ if (\is_string($address)) {
+ return new self($address);
+ }
+
+ throw new InvalidArgumentException(sprintf('An address can be an instance of Address or a string ("%s") given).', \is_object($address) ? \get_class($address) : \gettype($address)));
+ }
+
+ /**
+ * @param (Address|string)[] $addresses
+ *
+ * @return Address[]
+ */
+ public static function createArray(array $addresses): array
+ {
+ $addrs = [];
+ foreach ($addresses as $address) {
+ $addrs[] = self::create($address);
+ }
+
+ return $addrs;
+ }
+
+ public static function fromString(string $string): self
+ {
+ if (false === strpos($string, '<')) {
+ return new self($string, '');
+ }
+
+ if (!preg_match(self::FROM_STRING_PATTERN, $string, $matches)) {
+ throw new InvalidArgumentException(sprintf('Could not parse "%s" to a "%s" instance.', $string, static::class));
+ }
+
+ return new self($matches['addrSpec'], trim($matches['displayName'], ' \'"'));
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/BodyRendererInterface.php b/lam/lib/3rdParty/composer/symfony/mime/BodyRendererInterface.php
new file mode 100644
index 00000000..d6921726
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/BodyRendererInterface.php
@@ -0,0 +1,20 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+/**
+ * @author Fabien Potencier
+ */
+interface BodyRendererInterface
+{
+ public function render(Message $message): void;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/CHANGELOG.md b/lam/lib/3rdParty/composer/symfony/mime/CHANGELOG.md
new file mode 100644
index 00000000..6148360d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/CHANGELOG.md
@@ -0,0 +1,20 @@
+CHANGELOG
+=========
+
+4.4.0
+-----
+
+ * [BC BREAK] Removed `NamedAddress` (`Address` now supports a name)
+ * Added PHPUnit constraints
+ * Added `AbstractPart::asDebugString()`
+ * Added `Address::fromString()`
+
+4.3.3
+-----
+
+ * [BC BREAK] Renamed method `Headers::getAll()` to `Headers::all()`.
+
+4.3.0
+-----
+
+ * Introduced the component as experimental
diff --git a/lam/lib/3rdParty/composer/symfony/mime/CharacterStream.php b/lam/lib/3rdParty/composer/symfony/mime/CharacterStream.php
new file mode 100644
index 00000000..749066f2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/CharacterStream.php
@@ -0,0 +1,221 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+/**
+ * @author Fabien Potencier
+ * @author Xavier De Cock
+ *
+ * @internal
+ */
+final class CharacterStream
+{
+ /** Pre-computed for optimization */
+ private const UTF8_LENGTH_MAP = [
+ "\x00" => 1, "\x01" => 1, "\x02" => 1, "\x03" => 1, "\x04" => 1, "\x05" => 1, "\x06" => 1, "\x07" => 1,
+ "\x08" => 1, "\x09" => 1, "\x0a" => 1, "\x0b" => 1, "\x0c" => 1, "\x0d" => 1, "\x0e" => 1, "\x0f" => 1,
+ "\x10" => 1, "\x11" => 1, "\x12" => 1, "\x13" => 1, "\x14" => 1, "\x15" => 1, "\x16" => 1, "\x17" => 1,
+ "\x18" => 1, "\x19" => 1, "\x1a" => 1, "\x1b" => 1, "\x1c" => 1, "\x1d" => 1, "\x1e" => 1, "\x1f" => 1,
+ "\x20" => 1, "\x21" => 1, "\x22" => 1, "\x23" => 1, "\x24" => 1, "\x25" => 1, "\x26" => 1, "\x27" => 1,
+ "\x28" => 1, "\x29" => 1, "\x2a" => 1, "\x2b" => 1, "\x2c" => 1, "\x2d" => 1, "\x2e" => 1, "\x2f" => 1,
+ "\x30" => 1, "\x31" => 1, "\x32" => 1, "\x33" => 1, "\x34" => 1, "\x35" => 1, "\x36" => 1, "\x37" => 1,
+ "\x38" => 1, "\x39" => 1, "\x3a" => 1, "\x3b" => 1, "\x3c" => 1, "\x3d" => 1, "\x3e" => 1, "\x3f" => 1,
+ "\x40" => 1, "\x41" => 1, "\x42" => 1, "\x43" => 1, "\x44" => 1, "\x45" => 1, "\x46" => 1, "\x47" => 1,
+ "\x48" => 1, "\x49" => 1, "\x4a" => 1, "\x4b" => 1, "\x4c" => 1, "\x4d" => 1, "\x4e" => 1, "\x4f" => 1,
+ "\x50" => 1, "\x51" => 1, "\x52" => 1, "\x53" => 1, "\x54" => 1, "\x55" => 1, "\x56" => 1, "\x57" => 1,
+ "\x58" => 1, "\x59" => 1, "\x5a" => 1, "\x5b" => 1, "\x5c" => 1, "\x5d" => 1, "\x5e" => 1, "\x5f" => 1,
+ "\x60" => 1, "\x61" => 1, "\x62" => 1, "\x63" => 1, "\x64" => 1, "\x65" => 1, "\x66" => 1, "\x67" => 1,
+ "\x68" => 1, "\x69" => 1, "\x6a" => 1, "\x6b" => 1, "\x6c" => 1, "\x6d" => 1, "\x6e" => 1, "\x6f" => 1,
+ "\x70" => 1, "\x71" => 1, "\x72" => 1, "\x73" => 1, "\x74" => 1, "\x75" => 1, "\x76" => 1, "\x77" => 1,
+ "\x78" => 1, "\x79" => 1, "\x7a" => 1, "\x7b" => 1, "\x7c" => 1, "\x7d" => 1, "\x7e" => 1, "\x7f" => 1,
+ "\x80" => 0, "\x81" => 0, "\x82" => 0, "\x83" => 0, "\x84" => 0, "\x85" => 0, "\x86" => 0, "\x87" => 0,
+ "\x88" => 0, "\x89" => 0, "\x8a" => 0, "\x8b" => 0, "\x8c" => 0, "\x8d" => 0, "\x8e" => 0, "\x8f" => 0,
+ "\x90" => 0, "\x91" => 0, "\x92" => 0, "\x93" => 0, "\x94" => 0, "\x95" => 0, "\x96" => 0, "\x97" => 0,
+ "\x98" => 0, "\x99" => 0, "\x9a" => 0, "\x9b" => 0, "\x9c" => 0, "\x9d" => 0, "\x9e" => 0, "\x9f" => 0,
+ "\xa0" => 0, "\xa1" => 0, "\xa2" => 0, "\xa3" => 0, "\xa4" => 0, "\xa5" => 0, "\xa6" => 0, "\xa7" => 0,
+ "\xa8" => 0, "\xa9" => 0, "\xaa" => 0, "\xab" => 0, "\xac" => 0, "\xad" => 0, "\xae" => 0, "\xaf" => 0,
+ "\xb0" => 0, "\xb1" => 0, "\xb2" => 0, "\xb3" => 0, "\xb4" => 0, "\xb5" => 0, "\xb6" => 0, "\xb7" => 0,
+ "\xb8" => 0, "\xb9" => 0, "\xba" => 0, "\xbb" => 0, "\xbc" => 0, "\xbd" => 0, "\xbe" => 0, "\xbf" => 0,
+ "\xc0" => 2, "\xc1" => 2, "\xc2" => 2, "\xc3" => 2, "\xc4" => 2, "\xc5" => 2, "\xc6" => 2, "\xc7" => 2,
+ "\xc8" => 2, "\xc9" => 2, "\xca" => 2, "\xcb" => 2, "\xcc" => 2, "\xcd" => 2, "\xce" => 2, "\xcf" => 2,
+ "\xd0" => 2, "\xd1" => 2, "\xd2" => 2, "\xd3" => 2, "\xd4" => 2, "\xd5" => 2, "\xd6" => 2, "\xd7" => 2,
+ "\xd8" => 2, "\xd9" => 2, "\xda" => 2, "\xdb" => 2, "\xdc" => 2, "\xdd" => 2, "\xde" => 2, "\xdf" => 2,
+ "\xe0" => 3, "\xe1" => 3, "\xe2" => 3, "\xe3" => 3, "\xe4" => 3, "\xe5" => 3, "\xe6" => 3, "\xe7" => 3,
+ "\xe8" => 3, "\xe9" => 3, "\xea" => 3, "\xeb" => 3, "\xec" => 3, "\xed" => 3, "\xee" => 3, "\xef" => 3,
+ "\xf0" => 4, "\xf1" => 4, "\xf2" => 4, "\xf3" => 4, "\xf4" => 4, "\xf5" => 4, "\xf6" => 4, "\xf7" => 4,
+ "\xf8" => 5, "\xf9" => 5, "\xfa" => 5, "\xfb" => 5, "\xfc" => 6, "\xfd" => 6, "\xfe" => 0, "\xff" => 0,
+ ];
+
+ private $data = '';
+ private $dataSize = 0;
+ private $map = [];
+ private $charCount = 0;
+ private $currentPos = 0;
+ private $fixedWidth = 0;
+
+ /**
+ * @param resource|string $input
+ */
+ public function __construct($input, ?string $charset = 'utf-8')
+ {
+ $charset = strtolower(trim($charset)) ?: 'utf-8';
+ if ('utf-8' === $charset || 'utf8' === $charset) {
+ $this->fixedWidth = 0;
+ $this->map = ['p' => [], 'i' => []];
+ } else {
+ switch ($charset) {
+ // 16 bits
+ case 'ucs2':
+ case 'ucs-2':
+ case 'utf16':
+ case 'utf-16':
+ $this->fixedWidth = 2;
+ break;
+
+ // 32 bits
+ case 'ucs4':
+ case 'ucs-4':
+ case 'utf32':
+ case 'utf-32':
+ $this->fixedWidth = 4;
+ break;
+
+ // 7-8 bit charsets: (us-)?ascii, (iso|iec)-?8859-?[0-9]+, windows-?125[0-9], cp-?[0-9]+, ansi, macintosh,
+ // koi-?7, koi-?8-?.+, mik, (cork|t1), v?iscii
+ // and fallback
+ default:
+ $this->fixedWidth = 1;
+ }
+ }
+ if (\is_resource($input)) {
+ $blocks = 512;
+ if (stream_get_meta_data($input)['seekable'] ?? false) {
+ rewind($input);
+ }
+ while (false !== $read = fread($input, $blocks)) {
+ $this->write($read);
+ }
+ } else {
+ $this->write($input);
+ }
+ }
+
+ public function read(int $length): ?string
+ {
+ if ($this->currentPos >= $this->charCount) {
+ return null;
+ }
+ $length = ($this->currentPos + $length > $this->charCount) ? $this->charCount - $this->currentPos : $length;
+ if ($this->fixedWidth > 0) {
+ $len = $length * $this->fixedWidth;
+ $ret = substr($this->data, $this->currentPos * $this->fixedWidth, $len);
+ $this->currentPos += $length;
+ } else {
+ $end = $this->currentPos + $length;
+ $end = $end > $this->charCount ? $this->charCount : $end;
+ $ret = '';
+ $start = 0;
+ if ($this->currentPos > 0) {
+ $start = $this->map['p'][$this->currentPos - 1];
+ }
+ $to = $start;
+ for (; $this->currentPos < $end; ++$this->currentPos) {
+ if (isset($this->map['i'][$this->currentPos])) {
+ $ret .= substr($this->data, $start, $to - $start).'?';
+ $start = $this->map['p'][$this->currentPos];
+ } else {
+ $to = $this->map['p'][$this->currentPos];
+ }
+ }
+ $ret .= substr($this->data, $start, $to - $start);
+ }
+
+ return $ret;
+ }
+
+ public function readBytes(int $length): ?array
+ {
+ if (null !== $read = $this->read($length)) {
+ return array_map('ord', str_split($read, 1));
+ }
+
+ return null;
+ }
+
+ public function setPointer(int $charOffset): void
+ {
+ if ($this->charCount < $charOffset) {
+ $charOffset = $this->charCount;
+ }
+ $this->currentPos = $charOffset;
+ }
+
+ public function write(string $chars): void
+ {
+ $ignored = '';
+ $this->data .= $chars;
+ if ($this->fixedWidth > 0) {
+ $strlen = \strlen($chars);
+ $ignoredL = $strlen % $this->fixedWidth;
+ $ignored = $ignoredL ? substr($chars, -$ignoredL) : '';
+ $this->charCount += ($strlen - $ignoredL) / $this->fixedWidth;
+ } else {
+ $this->charCount += $this->getUtf8CharPositions($chars, $this->dataSize, $ignored);
+ }
+ $this->dataSize = \strlen($this->data) - \strlen($ignored);
+ }
+
+ private function getUtf8CharPositions(string $string, int $startOffset, string &$ignoredChars): int
+ {
+ $strlen = \strlen($string);
+ $charPos = \count($this->map['p']);
+ $foundChars = 0;
+ $invalid = false;
+ for ($i = 0; $i < $strlen; ++$i) {
+ $char = $string[$i];
+ $size = self::UTF8_LENGTH_MAP[$char];
+ if (0 == $size) {
+ /* char is invalid, we must wait for a resync */
+ $invalid = true;
+ continue;
+ }
+
+ if ($invalid) {
+ /* We mark the chars as invalid and start a new char */
+ $this->map['p'][$charPos + $foundChars] = $startOffset + $i;
+ $this->map['i'][$charPos + $foundChars] = true;
+ ++$foundChars;
+ $invalid = false;
+ }
+ if (($i + $size) > $strlen) {
+ $ignoredChars = substr($string, $i);
+ break;
+ }
+ for ($j = 1; $j < $size; ++$j) {
+ $char = $string[$i + $j];
+ if ($char > "\x7F" && $char < "\xC0") {
+ // Valid - continue parsing
+ } else {
+ /* char is invalid, we must wait for a resync */
+ $invalid = true;
+ continue 2;
+ }
+ }
+ /* Ok we got a complete char here */
+ $this->map['p'][$charPos + $foundChars] = $startOffset + $i + $size;
+ $i += $j - 1;
+ ++$foundChars;
+ }
+
+ return $foundChars;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMime.php b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMime.php
new file mode 100644
index 00000000..55941be9
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMime.php
@@ -0,0 +1,111 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Crypto;
+
+use Symfony\Component\Mime\Exception\RuntimeException;
+use Symfony\Component\Mime\Part\SMimePart;
+
+/**
+ * @author Sebastiaan Stok
+ *
+ * @internal
+ */
+abstract class SMime
+{
+ protected function normalizeFilePath(string $path): string
+ {
+ if (!file_exists($path)) {
+ throw new RuntimeException(sprintf('File does not exist: %s.', $path));
+ }
+
+ return 'file://'.str_replace('\\', '/', realpath($path));
+ }
+
+ protected function iteratorToFile(iterable $iterator, $stream): void
+ {
+ foreach ($iterator as $chunk) {
+ fwrite($stream, $chunk);
+ }
+ }
+
+ protected function convertMessageToSMimePart($stream, string $type, string $subtype): SMimePart
+ {
+ rewind($stream);
+
+ $headers = '';
+
+ while (!feof($stream)) {
+ $buffer = fread($stream, 78);
+ $headers .= $buffer;
+
+ // Detect ending of header list
+ if (preg_match('/(\r\n\r\n|\n\n)/', $headers, $match)) {
+ $headersPosEnd = strpos($headers, $headerBodySeparator = $match[0]);
+
+ break;
+ }
+ }
+
+ $headers = $this->getMessageHeaders(trim(substr($headers, 0, $headersPosEnd)));
+
+ fseek($stream, $headersPosEnd + \strlen($headerBodySeparator));
+
+ return new SMimePart($this->getStreamIterator($stream), $type, $subtype, $this->getParametersFromHeader($headers['content-type']));
+ }
+
+ protected function getStreamIterator($stream): iterable
+ {
+ while (!feof($stream)) {
+ yield fread($stream, 16372);
+ }
+ }
+
+ private function getMessageHeaders(string $headerData): array
+ {
+ $headers = [];
+ $headerLines = explode("\r\n", str_replace("\n", "\r\n", str_replace("\r\n", "\n", $headerData)));
+ $currentHeaderName = '';
+
+ // Transform header lines into an associative array
+ foreach ($headerLines as $headerLine) {
+ // Empty lines between headers indicate a new mime-entity
+ if ('' === $headerLine) {
+ break;
+ }
+
+ // Handle headers that span multiple lines
+ if (false === strpos($headerLine, ':')) {
+ $headers[$currentHeaderName] .= ' '.trim($headerLine);
+ continue;
+ }
+
+ $header = explode(':', $headerLine, 2);
+ $currentHeaderName = strtolower($header[0]);
+ $headers[$currentHeaderName] = trim($header[1]);
+ }
+
+ return $headers;
+ }
+
+ private function getParametersFromHeader(string $header): array
+ {
+ $params = [];
+
+ preg_match_all('/(?P[a-z-0-9]+)=(?P"[^"]+"|(?:[^\s;]+|$))(?:\s+;)?/i', $header, $matches);
+
+ foreach ($matches['value'] as $pos => $paramValue) {
+ $params[$matches['name'][$pos]] = trim($paramValue, '"');
+ }
+
+ return $params;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeEncrypter.php b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeEncrypter.php
new file mode 100644
index 00000000..d6961a6e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeEncrypter.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Crypto;
+
+use Symfony\Component\Mime\Exception\RuntimeException;
+use Symfony\Component\Mime\Message;
+
+/**
+ * @author Sebastiaan Stok
+ */
+final class SMimeEncrypter extends SMime
+{
+ private $certs;
+ private $cipher;
+
+ /**
+ * @param string|string[] $certificate The path (or array of paths) of the file(s) containing the X.509 certificate(s)
+ * @param int|null $cipher A set of algorithms used to encrypt the message. Must be one of these PHP constants: https://www.php.net/manual/en/openssl.ciphers.php
+ */
+ public function __construct($certificate, int $cipher = null)
+ {
+ if (!\extension_loaded('openssl')) {
+ throw new \LogicException('PHP extension "openssl" is required to use SMime.');
+ }
+
+ if (\is_array($certificate)) {
+ $this->certs = array_map([$this, 'normalizeFilePath'], $certificate);
+ } else {
+ $this->certs = $this->normalizeFilePath($certificate);
+ }
+
+ $this->cipher = $cipher ?? OPENSSL_CIPHER_AES_256_CBC;
+ }
+
+ public function encrypt(Message $message): Message
+ {
+ $bufferFile = tmpfile();
+ $outputFile = tmpfile();
+
+ $this->iteratorToFile($message->toIterable(), $bufferFile);
+
+ if (!@openssl_pkcs7_encrypt(stream_get_meta_data($bufferFile)['uri'], stream_get_meta_data($outputFile)['uri'], $this->certs, [], 0, $this->cipher)) {
+ throw new RuntimeException(sprintf('Failed to encrypt S/Mime message. Error: "%s".', openssl_error_string()));
+ }
+
+ $mimePart = $this->convertMessageToSMimePart($outputFile, 'application', 'pkcs7-mime');
+ $mimePart->getHeaders()
+ ->addTextHeader('Content-Transfer-Encoding', 'base64')
+ ->addParameterizedHeader('Content-Disposition', 'attachment', ['name' => 'smime.p7m'])
+ ;
+
+ return new Message($message->getHeaders(), $mimePart);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeSigner.php b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeSigner.php
new file mode 100644
index 00000000..243aaf10
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Crypto/SMimeSigner.php
@@ -0,0 +1,71 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Crypto;
+
+use Symfony\Component\Mime\Exception\RuntimeException;
+use Symfony\Component\Mime\Message;
+
+/**
+ * @author Sebastiaan Stok
+ */
+final class SMimeSigner extends SMime
+{
+ private $signCertificate;
+ private $signPrivateKey;
+ private $signOptions;
+ private $extraCerts;
+
+ /**
+ * @var string|null
+ */
+ private $privateKeyPassphrase;
+
+ /**
+ * @param string $certificate The path of the file containing the signing certificate (in PEM format)
+ * @param string $privateKey The path of the file containing the private key (in PEM format)
+ * @param string|null $privateKeyPassphrase A passphrase of the private key (if any)
+ * @param string|null $extraCerts The path of the file containing intermediate certificates (in PEM format) needed by the signing certificate
+ * @param int|null $signOptions Bitwise operator options for openssl_pkcs7_sign() (@see https://secure.php.net/manual/en/openssl.pkcs7.flags.php)
+ */
+ public function __construct(string $certificate, string $privateKey, string $privateKeyPassphrase = null, string $extraCerts = null, int $signOptions = null)
+ {
+ if (!\extension_loaded('openssl')) {
+ throw new \LogicException('PHP extension "openssl" is required to use SMime.');
+ }
+
+ $this->signCertificate = $this->normalizeFilePath($certificate);
+
+ if (null !== $privateKeyPassphrase) {
+ $this->signPrivateKey = [$this->normalizeFilePath($privateKey), $privateKeyPassphrase];
+ } else {
+ $this->signPrivateKey = $this->normalizeFilePath($privateKey);
+ }
+
+ $this->signOptions = $signOptions ?? PKCS7_DETACHED;
+ $this->extraCerts = $extraCerts ? realpath($extraCerts) : null;
+ $this->privateKeyPassphrase = $privateKeyPassphrase;
+ }
+
+ public function sign(Message $message): Message
+ {
+ $bufferFile = tmpfile();
+ $outputFile = tmpfile();
+
+ $this->iteratorToFile($message->getBody()->toIterable(), $bufferFile);
+
+ if (!@openssl_pkcs7_sign(stream_get_meta_data($bufferFile)['uri'], stream_get_meta_data($outputFile)['uri'], $this->signCertificate, $this->signPrivateKey, [], $this->signOptions, $this->extraCerts)) {
+ throw new RuntimeException(sprintf('Failed to sign S/Mime message. Error: "%s".', openssl_error_string()));
+ }
+
+ return new Message($message->getHeaders(), $this->convertMessageToSMimePart($outputFile, 'multipart', 'signed'));
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php b/lam/lib/3rdParty/composer/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php
new file mode 100644
index 00000000..e24beb0d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/DependencyInjection/AddMimeTypeGuesserPass.php
@@ -0,0 +1,46 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\DependencyInjection;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Registers custom mime types guessers.
+ *
+ * @author Fabien Potencier
+ */
+class AddMimeTypeGuesserPass implements CompilerPassInterface
+{
+ private $mimeTypesService;
+ private $mimeTypeGuesserTag;
+
+ public function __construct(string $mimeTypesService = 'mime_types', string $mimeTypeGuesserTag = 'mime.mime_type_guesser')
+ {
+ $this->mimeTypesService = $mimeTypesService;
+ $this->mimeTypeGuesserTag = $mimeTypeGuesserTag;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function process(ContainerBuilder $container)
+ {
+ if ($container->has($this->mimeTypesService)) {
+ $definition = $container->findDefinition($this->mimeTypesService);
+ foreach ($container->findTaggedServiceIds($this->mimeTypeGuesserTag, true) as $id => $attributes) {
+ $definition->addMethodCall('registerGuesser', [new Reference($id)]);
+ }
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Email.php b/lam/lib/3rdParty/composer/symfony/mime/Email.php
new file mode 100644
index 00000000..fb912621
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Email.php
@@ -0,0 +1,599 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\LogicException;
+use Symfony\Component\Mime\Part\AbstractPart;
+use Symfony\Component\Mime\Part\DataPart;
+use Symfony\Component\Mime\Part\Multipart\AlternativePart;
+use Symfony\Component\Mime\Part\Multipart\MixedPart;
+use Symfony\Component\Mime\Part\Multipart\RelatedPart;
+use Symfony\Component\Mime\Part\TextPart;
+
+/**
+ * @author Fabien Potencier
+ */
+class Email extends Message
+{
+ const PRIORITY_HIGHEST = 1;
+ const PRIORITY_HIGH = 2;
+ const PRIORITY_NORMAL = 3;
+ const PRIORITY_LOW = 4;
+ const PRIORITY_LOWEST = 5;
+
+ private const PRIORITY_MAP = [
+ self::PRIORITY_HIGHEST => 'Highest',
+ self::PRIORITY_HIGH => 'High',
+ self::PRIORITY_NORMAL => 'Normal',
+ self::PRIORITY_LOW => 'Low',
+ self::PRIORITY_LOWEST => 'Lowest',
+ ];
+
+ private $text;
+ private $textCharset;
+ private $html;
+ private $htmlCharset;
+ private $attachments = [];
+
+ /**
+ * @return $this
+ */
+ public function subject(string $subject)
+ {
+ return $this->setHeaderBody('Text', 'Subject', $subject);
+ }
+
+ public function getSubject(): ?string
+ {
+ return $this->getHeaders()->getHeaderBody('Subject');
+ }
+
+ /**
+ * @return $this
+ */
+ public function date(\DateTimeInterface $dateTime)
+ {
+ return $this->setHeaderBody('Date', 'Date', $dateTime);
+ }
+
+ public function getDate(): ?\DateTimeImmutable
+ {
+ return $this->getHeaders()->getHeaderBody('Date');
+ }
+
+ /**
+ * @param Address|string $address
+ *
+ * @return $this
+ */
+ public function returnPath($address)
+ {
+ return $this->setHeaderBody('Path', 'Return-Path', Address::create($address));
+ }
+
+ public function getReturnPath(): ?Address
+ {
+ return $this->getHeaders()->getHeaderBody('Return-Path');
+ }
+
+ /**
+ * @param Address|string $address
+ *
+ * @return $this
+ */
+ public function sender($address)
+ {
+ return $this->setHeaderBody('Mailbox', 'Sender', Address::create($address));
+ }
+
+ public function getSender(): ?Address
+ {
+ return $this->getHeaders()->getHeaderBody('Sender');
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function addFrom(...$addresses)
+ {
+ return $this->addListAddressHeaderBody('From', $addresses);
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function from(...$addresses)
+ {
+ return $this->setListAddressHeaderBody('From', $addresses);
+ }
+
+ /**
+ * @return Address[]
+ */
+ public function getFrom(): array
+ {
+ return $this->getHeaders()->getHeaderBody('From') ?: [];
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function addReplyTo(...$addresses)
+ {
+ return $this->addListAddressHeaderBody('Reply-To', $addresses);
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function replyTo(...$addresses)
+ {
+ return $this->setListAddressHeaderBody('Reply-To', $addresses);
+ }
+
+ /**
+ * @return Address[]
+ */
+ public function getReplyTo(): array
+ {
+ return $this->getHeaders()->getHeaderBody('Reply-To') ?: [];
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function addTo(...$addresses)
+ {
+ return $this->addListAddressHeaderBody('To', $addresses);
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function to(...$addresses)
+ {
+ return $this->setListAddressHeaderBody('To', $addresses);
+ }
+
+ /**
+ * @return Address[]
+ */
+ public function getTo(): array
+ {
+ return $this->getHeaders()->getHeaderBody('To') ?: [];
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function addCc(...$addresses)
+ {
+ return $this->addListAddressHeaderBody('Cc', $addresses);
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function cc(...$addresses)
+ {
+ return $this->setListAddressHeaderBody('Cc', $addresses);
+ }
+
+ /**
+ * @return Address[]
+ */
+ public function getCc(): array
+ {
+ return $this->getHeaders()->getHeaderBody('Cc') ?: [];
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function addBcc(...$addresses)
+ {
+ return $this->addListAddressHeaderBody('Bcc', $addresses);
+ }
+
+ /**
+ * @param Address|string ...$addresses
+ *
+ * @return $this
+ */
+ public function bcc(...$addresses)
+ {
+ return $this->setListAddressHeaderBody('Bcc', $addresses);
+ }
+
+ /**
+ * @return Address[]
+ */
+ public function getBcc(): array
+ {
+ return $this->getHeaders()->getHeaderBody('Bcc') ?: [];
+ }
+
+ /**
+ * Sets the priority of this message.
+ *
+ * The value is an integer where 1 is the highest priority and 5 is the lowest.
+ *
+ * @return $this
+ */
+ public function priority(int $priority)
+ {
+ if ($priority > 5) {
+ $priority = 5;
+ } elseif ($priority < 1) {
+ $priority = 1;
+ }
+
+ return $this->setHeaderBody('Text', 'X-Priority', sprintf('%d (%s)', $priority, self::PRIORITY_MAP[$priority]));
+ }
+
+ /**
+ * Get the priority of this message.
+ *
+ * The returned value is an integer where 1 is the highest priority and 5
+ * is the lowest.
+ */
+ public function getPriority(): int
+ {
+ list($priority) = sscanf($this->getHeaders()->getHeaderBody('X-Priority'), '%[1-5]');
+
+ return $priority ?? 3;
+ }
+
+ /**
+ * @param resource|string $body
+ *
+ * @return $this
+ */
+ public function text($body, string $charset = 'utf-8')
+ {
+ $this->text = $body;
+ $this->textCharset = $charset;
+
+ return $this;
+ }
+
+ /**
+ * @return resource|string|null
+ */
+ public function getTextBody()
+ {
+ return $this->text;
+ }
+
+ public function getTextCharset(): ?string
+ {
+ return $this->textCharset;
+ }
+
+ /**
+ * @param resource|string|null $body
+ *
+ * @return $this
+ */
+ public function html($body, string $charset = 'utf-8')
+ {
+ $this->html = $body;
+ $this->htmlCharset = $charset;
+
+ return $this;
+ }
+
+ /**
+ * @return resource|string|null
+ */
+ public function getHtmlBody()
+ {
+ return $this->html;
+ }
+
+ public function getHtmlCharset(): ?string
+ {
+ return $this->htmlCharset;
+ }
+
+ /**
+ * @param resource|string $body
+ *
+ * @return $this
+ */
+ public function attach($body, string $name = null, string $contentType = null)
+ {
+ $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => false];
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function attachFromPath(string $path, string $name = null, string $contentType = null)
+ {
+ $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => false];
+
+ return $this;
+ }
+
+ /**
+ * @param resource|string $body
+ *
+ * @return $this
+ */
+ public function embed($body, string $name = null, string $contentType = null)
+ {
+ $this->attachments[] = ['body' => $body, 'name' => $name, 'content-type' => $contentType, 'inline' => true];
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function embedFromPath(string $path, string $name = null, string $contentType = null)
+ {
+ $this->attachments[] = ['path' => $path, 'name' => $name, 'content-type' => $contentType, 'inline' => true];
+
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function attachPart(DataPart $part)
+ {
+ $this->attachments[] = ['part' => $part];
+
+ return $this;
+ }
+
+ /**
+ * @return DataPart[]
+ */
+ public function getAttachments(): array
+ {
+ $parts = [];
+ foreach ($this->attachments as $attachment) {
+ $parts[] = $this->createDataPart($attachment);
+ }
+
+ return $parts;
+ }
+
+ public function getBody(): AbstractPart
+ {
+ if (null !== $body = parent::getBody()) {
+ return $body;
+ }
+
+ return $this->generateBody();
+ }
+
+ public function ensureValidity()
+ {
+ if (null === $this->text && null === $this->html && !$this->attachments) {
+ throw new LogicException('A message must have a text or an HTML part or attachments.');
+ }
+
+ parent::ensureValidity();
+ }
+
+ /**
+ * Generates an AbstractPart based on the raw body of a message.
+ *
+ * The most "complex" part generated by this method is when there is text and HTML bodies
+ * with related images for the HTML part and some attachments:
+ *
+ * multipart/mixed
+ * |
+ * |------------> multipart/related
+ * | |
+ * | |------------> multipart/alternative
+ * | | |
+ * | | ------------> text/plain (with content)
+ * | | |
+ * | | ------------> text/html (with content)
+ * | |
+ * | ------------> image/png (with content)
+ * |
+ * ------------> application/pdf (with content)
+ */
+ private function generateBody(): AbstractPart
+ {
+ $this->ensureValidity();
+
+ [$htmlPart, $attachmentParts, $inlineParts] = $this->prepareParts();
+
+ $part = null === $this->text ? null : new TextPart($this->text, $this->textCharset);
+ if (null !== $htmlPart) {
+ if (null !== $part) {
+ $part = new AlternativePart($part, $htmlPart);
+ } else {
+ $part = $htmlPart;
+ }
+ }
+
+ if ($inlineParts) {
+ $part = new RelatedPart($part, ...$inlineParts);
+ }
+
+ if ($attachmentParts) {
+ if ($part) {
+ $part = new MixedPart($part, ...$attachmentParts);
+ } else {
+ $part = new MixedPart(...$attachmentParts);
+ }
+ }
+
+ return $part;
+ }
+
+ private function prepareParts(): ?array
+ {
+ $names = [];
+ $htmlPart = null;
+ $html = $this->html;
+ if (null !== $this->html) {
+ if (\is_resource($html)) {
+ if (stream_get_meta_data($html)['seekable'] ?? false) {
+ rewind($html);
+ }
+
+ $html = stream_get_contents($html);
+ }
+ $htmlPart = new TextPart($html, $this->htmlCharset, 'html');
+ preg_match_all('(]*src\s*=\s*(?:([\'"])cid:([^"]+)\\1|cid:([^>\s]+)))i', $html, $names);
+ $names = array_filter(array_unique(array_merge($names[2], $names[3])));
+ }
+
+ $attachmentParts = $inlineParts = [];
+ foreach ($this->attachments as $attachment) {
+ foreach ($names as $name) {
+ if (isset($attachment['part'])) {
+ continue;
+ }
+ if ($name !== $attachment['name']) {
+ continue;
+ }
+ if (isset($inlineParts[$name])) {
+ continue 2;
+ }
+ $attachment['inline'] = true;
+ $inlineParts[$name] = $part = $this->createDataPart($attachment);
+ $html = str_replace('cid:'.$name, 'cid:'.$part->getContentId(), $html);
+ continue 2;
+ }
+ $attachmentParts[] = $this->createDataPart($attachment);
+ }
+ if (null !== $htmlPart) {
+ $htmlPart = new TextPart($html, $this->htmlCharset, 'html');
+ }
+
+ return [$htmlPart, $attachmentParts, array_values($inlineParts)];
+ }
+
+ private function createDataPart(array $attachment): DataPart
+ {
+ if (isset($attachment['part'])) {
+ return $attachment['part'];
+ }
+
+ if (isset($attachment['body'])) {
+ $part = new DataPart($attachment['body'], $attachment['name'] ?? null, $attachment['content-type'] ?? null);
+ } else {
+ $part = DataPart::fromPath($attachment['path'] ?? '', $attachment['name'] ?? null, $attachment['content-type'] ?? null);
+ }
+ if ($attachment['inline']) {
+ $part->asInline();
+ }
+
+ return $part;
+ }
+
+ /**
+ * @return $this
+ */
+ private function setHeaderBody(string $type, string $name, $body): object
+ {
+ $this->getHeaders()->setHeaderBody($type, $name, $body);
+
+ return $this;
+ }
+
+ private function addListAddressHeaderBody(string $name, array $addresses)
+ {
+ if (!$header = $this->getHeaders()->get($name)) {
+ return $this->setListAddressHeaderBody($name, $addresses);
+ }
+ $header->addAddresses(Address::createArray($addresses));
+
+ return $this;
+ }
+
+ private function setListAddressHeaderBody(string $name, array $addresses)
+ {
+ $addresses = Address::createArray($addresses);
+ $headers = $this->getHeaders();
+ if ($header = $headers->get($name)) {
+ $header->setAddresses($addresses);
+ } else {
+ $headers->addMailboxListHeader($name, $addresses);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @internal
+ */
+ public function __serialize(): array
+ {
+ if (\is_resource($this->text)) {
+ if (stream_get_meta_data($this->text)['seekable'] ?? false) {
+ rewind($this->text);
+ }
+
+ $this->text = stream_get_contents($this->text);
+ }
+
+ if (\is_resource($this->html)) {
+ if (stream_get_meta_data($this->html)['seekable'] ?? false) {
+ rewind($this->html);
+ }
+
+ $this->html = stream_get_contents($this->html);
+ }
+
+ foreach ($this->attachments as $i => $attachment) {
+ if (isset($attachment['body']) && \is_resource($attachment['body'])) {
+ if (stream_get_meta_data($attachment['body'])['seekable'] ?? false) {
+ rewind($attachment['body']);
+ }
+
+ $this->attachments[$i]['body'] = stream_get_contents($attachment['body']);
+ }
+ }
+
+ return [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, parent::__serialize()];
+ }
+
+ /**
+ * @internal
+ */
+ public function __unserialize(array $data): void
+ {
+ [$this->text, $this->textCharset, $this->html, $this->htmlCharset, $this->attachments, $parentData] = $data;
+
+ parent::__unserialize($parentData);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/AddressEncoderInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/AddressEncoderInterface.php
new file mode 100644
index 00000000..de477d88
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/AddressEncoderInterface.php
@@ -0,0 +1,28 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+use Symfony\Component\Mime\Exception\AddressEncoderException;
+
+/**
+ * @author Christian Schmidt
+ */
+interface AddressEncoderInterface
+{
+ /**
+ * Encodes an email address.
+ *
+ * @throws AddressEncoderException if the email cannot be represented in
+ * the encoding implemented by this class
+ */
+ public function encodeString(string $address): string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64ContentEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64ContentEncoder.php
new file mode 100644
index 00000000..338490b3
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64ContentEncoder.php
@@ -0,0 +1,48 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+use Symfony\Component\Mime\Exception\RuntimeException;
+
+/**
+ * @author Fabien Potencier
+ */
+final class Base64ContentEncoder extends Base64Encoder implements ContentEncoderInterface
+{
+ public function encodeByteStream($stream, int $maxLineLength = 0): iterable
+ {
+ if (!\is_resource($stream)) {
+ throw new \TypeError(sprintf('Method "%s" takes a stream as a first argument.', __METHOD__));
+ }
+
+ $filter = stream_filter_append($stream, 'convert.base64-encode', \STREAM_FILTER_READ, [
+ 'line-length' => 0 >= $maxLineLength || 76 < $maxLineLength ? 76 : $maxLineLength,
+ 'line-break-chars' => "\r\n",
+ ]);
+ if (!\is_resource($filter)) {
+ throw new RuntimeException('Unable to set the base64 content encoder to the filter.');
+ }
+
+ if (stream_get_meta_data($stream)['seekable'] ?? false) {
+ rewind($stream);
+ }
+ while (!feof($stream)) {
+ yield fread($stream, 8192);
+ }
+ stream_filter_remove($filter);
+ }
+
+ public function getName(): string
+ {
+ return 'base64';
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64Encoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64Encoder.php
new file mode 100644
index 00000000..71064785
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64Encoder.php
@@ -0,0 +1,41 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+class Base64Encoder implements EncoderInterface
+{
+ /**
+ * Takes an unencoded string and produces a Base64 encoded string from it.
+ *
+ * Base64 encoded strings have a maximum line length of 76 characters.
+ * If the first line needs to be shorter, indicate the difference with
+ * $firstLineOffset.
+ */
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ if (0 >= $maxLineLength || 76 < $maxLineLength) {
+ $maxLineLength = 76;
+ }
+
+ $encodedString = base64_encode($string);
+ $firstLine = '';
+ if (0 !== $firstLineOffset) {
+ $firstLine = substr($encodedString, 0, $maxLineLength - $firstLineOffset)."\r\n";
+ $encodedString = substr($encodedString, $maxLineLength - $firstLineOffset);
+ }
+
+ return $firstLine.trim(chunk_split($encodedString, $maxLineLength, "\r\n"));
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64MimeHeaderEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64MimeHeaderEncoder.php
new file mode 100644
index 00000000..5c06f6d9
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Base64MimeHeaderEncoder.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+final class Base64MimeHeaderEncoder extends Base64Encoder implements MimeHeaderEncoderInterface
+{
+ public function getName(): string
+ {
+ return 'B';
+ }
+
+ /**
+ * Takes an unencoded string and produces a Base64 encoded string from it.
+ *
+ * If the charset is iso-2022-jp, it uses mb_encode_mimeheader instead of
+ * default encodeString, otherwise pass to the parent method.
+ */
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ if ('iso-2022-jp' === strtolower($charset)) {
+ $old = mb_internal_encoding();
+ mb_internal_encoding('utf-8');
+ $newstring = mb_encode_mimeheader($string, 'iso-2022-jp', $this->getName(), "\r\n");
+ mb_internal_encoding($old);
+
+ return $newstring;
+ }
+
+ return parent::encodeString($string, $charset, $firstLineOffset, $maxLineLength);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/ContentEncoderInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/ContentEncoderInterface.php
new file mode 100644
index 00000000..a45ad04c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/ContentEncoderInterface.php
@@ -0,0 +1,30 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+interface ContentEncoderInterface extends EncoderInterface
+{
+ /**
+ * Encodes the stream to a Generator.
+ *
+ * @param resource $stream
+ */
+ public function encodeByteStream($stream, int $maxLineLength = 0): iterable;
+
+ /**
+ * Gets the MIME name of this content encoding scheme.
+ */
+ public function getName(): string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/EightBitContentEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/EightBitContentEncoder.php
new file mode 100644
index 00000000..82831209
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/EightBitContentEncoder.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Fabien Potencier
+ */
+final class EightBitContentEncoder implements ContentEncoderInterface
+{
+ public function encodeByteStream($stream, int $maxLineLength = 0): iterable
+ {
+ while (!feof($stream)) {
+ yield fread($stream, 16372);
+ }
+ }
+
+ public function getName(): string
+ {
+ return '8bit';
+ }
+
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ return $string;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/EncoderInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/EncoderInterface.php
new file mode 100644
index 00000000..bbf6d488
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/EncoderInterface.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+interface EncoderInterface
+{
+ /**
+ * Encode a given string to produce an encoded string.
+ *
+ * @param int $firstLineOffset if first line needs to be shorter
+ * @param int $maxLineLength - 0 indicates the default length for this encoding
+ */
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/IdnAddressEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/IdnAddressEncoder.php
new file mode 100644
index 00000000..1c5e32c0
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/IdnAddressEncoder.php
@@ -0,0 +1,54 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+use Symfony\Component\Mime\Exception\AddressEncoderException;
+
+/**
+ * An IDN email address encoder.
+ *
+ * Encodes the domain part of an address using IDN. This is compatible will all
+ * SMTP servers.
+ *
+ * This encoder does not support email addresses with non-ASCII characters in
+ * local-part (the substring before @). To send to such addresses, use
+ * Utf8AddressEncoder together with SmtpUtf8Handler. Your outbound SMTP server must support
+ * the SMTPUTF8 extension.
+ *
+ * @author Christian Schmidt
+ */
+final class IdnAddressEncoder implements AddressEncoderInterface
+{
+ /**
+ * Encodes the domain part of an address using IDN.
+ *
+ * @throws AddressEncoderException If local-part contains non-ASCII characters
+ */
+ public function encodeString(string $address): string
+ {
+ $i = strrpos($address, '@');
+ if (false !== $i) {
+ $local = substr($address, 0, $i);
+ $domain = substr($address, $i + 1);
+
+ if (preg_match('/[^\x00-\x7F]/', $local)) {
+ throw new AddressEncoderException(sprintf('Non-ASCII characters not supported in local-part os "%s".', $address));
+ }
+
+ if (preg_match('/[^\x00-\x7F]/', $domain)) {
+ $address = sprintf('%s@%s', $local, idn_to_ascii($domain, 0, INTL_IDNA_VARIANT_UTS46));
+ }
+ }
+
+ return $address;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/MimeHeaderEncoderInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/MimeHeaderEncoderInterface.php
new file mode 100644
index 00000000..fff2c782
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/MimeHeaderEncoderInterface.php
@@ -0,0 +1,23 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+interface MimeHeaderEncoderInterface
+{
+ /**
+ * Get the MIME name of this content encoding scheme.
+ */
+ public function getName(): string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpContentEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpContentEncoder.php
new file mode 100644
index 00000000..e0b8605d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpContentEncoder.php
@@ -0,0 +1,64 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Lars Strojny
+ */
+final class QpContentEncoder implements ContentEncoderInterface
+{
+ public function encodeByteStream($stream, int $maxLineLength = 0): iterable
+ {
+ if (!\is_resource($stream)) {
+ throw new \TypeError(sprintf('Method "%s" takes a stream as a first argument.', __METHOD__));
+ }
+
+ // we don't use PHP stream filters here as the content should be small enough
+ if (stream_get_meta_data($stream)['seekable'] ?? false) {
+ rewind($stream);
+ }
+
+ yield $this->encodeString(stream_get_contents($stream), 'utf-8', 0, $maxLineLength);
+ }
+
+ public function getName(): string
+ {
+ return 'quoted-printable';
+ }
+
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ return $this->standardize(quoted_printable_encode($string));
+ }
+
+ /**
+ * Make sure CRLF is correct and HT/SPACE are in valid places.
+ */
+ private function standardize(string $string): string
+ {
+ // transform CR or LF to CRLF
+ $string = preg_replace('~=0D(?!=0A)|(?
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+use Symfony\Component\Mime\CharacterStream;
+
+/**
+ * @author Chris Corbyn
+ */
+class QpEncoder implements EncoderInterface
+{
+ /**
+ * Pre-computed QP for HUGE optimization.
+ */
+ private static $qpMap = [
+ 0 => '=00', 1 => '=01', 2 => '=02', 3 => '=03', 4 => '=04',
+ 5 => '=05', 6 => '=06', 7 => '=07', 8 => '=08', 9 => '=09',
+ 10 => '=0A', 11 => '=0B', 12 => '=0C', 13 => '=0D', 14 => '=0E',
+ 15 => '=0F', 16 => '=10', 17 => '=11', 18 => '=12', 19 => '=13',
+ 20 => '=14', 21 => '=15', 22 => '=16', 23 => '=17', 24 => '=18',
+ 25 => '=19', 26 => '=1A', 27 => '=1B', 28 => '=1C', 29 => '=1D',
+ 30 => '=1E', 31 => '=1F', 32 => '=20', 33 => '=21', 34 => '=22',
+ 35 => '=23', 36 => '=24', 37 => '=25', 38 => '=26', 39 => '=27',
+ 40 => '=28', 41 => '=29', 42 => '=2A', 43 => '=2B', 44 => '=2C',
+ 45 => '=2D', 46 => '=2E', 47 => '=2F', 48 => '=30', 49 => '=31',
+ 50 => '=32', 51 => '=33', 52 => '=34', 53 => '=35', 54 => '=36',
+ 55 => '=37', 56 => '=38', 57 => '=39', 58 => '=3A', 59 => '=3B',
+ 60 => '=3C', 61 => '=3D', 62 => '=3E', 63 => '=3F', 64 => '=40',
+ 65 => '=41', 66 => '=42', 67 => '=43', 68 => '=44', 69 => '=45',
+ 70 => '=46', 71 => '=47', 72 => '=48', 73 => '=49', 74 => '=4A',
+ 75 => '=4B', 76 => '=4C', 77 => '=4D', 78 => '=4E', 79 => '=4F',
+ 80 => '=50', 81 => '=51', 82 => '=52', 83 => '=53', 84 => '=54',
+ 85 => '=55', 86 => '=56', 87 => '=57', 88 => '=58', 89 => '=59',
+ 90 => '=5A', 91 => '=5B', 92 => '=5C', 93 => '=5D', 94 => '=5E',
+ 95 => '=5F', 96 => '=60', 97 => '=61', 98 => '=62', 99 => '=63',
+ 100 => '=64', 101 => '=65', 102 => '=66', 103 => '=67', 104 => '=68',
+ 105 => '=69', 106 => '=6A', 107 => '=6B', 108 => '=6C', 109 => '=6D',
+ 110 => '=6E', 111 => '=6F', 112 => '=70', 113 => '=71', 114 => '=72',
+ 115 => '=73', 116 => '=74', 117 => '=75', 118 => '=76', 119 => '=77',
+ 120 => '=78', 121 => '=79', 122 => '=7A', 123 => '=7B', 124 => '=7C',
+ 125 => '=7D', 126 => '=7E', 127 => '=7F', 128 => '=80', 129 => '=81',
+ 130 => '=82', 131 => '=83', 132 => '=84', 133 => '=85', 134 => '=86',
+ 135 => '=87', 136 => '=88', 137 => '=89', 138 => '=8A', 139 => '=8B',
+ 140 => '=8C', 141 => '=8D', 142 => '=8E', 143 => '=8F', 144 => '=90',
+ 145 => '=91', 146 => '=92', 147 => '=93', 148 => '=94', 149 => '=95',
+ 150 => '=96', 151 => '=97', 152 => '=98', 153 => '=99', 154 => '=9A',
+ 155 => '=9B', 156 => '=9C', 157 => '=9D', 158 => '=9E', 159 => '=9F',
+ 160 => '=A0', 161 => '=A1', 162 => '=A2', 163 => '=A3', 164 => '=A4',
+ 165 => '=A5', 166 => '=A6', 167 => '=A7', 168 => '=A8', 169 => '=A9',
+ 170 => '=AA', 171 => '=AB', 172 => '=AC', 173 => '=AD', 174 => '=AE',
+ 175 => '=AF', 176 => '=B0', 177 => '=B1', 178 => '=B2', 179 => '=B3',
+ 180 => '=B4', 181 => '=B5', 182 => '=B6', 183 => '=B7', 184 => '=B8',
+ 185 => '=B9', 186 => '=BA', 187 => '=BB', 188 => '=BC', 189 => '=BD',
+ 190 => '=BE', 191 => '=BF', 192 => '=C0', 193 => '=C1', 194 => '=C2',
+ 195 => '=C3', 196 => '=C4', 197 => '=C5', 198 => '=C6', 199 => '=C7',
+ 200 => '=C8', 201 => '=C9', 202 => '=CA', 203 => '=CB', 204 => '=CC',
+ 205 => '=CD', 206 => '=CE', 207 => '=CF', 208 => '=D0', 209 => '=D1',
+ 210 => '=D2', 211 => '=D3', 212 => '=D4', 213 => '=D5', 214 => '=D6',
+ 215 => '=D7', 216 => '=D8', 217 => '=D9', 218 => '=DA', 219 => '=DB',
+ 220 => '=DC', 221 => '=DD', 222 => '=DE', 223 => '=DF', 224 => '=E0',
+ 225 => '=E1', 226 => '=E2', 227 => '=E3', 228 => '=E4', 229 => '=E5',
+ 230 => '=E6', 231 => '=E7', 232 => '=E8', 233 => '=E9', 234 => '=EA',
+ 235 => '=EB', 236 => '=EC', 237 => '=ED', 238 => '=EE', 239 => '=EF',
+ 240 => '=F0', 241 => '=F1', 242 => '=F2', 243 => '=F3', 244 => '=F4',
+ 245 => '=F5', 246 => '=F6', 247 => '=F7', 248 => '=F8', 249 => '=F9',
+ 250 => '=FA', 251 => '=FB', 252 => '=FC', 253 => '=FD', 254 => '=FE',
+ 255 => '=FF',
+ ];
+
+ private static $safeMapShare = [];
+
+ /**
+ * A map of non-encoded ascii characters.
+ *
+ * @var string[]
+ *
+ * @internal
+ */
+ protected $safeMap = [];
+
+ public function __construct()
+ {
+ $id = \get_class($this);
+ if (!isset(self::$safeMapShare[$id])) {
+ $this->initSafeMap();
+ self::$safeMapShare[$id] = $this->safeMap;
+ } else {
+ $this->safeMap = self::$safeMapShare[$id];
+ }
+ }
+
+ protected function initSafeMap(): void
+ {
+ foreach (array_merge([0x09, 0x20], range(0x21, 0x3C), range(0x3E, 0x7E)) as $byte) {
+ $this->safeMap[$byte] = \chr($byte);
+ }
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * Takes an unencoded string and produces a QP encoded string from it.
+ *
+ * QP encoded strings have a maximum line length of 76 characters.
+ * If the first line needs to be shorter, indicate the difference with
+ * $firstLineOffset.
+ */
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ if ($maxLineLength > 76 || $maxLineLength <= 0) {
+ $maxLineLength = 76;
+ }
+
+ $thisLineLength = $maxLineLength - $firstLineOffset;
+
+ $lines = [];
+ $lNo = 0;
+ $lines[$lNo] = '';
+ $currentLine = &$lines[$lNo++];
+ $size = $lineLen = 0;
+ $charStream = new CharacterStream($string, $charset);
+
+ // Fetching more than 4 chars at one is slower, as is fetching fewer bytes
+ // Conveniently 4 chars is the UTF-8 safe number since UTF-8 has up to 6
+ // bytes per char and (6 * 4 * 3 = 72 chars per line) * =NN is 3 bytes
+ while (null !== $bytes = $charStream->readBytes(4)) {
+ $enc = $this->encodeByteSequence($bytes, $size);
+
+ $i = strpos($enc, '=0D=0A');
+ $newLineLength = $lineLen + (false === $i ? $size : $i);
+
+ if ($currentLine && $newLineLength >= $thisLineLength) {
+ $lines[$lNo] = '';
+ $currentLine = &$lines[$lNo++];
+ $thisLineLength = $maxLineLength;
+ $lineLen = 0;
+ }
+
+ $currentLine .= $enc;
+
+ if (false === $i) {
+ $lineLen += $size;
+ } else {
+ // 6 is the length of '=0D=0A'.
+ $lineLen = $size - strrpos($enc, '=0D=0A') - 6;
+ }
+ }
+
+ return $this->standardize(implode("=\r\n", $lines));
+ }
+
+ /**
+ * Encode the given byte array into a verbatim QP form.
+ */
+ private function encodeByteSequence(array $bytes, int &$size): string
+ {
+ $ret = '';
+ $size = 0;
+ foreach ($bytes as $b) {
+ if (isset($this->safeMap[$b])) {
+ $ret .= $this->safeMap[$b];
+ ++$size;
+ } else {
+ $ret .= self::$qpMap[$b];
+ $size += 3;
+ }
+ }
+
+ return $ret;
+ }
+
+ /**
+ * Make sure CRLF is correct and HT/SPACE are in valid places.
+ */
+ private function standardize(string $string): string
+ {
+ $string = str_replace(["\t=0D=0A", ' =0D=0A', '=0D=0A'], ["=09\r\n", "=20\r\n", "\r\n"], $string);
+ switch ($end = \ord(substr($string, -1))) {
+ case 0x09:
+ case 0x20:
+ $string = substr_replace($string, self::$qpMap[$end], -1);
+ }
+
+ return $string;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpMimeHeaderEncoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpMimeHeaderEncoder.php
new file mode 100644
index 00000000..d1d38375
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/QpMimeHeaderEncoder.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+/**
+ * @author Chris Corbyn
+ */
+final class QpMimeHeaderEncoder extends QpEncoder implements MimeHeaderEncoderInterface
+{
+ protected function initSafeMap(): void
+ {
+ foreach (array_merge(
+ range(0x61, 0x7A), range(0x41, 0x5A),
+ range(0x30, 0x39), [0x20, 0x21, 0x2A, 0x2B, 0x2D, 0x2F]
+ ) as $byte) {
+ $this->safeMap[$byte] = \chr($byte);
+ }
+ }
+
+ public function getName(): string
+ {
+ return 'Q';
+ }
+
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ return str_replace([' ', '=20', "=\r\n"], ['_', '_', "\r\n"],
+ parent::encodeString($string, $charset, $firstLineOffset, $maxLineLength)
+ );
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Encoder/Rfc2231Encoder.php b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Rfc2231Encoder.php
new file mode 100644
index 00000000..aa3e062f
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Encoder/Rfc2231Encoder.php
@@ -0,0 +1,50 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Encoder;
+
+use Symfony\Component\Mime\CharacterStream;
+
+/**
+ * @author Chris Corbyn
+ */
+final class Rfc2231Encoder implements EncoderInterface
+{
+ /**
+ * Takes an unencoded string and produces a string encoded according to RFC 2231 from it.
+ */
+ public function encodeString(string $string, ?string $charset = 'utf-8', int $firstLineOffset = 0, int $maxLineLength = 0): string
+ {
+ $lines = [];
+ $lineCount = 0;
+ $lines[] = '';
+ $currentLine = &$lines[$lineCount++];
+
+ if (0 >= $maxLineLength) {
+ $maxLineLength = 75;
+ }
+
+ $charStream = new CharacterStream($string, $charset);
+ $thisLineLength = $maxLineLength - $firstLineOffset;
+
+ while (null !== $char = $charStream->read(4)) {
+ $encodedChar = rawurlencode($char);
+ if (0 !== \strlen($currentLine) && \strlen($currentLine.$encodedChar) > $thisLineLength) {
+ $lines[] = '';
+ $currentLine = &$lines[$lineCount++];
+ $thisLineLength = $maxLineLength;
+ }
+ $currentLine .= $encodedChar;
+ }
+
+ return implode("\r\n", $lines);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/AddressEncoderException.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/AddressEncoderException.php
new file mode 100644
index 00000000..51ee2e06
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/AddressEncoderException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+class AddressEncoderException extends RfcComplianceException
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/ExceptionInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/ExceptionInterface.php
new file mode 100644
index 00000000..11933900
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/ExceptionInterface.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+interface ExceptionInterface extends \Throwable
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/InvalidArgumentException.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/InvalidArgumentException.php
new file mode 100644
index 00000000..e89ebae2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/InvalidArgumentException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+class InvalidArgumentException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/LogicException.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/LogicException.php
new file mode 100644
index 00000000..0508ee73
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/LogicException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+class LogicException extends \LogicException implements ExceptionInterface
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/RfcComplianceException.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/RfcComplianceException.php
new file mode 100644
index 00000000..26e4a509
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/RfcComplianceException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+class RfcComplianceException extends \InvalidArgumentException implements ExceptionInterface
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Exception/RuntimeException.php b/lam/lib/3rdParty/composer/symfony/mime/Exception/RuntimeException.php
new file mode 100644
index 00000000..fb018b00
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Exception/RuntimeException.php
@@ -0,0 +1,19 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Exception;
+
+/**
+ * @author Fabien Potencier
+ */
+class RuntimeException extends \RuntimeException implements ExceptionInterface
+{
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/FileBinaryMimeTypeGuesser.php b/lam/lib/3rdParty/composer/symfony/mime/FileBinaryMimeTypeGuesser.php
new file mode 100644
index 00000000..fe1e0cde
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/FileBinaryMimeTypeGuesser.php
@@ -0,0 +1,93 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Exception\LogicException;
+
+/**
+ * Guesses the MIME type with the binary "file" (only available on *nix).
+ *
+ * @author Bernhard Schussek
+ */
+class FileBinaryMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $cmd;
+
+ /**
+ * The $cmd pattern must contain a "%s" string that will be replaced
+ * with the file name to guess.
+ *
+ * The command output must start with the MIME type of the file.
+ *
+ * @param string $cmd The command to run to get the MIME type of a file
+ */
+ public function __construct(string $cmd = 'file -b --mime -- %s 2>/dev/null')
+ {
+ $this->cmd = $cmd;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isGuesserSupported(): bool
+ {
+ static $supported = null;
+
+ if (null !== $supported) {
+ return $supported;
+ }
+
+ if ('\\' === \DIRECTORY_SEPARATOR || !\function_exists('passthru') || !\function_exists('escapeshellarg')) {
+ return $supported = false;
+ }
+
+ ob_start();
+ passthru('command -v file', $exitStatus);
+ $binPath = trim(ob_get_clean());
+
+ return $supported = 0 === $exitStatus && '' !== $binPath;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guessMimeType(string $path): ?string
+ {
+ if (!is_file($path) || !is_readable($path)) {
+ throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path));
+ }
+
+ if (!$this->isGuesserSupported()) {
+ throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__));
+ }
+
+ ob_start();
+
+ // need to use --mime instead of -i. see #6641
+ passthru(sprintf($this->cmd, escapeshellarg((0 === strpos($path, '-') ? './' : '').$path)), $return);
+ if ($return > 0) {
+ ob_end_clean();
+
+ return null;
+ }
+
+ $type = trim(ob_get_clean());
+
+ if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-\+\.]+)#i', $type, $match)) {
+ // it's not a type, but an error message
+ return null;
+ }
+
+ return $match[1];
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/FileinfoMimeTypeGuesser.php b/lam/lib/3rdParty/composer/symfony/mime/FileinfoMimeTypeGuesser.php
new file mode 100644
index 00000000..b91a4ffe
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/FileinfoMimeTypeGuesser.php
@@ -0,0 +1,63 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Exception\LogicException;
+
+/**
+ * Guesses the MIME type using the PECL extension FileInfo.
+ *
+ * @author Bernhard Schussek
+ */
+class FileinfoMimeTypeGuesser implements MimeTypeGuesserInterface
+{
+ private $magicFile;
+
+ /**
+ * @param string $magicFile A magic file to use with the finfo instance
+ *
+ * @see http://www.php.net/manual/en/function.finfo-open.php
+ */
+ public function __construct(string $magicFile = null)
+ {
+ $this->magicFile = $magicFile;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isGuesserSupported(): bool
+ {
+ return \function_exists('finfo_open');
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function guessMimeType(string $path): ?string
+ {
+ if (!is_file($path) || !is_readable($path)) {
+ throw new InvalidArgumentException(sprintf('The "%s" file does not exist or is not readable.', $path));
+ }
+
+ if (!$this->isGuesserSupported()) {
+ throw new LogicException(sprintf('The "%s" guesser is not supported.', __CLASS__));
+ }
+
+ if (false === $finfo = new \finfo(FILEINFO_MIME_TYPE, $this->magicFile)) {
+ return null;
+ }
+
+ return $finfo->file($path);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/AbstractHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/AbstractHeader.php
new file mode 100644
index 00000000..548c1926
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/AbstractHeader.php
@@ -0,0 +1,279 @@
+
+ *
+ * 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
+{
+ 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 $encoder;
+
+ private $name;
+ private $lineLength = 76;
+ private $lang;
+ private $charset = 'utf-8';
+
+ public function __construct(string $name)
+ {
+ $this->name = $name;
+ }
+
+ 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'.
+ */
+ public function setLanguage(string $lang)
+ {
+ $this->lang = $lang;
+ }
+
+ public function getLanguage(): ?string
+ {
+ return $this->lang;
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ 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);
+ }
+ }
+
+ 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 (\strlen($encodedToken) > 0) {
+ $tokens[] = $encodedToken;
+ $encodedToken = '';
+ }
+ $tokens[] = $token;
+ }
+ }
+ if (\strlen($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
+ {
+ if (null === self::$encoder) {
+ 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
+ {
+ if (null === $string) {
+ $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)
+ && 0 < \strlen($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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/DateHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/DateHeader.php
new file mode 100644
index 00000000..a7385d4c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/DateHeader.php
@@ -0,0 +1,66 @@
+
+ *
+ * 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 $dateTime;
+
+ public function __construct(string $name, \DateTimeInterface $date)
+ {
+ parent::__construct($name);
+
+ $this->setDateTime($date);
+ }
+
+ /**
+ * @param \DateTimeInterface $body
+ */
+ public function setBody($body)
+ {
+ $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)
+ {
+ if ($dateTime instanceof \DateTime) {
+ $immutable = new \DateTimeImmutable('@'.$dateTime->getTimestamp());
+ $dateTime = $immutable->setTimezone($dateTime->getTimezone());
+ }
+ $this->dateTime = $dateTime;
+ }
+
+ public function getBodyAsString(): string
+ {
+ return $this->dateTime->format(\DateTime::RFC2822);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/HeaderInterface.php b/lam/lib/3rdParty/composer/symfony/mime/Header/HeaderInterface.php
new file mode 100644
index 00000000..4546947c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/HeaderInterface.php
@@ -0,0 +1,65 @@
+
+ *
+ * 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.
+ *
+ * @param mixed $body
+ */
+ public function setBody($body);
+
+ /**
+ * Gets the body.
+ *
+ * The return type depends on the Header concrete class.
+ *
+ * @return mixed
+ */
+ public function getBody();
+
+ public function setCharset(string $charset);
+
+ public function getCharset(): ?string;
+
+ public function setLanguage(string $lang);
+
+ public function getLanguage(): ?string;
+
+ public function getName(): string;
+
+ 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;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/Headers.php b/lam/lib/3rdParty/composer/symfony/mime/Header/Headers.php
new file mode 100644
index 00000000..9de506e3
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/Headers.php
@@ -0,0 +1,282 @@
+
+ *
+ * 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
+ */
+final class Headers
+{
+ private static $uniqueHeaders = [
+ 'date', 'from', 'sender', 'reply-to', 'to', 'cc', 'bcc',
+ 'message-id', 'in-reply-to', 'references', 'subject',
+ ];
+
+ private $headers = [];
+ private $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)
+ {
+ $this->lineLength = $lineLength;
+ foreach ($this->all() as $header) {
+ $header->setMaxLineLength($lineLength);
+ }
+ }
+
+ public function getMaxLineLength(): int
+ {
+ return $this->lineLength;
+ }
+
+ /**
+ * @param (Address|string)[] $addresses
+ *
+ * @return $this
+ */
+ public function addMailboxListHeader(string $name, array $addresses): self
+ {
+ return $this->add(new MailboxListHeader($name, Address::createArray($addresses)));
+ }
+
+ /**
+ * @param Address|string $address
+ *
+ * @return $this
+ */
+ public function addMailboxHeader(string $name, $address): self
+ {
+ return $this->add(new MailboxHeader($name, Address::create($address)));
+ }
+
+ /**
+ * @param string|array $ids
+ *
+ * @return $this
+ */
+ public function addIdHeader(string $name, $ids): self
+ {
+ return $this->add(new IdentificationHeader($name, $ids));
+ }
+
+ /**
+ * @param Address|string $path
+ *
+ * @return $this
+ */
+ public function addPathHeader(string $name, $path): self
+ {
+ return $this->add(new PathHeader($name, $path instanceof Address ? $path : new Address($path)));
+ }
+
+ /**
+ * @return $this
+ */
+ public function addDateHeader(string $name, \DateTimeInterface $dateTime): self
+ {
+ return $this->add(new DateHeader($name, $dateTime));
+ }
+
+ /**
+ * @return $this
+ */
+ public function addTextHeader(string $name, string $value): self
+ {
+ return $this->add(new UnstructuredHeader($name, $value));
+ }
+
+ /**
+ * @return $this
+ */
+ public function addParameterizedHeader(string $name, string $value, array $params = []): self
+ {
+ return $this->add(new ParameterizedHeader($name, $value, $params));
+ }
+
+ public function has(string $name): bool
+ {
+ return isset($this->headers[strtolower($name)]);
+ }
+
+ /**
+ * @return $this
+ */
+ public function add(HeaderInterface $header): self
+ {
+ static $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' => IdentificationHeader::class,
+ 'references' => IdentificationHeader::class,
+ 'return-path' => PathHeader::class,
+ ];
+
+ $header->setMaxLineLength($this->lineLength);
+ $name = strtolower($header->getName());
+
+ if (isset($map[$name]) && !$header instanceof $map[$name]) {
+ throw new LogicException(sprintf('The "%s" header must be an instance of "%s" (got "%s").', $header->getName(), $map[$name], \get_class($header)));
+ }
+
+ if (\in_array($name, self::$uniqueHeaders, 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($name, self::$uniqueHeaders, true);
+ }
+
+ 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;
+ }
+
+ /**
+ * @internal
+ */
+ public function getHeaderBody($name)
+ {
+ return $this->has($name) ? $this->get($name)->getBody() : null;
+ }
+
+ /**
+ * @internal
+ */
+ public function setHeaderBody(string $type, string $name, $body): void
+ {
+ if ($this->has($name)) {
+ $this->get($name)->setBody($body);
+ } else {
+ $this->{'add'.$type.'Header'}($name, $body);
+ }
+ }
+
+ /**
+ * @internal
+ */
+ 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, $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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/IdentificationHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/IdentificationHeader.php
new file mode 100644
index 00000000..8a94574e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/IdentificationHeader.php
@@ -0,0 +1,110 @@
+
+ *
+ * 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 $ids = [];
+ private $idsAsAddresses = [];
+
+ /**
+ * @param string|array $ids
+ */
+ public function __construct(string $name, $ids)
+ {
+ parent::__construct($name);
+
+ $this->setId($ids);
+ }
+
+ /**
+ * @param string|array $body a string ID or an array of IDs
+ *
+ * @throws RfcComplianceException
+ */
+ public function setBody($body)
+ {
+ $this->setId($body);
+ }
+
+ public function getBody(): array
+ {
+ return $this->getIds();
+ }
+
+ /**
+ * Set the ID used in the value of this header.
+ *
+ * @param string|array $id
+ *
+ * @throws RfcComplianceException
+ */
+ public function setId($id)
+ {
+ $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)
+ {
+ $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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxHeader.php
new file mode 100644
index 00000000..b58c8252
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxHeader.php
@@ -0,0 +1,85 @@
+
+ *
+ * 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
+ */
+final class MailboxHeader extends AbstractHeader
+{
+ private $address;
+
+ public function __construct(string $name, Address $address)
+ {
+ parent::__construct($name);
+
+ $this->setAddress($address);
+ }
+
+ /**
+ * @param Address $body
+ *
+ * @throws RfcComplianceException
+ */
+ public function setBody($body)
+ {
+ $this->setAddress($body);
+ }
+
+ /**
+ * @throws RfcComplianceException
+ */
+ public function getBody(): Address
+ {
+ return $this->getAddress();
+ }
+
+ /**
+ * @throws RfcComplianceException
+ */
+ public function setAddress(Address $address)
+ {
+ $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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxListHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxListHeader.php
new file mode 100644
index 00000000..1d00fdb1
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/MailboxListHeader.php
@@ -0,0 +1,136 @@
+
+ *
+ * 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 $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($body)
+ {
+ $this->setAddresses($body);
+ }
+
+ /**
+ * @throws RfcComplianceException
+ *
+ * @return Address[]
+ */
+ 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)
+ {
+ $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)
+ {
+ foreach ($addresses as $address) {
+ $this->addAddress($address);
+ }
+ }
+
+ /**
+ * @throws RfcComplianceException
+ */
+ public function addAddress(Address $address)
+ {
+ $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.
+ *
+ * @throws RfcComplianceException
+ *
+ * @return string[]
+ */
+ 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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/ParameterizedHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/ParameterizedHeader.php
new file mode 100644
index 00000000..d8e50011
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/ParameterizedHeader.php
@@ -0,0 +1,174 @@
+
+ *
+ * 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
+ */
+ const TOKEN_REGEX = '(?:[\x21\x23-\x27\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7E]+)';
+
+ private $encoder;
+ private $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)
+ {
+ $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)
+ {
+ $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 a 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()."'");
+ }
+ }
+
+ // 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
+ {
+ if (!preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
+ $value = '"'.$value.'"';
+ }
+ $prepend = '=';
+ if ($encoded) {
+ $prepend = '*=';
+ if ($firstLine) {
+ $prepend = '*='.$this->getCharset()."'".$this->getLanguage()."'";
+ }
+ }
+
+ return $prepend.$value;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/PathHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/PathHeader.php
new file mode 100644
index 00000000..5101ad0f
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/PathHeader.php
@@ -0,0 +1,62 @@
+
+ *
+ * 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;
+
+ public function __construct(string $name, Address $address)
+ {
+ parent::__construct($name);
+
+ $this->setAddress($address);
+ }
+
+ /**
+ * @param Address $body
+ *
+ * @throws RfcComplianceException
+ */
+ public function setBody($body)
+ {
+ $this->setAddress($body);
+ }
+
+ public function getBody(): Address
+ {
+ return $this->getAddress();
+ }
+
+ public function setAddress(Address $address)
+ {
+ $this->address = $address;
+ }
+
+ public function getAddress(): Address
+ {
+ return $this->address;
+ }
+
+ public function getBodyAsString(): string
+ {
+ return '<'.$this->address->toString().'>';
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Header/UnstructuredHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Header/UnstructuredHeader.php
new file mode 100644
index 00000000..2085ddfd
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Header/UnstructuredHeader.php
@@ -0,0 +1,69 @@
+
+ *
+ * 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 $value;
+
+ public function __construct(string $name, string $value)
+ {
+ parent::__construct($name);
+
+ $this->setValue($value);
+ }
+
+ /**
+ * @param string $body
+ */
+ public function setBody($body)
+ {
+ $this->setValue($body);
+ }
+
+ /**
+ * @return string
+ */
+ public function getBody()
+ {
+ return $this->getValue();
+ }
+
+ /**
+ * Get the (unencoded) value of this header.
+ */
+ public function getValue(): string
+ {
+ return $this->value;
+ }
+
+ /**
+ * Set the (unencoded) value of this header.
+ */
+ 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);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/LICENSE b/lam/lib/3rdParty/composer/symfony/mime/LICENSE
new file mode 100644
index 00000000..9a9a61b1
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2010-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Message.php b/lam/lib/3rdParty/composer/symfony/mime/Message.php
new file mode 100644
index 00000000..5b4e67f1
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Message.php
@@ -0,0 +1,151 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\LogicException;
+use Symfony\Component\Mime\Header\Headers;
+use Symfony\Component\Mime\Part\AbstractPart;
+use Symfony\Component\Mime\Part\TextPart;
+
+/**
+ * @author Fabien Potencier
+ */
+class Message extends RawMessage
+{
+ private $headers;
+ private $body;
+
+ public function __construct(Headers $headers = null, AbstractPart $body = null)
+ {
+ $this->headers = $headers ? clone $headers : new Headers();
+ $this->body = $body;
+ }
+
+ public function __clone()
+ {
+ $this->headers = clone $this->headers;
+
+ if (null !== $this->body) {
+ $this->body = clone $this->body;
+ }
+ }
+
+ /**
+ * @return $this
+ */
+ public function setBody(AbstractPart $body = null)
+ {
+ $this->body = $body;
+
+ return $this;
+ }
+
+ public function getBody(): ?AbstractPart
+ {
+ return $this->body;
+ }
+
+ /**
+ * @return $this
+ */
+ public function setHeaders(Headers $headers)
+ {
+ $this->headers = $headers;
+
+ return $this;
+ }
+
+ public function getHeaders(): Headers
+ {
+ return $this->headers;
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = clone $this->headers;
+
+ if (!$headers->has('From')) {
+ throw new LogicException('An email must have a "From" header.');
+ }
+
+ $headers->addTextHeader('MIME-Version', '1.0');
+
+ if (!$headers->has('Date')) {
+ $headers->addDateHeader('Date', new \DateTimeImmutable());
+ }
+
+ // determine the "real" sender
+ if (!$headers->has('Sender') && \count($froms = $headers->get('From')->getAddresses()) > 1) {
+ $headers->addMailboxHeader('Sender', $froms[0]);
+ }
+
+ if (!$headers->has('Message-ID')) {
+ $headers->addIdHeader('Message-ID', $this->generateMessageId());
+ }
+
+ // remove the Bcc field which should NOT be part of the sent message
+ $headers->remove('Bcc');
+
+ return $headers;
+ }
+
+ public function toString(): string
+ {
+ if (null === $body = $this->getBody()) {
+ $body = new TextPart('');
+ }
+
+ return $this->getPreparedHeaders()->toString().$body->toString();
+ }
+
+ public function toIterable(): iterable
+ {
+ if (null === $body = $this->getBody()) {
+ $body = new TextPart('');
+ }
+
+ yield $this->getPreparedHeaders()->toString();
+ yield from $body->toIterable();
+ }
+
+ public function ensureValidity()
+ {
+ if (!$this->headers->has('From')) {
+ throw new LogicException('An email must have a "From" header.');
+ }
+
+ parent::ensureValidity();
+ }
+
+ public function generateMessageId(): string
+ {
+ if ($this->headers->has('Sender')) {
+ $sender = $this->headers->get('Sender')->getAddress();
+ } elseif ($this->headers->has('From')) {
+ $sender = $this->headers->get('From')->getAddresses()[0];
+ } else {
+ throw new LogicException('An email must have a "From" or a "Sender" header to compute a Messsage ID.');
+ }
+
+ return bin2hex(random_bytes(16)).strstr($sender->getAddress(), '@');
+ }
+
+ public function __serialize(): array
+ {
+ return [$this->headers, $this->body];
+ }
+
+ public function __unserialize(array $data): void
+ {
+ [$this->headers, $this->body] = $data;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/MessageConverter.php b/lam/lib/3rdParty/composer/symfony/mime/MessageConverter.php
new file mode 100644
index 00000000..a810cb70
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/MessageConverter.php
@@ -0,0 +1,125 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\RuntimeException;
+use Symfony\Component\Mime\Part\DataPart;
+use Symfony\Component\Mime\Part\Multipart\AlternativePart;
+use Symfony\Component\Mime\Part\Multipart\MixedPart;
+use Symfony\Component\Mime\Part\Multipart\RelatedPart;
+use Symfony\Component\Mime\Part\TextPart;
+
+/**
+ * @author Fabien Potencier
+ */
+final class MessageConverter
+{
+ /**
+ * @throws RuntimeException when unable to convert the message to an email
+ */
+ public static function toEmail(Message $message): Email
+ {
+ if ($message instanceof Email) {
+ return $message;
+ }
+
+ // try to convert to a "simple" Email instance
+ $body = $message->getBody();
+ if ($body instanceof TextPart) {
+ return self::createEmailFromTextPart($message, $body);
+ }
+
+ if ($body instanceof AlternativePart) {
+ return self::createEmailFromAlternativePart($message, $body);
+ }
+
+ if ($body instanceof RelatedPart) {
+ return self::createEmailFromRelatedPart($message, $body);
+ }
+
+ if ($body instanceof MixedPart) {
+ $parts = $body->getParts();
+ if ($parts[0] instanceof RelatedPart) {
+ $email = self::createEmailFromRelatedPart($message, $parts[0]);
+ } elseif ($parts[0] instanceof AlternativePart) {
+ $email = self::createEmailFromAlternativePart($message, $parts[0]);
+ } elseif ($parts[0] instanceof TextPart) {
+ $email = self::createEmailFromTextPart($message, $parts[0]);
+ } else {
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message)));
+ }
+
+ return self::attachParts($email, \array_slice($parts, 1));
+ }
+
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message)));
+ }
+
+ private static function createEmailFromTextPart(Message $message, TextPart $part): Email
+ {
+ if ('text' === $part->getMediaType() && 'plain' === $part->getMediaSubtype()) {
+ return (new Email(clone $message->getHeaders()))->text($part->getBody(), $part->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8');
+ }
+ if ('text' === $part->getMediaType() && 'html' === $part->getMediaSubtype()) {
+ return (new Email(clone $message->getHeaders()))->html($part->getBody(), $part->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8');
+ }
+
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message)));
+ }
+
+ private static function createEmailFromAlternativePart(Message $message, AlternativePart $part): Email
+ {
+ $parts = $part->getParts();
+ if (
+ 2 === \count($parts) &&
+ $parts[0] instanceof TextPart && 'text' === $parts[0]->getMediaType() && 'plain' === $parts[0]->getMediaSubtype() &&
+ $parts[1] instanceof TextPart && 'text' === $parts[1]->getMediaType() && 'html' === $parts[1]->getMediaSubtype()
+ ) {
+ return (new Email(clone $message->getHeaders()))
+ ->text($parts[0]->getBody(), $parts[0]->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8')
+ ->html($parts[1]->getBody(), $parts[1]->getPreparedHeaders()->getHeaderParameter('Content-Type', 'charset') ?: 'utf-8')
+ ;
+ }
+
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message)));
+ }
+
+ private static function createEmailFromRelatedPart(Message $message, RelatedPart $part): Email
+ {
+ $parts = $part->getParts();
+ if ($parts[0] instanceof AlternativePart) {
+ $email = self::createEmailFromAlternativePart($message, $parts[0]);
+ } elseif ($parts[0] instanceof TextPart) {
+ $email = self::createEmailFromTextPart($message, $parts[0]);
+ } else {
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($message)));
+ }
+
+ return self::attachParts($email, \array_slice($parts, 1));
+ }
+
+ private static function attachParts(Email $email, array $parts): Email
+ {
+ foreach ($parts as $part) {
+ if (!$part instanceof DataPart) {
+ throw new RuntimeException(sprintf('Unable to create an Email from an instance of "%s" as the body is too complex.', \get_class($email)));
+ }
+
+ $headers = $part->getPreparedHeaders();
+ $method = 'inline' === $headers->getHeaderBody('Content-Disposition') ? 'embed' : 'attach';
+ $name = $headers->getHeaderParameter('Content-Disposition', 'filename');
+ $email->$method($part->getBody(), $name, $part->getMediaType().'/'.$part->getMediaSubtype());
+ }
+
+ return $email;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/MimeTypeGuesserInterface.php b/lam/lib/3rdParty/composer/symfony/mime/MimeTypeGuesserInterface.php
new file mode 100644
index 00000000..68b05055
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/MimeTypeGuesserInterface.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+/**
+ * Guesses the MIME type of a file.
+ *
+ * @author Fabien Potencier
+ */
+interface MimeTypeGuesserInterface
+{
+ /**
+ * Returns true if this guesser is supported.
+ */
+ public function isGuesserSupported(): bool;
+
+ /**
+ * Guesses the MIME type of the file with the given path.
+ *
+ * @param string $path The path to the file
+ *
+ * @return string|null The MIME type or null, if none could be guessed
+ *
+ * @throws \LogicException If the guesser is not supported
+ * @throws \InvalidArgumentException If the file does not exist or is not readable
+ */
+ public function guessMimeType(string $path): ?string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/MimeTypes.php b/lam/lib/3rdParty/composer/symfony/mime/MimeTypes.php
new file mode 100644
index 00000000..268658d1
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/MimeTypes.php
@@ -0,0 +1,3153 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\LogicException;
+
+/**
+ * Manages MIME types and file extensions.
+ *
+ * For MIME type guessing, you can register custom guessers
+ * by calling the registerGuesser() method.
+ * Custom guessers are always called before any default ones:
+ *
+ * $guesser = new MimeTypes();
+ * $guesser->registerGuesser(new MyCustomMimeTypeGuesser());
+ *
+ * If you want to change the order of the default guessers, just re-register your
+ * preferred one as a custom one. The last registered guesser is preferred over
+ * previously registered ones.
+ *
+ * Re-registering a built-in guesser also allows you to configure it:
+ *
+ * $guesser = new MimeTypes();
+ * $guesser->registerGuesser(new FileinfoMimeTypeGuesser('/path/to/magic/file'));
+ *
+ * @author Fabien Potencier
+ */
+final class MimeTypes implements MimeTypesInterface
+{
+ private $extensions = [];
+ private $mimeTypes = [];
+
+ /**
+ * @var MimeTypeGuesserInterface[]
+ */
+ private $guessers = [];
+ private static $default;
+
+ public function __construct(array $map = [])
+ {
+ foreach ($map as $mimeType => $extensions) {
+ $this->extensions[$mimeType] = $extensions;
+
+ foreach ($extensions as $extension) {
+ $this->mimeTypes[$extension] = $mimeType;
+ }
+ }
+ $this->registerGuesser(new FileBinaryMimeTypeGuesser());
+ $this->registerGuesser(new FileinfoMimeTypeGuesser());
+ }
+
+ public static function setDefault(self $default)
+ {
+ self::$default = $default;
+ }
+
+ public static function getDefault(): self
+ {
+ return self::$default ?? self::$default = new self();
+ }
+
+ /**
+ * Registers a MIME type guesser.
+ *
+ * The last registered guesser has precedence over the other ones.
+ */
+ public function registerGuesser(MimeTypeGuesserInterface $guesser)
+ {
+ array_unshift($this->guessers, $guesser);
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getExtensions(string $mimeType): array
+ {
+ if ($this->extensions) {
+ $extensions = $this->extensions[$mimeType] ?? $this->extensions[$lcMimeType = strtolower($mimeType)] ?? null;
+ }
+
+ return $extensions ?? self::$map[$mimeType] ?? self::$map[$lcMimeType ?? strtolower($mimeType)] ?? [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function getMimeTypes(string $ext): array
+ {
+ if ($this->mimeTypes) {
+ $mimeTypes = $this->mimeTypes[$ext] ?? $this->mimeTypes[$lcExt = strtolower($ext)] ?? null;
+ }
+
+ return $mimeTypes ?? self::$reverseMap[$ext] ?? self::$reverseMap[$lcExt ?? strtolower($ext)] ?? [];
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function isGuesserSupported(): bool
+ {
+ foreach ($this->guessers as $guesser) {
+ if ($guesser->isGuesserSupported()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * The file is passed to each registered MIME type guesser in reverse order
+ * of their registration (last registered is queried first). Once a guesser
+ * returns a value that is not null, this method terminates and returns the
+ * value.
+ */
+ public function guessMimeType(string $path): ?string
+ {
+ foreach ($this->guessers as $guesser) {
+ if (!$guesser->isGuesserSupported()) {
+ continue;
+ }
+
+ if (null !== $mimeType = $guesser->guessMimeType($path)) {
+ return $mimeType;
+ }
+ }
+
+ if (!$this->isGuesserSupported()) {
+ throw new LogicException('Unable to guess the MIME type as no guessers are available (have you enable the php_fileinfo extension?).');
+ }
+
+ return null;
+ }
+
+ /**
+ * A map of MIME types and their default extensions.
+ *
+ * Updated from upstream on 2019-01-16
+ *
+ * @see Resources/bin/update_mime_types.php
+ */
+ private static $map = [
+ 'application/acrobat' => ['pdf'],
+ 'application/andrew-inset' => ['ez'],
+ 'application/annodex' => ['anx'],
+ 'application/applixware' => ['aw'],
+ 'application/atom+xml' => ['atom'],
+ 'application/atomcat+xml' => ['atomcat'],
+ 'application/atomsvc+xml' => ['atomsvc'],
+ 'application/ccxml+xml' => ['ccxml'],
+ 'application/cdmi-capability' => ['cdmia'],
+ 'application/cdmi-container' => ['cdmic'],
+ 'application/cdmi-domain' => ['cdmid'],
+ 'application/cdmi-object' => ['cdmio'],
+ 'application/cdmi-queue' => ['cdmiq'],
+ 'application/cdr' => ['cdr'],
+ 'application/coreldraw' => ['cdr'],
+ 'application/cu-seeme' => ['cu'],
+ 'application/davmount+xml' => ['davmount'],
+ 'application/dbase' => ['dbf'],
+ 'application/dbf' => ['dbf'],
+ 'application/dicom' => ['dcm'],
+ 'application/docbook+xml' => ['dbk', 'docbook'],
+ 'application/dssc+der' => ['dssc'],
+ 'application/dssc+xml' => ['xdssc'],
+ 'application/ecmascript' => ['ecma', 'es'],
+ 'application/emf' => ['emf'],
+ 'application/emma+xml' => ['emma'],
+ 'application/epub+zip' => ['epub'],
+ 'application/exi' => ['exi'],
+ 'application/font-tdpfr' => ['pfr'],
+ 'application/font-woff' => ['woff'],
+ 'application/futuresplash' => ['swf', 'spl'],
+ 'application/geo+json' => ['geojson', 'geo.json'],
+ 'application/gml+xml' => ['gml'],
+ 'application/gnunet-directory' => ['gnd'],
+ 'application/gpx' => ['gpx'],
+ 'application/gpx+xml' => ['gpx'],
+ 'application/gxf' => ['gxf'],
+ 'application/gzip' => ['gz'],
+ 'application/hyperstudio' => ['stk'],
+ 'application/ico' => ['ico'],
+ 'application/ics' => ['vcs', 'ics'],
+ 'application/illustrator' => ['ai'],
+ 'application/inkml+xml' => ['ink', 'inkml'],
+ 'application/ipfix' => ['ipfix'],
+ 'application/java' => ['class'],
+ 'application/java-archive' => ['jar'],
+ 'application/java-byte-code' => ['class'],
+ 'application/java-serialized-object' => ['ser'],
+ 'application/java-vm' => ['class'],
+ 'application/javascript' => ['js', 'jsm', 'mjs'],
+ 'application/jrd+json' => ['jrd'],
+ 'application/json' => ['json'],
+ 'application/json-patch+json' => ['json-patch'],
+ 'application/jsonml+json' => ['jsonml'],
+ 'application/ld+json' => ['jsonld'],
+ 'application/lost+xml' => ['lostxml'],
+ 'application/lotus123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'application/m3u' => ['m3u', 'm3u8', 'vlc'],
+ 'application/mac-binhex40' => ['hqx'],
+ 'application/mac-compactpro' => ['cpt'],
+ 'application/mads+xml' => ['mads'],
+ 'application/marc' => ['mrc'],
+ 'application/marcxml+xml' => ['mrcx'],
+ 'application/mathematica' => ['ma', 'nb', 'mb'],
+ 'application/mathml+xml' => ['mathml', 'mml'],
+ 'application/mbox' => ['mbox'],
+ 'application/mdb' => ['mdb'],
+ 'application/mediaservercontrol+xml' => ['mscml'],
+ 'application/metalink+xml' => ['metalink'],
+ 'application/metalink4+xml' => ['meta4'],
+ 'application/mets+xml' => ['mets'],
+ 'application/mods+xml' => ['mods'],
+ 'application/mp21' => ['m21', 'mp21'],
+ 'application/mp4' => ['mp4s'],
+ 'application/ms-tnef' => ['tnef', 'tnf'],
+ 'application/msaccess' => ['mdb'],
+ 'application/msexcel' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'],
+ 'application/mspowerpoint' => ['ppz', 'ppt', 'pps', 'pot'],
+ 'application/msword' => ['doc', 'dot'],
+ 'application/msword-template' => ['dot'],
+ 'application/mxf' => ['mxf'],
+ 'application/nappdf' => ['pdf'],
+ 'application/octet-stream' => ['bin', 'dms', 'lrf', 'mar', 'so', 'dist', 'distz', 'pkg', 'bpk', 'dump', 'elc', 'deploy'],
+ 'application/oda' => ['oda'],
+ 'application/oebps-package+xml' => ['opf'],
+ 'application/ogg' => ['ogx'],
+ 'application/omdoc+xml' => ['omdoc'],
+ 'application/onenote' => ['onetoc', 'onetoc2', 'onetmp', 'onepkg'],
+ 'application/owl+xml' => ['owx'],
+ 'application/oxps' => ['oxps', 'xps'],
+ 'application/patch-ops-error+xml' => ['xer'],
+ 'application/pcap' => ['pcap', 'cap', 'dmp'],
+ 'application/pdf' => ['pdf'],
+ 'application/pgp' => ['pgp', 'gpg', 'asc'],
+ 'application/pgp-encrypted' => ['pgp', 'gpg', 'asc'],
+ 'application/pgp-keys' => ['skr', 'pkr', 'asc', 'pgp', 'gpg'],
+ 'application/pgp-signature' => ['asc', 'sig', 'pgp', 'gpg'],
+ 'application/photoshop' => ['psd'],
+ 'application/pics-rules' => ['prf'],
+ 'application/pkcs10' => ['p10'],
+ 'application/pkcs12' => ['p12', 'pfx'],
+ 'application/pkcs7-mime' => ['p7m', 'p7c'],
+ 'application/pkcs7-signature' => ['p7s'],
+ 'application/pkcs8' => ['p8'],
+ 'application/pkcs8-encrypted' => ['p8e'],
+ 'application/pkix-attr-cert' => ['ac'],
+ 'application/pkix-cert' => ['cer'],
+ 'application/pkix-crl' => ['crl'],
+ 'application/pkix-pkipath' => ['pkipath'],
+ 'application/pkixcmp' => ['pki'],
+ 'application/pls' => ['pls'],
+ 'application/pls+xml' => ['pls'],
+ 'application/postscript' => ['ai', 'eps', 'ps'],
+ 'application/powerpoint' => ['ppz', 'ppt', 'pps', 'pot'],
+ 'application/prs.cww' => ['cww'],
+ 'application/pskc+xml' => ['pskcxml'],
+ 'application/ram' => ['ram'],
+ 'application/raml+yaml' => ['raml'],
+ 'application/rdf+xml' => ['rdf', 'rdfs', 'owl'],
+ 'application/reginfo+xml' => ['rif'],
+ 'application/relax-ng-compact-syntax' => ['rnc'],
+ 'application/resource-lists+xml' => ['rl'],
+ 'application/resource-lists-diff+xml' => ['rld'],
+ 'application/rls-services+xml' => ['rs'],
+ 'application/rpki-ghostbusters' => ['gbr'],
+ 'application/rpki-manifest' => ['mft'],
+ 'application/rpki-roa' => ['roa'],
+ 'application/rsd+xml' => ['rsd'],
+ 'application/rss+xml' => ['rss'],
+ 'application/rtf' => ['rtf'],
+ 'application/sbml+xml' => ['sbml'],
+ 'application/scvp-cv-request' => ['scq'],
+ 'application/scvp-cv-response' => ['scs'],
+ 'application/scvp-vp-request' => ['spq'],
+ 'application/scvp-vp-response' => ['spp'],
+ 'application/sdp' => ['sdp'],
+ 'application/set-payment-initiation' => ['setpay'],
+ 'application/set-registration-initiation' => ['setreg'],
+ 'application/shf+xml' => ['shf'],
+ 'application/sieve' => ['siv'],
+ 'application/smil' => ['smil', 'smi', 'sml', 'kino'],
+ 'application/smil+xml' => ['smi', 'smil', 'sml', 'kino'],
+ 'application/sparql-query' => ['rq'],
+ 'application/sparql-results+xml' => ['srx'],
+ 'application/sql' => ['sql'],
+ 'application/srgs' => ['gram'],
+ 'application/srgs+xml' => ['grxml'],
+ 'application/sru+xml' => ['sru'],
+ 'application/ssdl+xml' => ['ssdl'],
+ 'application/ssml+xml' => ['ssml'],
+ 'application/stuffit' => ['sit'],
+ 'application/tei+xml' => ['tei', 'teicorpus'],
+ 'application/thraud+xml' => ['tfi'],
+ 'application/timestamped-data' => ['tsd'],
+ 'application/trig' => ['trig'],
+ 'application/vnd.3gpp.pic-bw-large' => ['plb'],
+ 'application/vnd.3gpp.pic-bw-small' => ['psb'],
+ 'application/vnd.3gpp.pic-bw-var' => ['pvb'],
+ 'application/vnd.3gpp2.tcap' => ['tcap'],
+ 'application/vnd.3m.post-it-notes' => ['pwn'],
+ 'application/vnd.accpac.simply.aso' => ['aso'],
+ 'application/vnd.accpac.simply.imp' => ['imp'],
+ 'application/vnd.acucobol' => ['acu'],
+ 'application/vnd.acucorp' => ['atc', 'acutc'],
+ 'application/vnd.adobe.air-application-installer-package+zip' => ['air'],
+ 'application/vnd.adobe.flash.movie' => ['swf', 'spl'],
+ 'application/vnd.adobe.formscentral.fcdt' => ['fcdt'],
+ 'application/vnd.adobe.fxp' => ['fxp', 'fxpl'],
+ 'application/vnd.adobe.illustrator' => ['ai'],
+ 'application/vnd.adobe.xdp+xml' => ['xdp'],
+ 'application/vnd.adobe.xfdf' => ['xfdf'],
+ 'application/vnd.ahead.space' => ['ahead'],
+ 'application/vnd.airzip.filesecure.azf' => ['azf'],
+ 'application/vnd.airzip.filesecure.azs' => ['azs'],
+ 'application/vnd.amazon.ebook' => ['azw'],
+ 'application/vnd.americandynamics.acc' => ['acc'],
+ 'application/vnd.amiga.ami' => ['ami'],
+ 'application/vnd.android.package-archive' => ['apk'],
+ 'application/vnd.anser-web-certificate-issue-initiation' => ['cii'],
+ 'application/vnd.anser-web-funds-transfer-initiation' => ['fti'],
+ 'application/vnd.antix.game-component' => ['atx'],
+ 'application/vnd.appimage' => ['appimage'],
+ 'application/vnd.apple.installer+xml' => ['mpkg'],
+ 'application/vnd.apple.keynote' => ['key'],
+ 'application/vnd.apple.mpegurl' => ['m3u8', 'm3u'],
+ 'application/vnd.aristanetworks.swi' => ['swi'],
+ 'application/vnd.astraea-software.iota' => ['iota'],
+ 'application/vnd.audiograph' => ['aep'],
+ 'application/vnd.blueice.multipass' => ['mpm'],
+ 'application/vnd.bmi' => ['bmi'],
+ 'application/vnd.businessobjects' => ['rep'],
+ 'application/vnd.chemdraw+xml' => ['cdxml'],
+ 'application/vnd.chess-pgn' => ['pgn'],
+ 'application/vnd.chipnuts.karaoke-mmd' => ['mmd'],
+ 'application/vnd.cinderella' => ['cdy'],
+ 'application/vnd.claymore' => ['cla'],
+ 'application/vnd.cloanto.rp9' => ['rp9'],
+ 'application/vnd.clonk.c4group' => ['c4g', 'c4d', 'c4f', 'c4p', 'c4u'],
+ 'application/vnd.cluetrust.cartomobile-config' => ['c11amc'],
+ 'application/vnd.cluetrust.cartomobile-config-pkg' => ['c11amz'],
+ 'application/vnd.coffeescript' => ['coffee'],
+ 'application/vnd.comicbook+zip' => ['cbz'],
+ 'application/vnd.comicbook-rar' => ['cbr'],
+ 'application/vnd.commonspace' => ['csp'],
+ 'application/vnd.contact.cmsg' => ['cdbcmsg'],
+ 'application/vnd.corel-draw' => ['cdr'],
+ 'application/vnd.cosmocaller' => ['cmc'],
+ 'application/vnd.crick.clicker' => ['clkx'],
+ 'application/vnd.crick.clicker.keyboard' => ['clkk'],
+ 'application/vnd.crick.clicker.palette' => ['clkp'],
+ 'application/vnd.crick.clicker.template' => ['clkt'],
+ 'application/vnd.crick.clicker.wordbank' => ['clkw'],
+ 'application/vnd.criticaltools.wbs+xml' => ['wbs'],
+ 'application/vnd.ctc-posml' => ['pml'],
+ 'application/vnd.cups-ppd' => ['ppd'],
+ 'application/vnd.curl.car' => ['car'],
+ 'application/vnd.curl.pcurl' => ['pcurl'],
+ 'application/vnd.dart' => ['dart'],
+ 'application/vnd.data-vision.rdz' => ['rdz'],
+ 'application/vnd.debian.binary-package' => ['deb', 'udeb'],
+ 'application/vnd.dece.data' => ['uvf', 'uvvf', 'uvd', 'uvvd'],
+ 'application/vnd.dece.ttml+xml' => ['uvt', 'uvvt'],
+ 'application/vnd.dece.unspecified' => ['uvx', 'uvvx'],
+ 'application/vnd.dece.zip' => ['uvz', 'uvvz'],
+ 'application/vnd.denovo.fcselayout-link' => ['fe_launch'],
+ 'application/vnd.dna' => ['dna'],
+ 'application/vnd.dolby.mlp' => ['mlp'],
+ 'application/vnd.dpgraph' => ['dpg'],
+ 'application/vnd.dreamfactory' => ['dfac'],
+ 'application/vnd.ds-keypoint' => ['kpxx'],
+ 'application/vnd.dvb.ait' => ['ait'],
+ 'application/vnd.dvb.service' => ['svc'],
+ 'application/vnd.dynageo' => ['geo'],
+ 'application/vnd.ecowin.chart' => ['mag'],
+ 'application/vnd.emusic-emusic_package' => ['emp'],
+ 'application/vnd.enliven' => ['nml'],
+ 'application/vnd.epson.esf' => ['esf'],
+ 'application/vnd.epson.msf' => ['msf'],
+ 'application/vnd.epson.quickanime' => ['qam'],
+ 'application/vnd.epson.salt' => ['slt'],
+ 'application/vnd.epson.ssf' => ['ssf'],
+ 'application/vnd.eszigno3+xml' => ['es3', 'et3'],
+ 'application/vnd.ezpix-album' => ['ez2'],
+ 'application/vnd.ezpix-package' => ['ez3'],
+ 'application/vnd.fdf' => ['fdf'],
+ 'application/vnd.fdsn.mseed' => ['mseed'],
+ 'application/vnd.fdsn.seed' => ['seed', 'dataless'],
+ 'application/vnd.flatpak' => ['flatpak', 'xdgapp'],
+ 'application/vnd.flatpak.ref' => ['flatpakref'],
+ 'application/vnd.flatpak.repo' => ['flatpakrepo'],
+ 'application/vnd.flographit' => ['gph'],
+ 'application/vnd.fluxtime.clip' => ['ftc'],
+ 'application/vnd.framemaker' => ['fm', 'frame', 'maker', 'book'],
+ 'application/vnd.frogans.fnc' => ['fnc'],
+ 'application/vnd.frogans.ltf' => ['ltf'],
+ 'application/vnd.fsc.weblaunch' => ['fsc'],
+ 'application/vnd.fujitsu.oasys' => ['oas'],
+ 'application/vnd.fujitsu.oasys2' => ['oa2'],
+ 'application/vnd.fujitsu.oasys3' => ['oa3'],
+ 'application/vnd.fujitsu.oasysgp' => ['fg5'],
+ 'application/vnd.fujitsu.oasysprs' => ['bh2'],
+ 'application/vnd.fujixerox.ddd' => ['ddd'],
+ 'application/vnd.fujixerox.docuworks' => ['xdw'],
+ 'application/vnd.fujixerox.docuworks.binder' => ['xbd'],
+ 'application/vnd.fuzzysheet' => ['fzs'],
+ 'application/vnd.genomatix.tuxedo' => ['txd'],
+ 'application/vnd.geo+json' => ['geojson', 'geo.json'],
+ 'application/vnd.geogebra.file' => ['ggb'],
+ 'application/vnd.geogebra.tool' => ['ggt'],
+ 'application/vnd.geometry-explorer' => ['gex', 'gre'],
+ 'application/vnd.geonext' => ['gxt'],
+ 'application/vnd.geoplan' => ['g2w'],
+ 'application/vnd.geospace' => ['g3w'],
+ 'application/vnd.gmx' => ['gmx'],
+ 'application/vnd.google-earth.kml+xml' => ['kml'],
+ 'application/vnd.google-earth.kmz' => ['kmz'],
+ 'application/vnd.grafeq' => ['gqf', 'gqs'],
+ 'application/vnd.groove-account' => ['gac'],
+ 'application/vnd.groove-help' => ['ghf'],
+ 'application/vnd.groove-identity-message' => ['gim'],
+ 'application/vnd.groove-injector' => ['grv'],
+ 'application/vnd.groove-tool-message' => ['gtm'],
+ 'application/vnd.groove-tool-template' => ['tpl'],
+ 'application/vnd.groove-vcard' => ['vcg'],
+ 'application/vnd.haansoft-hwp' => ['hwp'],
+ 'application/vnd.haansoft-hwt' => ['hwt'],
+ 'application/vnd.hal+xml' => ['hal'],
+ 'application/vnd.handheld-entertainment+xml' => ['zmm'],
+ 'application/vnd.hbci' => ['hbci'],
+ 'application/vnd.hhe.lesson-player' => ['les'],
+ 'application/vnd.hp-hpgl' => ['hpgl'],
+ 'application/vnd.hp-hpid' => ['hpid'],
+ 'application/vnd.hp-hps' => ['hps'],
+ 'application/vnd.hp-jlyt' => ['jlt'],
+ 'application/vnd.hp-pcl' => ['pcl'],
+ 'application/vnd.hp-pclxl' => ['pclxl'],
+ 'application/vnd.hydrostatix.sof-data' => ['sfd-hdstx'],
+ 'application/vnd.ibm.minipay' => ['mpy'],
+ 'application/vnd.ibm.modcap' => ['afp', 'listafp', 'list3820'],
+ 'application/vnd.ibm.rights-management' => ['irm'],
+ 'application/vnd.ibm.secure-container' => ['sc'],
+ 'application/vnd.iccprofile' => ['icc', 'icm'],
+ 'application/vnd.igloader' => ['igl'],
+ 'application/vnd.immervision-ivp' => ['ivp'],
+ 'application/vnd.immervision-ivu' => ['ivu'],
+ 'application/vnd.insors.igm' => ['igm'],
+ 'application/vnd.intercon.formnet' => ['xpw', 'xpx'],
+ 'application/vnd.intergeo' => ['i2g'],
+ 'application/vnd.intu.qbo' => ['qbo'],
+ 'application/vnd.intu.qfx' => ['qfx'],
+ 'application/vnd.ipunplugged.rcprofile' => ['rcprofile'],
+ 'application/vnd.irepository.package+xml' => ['irp'],
+ 'application/vnd.is-xpr' => ['xpr'],
+ 'application/vnd.isac.fcs' => ['fcs'],
+ 'application/vnd.jam' => ['jam'],
+ 'application/vnd.jcp.javame.midlet-rms' => ['rms'],
+ 'application/vnd.jisp' => ['jisp'],
+ 'application/vnd.joost.joda-archive' => ['joda'],
+ 'application/vnd.kahootz' => ['ktz', 'ktr'],
+ 'application/vnd.kde.karbon' => ['karbon'],
+ 'application/vnd.kde.kchart' => ['chrt'],
+ 'application/vnd.kde.kformula' => ['kfo'],
+ 'application/vnd.kde.kivio' => ['flw'],
+ 'application/vnd.kde.kontour' => ['kon'],
+ 'application/vnd.kde.kpresenter' => ['kpr', 'kpt'],
+ 'application/vnd.kde.kspread' => ['ksp'],
+ 'application/vnd.kde.kword' => ['kwd', 'kwt'],
+ 'application/vnd.kenameaapp' => ['htke'],
+ 'application/vnd.kidspiration' => ['kia'],
+ 'application/vnd.kinar' => ['kne', 'knp'],
+ 'application/vnd.koan' => ['skp', 'skd', 'skt', 'skm'],
+ 'application/vnd.kodak-descriptor' => ['sse'],
+ 'application/vnd.las.las+xml' => ['lasxml'],
+ 'application/vnd.llamagraphics.life-balance.desktop' => ['lbd'],
+ 'application/vnd.llamagraphics.life-balance.exchange+xml' => ['lbe'],
+ 'application/vnd.lotus-1-2-3' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'application/vnd.lotus-approach' => ['apr'],
+ 'application/vnd.lotus-freelance' => ['pre'],
+ 'application/vnd.lotus-notes' => ['nsf'],
+ 'application/vnd.lotus-organizer' => ['org'],
+ 'application/vnd.lotus-screencam' => ['scm'],
+ 'application/vnd.lotus-wordpro' => ['lwp'],
+ 'application/vnd.macports.portpkg' => ['portpkg'],
+ 'application/vnd.mcd' => ['mcd'],
+ 'application/vnd.medcalcdata' => ['mc1'],
+ 'application/vnd.mediastation.cdkey' => ['cdkey'],
+ 'application/vnd.mfer' => ['mwf'],
+ 'application/vnd.mfmp' => ['mfm'],
+ 'application/vnd.micrografx.flo' => ['flo'],
+ 'application/vnd.micrografx.igx' => ['igx'],
+ 'application/vnd.mif' => ['mif'],
+ 'application/vnd.mobius.daf' => ['daf'],
+ 'application/vnd.mobius.dis' => ['dis'],
+ 'application/vnd.mobius.mbk' => ['mbk'],
+ 'application/vnd.mobius.mqy' => ['mqy'],
+ 'application/vnd.mobius.msl' => ['msl'],
+ 'application/vnd.mobius.plc' => ['plc'],
+ 'application/vnd.mobius.txf' => ['txf'],
+ 'application/vnd.mophun.application' => ['mpn'],
+ 'application/vnd.mophun.certificate' => ['mpc'],
+ 'application/vnd.mozilla.xul+xml' => ['xul'],
+ 'application/vnd.ms-access' => ['mdb'],
+ 'application/vnd.ms-artgalry' => ['cil'],
+ 'application/vnd.ms-asf' => ['asf'],
+ 'application/vnd.ms-cab-compressed' => ['cab'],
+ 'application/vnd.ms-excel' => ['xls', 'xlm', 'xla', 'xlc', 'xlt', 'xlw', 'xll', 'xld'],
+ 'application/vnd.ms-excel.addin.macroenabled.12' => ['xlam'],
+ 'application/vnd.ms-excel.sheet.binary.macroenabled.12' => ['xlsb'],
+ 'application/vnd.ms-excel.sheet.macroenabled.12' => ['xlsm'],
+ 'application/vnd.ms-excel.template.macroenabled.12' => ['xltm'],
+ 'application/vnd.ms-fontobject' => ['eot'],
+ 'application/vnd.ms-htmlhelp' => ['chm'],
+ 'application/vnd.ms-ims' => ['ims'],
+ 'application/vnd.ms-lrm' => ['lrm'],
+ 'application/vnd.ms-officetheme' => ['thmx'],
+ 'application/vnd.ms-pki.seccat' => ['cat'],
+ 'application/vnd.ms-pki.stl' => ['stl'],
+ 'application/vnd.ms-powerpoint' => ['ppt', 'pps', 'pot', 'ppz'],
+ 'application/vnd.ms-powerpoint.addin.macroenabled.12' => ['ppam'],
+ 'application/vnd.ms-powerpoint.presentation.macroenabled.12' => ['pptm'],
+ 'application/vnd.ms-powerpoint.slide.macroenabled.12' => ['sldm'],
+ 'application/vnd.ms-powerpoint.slideshow.macroenabled.12' => ['ppsm'],
+ 'application/vnd.ms-powerpoint.template.macroenabled.12' => ['potm'],
+ 'application/vnd.ms-project' => ['mpp', 'mpt'],
+ 'application/vnd.ms-publisher' => ['pub'],
+ 'application/vnd.ms-tnef' => ['tnef', 'tnf'],
+ 'application/vnd.ms-visio.drawing.macroenabled.main+xml' => ['vsdm'],
+ 'application/vnd.ms-visio.drawing.main+xml' => ['vsdx'],
+ 'application/vnd.ms-visio.stencil.macroenabled.main+xml' => ['vssm'],
+ 'application/vnd.ms-visio.stencil.main+xml' => ['vssx'],
+ 'application/vnd.ms-visio.template.macroenabled.main+xml' => ['vstm'],
+ 'application/vnd.ms-visio.template.main+xml' => ['vstx'],
+ 'application/vnd.ms-word' => ['doc'],
+ 'application/vnd.ms-word.document.macroenabled.12' => ['docm'],
+ 'application/vnd.ms-word.template.macroenabled.12' => ['dotm'],
+ 'application/vnd.ms-works' => ['wps', 'wks', 'wcm', 'wdb', 'xlr'],
+ 'application/vnd.ms-wpl' => ['wpl'],
+ 'application/vnd.ms-xpsdocument' => ['xps', 'oxps'],
+ 'application/vnd.msaccess' => ['mdb'],
+ 'application/vnd.mseq' => ['mseq'],
+ 'application/vnd.musician' => ['mus'],
+ 'application/vnd.muvee.style' => ['msty'],
+ 'application/vnd.mynfc' => ['taglet'],
+ 'application/vnd.neurolanguage.nlu' => ['nlu'],
+ 'application/vnd.nintendo.snes.rom' => ['sfc', 'smc'],
+ 'application/vnd.nitf' => ['ntf', 'nitf'],
+ 'application/vnd.noblenet-directory' => ['nnd'],
+ 'application/vnd.noblenet-sealer' => ['nns'],
+ 'application/vnd.noblenet-web' => ['nnw'],
+ 'application/vnd.nokia.n-gage.data' => ['ngdat'],
+ 'application/vnd.nokia.n-gage.symbian.install' => ['n-gage'],
+ 'application/vnd.nokia.radio-preset' => ['rpst'],
+ 'application/vnd.nokia.radio-presets' => ['rpss'],
+ 'application/vnd.novadigm.edm' => ['edm'],
+ 'application/vnd.novadigm.edx' => ['edx'],
+ 'application/vnd.novadigm.ext' => ['ext'],
+ 'application/vnd.oasis.docbook+xml' => ['dbk', 'docbook'],
+ 'application/vnd.oasis.opendocument.chart' => ['odc'],
+ 'application/vnd.oasis.opendocument.chart-template' => ['otc'],
+ 'application/vnd.oasis.opendocument.database' => ['odb'],
+ 'application/vnd.oasis.opendocument.formula' => ['odf'],
+ 'application/vnd.oasis.opendocument.formula-template' => ['odft', 'otf'],
+ 'application/vnd.oasis.opendocument.graphics' => ['odg'],
+ 'application/vnd.oasis.opendocument.graphics-flat-xml' => ['fodg'],
+ 'application/vnd.oasis.opendocument.graphics-template' => ['otg'],
+ 'application/vnd.oasis.opendocument.image' => ['odi'],
+ 'application/vnd.oasis.opendocument.image-template' => ['oti'],
+ 'application/vnd.oasis.opendocument.presentation' => ['odp'],
+ 'application/vnd.oasis.opendocument.presentation-flat-xml' => ['fodp'],
+ 'application/vnd.oasis.opendocument.presentation-template' => ['otp'],
+ 'application/vnd.oasis.opendocument.spreadsheet' => ['ods'],
+ 'application/vnd.oasis.opendocument.spreadsheet-flat-xml' => ['fods'],
+ 'application/vnd.oasis.opendocument.spreadsheet-template' => ['ots'],
+ 'application/vnd.oasis.opendocument.text' => ['odt'],
+ 'application/vnd.oasis.opendocument.text-flat-xml' => ['fodt'],
+ 'application/vnd.oasis.opendocument.text-master' => ['odm'],
+ 'application/vnd.oasis.opendocument.text-template' => ['ott'],
+ 'application/vnd.oasis.opendocument.text-web' => ['oth'],
+ 'application/vnd.olpc-sugar' => ['xo'],
+ 'application/vnd.oma.dd2+xml' => ['dd2'],
+ 'application/vnd.openofficeorg.extension' => ['oxt'],
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation' => ['pptx'],
+ 'application/vnd.openxmlformats-officedocument.presentationml.slide' => ['sldx'],
+ 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' => ['ppsx'],
+ 'application/vnd.openxmlformats-officedocument.presentationml.template' => ['potx'],
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => ['xlsx'],
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' => ['xltx'],
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => ['docx'],
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' => ['dotx'],
+ 'application/vnd.osgeo.mapguide.package' => ['mgp'],
+ 'application/vnd.osgi.dp' => ['dp'],
+ 'application/vnd.osgi.subsystem' => ['esa'],
+ 'application/vnd.palm' => ['pdb', 'pqa', 'oprc', 'prc'],
+ 'application/vnd.pawaafile' => ['paw'],
+ 'application/vnd.pg.format' => ['str'],
+ 'application/vnd.pg.osasli' => ['ei6'],
+ 'application/vnd.picsel' => ['efif'],
+ 'application/vnd.pmi.widget' => ['wg'],
+ 'application/vnd.pocketlearn' => ['plf'],
+ 'application/vnd.powerbuilder6' => ['pbd'],
+ 'application/vnd.previewsystems.box' => ['box'],
+ 'application/vnd.proteus.magazine' => ['mgz'],
+ 'application/vnd.publishare-delta-tree' => ['qps'],
+ 'application/vnd.pvi.ptid1' => ['ptid'],
+ 'application/vnd.quark.quarkxpress' => ['qxd', 'qxt', 'qwd', 'qwt', 'qxl', 'qxb'],
+ 'application/vnd.rar' => ['rar'],
+ 'application/vnd.realvnc.bed' => ['bed'],
+ 'application/vnd.recordare.musicxml' => ['mxl'],
+ 'application/vnd.recordare.musicxml+xml' => ['musicxml'],
+ 'application/vnd.rig.cryptonote' => ['cryptonote'],
+ 'application/vnd.rim.cod' => ['cod'],
+ 'application/vnd.rn-realmedia' => ['rm', 'rmj', 'rmm', 'rms', 'rmx', 'rmvb'],
+ 'application/vnd.rn-realmedia-vbr' => ['rmvb', 'rm', 'rmj', 'rmm', 'rms', 'rmx'],
+ 'application/vnd.route66.link66+xml' => ['link66'],
+ 'application/vnd.sailingtracker.track' => ['st'],
+ 'application/vnd.sdp' => ['sdp'],
+ 'application/vnd.seemail' => ['see'],
+ 'application/vnd.sema' => ['sema'],
+ 'application/vnd.semd' => ['semd'],
+ 'application/vnd.semf' => ['semf'],
+ 'application/vnd.shana.informed.formdata' => ['ifm'],
+ 'application/vnd.shana.informed.formtemplate' => ['itp'],
+ 'application/vnd.shana.informed.interchange' => ['iif'],
+ 'application/vnd.shana.informed.package' => ['ipk'],
+ 'application/vnd.simtech-mindmapper' => ['twd', 'twds'],
+ 'application/vnd.smaf' => ['mmf', 'smaf'],
+ 'application/vnd.smart.teacher' => ['teacher'],
+ 'application/vnd.snap' => ['snap'],
+ 'application/vnd.solent.sdkm+xml' => ['sdkm', 'sdkd'],
+ 'application/vnd.spotfire.dxp' => ['dxp'],
+ 'application/vnd.spotfire.sfs' => ['sfs'],
+ 'application/vnd.sqlite3' => ['sqlite3'],
+ 'application/vnd.squashfs' => ['sqsh'],
+ 'application/vnd.stardivision.calc' => ['sdc'],
+ 'application/vnd.stardivision.chart' => ['sds'],
+ 'application/vnd.stardivision.draw' => ['sda'],
+ 'application/vnd.stardivision.impress' => ['sdd', 'sdp'],
+ 'application/vnd.stardivision.mail' => ['smd'],
+ 'application/vnd.stardivision.math' => ['smf'],
+ 'application/vnd.stardivision.writer' => ['sdw', 'vor', 'sgl'],
+ 'application/vnd.stardivision.writer-global' => ['sgl', 'sdw', 'vor'],
+ 'application/vnd.stepmania.package' => ['smzip'],
+ 'application/vnd.stepmania.stepchart' => ['sm'],
+ 'application/vnd.sun.xml.base' => ['odb'],
+ 'application/vnd.sun.xml.calc' => ['sxc'],
+ 'application/vnd.sun.xml.calc.template' => ['stc'],
+ 'application/vnd.sun.xml.draw' => ['sxd'],
+ 'application/vnd.sun.xml.draw.template' => ['std'],
+ 'application/vnd.sun.xml.impress' => ['sxi'],
+ 'application/vnd.sun.xml.impress.template' => ['sti'],
+ 'application/vnd.sun.xml.math' => ['sxm'],
+ 'application/vnd.sun.xml.writer' => ['sxw'],
+ 'application/vnd.sun.xml.writer.global' => ['sxg'],
+ 'application/vnd.sun.xml.writer.template' => ['stw'],
+ 'application/vnd.sus-calendar' => ['sus', 'susp'],
+ 'application/vnd.svd' => ['svd'],
+ 'application/vnd.symbian.install' => ['sis', 'sisx'],
+ 'application/vnd.syncml+xml' => ['xsm'],
+ 'application/vnd.syncml.dm+wbxml' => ['bdm'],
+ 'application/vnd.syncml.dm+xml' => ['xdm'],
+ 'application/vnd.tao.intent-module-archive' => ['tao'],
+ 'application/vnd.tcpdump.pcap' => ['pcap', 'cap', 'dmp'],
+ 'application/vnd.tmobile-livetv' => ['tmo'],
+ 'application/vnd.trid.tpt' => ['tpt'],
+ 'application/vnd.triscape.mxs' => ['mxs'],
+ 'application/vnd.trueapp' => ['tra'],
+ 'application/vnd.ufdl' => ['ufd', 'ufdl'],
+ 'application/vnd.uiq.theme' => ['utz'],
+ 'application/vnd.umajin' => ['umj'],
+ 'application/vnd.unity' => ['unityweb'],
+ 'application/vnd.uoml+xml' => ['uoml'],
+ 'application/vnd.vcx' => ['vcx'],
+ 'application/vnd.visio' => ['vsd', 'vst', 'vss', 'vsw'],
+ 'application/vnd.visionary' => ['vis'],
+ 'application/vnd.vsf' => ['vsf'],
+ 'application/vnd.wap.wbxml' => ['wbxml'],
+ 'application/vnd.wap.wmlc' => ['wmlc'],
+ 'application/vnd.wap.wmlscriptc' => ['wmlsc'],
+ 'application/vnd.webturbo' => ['wtb'],
+ 'application/vnd.wolfram.player' => ['nbp'],
+ 'application/vnd.wordperfect' => ['wpd', 'wp', 'wp4', 'wp5', 'wp6', 'wpp'],
+ 'application/vnd.wqd' => ['wqd'],
+ 'application/vnd.wt.stf' => ['stf'],
+ 'application/vnd.xara' => ['xar'],
+ 'application/vnd.xdgapp' => ['flatpak', 'xdgapp'],
+ 'application/vnd.xfdl' => ['xfdl'],
+ 'application/vnd.yamaha.hv-dic' => ['hvd'],
+ 'application/vnd.yamaha.hv-script' => ['hvs'],
+ 'application/vnd.yamaha.hv-voice' => ['hvp'],
+ 'application/vnd.yamaha.openscoreformat' => ['osf'],
+ 'application/vnd.yamaha.openscoreformat.osfpvg+xml' => ['osfpvg'],
+ 'application/vnd.yamaha.smaf-audio' => ['saf'],
+ 'application/vnd.yamaha.smaf-phrase' => ['spf'],
+ 'application/vnd.yellowriver-custom-menu' => ['cmp'],
+ 'application/vnd.youtube.yt' => ['yt'],
+ 'application/vnd.zul' => ['zir', 'zirz'],
+ 'application/vnd.zzazz.deck+xml' => ['zaz'],
+ 'application/voicexml+xml' => ['vxml'],
+ 'application/widget' => ['wgt'],
+ 'application/winhlp' => ['hlp'],
+ 'application/wk1' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'application/wmf' => ['wmf'],
+ 'application/wordperfect' => ['wp', 'wp4', 'wp5', 'wp6', 'wpd', 'wpp'],
+ 'application/wsdl+xml' => ['wsdl'],
+ 'application/wspolicy+xml' => ['wspolicy'],
+ 'application/wwf' => ['wwf'],
+ 'application/x-123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'application/x-7z-compressed' => ['7z'],
+ 'application/x-abiword' => ['abw', 'abw.CRASHED', 'abw.gz', 'zabw'],
+ 'application/x-ace' => ['ace'],
+ 'application/x-ace-compressed' => ['ace'],
+ 'application/x-alz' => ['alz'],
+ 'application/x-amiga-disk-format' => ['adf'],
+ 'application/x-amipro' => ['sam'],
+ 'application/x-annodex' => ['anx'],
+ 'application/x-aportisdoc' => ['pdb', 'pdc'],
+ 'application/x-apple-diskimage' => ['dmg'],
+ 'application/x-applix-spreadsheet' => ['as'],
+ 'application/x-applix-word' => ['aw'],
+ 'application/x-archive' => ['a', 'ar'],
+ 'application/x-arj' => ['arj'],
+ 'application/x-asp' => ['asp'],
+ 'application/x-atari-2600-rom' => ['a26'],
+ 'application/x-atari-7800-rom' => ['a78'],
+ 'application/x-atari-lynx-rom' => ['lnx'],
+ 'application/x-authorware-bin' => ['aab', 'x32', 'u32', 'vox'],
+ 'application/x-authorware-map' => ['aam'],
+ 'application/x-authorware-seg' => ['aas'],
+ 'application/x-awk' => ['awk'],
+ 'application/x-bcpio' => ['bcpio'],
+ 'application/x-bittorrent' => ['torrent'],
+ 'application/x-blender' => ['blender', 'blend', 'BLEND'],
+ 'application/x-blorb' => ['blb', 'blorb'],
+ 'application/x-bsdiff' => ['bsdiff'],
+ 'application/x-bzdvi' => ['dvi.bz2'],
+ 'application/x-bzip' => ['bz', 'bz2'],
+ 'application/x-bzip-compressed-tar' => ['tar.bz2', 'tar.bz', 'tbz2', 'tbz', 'tb2'],
+ 'application/x-bzip2' => ['bz2', 'boz', 'bz'],
+ 'application/x-bzpdf' => ['pdf.bz2'],
+ 'application/x-bzpostscript' => ['ps.bz2'],
+ 'application/x-cb7' => ['cb7'],
+ 'application/x-cbr' => ['cbr', 'cba', 'cbt', 'cbz', 'cb7'],
+ 'application/x-cbt' => ['cbt'],
+ 'application/x-cbz' => ['cbz'],
+ 'application/x-ccmx' => ['ccmx'],
+ 'application/x-cd-image' => ['iso', 'iso9660'],
+ 'application/x-cdlink' => ['vcd'],
+ 'application/x-cdr' => ['cdr'],
+ 'application/x-cdrdao-toc' => ['toc'],
+ 'application/x-cfs-compressed' => ['cfs'],
+ 'application/x-chat' => ['chat'],
+ 'application/x-chess-pgn' => ['pgn'],
+ 'application/x-chm' => ['chm'],
+ 'application/x-cisco-vpn-settings' => ['pcf'],
+ 'application/x-compress' => ['Z'],
+ 'application/x-compressed-tar' => ['tar.gz', 'tgz'],
+ 'application/x-conference' => ['nsc'],
+ 'application/x-coreldraw' => ['cdr'],
+ 'application/x-cpio' => ['cpio'],
+ 'application/x-cpio-compressed' => ['cpio.gz'],
+ 'application/x-csh' => ['csh'],
+ 'application/x-cue' => ['cue'],
+ 'application/x-dar' => ['dar'],
+ 'application/x-dbase' => ['dbf'],
+ 'application/x-dbf' => ['dbf'],
+ 'application/x-dc-rom' => ['dc'],
+ 'application/x-deb' => ['deb', 'udeb'],
+ 'application/x-debian-package' => ['deb', 'udeb'],
+ 'application/x-designer' => ['ui'],
+ 'application/x-desktop' => ['desktop', 'kdelnk'],
+ 'application/x-dgc-compressed' => ['dgc'],
+ 'application/x-dia-diagram' => ['dia'],
+ 'application/x-dia-shape' => ['shape'],
+ 'application/x-director' => ['dir', 'dcr', 'dxr', 'cst', 'cct', 'cxt', 'w3d', 'fgd', 'swa'],
+ 'application/x-docbook+xml' => ['dbk', 'docbook'],
+ 'application/x-doom' => ['wad'],
+ 'application/x-doom-wad' => ['wad'],
+ 'application/x-dtbncx+xml' => ['ncx'],
+ 'application/x-dtbook+xml' => ['dtb'],
+ 'application/x-dtbresource+xml' => ['res'],
+ 'application/x-dvi' => ['dvi'],
+ 'application/x-e-theme' => ['etheme'],
+ 'application/x-egon' => ['egon'],
+ 'application/x-emf' => ['emf'],
+ 'application/x-envoy' => ['evy'],
+ 'application/x-eva' => ['eva'],
+ 'application/x-fd-file' => ['fd', 'qd'],
+ 'application/x-fds-disk' => ['fds'],
+ 'application/x-fictionbook' => ['fb2'],
+ 'application/x-fictionbook+xml' => ['fb2'],
+ 'application/x-flash-video' => ['flv'],
+ 'application/x-fluid' => ['fl'],
+ 'application/x-font-afm' => ['afm'],
+ 'application/x-font-bdf' => ['bdf'],
+ 'application/x-font-ghostscript' => ['gsf'],
+ 'application/x-font-linux-psf' => ['psf'],
+ 'application/x-font-otf' => ['otf'],
+ 'application/x-font-pcf' => ['pcf', 'pcf.Z', 'pcf.gz'],
+ 'application/x-font-snf' => ['snf'],
+ 'application/x-font-speedo' => ['spd'],
+ 'application/x-font-ttf' => ['ttf'],
+ 'application/x-font-ttx' => ['ttx'],
+ 'application/x-font-type1' => ['pfa', 'pfb', 'pfm', 'afm', 'gsf'],
+ 'application/x-font-woff' => ['woff'],
+ 'application/x-frame' => ['fm'],
+ 'application/x-freearc' => ['arc'],
+ 'application/x-futuresplash' => ['spl'],
+ 'application/x-gameboy-color-rom' => ['gbc', 'cgb'],
+ 'application/x-gameboy-rom' => ['gb', 'sgb'],
+ 'application/x-gamecube-iso-image' => ['iso'],
+ 'application/x-gamecube-rom' => ['iso'],
+ 'application/x-gamegear-rom' => ['gg'],
+ 'application/x-gba-rom' => ['gba', 'agb'],
+ 'application/x-gca-compressed' => ['gca'],
+ 'application/x-gedcom' => ['ged', 'gedcom'],
+ 'application/x-genesis-32x-rom' => ['32x', 'mdx'],
+ 'application/x-genesis-rom' => ['gen', 'smd'],
+ 'application/x-gettext' => ['po'],
+ 'application/x-gettext-translation' => ['gmo', 'mo'],
+ 'application/x-glade' => ['glade'],
+ 'application/x-glulx' => ['ulx'],
+ 'application/x-gnome-app-info' => ['desktop', 'kdelnk'],
+ 'application/x-gnucash' => ['gnucash', 'gnc', 'xac'],
+ 'application/x-gnumeric' => ['gnumeric'],
+ 'application/x-gnuplot' => ['gp', 'gplt', 'gnuplot'],
+ 'application/x-go-sgf' => ['sgf'],
+ 'application/x-gpx' => ['gpx'],
+ 'application/x-gpx+xml' => ['gpx'],
+ 'application/x-gramps-xml' => ['gramps'],
+ 'application/x-graphite' => ['gra'],
+ 'application/x-gtar' => ['gtar', 'tar', 'gem'],
+ 'application/x-gtk-builder' => ['ui'],
+ 'application/x-gz-font-linux-psf' => ['psf.gz'],
+ 'application/x-gzdvi' => ['dvi.gz'],
+ 'application/x-gzip' => ['gz'],
+ 'application/x-gzpdf' => ['pdf.gz'],
+ 'application/x-gzpostscript' => ['ps.gz'],
+ 'application/x-hdf' => ['hdf', 'hdf4', 'h4', 'hdf5', 'h5'],
+ 'application/x-hfe-file' => ['hfe'],
+ 'application/x-hfe-floppy-image' => ['hfe'],
+ 'application/x-hwp' => ['hwp'],
+ 'application/x-hwt' => ['hwt'],
+ 'application/x-ica' => ['ica'],
+ 'application/x-install-instructions' => ['install'],
+ 'application/x-ipynb+json' => ['ipynb'],
+ 'application/x-iso9660-appimage' => ['appimage'],
+ 'application/x-iso9660-image' => ['iso', 'iso9660'],
+ 'application/x-it87' => ['it87'],
+ 'application/x-iwork-keynote-sffkey' => ['key'],
+ 'application/x-jar' => ['jar'],
+ 'application/x-java' => ['class'],
+ 'application/x-java-archive' => ['jar'],
+ 'application/x-java-class' => ['class'],
+ 'application/x-java-jce-keystore' => ['jceks'],
+ 'application/x-java-jnlp-file' => ['jnlp'],
+ 'application/x-java-keystore' => ['jks', 'ks'],
+ 'application/x-java-pack200' => ['pack'],
+ 'application/x-java-vm' => ['class'],
+ 'application/x-javascript' => ['js', 'jsm', 'mjs'],
+ 'application/x-jbuilder-project' => ['jpr', 'jpx'],
+ 'application/x-karbon' => ['karbon'],
+ 'application/x-kchart' => ['chrt'],
+ 'application/x-kexi-connectiondata' => ['kexic'],
+ 'application/x-kexiproject-shortcut' => ['kexis'],
+ 'application/x-kexiproject-sqlite' => ['kexi'],
+ 'application/x-kexiproject-sqlite2' => ['kexi'],
+ 'application/x-kexiproject-sqlite3' => ['kexi'],
+ 'application/x-kformula' => ['kfo'],
+ 'application/x-killustrator' => ['kil'],
+ 'application/x-kivio' => ['flw'],
+ 'application/x-kontour' => ['kon'],
+ 'application/x-kpovmodeler' => ['kpm'],
+ 'application/x-kpresenter' => ['kpr', 'kpt'],
+ 'application/x-krita' => ['kra'],
+ 'application/x-kspread' => ['ksp'],
+ 'application/x-kugar' => ['kud'],
+ 'application/x-kword' => ['kwd', 'kwt'],
+ 'application/x-latex' => ['latex'],
+ 'application/x-lha' => ['lha', 'lzh'],
+ 'application/x-lhz' => ['lhz'],
+ 'application/x-linguist' => ['ts'],
+ 'application/x-lotus123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'application/x-lrzip' => ['lrz'],
+ 'application/x-lrzip-compressed-tar' => ['tar.lrz', 'tlrz'],
+ 'application/x-lyx' => ['lyx'],
+ 'application/x-lz4' => ['lz4'],
+ 'application/x-lz4-compressed-tar' => ['tar.lz4'],
+ 'application/x-lzh-compressed' => ['lzh', 'lha'],
+ 'application/x-lzip' => ['lz'],
+ 'application/x-lzip-compressed-tar' => ['tar.lz'],
+ 'application/x-lzma' => ['lzma'],
+ 'application/x-lzma-compressed-tar' => ['tar.lzma', 'tlz'],
+ 'application/x-lzop' => ['lzo'],
+ 'application/x-lzpdf' => ['pdf.lz'],
+ 'application/x-m4' => ['m4'],
+ 'application/x-magicpoint' => ['mgp'],
+ 'application/x-markaby' => ['mab'],
+ 'application/x-mathematica' => ['nb'],
+ 'application/x-mdb' => ['mdb'],
+ 'application/x-mie' => ['mie'],
+ 'application/x-mif' => ['mif'],
+ 'application/x-mimearchive' => ['mhtml', 'mht'],
+ 'application/x-mobipocket-ebook' => ['prc', 'mobi'],
+ 'application/x-ms-application' => ['application'],
+ 'application/x-ms-asx' => ['asx', 'wax', 'wvx', 'wmx'],
+ 'application/x-ms-dos-executable' => ['exe'],
+ 'application/x-ms-shortcut' => ['lnk'],
+ 'application/x-ms-wim' => ['wim', 'swm'],
+ 'application/x-ms-wmd' => ['wmd'],
+ 'application/x-ms-wmz' => ['wmz'],
+ 'application/x-ms-xbap' => ['xbap'],
+ 'application/x-msaccess' => ['mdb'],
+ 'application/x-msbinder' => ['obd'],
+ 'application/x-mscardfile' => ['crd'],
+ 'application/x-msclip' => ['clp'],
+ 'application/x-msdownload' => ['exe', 'dll', 'com', 'bat', 'msi'],
+ 'application/x-msexcel' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'],
+ 'application/x-msi' => ['msi'],
+ 'application/x-msmediaview' => ['mvb', 'm13', 'm14'],
+ 'application/x-msmetafile' => ['wmf', 'wmz', 'emf', 'emz'],
+ 'application/x-msmoney' => ['mny'],
+ 'application/x-mspowerpoint' => ['ppz', 'ppt', 'pps', 'pot'],
+ 'application/x-mspublisher' => ['pub'],
+ 'application/x-msschedule' => ['scd'],
+ 'application/x-msterminal' => ['trm'],
+ 'application/x-mswinurl' => ['url'],
+ 'application/x-msword' => ['doc'],
+ 'application/x-mswrite' => ['wri'],
+ 'application/x-msx-rom' => ['msx'],
+ 'application/x-n64-rom' => ['n64', 'z64', 'v64'],
+ 'application/x-navi-animation' => ['ani'],
+ 'application/x-neo-geo-pocket-color-rom' => ['ngc'],
+ 'application/x-neo-geo-pocket-rom' => ['ngp'],
+ 'application/x-nes-rom' => ['nes', 'nez', 'unf', 'unif'],
+ 'application/x-netcdf' => ['nc', 'cdf'],
+ 'application/x-netshow-channel' => ['nsc'],
+ 'application/x-nintendo-ds-rom' => ['nds'],
+ 'application/x-nzb' => ['nzb'],
+ 'application/x-object' => ['o'],
+ 'application/x-ogg' => ['ogx'],
+ 'application/x-oleo' => ['oleo'],
+ 'application/x-pagemaker' => ['p65', 'pm', 'pm6', 'pmd'],
+ 'application/x-pak' => ['pak'],
+ 'application/x-palm-database' => ['prc', 'pdb', 'pqa', 'oprc'],
+ 'application/x-par2' => ['PAR2', 'par2'],
+ 'application/x-partial-download' => ['wkdownload', 'crdownload', 'part'],
+ 'application/x-pc-engine-rom' => ['pce'],
+ 'application/x-pcap' => ['pcap', 'cap', 'dmp'],
+ 'application/x-pdf' => ['pdf'],
+ 'application/x-perl' => ['pl', 'PL', 'pm', 'al', 'perl', 'pod', 't'],
+ 'application/x-photoshop' => ['psd'],
+ 'application/x-php' => ['php', 'php3', 'php4', 'php5', 'phps'],
+ 'application/x-pkcs12' => ['p12', 'pfx'],
+ 'application/x-pkcs7-certificates' => ['p7b', 'spc'],
+ 'application/x-pkcs7-certreqresp' => ['p7r'],
+ 'application/x-planperfect' => ['pln'],
+ 'application/x-pocket-word' => ['psw'],
+ 'application/x-pw' => ['pw'],
+ 'application/x-python-bytecode' => ['pyc', 'pyo'],
+ 'application/x-qpress' => ['qp'],
+ 'application/x-qtiplot' => ['qti', 'qti.gz'],
+ 'application/x-quattropro' => ['wb1', 'wb2', 'wb3'],
+ 'application/x-quicktime-media-link' => ['qtl'],
+ 'application/x-quicktimeplayer' => ['qtl'],
+ 'application/x-qw' => ['qif'],
+ 'application/x-rar' => ['rar'],
+ 'application/x-rar-compressed' => ['rar'],
+ 'application/x-raw-disk-image' => ['raw-disk-image', 'img'],
+ 'application/x-raw-disk-image-xz-compressed' => ['raw-disk-image.xz', 'img.xz'],
+ 'application/x-raw-floppy-disk-image' => ['fd', 'qd'],
+ 'application/x-redhat-package-manager' => ['rpm'],
+ 'application/x-reject' => ['rej'],
+ 'application/x-research-info-systems' => ['ris'],
+ 'application/x-rnc' => ['rnc'],
+ 'application/x-rpm' => ['rpm'],
+ 'application/x-ruby' => ['rb'],
+ 'application/x-sami' => ['smi', 'sami'],
+ 'application/x-sap-file' => ['sap'],
+ 'application/x-saturn-rom' => ['bin', 'iso'],
+ 'application/x-sdp' => ['sdp'],
+ 'application/x-sega-cd-rom' => ['bin', 'iso'],
+ 'application/x-sg1000-rom' => ['sg'],
+ 'application/x-sh' => ['sh'],
+ 'application/x-shar' => ['shar'],
+ 'application/x-shared-library-la' => ['la'],
+ 'application/x-sharedlib' => ['so'],
+ 'application/x-shellscript' => ['sh'],
+ 'application/x-shockwave-flash' => ['swf', 'spl'],
+ 'application/x-shorten' => ['shn'],
+ 'application/x-siag' => ['siag'],
+ 'application/x-silverlight-app' => ['xap'],
+ 'application/x-sit' => ['sit'],
+ 'application/x-smaf' => ['mmf', 'smaf'],
+ 'application/x-sms-rom' => ['sms'],
+ 'application/x-snes-rom' => ['sfc', 'smc'],
+ 'application/x-source-rpm' => ['src.rpm', 'spm'],
+ 'application/x-spss-por' => ['por'],
+ 'application/x-spss-sav' => ['sav', 'zsav'],
+ 'application/x-spss-savefile' => ['sav', 'zsav'],
+ 'application/x-sql' => ['sql'],
+ 'application/x-sqlite2' => ['sqlite2'],
+ 'application/x-sqlite3' => ['sqlite3'],
+ 'application/x-srt' => ['srt'],
+ 'application/x-stuffit' => ['sit'],
+ 'application/x-stuffitx' => ['sitx'],
+ 'application/x-subrip' => ['srt'],
+ 'application/x-sv4cpio' => ['sv4cpio'],
+ 'application/x-sv4crc' => ['sv4crc'],
+ 'application/x-t3vm-image' => ['t3'],
+ 'application/x-t602' => ['602'],
+ 'application/x-tads' => ['gam'],
+ 'application/x-tar' => ['tar', 'gtar', 'gem'],
+ 'application/x-tarz' => ['tar.Z', 'taz'],
+ 'application/x-tcl' => ['tcl'],
+ 'application/x-tex' => ['tex', 'ltx', 'sty', 'cls', 'dtx', 'ins', 'latex'],
+ 'application/x-tex-gf' => ['gf'],
+ 'application/x-tex-pk' => ['pk'],
+ 'application/x-tex-tfm' => ['tfm'],
+ 'application/x-texinfo' => ['texinfo', 'texi'],
+ 'application/x-tgif' => ['obj'],
+ 'application/x-theme' => ['theme'],
+ 'application/x-thomson-cartridge-memo7' => ['m7'],
+ 'application/x-thomson-cassette' => ['k7'],
+ 'application/x-thomson-sap-image' => ['sap'],
+ 'application/x-trash' => ['bak', 'old', 'sik'],
+ 'application/x-trig' => ['trig'],
+ 'application/x-troff' => ['tr', 'roff', 't'],
+ 'application/x-troff-man' => ['man'],
+ 'application/x-tzo' => ['tar.lzo', 'tzo'],
+ 'application/x-ufraw' => ['ufraw'],
+ 'application/x-ustar' => ['ustar'],
+ 'application/x-virtual-boy-rom' => ['vb'],
+ 'application/x-vnd.kde.kexi' => ['kexi'],
+ 'application/x-wais-source' => ['src'],
+ 'application/x-wbfs' => ['iso'],
+ 'application/x-wia' => ['iso'],
+ 'application/x-wii-iso-image' => ['iso'],
+ 'application/x-wii-rom' => ['iso'],
+ 'application/x-wii-wad' => ['wad'],
+ 'application/x-windows-themepack' => ['themepack'],
+ 'application/x-wmf' => ['wmf'],
+ 'application/x-wonderswan-color-rom' => ['wsc'],
+ 'application/x-wonderswan-rom' => ['ws'],
+ 'application/x-wordperfect' => ['wp', 'wp4', 'wp5', 'wp6', 'wpd', 'wpp'],
+ 'application/x-wpg' => ['wpg'],
+ 'application/x-wwf' => ['wwf'],
+ 'application/x-x509-ca-cert' => ['der', 'crt', 'cert', 'pem'],
+ 'application/x-xar' => ['xar', 'pkg'],
+ 'application/x-xbel' => ['xbel'],
+ 'application/x-xfig' => ['fig'],
+ 'application/x-xliff' => ['xlf', 'xliff'],
+ 'application/x-xliff+xml' => ['xlf'],
+ 'application/x-xpinstall' => ['xpi'],
+ 'application/x-xspf+xml' => ['xspf'],
+ 'application/x-xz' => ['xz'],
+ 'application/x-xz-compressed-tar' => ['tar.xz', 'txz'],
+ 'application/x-xzpdf' => ['pdf.xz'],
+ 'application/x-yaml' => ['yaml', 'yml'],
+ 'application/x-zip' => ['zip'],
+ 'application/x-zip-compressed' => ['zip'],
+ 'application/x-zip-compressed-fb2' => ['fb2.zip'],
+ 'application/x-zmachine' => ['z1', 'z2', 'z3', 'z4', 'z5', 'z6', 'z7', 'z8'],
+ 'application/x-zoo' => ['zoo'],
+ 'application/xaml+xml' => ['xaml'],
+ 'application/xcap-diff+xml' => ['xdf'],
+ 'application/xenc+xml' => ['xenc'],
+ 'application/xhtml+xml' => ['xhtml', 'xht'],
+ 'application/xliff+xml' => ['xlf', 'xliff'],
+ 'application/xml' => ['xml', 'xsl', 'xbl', 'xsd', 'rng'],
+ 'application/xml-dtd' => ['dtd'],
+ 'application/xml-external-parsed-entity' => ['ent'],
+ 'application/xop+xml' => ['xop'],
+ 'application/xproc+xml' => ['xpl'],
+ 'application/xps' => ['oxps', 'xps'],
+ 'application/xslt+xml' => ['xslt', 'xsl'],
+ 'application/xspf+xml' => ['xspf'],
+ 'application/xv+xml' => ['mxml', 'xhvml', 'xvml', 'xvm'],
+ 'application/yang' => ['yang'],
+ 'application/yin+xml' => ['yin'],
+ 'application/zip' => ['zip'],
+ 'application/zlib' => ['zz'],
+ 'audio/3gpp' => ['3gp', '3gpp', '3ga'],
+ 'audio/3gpp-encrypted' => ['3gp', '3gpp', '3ga'],
+ 'audio/3gpp2' => ['3g2', '3gp2', '3gpp2'],
+ 'audio/aac' => ['aac', 'adts', 'ass'],
+ 'audio/ac3' => ['ac3'],
+ 'audio/adpcm' => ['adp'],
+ 'audio/amr' => ['amr'],
+ 'audio/amr-encrypted' => ['amr'],
+ 'audio/amr-wb' => ['awb'],
+ 'audio/amr-wb-encrypted' => ['awb'],
+ 'audio/annodex' => ['axa'],
+ 'audio/basic' => ['au', 'snd'],
+ 'audio/flac' => ['flac'],
+ 'audio/imelody' => ['imy', 'ime'],
+ 'audio/m3u' => ['m3u', 'm3u8', 'vlc'],
+ 'audio/m4a' => ['m4a', 'f4a'],
+ 'audio/midi' => ['mid', 'midi', 'kar', 'rmi'],
+ 'audio/mobile-xmf' => ['xmf'],
+ 'audio/mp2' => ['mp2'],
+ 'audio/mp3' => ['mp3', 'mpga'],
+ 'audio/mp4' => ['m4a', 'mp4a', 'f4a'],
+ 'audio/mpeg' => ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'],
+ 'audio/mpegurl' => ['m3u', 'm3u8', 'vlc'],
+ 'audio/ogg' => ['oga', 'ogg', 'spx', 'opus'],
+ 'audio/prs.sid' => ['sid', 'psid'],
+ 'audio/s3m' => ['s3m'],
+ 'audio/scpls' => ['pls'],
+ 'audio/silk' => ['sil'],
+ 'audio/tta' => ['tta'],
+ 'audio/usac' => ['loas', 'xhe'],
+ 'audio/vnd.audible' => ['aa', 'aax'],
+ 'audio/vnd.audible.aax' => ['aa', 'aax'],
+ 'audio/vnd.dece.audio' => ['uva', 'uvva'],
+ 'audio/vnd.digital-winds' => ['eol'],
+ 'audio/vnd.dra' => ['dra'],
+ 'audio/vnd.dts' => ['dts'],
+ 'audio/vnd.dts.hd' => ['dtshd'],
+ 'audio/vnd.lucent.voice' => ['lvp'],
+ 'audio/vnd.m-realaudio' => ['ra', 'rax'],
+ 'audio/vnd.ms-playready.media.pya' => ['pya'],
+ 'audio/vnd.nuera.ecelp4800' => ['ecelp4800'],
+ 'audio/vnd.nuera.ecelp7470' => ['ecelp7470'],
+ 'audio/vnd.nuera.ecelp9600' => ['ecelp9600'],
+ 'audio/vnd.rip' => ['rip'],
+ 'audio/vnd.rn-realaudio' => ['ra', 'rax'],
+ 'audio/vnd.wave' => ['wav'],
+ 'audio/vorbis' => ['oga', 'ogg'],
+ 'audio/wav' => ['wav'],
+ 'audio/webm' => ['weba'],
+ 'audio/wma' => ['wma'],
+ 'audio/x-aac' => ['aac', 'adts', 'ass'],
+ 'audio/x-aifc' => ['aifc', 'aiffc'],
+ 'audio/x-aiff' => ['aif', 'aiff', 'aifc'],
+ 'audio/x-aiffc' => ['aifc', 'aiffc'],
+ 'audio/x-amzxml' => ['amz'],
+ 'audio/x-annodex' => ['axa'],
+ 'audio/x-ape' => ['ape'],
+ 'audio/x-caf' => ['caf'],
+ 'audio/x-dts' => ['dts'],
+ 'audio/x-dtshd' => ['dtshd'],
+ 'audio/x-flac' => ['flac'],
+ 'audio/x-flac+ogg' => ['oga', 'ogg'],
+ 'audio/x-gsm' => ['gsm'],
+ 'audio/x-hx-aac-adts' => ['aac', 'adts', 'ass'],
+ 'audio/x-imelody' => ['imy', 'ime'],
+ 'audio/x-iriver-pla' => ['pla'],
+ 'audio/x-it' => ['it'],
+ 'audio/x-m3u' => ['m3u', 'm3u8', 'vlc'],
+ 'audio/x-m4a' => ['m4a', 'f4a'],
+ 'audio/x-m4b' => ['m4b', 'f4b'],
+ 'audio/x-m4r' => ['m4r'],
+ 'audio/x-matroska' => ['mka'],
+ 'audio/x-midi' => ['mid', 'midi', 'kar'],
+ 'audio/x-minipsf' => ['minipsf'],
+ 'audio/x-mo3' => ['mo3'],
+ 'audio/x-mod' => ['mod', 'ult', 'uni', 'm15', 'mtm', '669', 'med'],
+ 'audio/x-mp2' => ['mp2'],
+ 'audio/x-mp3' => ['mp3', 'mpga'],
+ 'audio/x-mp3-playlist' => ['m3u', 'm3u8', 'vlc'],
+ 'audio/x-mpeg' => ['mp3', 'mpga'],
+ 'audio/x-mpegurl' => ['m3u', 'm3u8', 'vlc'],
+ 'audio/x-mpg' => ['mp3', 'mpga'],
+ 'audio/x-ms-asx' => ['asx', 'wax', 'wvx', 'wmx'],
+ 'audio/x-ms-wax' => ['wax'],
+ 'audio/x-ms-wma' => ['wma'],
+ 'audio/x-musepack' => ['mpc', 'mpp', 'mp+'],
+ 'audio/x-ogg' => ['oga', 'ogg', 'opus'],
+ 'audio/x-oggflac' => ['oga', 'ogg'],
+ 'audio/x-opus+ogg' => ['opus'],
+ 'audio/x-pn-audibleaudio' => ['aa', 'aax'],
+ 'audio/x-pn-realaudio' => ['ram', 'ra', 'rax'],
+ 'audio/x-pn-realaudio-plugin' => ['rmp'],
+ 'audio/x-psf' => ['psf'],
+ 'audio/x-psflib' => ['psflib'],
+ 'audio/x-rn-3gpp-amr' => ['3gp', '3gpp', '3ga'],
+ 'audio/x-rn-3gpp-amr-encrypted' => ['3gp', '3gpp', '3ga'],
+ 'audio/x-rn-3gpp-amr-wb' => ['3gp', '3gpp', '3ga'],
+ 'audio/x-rn-3gpp-amr-wb-encrypted' => ['3gp', '3gpp', '3ga'],
+ 'audio/x-s3m' => ['s3m'],
+ 'audio/x-scpls' => ['pls'],
+ 'audio/x-shorten' => ['shn'],
+ 'audio/x-speex' => ['spx'],
+ 'audio/x-speex+ogg' => ['oga', 'ogg'],
+ 'audio/x-stm' => ['stm'],
+ 'audio/x-tta' => ['tta'],
+ 'audio/x-voc' => ['voc'],
+ 'audio/x-vorbis' => ['oga', 'ogg'],
+ 'audio/x-vorbis+ogg' => ['oga', 'ogg'],
+ 'audio/x-wav' => ['wav'],
+ 'audio/x-wavpack' => ['wv', 'wvp'],
+ 'audio/x-wavpack-correction' => ['wvc'],
+ 'audio/x-xi' => ['xi'],
+ 'audio/x-xm' => ['xm'],
+ 'audio/x-xmf' => ['xmf'],
+ 'audio/xm' => ['xm'],
+ 'audio/xmf' => ['xmf'],
+ 'chemical/x-cdx' => ['cdx'],
+ 'chemical/x-cif' => ['cif'],
+ 'chemical/x-cmdf' => ['cmdf'],
+ 'chemical/x-cml' => ['cml'],
+ 'chemical/x-csml' => ['csml'],
+ 'chemical/x-xyz' => ['xyz'],
+ 'flv-application/octet-stream' => ['flv'],
+ 'font/collection' => ['ttc'],
+ 'font/otf' => ['otf'],
+ 'font/ttf' => ['ttf'],
+ 'font/woff' => ['woff', 'woff2'],
+ 'font/woff2' => ['woff2'],
+ 'image/bmp' => ['bmp', 'dib'],
+ 'image/cdr' => ['cdr'],
+ 'image/cgm' => ['cgm'],
+ 'image/emf' => ['emf'],
+ 'image/fax-g3' => ['g3'],
+ 'image/fits' => ['fits'],
+ 'image/g3fax' => ['g3'],
+ 'image/gif' => ['gif'],
+ 'image/heic' => ['heic', 'heif'],
+ 'image/heic-sequence' => ['heic', 'heif'],
+ 'image/heif' => ['heic', 'heif'],
+ 'image/heif-sequence' => ['heic', 'heif'],
+ 'image/ico' => ['ico'],
+ 'image/icon' => ['ico'],
+ 'image/ief' => ['ief'],
+ 'image/jp2' => ['jp2', 'jpg2'],
+ 'image/jpeg' => ['jpeg', 'jpg', 'jpe'],
+ 'image/jpeg2000' => ['jp2', 'jpg2'],
+ 'image/jpeg2000-image' => ['jp2', 'jpg2'],
+ 'image/jpm' => ['jpm', 'jpgm'],
+ 'image/jpx' => ['jpf', 'jpx'],
+ 'image/ktx' => ['ktx'],
+ 'image/openraster' => ['ora'],
+ 'image/pdf' => ['pdf'],
+ 'image/photoshop' => ['psd'],
+ 'image/pjpeg' => ['jpeg', 'jpg', 'jpe'],
+ 'image/png' => ['png'],
+ 'image/prs.btif' => ['btif'],
+ 'image/psd' => ['psd'],
+ 'image/rle' => ['rle'],
+ 'image/sgi' => ['sgi'],
+ 'image/svg' => ['svg'],
+ 'image/svg+xml' => ['svg', 'svgz'],
+ 'image/svg+xml-compressed' => ['svgz'],
+ 'image/tiff' => ['tiff', 'tif'],
+ 'image/vnd.adobe.photoshop' => ['psd'],
+ 'image/vnd.dece.graphic' => ['uvi', 'uvvi', 'uvg', 'uvvg'],
+ 'image/vnd.djvu' => ['djvu', 'djv'],
+ 'image/vnd.djvu+multipage' => ['djvu', 'djv'],
+ 'image/vnd.dvb.subtitle' => ['sub'],
+ 'image/vnd.dwg' => ['dwg'],
+ 'image/vnd.dxf' => ['dxf'],
+ 'image/vnd.fastbidsheet' => ['fbs'],
+ 'image/vnd.fpx' => ['fpx'],
+ 'image/vnd.fst' => ['fst'],
+ 'image/vnd.fujixerox.edmics-mmr' => ['mmr'],
+ 'image/vnd.fujixerox.edmics-rlc' => ['rlc'],
+ 'image/vnd.microsoft.icon' => ['ico'],
+ 'image/vnd.ms-modi' => ['mdi'],
+ 'image/vnd.ms-photo' => ['wdp'],
+ 'image/vnd.net-fpx' => ['npx'],
+ 'image/vnd.rn-realpix' => ['rp'],
+ 'image/vnd.wap.wbmp' => ['wbmp'],
+ 'image/vnd.xiff' => ['xif'],
+ 'image/vnd.zbrush.pcx' => ['pcx'],
+ 'image/webp' => ['webp'],
+ 'image/wmf' => ['wmf'],
+ 'image/x-3ds' => ['3ds'],
+ 'image/x-adobe-dng' => ['dng'],
+ 'image/x-applix-graphics' => ['ag'],
+ 'image/x-bmp' => ['bmp', 'dib'],
+ 'image/x-bzeps' => ['eps.bz2', 'epsi.bz2', 'epsf.bz2'],
+ 'image/x-canon-cr2' => ['cr2'],
+ 'image/x-canon-crw' => ['crw'],
+ 'image/x-cdr' => ['cdr'],
+ 'image/x-cmu-raster' => ['ras'],
+ 'image/x-cmx' => ['cmx'],
+ 'image/x-compressed-xcf' => ['xcf.gz', 'xcf.bz2'],
+ 'image/x-dds' => ['dds'],
+ 'image/x-djvu' => ['djvu', 'djv'],
+ 'image/x-emf' => ['emf'],
+ 'image/x-eps' => ['eps', 'epsi', 'epsf'],
+ 'image/x-exr' => ['exr'],
+ 'image/x-fits' => ['fits'],
+ 'image/x-freehand' => ['fh', 'fhc', 'fh4', 'fh5', 'fh7'],
+ 'image/x-fuji-raf' => ['raf'],
+ 'image/x-gimp-gbr' => ['gbr'],
+ 'image/x-gimp-gih' => ['gih'],
+ 'image/x-gimp-pat' => ['pat'],
+ 'image/x-gzeps' => ['eps.gz', 'epsi.gz', 'epsf.gz'],
+ 'image/x-icb' => ['tga', 'icb', 'tpic', 'vda', 'vst'],
+ 'image/x-icns' => ['icns'],
+ 'image/x-ico' => ['ico'],
+ 'image/x-icon' => ['ico'],
+ 'image/x-iff' => ['iff', 'ilbm', 'lbm'],
+ 'image/x-ilbm' => ['iff', 'ilbm', 'lbm'],
+ 'image/x-jng' => ['jng'],
+ 'image/x-jp2-codestream' => ['j2c', 'j2k', 'jpc'],
+ 'image/x-jpeg2000-image' => ['jp2', 'jpg2'],
+ 'image/x-kodak-dcr' => ['dcr'],
+ 'image/x-kodak-k25' => ['k25'],
+ 'image/x-kodak-kdc' => ['kdc'],
+ 'image/x-lwo' => ['lwo', 'lwob'],
+ 'image/x-lws' => ['lws'],
+ 'image/x-macpaint' => ['pntg'],
+ 'image/x-minolta-mrw' => ['mrw'],
+ 'image/x-mrsid-image' => ['sid'],
+ 'image/x-ms-bmp' => ['bmp', 'dib'],
+ 'image/x-msod' => ['msod'],
+ 'image/x-nikon-nef' => ['nef'],
+ 'image/x-olympus-orf' => ['orf'],
+ 'image/x-panasonic-raw' => ['raw'],
+ 'image/x-panasonic-raw2' => ['rw2'],
+ 'image/x-panasonic-rw' => ['raw'],
+ 'image/x-panasonic-rw2' => ['rw2'],
+ 'image/x-pcx' => ['pcx'],
+ 'image/x-pentax-pef' => ['pef'],
+ 'image/x-photo-cd' => ['pcd'],
+ 'image/x-photoshop' => ['psd'],
+ 'image/x-pict' => ['pic', 'pct', 'pict', 'pict1', 'pict2'],
+ 'image/x-portable-anymap' => ['pnm'],
+ 'image/x-portable-bitmap' => ['pbm'],
+ 'image/x-portable-graymap' => ['pgm'],
+ 'image/x-portable-pixmap' => ['ppm'],
+ 'image/x-psd' => ['psd'],
+ 'image/x-quicktime' => ['qtif', 'qif'],
+ 'image/x-rgb' => ['rgb'],
+ 'image/x-sgi' => ['sgi'],
+ 'image/x-sigma-x3f' => ['x3f'],
+ 'image/x-skencil' => ['sk', 'sk1'],
+ 'image/x-sony-arw' => ['arw'],
+ 'image/x-sony-sr2' => ['sr2'],
+ 'image/x-sony-srf' => ['srf'],
+ 'image/x-sun-raster' => ['sun'],
+ 'image/x-tga' => ['tga', 'icb', 'tpic', 'vda', 'vst'],
+ 'image/x-win-bitmap' => ['cur'],
+ 'image/x-win-metafile' => ['wmf'],
+ 'image/x-wmf' => ['wmf'],
+ 'image/x-xbitmap' => ['xbm'],
+ 'image/x-xcf' => ['xcf'],
+ 'image/x-xfig' => ['fig'],
+ 'image/x-xpixmap' => ['xpm'],
+ 'image/x-xpm' => ['xpm'],
+ 'image/x-xwindowdump' => ['xwd'],
+ 'image/x.djvu' => ['djvu', 'djv'],
+ 'message/rfc822' => ['eml', 'mime'],
+ 'model/iges' => ['igs', 'iges'],
+ 'model/mesh' => ['msh', 'mesh', 'silo'],
+ 'model/stl' => ['stl'],
+ 'model/vnd.collada+xml' => ['dae'],
+ 'model/vnd.dwf' => ['dwf'],
+ 'model/vnd.gdl' => ['gdl'],
+ 'model/vnd.gtw' => ['gtw'],
+ 'model/vnd.mts' => ['mts'],
+ 'model/vnd.vtu' => ['vtu'],
+ 'model/vrml' => ['wrl', 'vrml', 'vrm'],
+ 'model/x.stl-ascii' => ['stl'],
+ 'model/x.stl-binary' => ['stl'],
+ 'model/x3d+binary' => ['x3db', 'x3dbz'],
+ 'model/x3d+vrml' => ['x3dv', 'x3dvz'],
+ 'model/x3d+xml' => ['x3d', 'x3dz'],
+ 'text/cache-manifest' => ['appcache', 'manifest'],
+ 'text/calendar' => ['ics', 'ifb', 'vcs'],
+ 'text/css' => ['css'],
+ 'text/csv' => ['csv'],
+ 'text/csv-schema' => ['csvs'],
+ 'text/directory' => ['vcard', 'vcf', 'vct', 'gcrd'],
+ 'text/ecmascript' => ['es'],
+ 'text/gedcom' => ['ged', 'gedcom'],
+ 'text/google-video-pointer' => ['gvp'],
+ 'text/html' => ['html', 'htm'],
+ 'text/ico' => ['ico'],
+ 'text/javascript' => ['js', 'jsm', 'mjs'],
+ 'text/markdown' => ['md', 'mkd', 'markdown'],
+ 'text/mathml' => ['mml'],
+ 'text/n3' => ['n3'],
+ 'text/plain' => ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'asc'],
+ 'text/prs.lines.tag' => ['dsc'],
+ 'text/rdf' => ['rdf', 'rdfs', 'owl'],
+ 'text/richtext' => ['rtx'],
+ 'text/rss' => ['rss'],
+ 'text/rtf' => ['rtf'],
+ 'text/rust' => ['rs'],
+ 'text/sgml' => ['sgml', 'sgm'],
+ 'text/spreadsheet' => ['sylk', 'slk'],
+ 'text/tab-separated-values' => ['tsv'],
+ 'text/troff' => ['t', 'tr', 'roff', 'man', 'me', 'ms'],
+ 'text/turtle' => ['ttl'],
+ 'text/uri-list' => ['uri', 'uris', 'urls'],
+ 'text/vcard' => ['vcard', 'vcf', 'vct', 'gcrd'],
+ 'text/vnd.curl' => ['curl'],
+ 'text/vnd.curl.dcurl' => ['dcurl'],
+ 'text/vnd.curl.mcurl' => ['mcurl'],
+ 'text/vnd.curl.scurl' => ['scurl'],
+ 'text/vnd.dvb.subtitle' => ['sub'],
+ 'text/vnd.fly' => ['fly'],
+ 'text/vnd.fmi.flexstor' => ['flx'],
+ 'text/vnd.graphviz' => ['gv', 'dot'],
+ 'text/vnd.in3d.3dml' => ['3dml'],
+ 'text/vnd.in3d.spot' => ['spot'],
+ 'text/vnd.qt.linguist' => ['ts'],
+ 'text/vnd.rn-realtext' => ['rt'],
+ 'text/vnd.sun.j2me.app-descriptor' => ['jad'],
+ 'text/vnd.trolltech.linguist' => ['ts'],
+ 'text/vnd.wap.wml' => ['wml'],
+ 'text/vnd.wap.wmlscript' => ['wmls'],
+ 'text/vtt' => ['vtt'],
+ 'text/x-adasrc' => ['adb', 'ads'],
+ 'text/x-asm' => ['s', 'asm'],
+ 'text/x-bibtex' => ['bib'],
+ 'text/x-c' => ['c', 'cc', 'cxx', 'cpp', 'h', 'hh', 'dic'],
+ 'text/x-c++hdr' => ['hh', 'hp', 'hpp', 'h++', 'hxx'],
+ 'text/x-c++src' => ['cpp', 'cxx', 'cc', 'C', 'c++'],
+ 'text/x-chdr' => ['h'],
+ 'text/x-cmake' => ['cmake'],
+ 'text/x-cobol' => ['cbl', 'cob'],
+ 'text/x-comma-separated-values' => ['csv'],
+ 'text/x-csharp' => ['cs'],
+ 'text/x-csrc' => ['c'],
+ 'text/x-csv' => ['csv'],
+ 'text/x-dbus-service' => ['service'],
+ 'text/x-dcl' => ['dcl'],
+ 'text/x-diff' => ['diff', 'patch'],
+ 'text/x-dsl' => ['dsl'],
+ 'text/x-dsrc' => ['d', 'di'],
+ 'text/x-dtd' => ['dtd'],
+ 'text/x-eiffel' => ['e', 'eif'],
+ 'text/x-emacs-lisp' => ['el'],
+ 'text/x-erlang' => ['erl'],
+ 'text/x-fortran' => ['f', 'for', 'f77', 'f90', 'f95'],
+ 'text/x-genie' => ['gs'],
+ 'text/x-gettext-translation' => ['po'],
+ 'text/x-gettext-translation-template' => ['pot'],
+ 'text/x-gherkin' => ['feature'],
+ 'text/x-go' => ['go'],
+ 'text/x-google-video-pointer' => ['gvp'],
+ 'text/x-haskell' => ['hs'],
+ 'text/x-idl' => ['idl'],
+ 'text/x-imelody' => ['imy', 'ime'],
+ 'text/x-iptables' => ['iptables'],
+ 'text/x-java' => ['java'],
+ 'text/x-java-source' => ['java'],
+ 'text/x-ldif' => ['ldif'],
+ 'text/x-lilypond' => ['ly'],
+ 'text/x-literate-haskell' => ['lhs'],
+ 'text/x-log' => ['log'],
+ 'text/x-lua' => ['lua'],
+ 'text/x-lyx' => ['lyx'],
+ 'text/x-makefile' => ['mk', 'mak'],
+ 'text/x-markdown' => ['md', 'mkd', 'markdown'],
+ 'text/x-matlab' => ['m'],
+ 'text/x-microdvd' => ['sub'],
+ 'text/x-moc' => ['moc'],
+ 'text/x-modelica' => ['mo'],
+ 'text/x-mof' => ['mof'],
+ 'text/x-mpsub' => ['sub'],
+ 'text/x-mrml' => ['mrml', 'mrl'],
+ 'text/x-ms-regedit' => ['reg'],
+ 'text/x-mup' => ['mup', 'not'],
+ 'text/x-nfo' => ['nfo'],
+ 'text/x-objcsrc' => ['m'],
+ 'text/x-ocaml' => ['ml', 'mli'],
+ 'text/x-ocl' => ['ocl'],
+ 'text/x-octave' => ['m'],
+ 'text/x-ooc' => ['ooc'],
+ 'text/x-opencl-src' => ['cl'],
+ 'text/x-opml' => ['opml'],
+ 'text/x-opml+xml' => ['opml'],
+ 'text/x-pascal' => ['p', 'pas'],
+ 'text/x-patch' => ['diff', 'patch'],
+ 'text/x-perl' => ['pl', 'PL', 'pm', 'al', 'perl', 'pod', 't'],
+ 'text/x-po' => ['po'],
+ 'text/x-pot' => ['pot'],
+ 'text/x-python' => ['py', 'pyx', 'wsgi'],
+ 'text/x-python3' => ['py', 'py3', 'py3x'],
+ 'text/x-qml' => ['qml', 'qmltypes', 'qmlproject'],
+ 'text/x-reject' => ['rej'],
+ 'text/x-rpm-spec' => ['spec'],
+ 'text/x-sass' => ['sass'],
+ 'text/x-scala' => ['scala'],
+ 'text/x-scheme' => ['scm', 'ss'],
+ 'text/x-scss' => ['scss'],
+ 'text/x-setext' => ['etx'],
+ 'text/x-sfv' => ['sfv'],
+ 'text/x-sh' => ['sh'],
+ 'text/x-sql' => ['sql'],
+ 'text/x-ssa' => ['ssa', 'ass'],
+ 'text/x-subviewer' => ['sub'],
+ 'text/x-svhdr' => ['svh'],
+ 'text/x-svsrc' => ['sv'],
+ 'text/x-systemd-unit' => ['automount', 'device', 'mount', 'path', 'scope', 'service', 'slice', 'socket', 'swap', 'target', 'timer'],
+ 'text/x-tcl' => ['tcl', 'tk'],
+ 'text/x-tex' => ['tex', 'ltx', 'sty', 'cls', 'dtx', 'ins', 'latex'],
+ 'text/x-texinfo' => ['texi', 'texinfo'],
+ 'text/x-troff' => ['tr', 'roff', 't'],
+ 'text/x-troff-me' => ['me'],
+ 'text/x-troff-mm' => ['mm'],
+ 'text/x-troff-ms' => ['ms'],
+ 'text/x-twig' => ['twig'],
+ 'text/x-txt2tags' => ['t2t'],
+ 'text/x-uil' => ['uil'],
+ 'text/x-uuencode' => ['uu', 'uue'],
+ 'text/x-vala' => ['vala', 'vapi'],
+ 'text/x-vcalendar' => ['vcs', 'ics'],
+ 'text/x-vcard' => ['vcf', 'vcard', 'vct', 'gcrd'],
+ 'text/x-verilog' => ['v'],
+ 'text/x-vhdl' => ['vhd', 'vhdl'],
+ 'text/x-xmi' => ['xmi'],
+ 'text/x-xslfo' => ['fo', 'xslfo'],
+ 'text/x-yaml' => ['yaml', 'yml'],
+ 'text/x.gcode' => ['gcode'],
+ 'text/xml' => ['xml', 'xbl', 'xsd', 'rng'],
+ 'text/xml-external-parsed-entity' => ['ent'],
+ 'text/yaml' => ['yaml', 'yml'],
+ 'video/3gp' => ['3gp', '3gpp', '3ga'],
+ 'video/3gpp' => ['3gp', '3gpp', '3ga'],
+ 'video/3gpp-encrypted' => ['3gp', '3gpp', '3ga'],
+ 'video/3gpp2' => ['3g2', '3gp2', '3gpp2'],
+ 'video/annodex' => ['axv'],
+ 'video/avi' => ['avi', 'avf', 'divx'],
+ 'video/divx' => ['avi', 'avf', 'divx'],
+ 'video/dv' => ['dv'],
+ 'video/fli' => ['fli', 'flc'],
+ 'video/flv' => ['flv'],
+ 'video/h261' => ['h261'],
+ 'video/h263' => ['h263'],
+ 'video/h264' => ['h264'],
+ 'video/jpeg' => ['jpgv'],
+ 'video/jpm' => ['jpm', 'jpgm'],
+ 'video/mj2' => ['mj2', 'mjp2'],
+ 'video/mp2t' => ['m2t', 'm2ts', 'ts', 'mts', 'cpi', 'clpi', 'mpl', 'mpls', 'bdm', 'bdmv'],
+ 'video/mp4' => ['mp4', 'mp4v', 'mpg4', 'm4v', 'f4v', 'lrv'],
+ 'video/mp4v-es' => ['mp4', 'm4v', 'f4v', 'lrv'],
+ 'video/mpeg' => ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v', 'mp2', 'vob'],
+ 'video/mpeg-system' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'],
+ 'video/msvideo' => ['avi', 'avf', 'divx'],
+ 'video/ogg' => ['ogv', 'ogg'],
+ 'video/quicktime' => ['qt', 'mov', 'moov', 'qtvr'],
+ 'video/vivo' => ['viv', 'vivo'],
+ 'video/vnd.dece.hd' => ['uvh', 'uvvh'],
+ 'video/vnd.dece.mobile' => ['uvm', 'uvvm'],
+ 'video/vnd.dece.pd' => ['uvp', 'uvvp'],
+ 'video/vnd.dece.sd' => ['uvs', 'uvvs'],
+ 'video/vnd.dece.video' => ['uvv', 'uvvv'],
+ 'video/vnd.divx' => ['avi', 'avf', 'divx'],
+ 'video/vnd.dvb.file' => ['dvb'],
+ 'video/vnd.fvt' => ['fvt'],
+ 'video/vnd.mpegurl' => ['mxu', 'm4u', 'm1u'],
+ 'video/vnd.ms-playready.media.pyv' => ['pyv'],
+ 'video/vnd.rn-realvideo' => ['rv', 'rvx'],
+ 'video/vnd.uvvu.mp4' => ['uvu', 'uvvu'],
+ 'video/vnd.vivo' => ['viv', 'vivo'],
+ 'video/webm' => ['webm'],
+ 'video/x-anim' => ['anim[1-9j]'],
+ 'video/x-annodex' => ['axv'],
+ 'video/x-avi' => ['avi', 'avf', 'divx'],
+ 'video/x-f4v' => ['f4v'],
+ 'video/x-fli' => ['fli', 'flc'],
+ 'video/x-flic' => ['fli', 'flc'],
+ 'video/x-flv' => ['flv'],
+ 'video/x-javafx' => ['fxm'],
+ 'video/x-m4v' => ['m4v', 'mp4', 'f4v', 'lrv'],
+ 'video/x-matroska' => ['mkv', 'mk3d', 'mks'],
+ 'video/x-matroska-3d' => ['mk3d'],
+ 'video/x-mjpeg' => ['mjpeg', 'mjpg'],
+ 'video/x-mng' => ['mng'],
+ 'video/x-mpeg' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'],
+ 'video/x-mpeg-system' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'],
+ 'video/x-mpeg2' => ['mpeg', 'mpg', 'mp2', 'mpe', 'vob'],
+ 'video/x-mpegurl' => ['m1u', 'm4u', 'mxu'],
+ 'video/x-ms-asf' => ['asf', 'asx'],
+ 'video/x-ms-asf-plugin' => ['asf'],
+ 'video/x-ms-vob' => ['vob'],
+ 'video/x-ms-wax' => ['asx', 'wax', 'wvx', 'wmx'],
+ 'video/x-ms-wm' => ['wm', 'asf'],
+ 'video/x-ms-wmv' => ['wmv'],
+ 'video/x-ms-wmx' => ['wmx', 'asx', 'wax', 'wvx'],
+ 'video/x-ms-wvx' => ['wvx', 'asx', 'wax', 'wmx'],
+ 'video/x-msvideo' => ['avi', 'avf', 'divx'],
+ 'video/x-nsv' => ['nsv'],
+ 'video/x-ogg' => ['ogv', 'ogg'],
+ 'video/x-ogm' => ['ogm'],
+ 'video/x-ogm+ogg' => ['ogm'],
+ 'video/x-real-video' => ['rv', 'rvx'],
+ 'video/x-sgi-movie' => ['movie'],
+ 'video/x-smv' => ['smv'],
+ 'video/x-theora' => ['ogg'],
+ 'video/x-theora+ogg' => ['ogg'],
+ 'x-conference/x-cooltalk' => ['ice'],
+ 'x-epoc/x-sisx-app' => ['sisx'],
+ 'zz-application/zz-winassoc-123' => ['123', 'wk1', 'wk3', 'wk4', 'wks'],
+ 'zz-application/zz-winassoc-cab' => ['cab'],
+ 'zz-application/zz-winassoc-cdr' => ['cdr'],
+ 'zz-application/zz-winassoc-doc' => ['doc'],
+ 'zz-application/zz-winassoc-hlp' => ['hlp'],
+ 'zz-application/zz-winassoc-mdb' => ['mdb'],
+ 'zz-application/zz-winassoc-uu' => ['uue'],
+ 'zz-application/zz-winassoc-xls' => ['xls', 'xlc', 'xll', 'xlm', 'xlw', 'xla', 'xlt', 'xld'],
+ ];
+
+ private static $reverseMap = [
+ '32x' => ['application/x-genesis-32x-rom'],
+ '3dml' => ['text/vnd.in3d.3dml'],
+ '3ds' => ['image/x-3ds'],
+ '3g2' => ['audio/3gpp2', 'video/3gpp2'],
+ '3ga' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'],
+ '3gp' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'],
+ '3gp2' => ['audio/3gpp2', 'video/3gpp2'],
+ '3gpp' => ['audio/3gpp', 'audio/3gpp-encrypted', 'audio/x-rn-3gpp-amr', 'audio/x-rn-3gpp-amr-encrypted', 'audio/x-rn-3gpp-amr-wb', 'audio/x-rn-3gpp-amr-wb-encrypted', 'video/3gp', 'video/3gpp', 'video/3gpp-encrypted'],
+ '3gpp2' => ['audio/3gpp2', 'video/3gpp2'],
+ '7z' => ['application/x-7z-compressed'],
+ 'BLEND' => ['application/x-blender'],
+ 'C' => ['text/x-c++src'],
+ 'PAR2' => ['application/x-par2'],
+ 'PL' => ['application/x-perl', 'text/x-perl'],
+ 'Z' => ['application/x-compress'],
+ 'a' => ['application/x-archive'],
+ 'a26' => ['application/x-atari-2600-rom'],
+ 'a78' => ['application/x-atari-7800-rom'],
+ 'aa' => ['audio/vnd.audible', 'audio/vnd.audible.aax', 'audio/x-pn-audibleaudio'],
+ 'aab' => ['application/x-authorware-bin'],
+ 'aac' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts'],
+ 'aam' => ['application/x-authorware-map'],
+ 'aas' => ['application/x-authorware-seg'],
+ 'aax' => ['audio/vnd.audible', 'audio/vnd.audible.aax', 'audio/x-pn-audibleaudio'],
+ 'abw' => ['application/x-abiword'],
+ 'abw.CRASHED' => ['application/x-abiword'],
+ 'abw.gz' => ['application/x-abiword'],
+ 'ac' => ['application/pkix-attr-cert'],
+ 'ac3' => ['audio/ac3'],
+ 'acc' => ['application/vnd.americandynamics.acc'],
+ 'ace' => ['application/x-ace', 'application/x-ace-compressed'],
+ 'acu' => ['application/vnd.acucobol'],
+ 'acutc' => ['application/vnd.acucorp'],
+ 'adb' => ['text/x-adasrc'],
+ 'adf' => ['application/x-amiga-disk-format'],
+ 'adp' => ['audio/adpcm'],
+ 'ads' => ['text/x-adasrc'],
+ 'adts' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts'],
+ 'aep' => ['application/vnd.audiograph'],
+ 'afm' => ['application/x-font-afm', 'application/x-font-type1'],
+ 'afp' => ['application/vnd.ibm.modcap'],
+ 'ag' => ['image/x-applix-graphics'],
+ 'agb' => ['application/x-gba-rom'],
+ 'ahead' => ['application/vnd.ahead.space'],
+ 'ai' => ['application/illustrator', 'application/postscript', 'application/vnd.adobe.illustrator'],
+ 'aif' => ['audio/x-aiff'],
+ 'aifc' => ['audio/x-aifc', 'audio/x-aiff', 'audio/x-aiffc'],
+ 'aiff' => ['audio/x-aiff'],
+ 'aiffc' => ['audio/x-aifc', 'audio/x-aiffc'],
+ 'air' => ['application/vnd.adobe.air-application-installer-package+zip'],
+ 'ait' => ['application/vnd.dvb.ait'],
+ 'al' => ['application/x-perl', 'text/x-perl'],
+ 'alz' => ['application/x-alz'],
+ 'ami' => ['application/vnd.amiga.ami'],
+ 'amr' => ['audio/amr', 'audio/amr-encrypted'],
+ 'amz' => ['audio/x-amzxml'],
+ 'ani' => ['application/x-navi-animation'],
+ 'anim[1-9j]' => ['video/x-anim'],
+ 'anx' => ['application/annodex', 'application/x-annodex'],
+ 'ape' => ['audio/x-ape'],
+ 'apk' => ['application/vnd.android.package-archive'],
+ 'appcache' => ['text/cache-manifest'],
+ 'appimage' => ['application/vnd.appimage', 'application/x-iso9660-appimage'],
+ 'application' => ['application/x-ms-application'],
+ 'apr' => ['application/vnd.lotus-approach'],
+ 'aps' => ['application/postscript'],
+ 'ar' => ['application/x-archive'],
+ 'arc' => ['application/x-freearc'],
+ 'arj' => ['application/x-arj'],
+ 'arw' => ['image/x-sony-arw'],
+ 'as' => ['application/x-applix-spreadsheet'],
+ 'asc' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature', 'text/plain'],
+ 'asf' => ['application/vnd.ms-asf', 'video/x-ms-asf', 'video/x-ms-asf-plugin', 'video/x-ms-wm'],
+ 'asm' => ['text/x-asm'],
+ 'aso' => ['application/vnd.accpac.simply.aso'],
+ 'asp' => ['application/x-asp'],
+ 'ass' => ['audio/aac', 'audio/x-aac', 'audio/x-hx-aac-adts', 'text/x-ssa'],
+ 'asx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-asf', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'],
+ 'atc' => ['application/vnd.acucorp'],
+ 'atom' => ['application/atom+xml'],
+ 'atomcat' => ['application/atomcat+xml'],
+ 'atomsvc' => ['application/atomsvc+xml'],
+ 'atx' => ['application/vnd.antix.game-component'],
+ 'au' => ['audio/basic'],
+ 'automount' => ['text/x-systemd-unit'],
+ 'avf' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'],
+ 'avi' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'],
+ 'aw' => ['application/applixware', 'application/x-applix-word'],
+ 'awb' => ['audio/amr-wb', 'audio/amr-wb-encrypted'],
+ 'awk' => ['application/x-awk'],
+ 'axa' => ['audio/annodex', 'audio/x-annodex'],
+ 'axv' => ['video/annodex', 'video/x-annodex'],
+ 'azf' => ['application/vnd.airzip.filesecure.azf'],
+ 'azs' => ['application/vnd.airzip.filesecure.azs'],
+ 'azw' => ['application/vnd.amazon.ebook'],
+ 'bak' => ['application/x-trash'],
+ 'bat' => ['application/x-msdownload'],
+ 'bcpio' => ['application/x-bcpio'],
+ 'bdf' => ['application/x-font-bdf'],
+ 'bdm' => ['application/vnd.syncml.dm+wbxml', 'video/mp2t'],
+ 'bdmv' => ['video/mp2t'],
+ 'bed' => ['application/vnd.realvnc.bed'],
+ 'bh2' => ['application/vnd.fujitsu.oasysprs'],
+ 'bib' => ['text/x-bibtex'],
+ 'bin' => ['application/octet-stream', 'application/x-saturn-rom', 'application/x-sega-cd-rom'],
+ 'blb' => ['application/x-blorb'],
+ 'blend' => ['application/x-blender'],
+ 'blender' => ['application/x-blender'],
+ 'blorb' => ['application/x-blorb'],
+ 'bmi' => ['application/vnd.bmi'],
+ 'bmp' => ['image/bmp', 'image/x-bmp', 'image/x-ms-bmp'],
+ 'book' => ['application/vnd.framemaker'],
+ 'box' => ['application/vnd.previewsystems.box'],
+ 'boz' => ['application/x-bzip2'],
+ 'bpk' => ['application/octet-stream'],
+ 'bsdiff' => ['application/x-bsdiff'],
+ 'btif' => ['image/prs.btif'],
+ 'bz' => ['application/x-bzip', 'application/x-bzip2'],
+ 'bz2' => ['application/x-bz2', 'application/x-bzip', 'application/x-bzip2'],
+ 'c' => ['text/x-c', 'text/x-csrc'],
+ 'c++' => ['text/x-c++src'],
+ 'c11amc' => ['application/vnd.cluetrust.cartomobile-config'],
+ 'c11amz' => ['application/vnd.cluetrust.cartomobile-config-pkg'],
+ 'c4d' => ['application/vnd.clonk.c4group'],
+ 'c4f' => ['application/vnd.clonk.c4group'],
+ 'c4g' => ['application/vnd.clonk.c4group'],
+ 'c4p' => ['application/vnd.clonk.c4group'],
+ 'c4u' => ['application/vnd.clonk.c4group'],
+ 'cab' => ['application/vnd.ms-cab-compressed', 'zz-application/zz-winassoc-cab'],
+ 'caf' => ['audio/x-caf'],
+ 'cap' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'],
+ 'car' => ['application/vnd.curl.car'],
+ 'cat' => ['application/vnd.ms-pki.seccat'],
+ 'cb7' => ['application/x-cb7', 'application/x-cbr'],
+ 'cba' => ['application/x-cbr'],
+ 'cbl' => ['text/x-cobol'],
+ 'cbr' => ['application/vnd.comicbook-rar', 'application/x-cbr'],
+ 'cbt' => ['application/x-cbr', 'application/x-cbt'],
+ 'cbz' => ['application/vnd.comicbook+zip', 'application/x-cbr', 'application/x-cbz'],
+ 'cc' => ['text/x-c', 'text/x-c++src'],
+ 'ccmx' => ['application/x-ccmx'],
+ 'cct' => ['application/x-director'],
+ 'ccxml' => ['application/ccxml+xml'],
+ 'cdbcmsg' => ['application/vnd.contact.cmsg'],
+ 'cdf' => ['application/x-netcdf'],
+ 'cdkey' => ['application/vnd.mediastation.cdkey'],
+ 'cdmia' => ['application/cdmi-capability'],
+ 'cdmic' => ['application/cdmi-container'],
+ 'cdmid' => ['application/cdmi-domain'],
+ 'cdmio' => ['application/cdmi-object'],
+ 'cdmiq' => ['application/cdmi-queue'],
+ 'cdr' => ['application/cdr', 'application/coreldraw', 'application/vnd.corel-draw', 'application/x-cdr', 'application/x-coreldraw', 'image/cdr', 'image/x-cdr', 'zz-application/zz-winassoc-cdr'],
+ 'cdx' => ['chemical/x-cdx'],
+ 'cdxml' => ['application/vnd.chemdraw+xml'],
+ 'cdy' => ['application/vnd.cinderella'],
+ 'cer' => ['application/pkix-cert'],
+ 'cert' => ['application/x-x509-ca-cert'],
+ 'cfs' => ['application/x-cfs-compressed'],
+ 'cgb' => ['application/x-gameboy-color-rom'],
+ 'cgm' => ['image/cgm'],
+ 'chat' => ['application/x-chat'],
+ 'chm' => ['application/vnd.ms-htmlhelp', 'application/x-chm'],
+ 'chrt' => ['application/vnd.kde.kchart', 'application/x-kchart'],
+ 'cif' => ['chemical/x-cif'],
+ 'cii' => ['application/vnd.anser-web-certificate-issue-initiation'],
+ 'cil' => ['application/vnd.ms-artgalry'],
+ 'cl' => ['text/x-opencl-src'],
+ 'cla' => ['application/vnd.claymore'],
+ 'class' => ['application/java', 'application/java-byte-code', 'application/java-vm', 'application/x-java', 'application/x-java-class', 'application/x-java-vm'],
+ 'clkk' => ['application/vnd.crick.clicker.keyboard'],
+ 'clkp' => ['application/vnd.crick.clicker.palette'],
+ 'clkt' => ['application/vnd.crick.clicker.template'],
+ 'clkw' => ['application/vnd.crick.clicker.wordbank'],
+ 'clkx' => ['application/vnd.crick.clicker'],
+ 'clp' => ['application/x-msclip'],
+ 'clpi' => ['video/mp2t'],
+ 'cls' => ['application/x-tex', 'text/x-tex'],
+ 'cmake' => ['text/x-cmake'],
+ 'cmc' => ['application/vnd.cosmocaller'],
+ 'cmdf' => ['chemical/x-cmdf'],
+ 'cml' => ['chemical/x-cml'],
+ 'cmp' => ['application/vnd.yellowriver-custom-menu'],
+ 'cmx' => ['image/x-cmx'],
+ 'cob' => ['text/x-cobol'],
+ 'cod' => ['application/vnd.rim.cod'],
+ 'coffee' => ['application/vnd.coffeescript'],
+ 'com' => ['application/x-msdownload'],
+ 'conf' => ['text/plain'],
+ 'cpi' => ['video/mp2t'],
+ 'cpio' => ['application/x-cpio'],
+ 'cpio.gz' => ['application/x-cpio-compressed'],
+ 'cpp' => ['text/x-c', 'text/x-c++src'],
+ 'cpt' => ['application/mac-compactpro'],
+ 'cr2' => ['image/x-canon-cr2'],
+ 'crd' => ['application/x-mscardfile'],
+ 'crdownload' => ['application/x-partial-download'],
+ 'crl' => ['application/pkix-crl'],
+ 'crt' => ['application/x-x509-ca-cert'],
+ 'crw' => ['image/x-canon-crw'],
+ 'cryptonote' => ['application/vnd.rig.cryptonote'],
+ 'cs' => ['text/x-csharp'],
+ 'csh' => ['application/x-csh'],
+ 'csml' => ['chemical/x-csml'],
+ 'csp' => ['application/vnd.commonspace'],
+ 'css' => ['text/css'],
+ 'cst' => ['application/x-director'],
+ 'csv' => ['text/csv', 'text/x-comma-separated-values', 'text/x-csv'],
+ 'csvs' => ['text/csv-schema'],
+ 'cu' => ['application/cu-seeme'],
+ 'cue' => ['application/x-cue'],
+ 'cur' => ['image/x-win-bitmap'],
+ 'curl' => ['text/vnd.curl'],
+ 'cww' => ['application/prs.cww'],
+ 'cxt' => ['application/x-director'],
+ 'cxx' => ['text/x-c', 'text/x-c++src'],
+ 'd' => ['text/x-dsrc'],
+ 'dae' => ['model/vnd.collada+xml'],
+ 'daf' => ['application/vnd.mobius.daf'],
+ 'dar' => ['application/x-dar'],
+ 'dart' => ['application/vnd.dart'],
+ 'dataless' => ['application/vnd.fdsn.seed'],
+ 'davmount' => ['application/davmount+xml'],
+ 'dbf' => ['application/dbase', 'application/dbf', 'application/x-dbase', 'application/x-dbf'],
+ 'dbk' => ['application/docbook+xml', 'application/vnd.oasis.docbook+xml', 'application/x-docbook+xml'],
+ 'dc' => ['application/x-dc-rom'],
+ 'dcl' => ['text/x-dcl'],
+ 'dcm' => ['application/dicom'],
+ 'dcr' => ['application/x-director', 'image/x-kodak-dcr'],
+ 'dcurl' => ['text/vnd.curl.dcurl'],
+ 'dd2' => ['application/vnd.oma.dd2+xml'],
+ 'ddd' => ['application/vnd.fujixerox.ddd'],
+ 'dds' => ['image/x-dds'],
+ 'deb' => ['application/vnd.debian.binary-package', 'application/x-deb', 'application/x-debian-package'],
+ 'def' => ['text/plain'],
+ 'deploy' => ['application/octet-stream'],
+ 'der' => ['application/x-x509-ca-cert'],
+ 'desktop' => ['application/x-desktop', 'application/x-gnome-app-info'],
+ 'device' => ['text/x-systemd-unit'],
+ 'dfac' => ['application/vnd.dreamfactory'],
+ 'dgc' => ['application/x-dgc-compressed'],
+ 'di' => ['text/x-dsrc'],
+ 'dia' => ['application/x-dia-diagram'],
+ 'dib' => ['image/bmp', 'image/x-bmp', 'image/x-ms-bmp'],
+ 'dic' => ['text/x-c'],
+ 'diff' => ['text/x-diff', 'text/x-patch'],
+ 'dir' => ['application/x-director'],
+ 'dis' => ['application/vnd.mobius.dis'],
+ 'dist' => ['application/octet-stream'],
+ 'distz' => ['application/octet-stream'],
+ 'divx' => ['video/avi', 'video/divx', 'video/msvideo', 'video/vnd.divx', 'video/x-avi', 'video/x-msvideo'],
+ 'djv' => ['image/vnd.djvu', 'image/vnd.djvu+multipage', 'image/x-djvu', 'image/x.djvu'],
+ 'djvu' => ['image/vnd.djvu', 'image/vnd.djvu+multipage', 'image/x-djvu', 'image/x.djvu'],
+ 'dll' => ['application/x-msdownload'],
+ 'dmg' => ['application/x-apple-diskimage'],
+ 'dmp' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'],
+ 'dms' => ['application/octet-stream'],
+ 'dna' => ['application/vnd.dna'],
+ 'dng' => ['image/x-adobe-dng'],
+ 'doc' => ['application/msword', 'application/vnd.ms-word', 'application/x-msword', 'zz-application/zz-winassoc-doc'],
+ 'docbook' => ['application/docbook+xml', 'application/vnd.oasis.docbook+xml', 'application/x-docbook+xml'],
+ 'docm' => ['application/vnd.ms-word.document.macroenabled.12'],
+ 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
+ 'dot' => ['application/msword', 'application/msword-template', 'text/vnd.graphviz'],
+ 'dotm' => ['application/vnd.ms-word.template.macroenabled.12'],
+ 'dotx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.template'],
+ 'dp' => ['application/vnd.osgi.dp'],
+ 'dpg' => ['application/vnd.dpgraph'],
+ 'dra' => ['audio/vnd.dra'],
+ 'dsc' => ['text/prs.lines.tag'],
+ 'dsl' => ['text/x-dsl'],
+ 'dssc' => ['application/dssc+der'],
+ 'dtb' => ['application/x-dtbook+xml'],
+ 'dtd' => ['application/xml-dtd', 'text/x-dtd'],
+ 'dts' => ['audio/vnd.dts', 'audio/x-dts'],
+ 'dtshd' => ['audio/vnd.dts.hd', 'audio/x-dtshd'],
+ 'dtx' => ['application/x-tex', 'text/x-tex'],
+ 'dump' => ['application/octet-stream'],
+ 'dv' => ['video/dv'],
+ 'dvb' => ['video/vnd.dvb.file'],
+ 'dvi' => ['application/x-dvi'],
+ 'dvi.bz2' => ['application/x-bzdvi'],
+ 'dvi.gz' => ['application/x-gzdvi'],
+ 'dwf' => ['model/vnd.dwf'],
+ 'dwg' => ['image/vnd.dwg'],
+ 'dxf' => ['image/vnd.dxf'],
+ 'dxp' => ['application/vnd.spotfire.dxp'],
+ 'dxr' => ['application/x-director'],
+ 'e' => ['text/x-eiffel'],
+ 'ecelp4800' => ['audio/vnd.nuera.ecelp4800'],
+ 'ecelp7470' => ['audio/vnd.nuera.ecelp7470'],
+ 'ecelp9600' => ['audio/vnd.nuera.ecelp9600'],
+ 'ecma' => ['application/ecmascript'],
+ 'edm' => ['application/vnd.novadigm.edm'],
+ 'edx' => ['application/vnd.novadigm.edx'],
+ 'efif' => ['application/vnd.picsel'],
+ 'egon' => ['application/x-egon'],
+ 'ei6' => ['application/vnd.pg.osasli'],
+ 'eif' => ['text/x-eiffel'],
+ 'el' => ['text/x-emacs-lisp'],
+ 'elc' => ['application/octet-stream'],
+ 'emf' => ['application/emf', 'application/x-emf', 'application/x-msmetafile', 'image/emf', 'image/x-emf'],
+ 'eml' => ['message/rfc822'],
+ 'emma' => ['application/emma+xml'],
+ 'emp' => ['application/vnd.emusic-emusic_package'],
+ 'emz' => ['application/x-msmetafile'],
+ 'ent' => ['application/xml-external-parsed-entity', 'text/xml-external-parsed-entity'],
+ 'eol' => ['audio/vnd.digital-winds'],
+ 'eot' => ['application/vnd.ms-fontobject'],
+ 'eps' => ['application/postscript', 'image/x-eps'],
+ 'eps.bz2' => ['image/x-bzeps'],
+ 'eps.gz' => ['image/x-gzeps'],
+ 'epsf' => ['image/x-eps'],
+ 'epsf.bz2' => ['image/x-bzeps'],
+ 'epsf.gz' => ['image/x-gzeps'],
+ 'epsi' => ['image/x-eps'],
+ 'epsi.bz2' => ['image/x-bzeps'],
+ 'epsi.gz' => ['image/x-gzeps'],
+ 'epub' => ['application/epub+zip'],
+ 'erl' => ['text/x-erlang'],
+ 'es' => ['application/ecmascript', 'text/ecmascript'],
+ 'es3' => ['application/vnd.eszigno3+xml'],
+ 'esa' => ['application/vnd.osgi.subsystem'],
+ 'esf' => ['application/vnd.epson.esf'],
+ 'et3' => ['application/vnd.eszigno3+xml'],
+ 'etheme' => ['application/x-e-theme'],
+ 'etx' => ['text/x-setext'],
+ 'eva' => ['application/x-eva'],
+ 'evy' => ['application/x-envoy'],
+ 'exe' => ['application/x-ms-dos-executable', 'application/x-msdownload'],
+ 'exi' => ['application/exi'],
+ 'exr' => ['image/x-exr'],
+ 'ext' => ['application/vnd.novadigm.ext'],
+ 'ez' => ['application/andrew-inset'],
+ 'ez2' => ['application/vnd.ezpix-album'],
+ 'ez3' => ['application/vnd.ezpix-package'],
+ 'f' => ['text/x-fortran'],
+ 'f4a' => ['audio/m4a', 'audio/mp4', 'audio/x-m4a'],
+ 'f4b' => ['audio/x-m4b'],
+ 'f4v' => ['video/mp4', 'video/mp4v-es', 'video/x-f4v', 'video/x-m4v'],
+ 'f77' => ['text/x-fortran'],
+ 'f90' => ['text/x-fortran'],
+ 'f95' => ['text/x-fortran'],
+ 'fb2' => ['application/x-fictionbook', 'application/x-fictionbook+xml'],
+ 'fb2.zip' => ['application/x-zip-compressed-fb2'],
+ 'fbs' => ['image/vnd.fastbidsheet'],
+ 'fcdt' => ['application/vnd.adobe.formscentral.fcdt'],
+ 'fcs' => ['application/vnd.isac.fcs'],
+ 'fd' => ['application/x-fd-file', 'application/x-raw-floppy-disk-image'],
+ 'fdf' => ['application/vnd.fdf'],
+ 'fds' => ['application/x-fds-disk'],
+ 'fe_launch' => ['application/vnd.denovo.fcselayout-link'],
+ 'feature' => ['text/x-gherkin'],
+ 'fg5' => ['application/vnd.fujitsu.oasysgp'],
+ 'fgd' => ['application/x-director'],
+ 'fh' => ['image/x-freehand'],
+ 'fh4' => ['image/x-freehand'],
+ 'fh5' => ['image/x-freehand'],
+ 'fh7' => ['image/x-freehand'],
+ 'fhc' => ['image/x-freehand'],
+ 'fig' => ['application/x-xfig', 'image/x-xfig'],
+ 'fits' => ['image/fits', 'image/x-fits'],
+ 'fl' => ['application/x-fluid'],
+ 'flac' => ['audio/flac', 'audio/x-flac'],
+ 'flatpak' => ['application/vnd.flatpak', 'application/vnd.xdgapp'],
+ 'flatpakref' => ['application/vnd.flatpak.ref'],
+ 'flatpakrepo' => ['application/vnd.flatpak.repo'],
+ 'flc' => ['video/fli', 'video/x-fli', 'video/x-flic'],
+ 'fli' => ['video/fli', 'video/x-fli', 'video/x-flic'],
+ 'flo' => ['application/vnd.micrografx.flo'],
+ 'flv' => ['video/x-flv', 'application/x-flash-video', 'flv-application/octet-stream', 'video/flv'],
+ 'flw' => ['application/vnd.kde.kivio', 'application/x-kivio'],
+ 'flx' => ['text/vnd.fmi.flexstor'],
+ 'fly' => ['text/vnd.fly'],
+ 'fm' => ['application/vnd.framemaker', 'application/x-frame'],
+ 'fnc' => ['application/vnd.frogans.fnc'],
+ 'fo' => ['text/x-xslfo'],
+ 'fodg' => ['application/vnd.oasis.opendocument.graphics-flat-xml'],
+ 'fodp' => ['application/vnd.oasis.opendocument.presentation-flat-xml'],
+ 'fods' => ['application/vnd.oasis.opendocument.spreadsheet-flat-xml'],
+ 'fodt' => ['application/vnd.oasis.opendocument.text-flat-xml'],
+ 'for' => ['text/x-fortran'],
+ 'fpx' => ['image/vnd.fpx'],
+ 'frame' => ['application/vnd.framemaker'],
+ 'fsc' => ['application/vnd.fsc.weblaunch'],
+ 'fst' => ['image/vnd.fst'],
+ 'ftc' => ['application/vnd.fluxtime.clip'],
+ 'fti' => ['application/vnd.anser-web-funds-transfer-initiation'],
+ 'fvt' => ['video/vnd.fvt'],
+ 'fxm' => ['video/x-javafx'],
+ 'fxp' => ['application/vnd.adobe.fxp'],
+ 'fxpl' => ['application/vnd.adobe.fxp'],
+ 'fzs' => ['application/vnd.fuzzysheet'],
+ 'g2w' => ['application/vnd.geoplan'],
+ 'g3' => ['image/fax-g3', 'image/g3fax'],
+ 'g3w' => ['application/vnd.geospace'],
+ 'gac' => ['application/vnd.groove-account'],
+ 'gam' => ['application/x-tads'],
+ 'gb' => ['application/x-gameboy-rom'],
+ 'gba' => ['application/x-gba-rom'],
+ 'gbc' => ['application/x-gameboy-color-rom'],
+ 'gbr' => ['application/rpki-ghostbusters', 'image/x-gimp-gbr'],
+ 'gca' => ['application/x-gca-compressed'],
+ 'gcode' => ['text/x.gcode'],
+ 'gcrd' => ['text/directory', 'text/vcard', 'text/x-vcard'],
+ 'gdl' => ['model/vnd.gdl'],
+ 'ged' => ['application/x-gedcom', 'text/gedcom'],
+ 'gedcom' => ['application/x-gedcom', 'text/gedcom'],
+ 'gem' => ['application/x-gtar', 'application/x-tar'],
+ 'gen' => ['application/x-genesis-rom'],
+ 'geo' => ['application/vnd.dynageo'],
+ 'geo.json' => ['application/geo+json', 'application/vnd.geo+json'],
+ 'geojson' => ['application/geo+json', 'application/vnd.geo+json'],
+ 'gex' => ['application/vnd.geometry-explorer'],
+ 'gf' => ['application/x-tex-gf'],
+ 'gg' => ['application/x-gamegear-rom'],
+ 'ggb' => ['application/vnd.geogebra.file'],
+ 'ggt' => ['application/vnd.geogebra.tool'],
+ 'ghf' => ['application/vnd.groove-help'],
+ 'gif' => ['image/gif'],
+ 'gih' => ['image/x-gimp-gih'],
+ 'gim' => ['application/vnd.groove-identity-message'],
+ 'glade' => ['application/x-glade'],
+ 'gml' => ['application/gml+xml'],
+ 'gmo' => ['application/x-gettext-translation'],
+ 'gmx' => ['application/vnd.gmx'],
+ 'gnc' => ['application/x-gnucash'],
+ 'gnd' => ['application/gnunet-directory'],
+ 'gnucash' => ['application/x-gnucash'],
+ 'gnumeric' => ['application/x-gnumeric'],
+ 'gnuplot' => ['application/x-gnuplot'],
+ 'go' => ['text/x-go'],
+ 'gp' => ['application/x-gnuplot'],
+ 'gpg' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature'],
+ 'gph' => ['application/vnd.flographit'],
+ 'gplt' => ['application/x-gnuplot'],
+ 'gpx' => ['application/gpx', 'application/gpx+xml', 'application/x-gpx', 'application/x-gpx+xml'],
+ 'gqf' => ['application/vnd.grafeq'],
+ 'gqs' => ['application/vnd.grafeq'],
+ 'gra' => ['application/x-graphite'],
+ 'gram' => ['application/srgs'],
+ 'gramps' => ['application/x-gramps-xml'],
+ 'gre' => ['application/vnd.geometry-explorer'],
+ 'grv' => ['application/vnd.groove-injector'],
+ 'grxml' => ['application/srgs+xml'],
+ 'gs' => ['text/x-genie'],
+ 'gsf' => ['application/x-font-ghostscript', 'application/x-font-type1'],
+ 'gsm' => ['audio/x-gsm'],
+ 'gtar' => ['application/x-gtar', 'application/x-tar'],
+ 'gtm' => ['application/vnd.groove-tool-message'],
+ 'gtw' => ['model/vnd.gtw'],
+ 'gv' => ['text/vnd.graphviz'],
+ 'gvp' => ['text/google-video-pointer', 'text/x-google-video-pointer'],
+ 'gxf' => ['application/gxf'],
+ 'gxt' => ['application/vnd.geonext'],
+ 'gz' => ['application/x-gzip', 'application/gzip'],
+ 'h' => ['text/x-c', 'text/x-chdr'],
+ 'h++' => ['text/x-c++hdr'],
+ 'h261' => ['video/h261'],
+ 'h263' => ['video/h263'],
+ 'h264' => ['video/h264'],
+ 'h4' => ['application/x-hdf'],
+ 'h5' => ['application/x-hdf'],
+ 'hal' => ['application/vnd.hal+xml'],
+ 'hbci' => ['application/vnd.hbci'],
+ 'hdf' => ['application/x-hdf'],
+ 'hdf4' => ['application/x-hdf'],
+ 'hdf5' => ['application/x-hdf'],
+ 'heic' => ['image/heic', 'image/heic-sequence', 'image/heif', 'image/heif-sequence'],
+ 'heif' => ['image/heic', 'image/heic-sequence', 'image/heif', 'image/heif-sequence'],
+ 'hfe' => ['application/x-hfe-file', 'application/x-hfe-floppy-image'],
+ 'hh' => ['text/x-c', 'text/x-c++hdr'],
+ 'hlp' => ['application/winhlp', 'zz-application/zz-winassoc-hlp'],
+ 'hp' => ['text/x-c++hdr'],
+ 'hpgl' => ['application/vnd.hp-hpgl'],
+ 'hpid' => ['application/vnd.hp-hpid'],
+ 'hpp' => ['text/x-c++hdr'],
+ 'hps' => ['application/vnd.hp-hps'],
+ 'hqx' => ['application/stuffit', 'application/mac-binhex40'],
+ 'hs' => ['text/x-haskell'],
+ 'htke' => ['application/vnd.kenameaapp'],
+ 'htm' => ['text/html'],
+ 'html' => ['text/html'],
+ 'hvd' => ['application/vnd.yamaha.hv-dic'],
+ 'hvp' => ['application/vnd.yamaha.hv-voice'],
+ 'hvs' => ['application/vnd.yamaha.hv-script'],
+ 'hwp' => ['application/vnd.haansoft-hwp', 'application/x-hwp'],
+ 'hwt' => ['application/vnd.haansoft-hwt', 'application/x-hwt'],
+ 'hxx' => ['text/x-c++hdr'],
+ 'i2g' => ['application/vnd.intergeo'],
+ 'ica' => ['application/x-ica'],
+ 'icb' => ['image/x-icb', 'image/x-tga'],
+ 'icc' => ['application/vnd.iccprofile'],
+ 'ice' => ['x-conference/x-cooltalk'],
+ 'icm' => ['application/vnd.iccprofile'],
+ 'icns' => ['image/x-icns'],
+ 'ico' => ['application/ico', 'image/ico', 'image/icon', 'image/vnd.microsoft.icon', 'image/x-ico', 'image/x-icon', 'text/ico'],
+ 'ics' => ['application/ics', 'text/calendar', 'text/x-vcalendar'],
+ 'idl' => ['text/x-idl'],
+ 'ief' => ['image/ief'],
+ 'ifb' => ['text/calendar'],
+ 'iff' => ['image/x-iff', 'image/x-ilbm'],
+ 'ifm' => ['application/vnd.shana.informed.formdata'],
+ 'iges' => ['model/iges'],
+ 'igl' => ['application/vnd.igloader'],
+ 'igm' => ['application/vnd.insors.igm'],
+ 'igs' => ['model/iges'],
+ 'igx' => ['application/vnd.micrografx.igx'],
+ 'iif' => ['application/vnd.shana.informed.interchange'],
+ 'ilbm' => ['image/x-iff', 'image/x-ilbm'],
+ 'ime' => ['audio/imelody', 'audio/x-imelody', 'text/x-imelody'],
+ 'img' => ['application/x-raw-disk-image'],
+ 'img.xz' => ['application/x-raw-disk-image-xz-compressed'],
+ 'imp' => ['application/vnd.accpac.simply.imp'],
+ 'ims' => ['application/vnd.ms-ims'],
+ 'imy' => ['audio/imelody', 'audio/x-imelody', 'text/x-imelody'],
+ 'in' => ['text/plain'],
+ 'ink' => ['application/inkml+xml'],
+ 'inkml' => ['application/inkml+xml'],
+ 'ins' => ['application/x-tex', 'text/x-tex'],
+ 'install' => ['application/x-install-instructions'],
+ 'iota' => ['application/vnd.astraea-software.iota'],
+ 'ipfix' => ['application/ipfix'],
+ 'ipk' => ['application/vnd.shana.informed.package'],
+ 'iptables' => ['text/x-iptables'],
+ 'ipynb' => ['application/x-ipynb+json'],
+ 'irm' => ['application/vnd.ibm.rights-management'],
+ 'irp' => ['application/vnd.irepository.package+xml'],
+ 'iso' => ['application/x-cd-image', 'application/x-gamecube-iso-image', 'application/x-gamecube-rom', 'application/x-iso9660-image', 'application/x-saturn-rom', 'application/x-sega-cd-rom', 'application/x-wbfs', 'application/x-wia', 'application/x-wii-iso-image', 'application/x-wii-rom'],
+ 'iso9660' => ['application/x-cd-image', 'application/x-iso9660-image'],
+ 'it' => ['audio/x-it'],
+ 'it87' => ['application/x-it87'],
+ 'itp' => ['application/vnd.shana.informed.formtemplate'],
+ 'ivp' => ['application/vnd.immervision-ivp'],
+ 'ivu' => ['application/vnd.immervision-ivu'],
+ 'j2c' => ['image/x-jp2-codestream'],
+ 'j2k' => ['image/x-jp2-codestream'],
+ 'jad' => ['text/vnd.sun.j2me.app-descriptor'],
+ 'jam' => ['application/vnd.jam'],
+ 'jar' => ['application/x-java-archive', 'application/java-archive', 'application/x-jar'],
+ 'java' => ['text/x-java', 'text/x-java-source'],
+ 'jceks' => ['application/x-java-jce-keystore'],
+ 'jisp' => ['application/vnd.jisp'],
+ 'jks' => ['application/x-java-keystore'],
+ 'jlt' => ['application/vnd.hp-jlyt'],
+ 'jng' => ['image/x-jng'],
+ 'jnlp' => ['application/x-java-jnlp-file'],
+ 'joda' => ['application/vnd.joost.joda-archive'],
+ 'jp2' => ['image/jp2', 'image/jpeg2000', 'image/jpeg2000-image', 'image/x-jpeg2000-image'],
+ 'jpc' => ['image/x-jp2-codestream'],
+ 'jpe' => ['image/jpeg', 'image/pjpeg'],
+ 'jpeg' => ['image/jpeg', 'image/pjpeg'],
+ 'jpf' => ['image/jpx'],
+ 'jpg' => ['image/jpeg', 'image/pjpeg'],
+ 'jpg2' => ['image/jp2', 'image/jpeg2000', 'image/jpeg2000-image', 'image/x-jpeg2000-image'],
+ 'jpgm' => ['image/jpm', 'video/jpm'],
+ 'jpgv' => ['video/jpeg'],
+ 'jpm' => ['image/jpm', 'video/jpm'],
+ 'jpr' => ['application/x-jbuilder-project'],
+ 'jpx' => ['application/x-jbuilder-project', 'image/jpx'],
+ 'jrd' => ['application/jrd+json'],
+ 'js' => ['text/javascript', 'application/javascript', 'application/x-javascript'],
+ 'jsm' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
+ 'json' => ['application/json'],
+ 'json-patch' => ['application/json-patch+json'],
+ 'jsonld' => ['application/ld+json'],
+ 'jsonml' => ['application/jsonml+json'],
+ 'k25' => ['image/x-kodak-k25'],
+ 'k7' => ['application/x-thomson-cassette'],
+ 'kar' => ['audio/midi', 'audio/x-midi'],
+ 'karbon' => ['application/vnd.kde.karbon', 'application/x-karbon'],
+ 'kdc' => ['image/x-kodak-kdc'],
+ 'kdelnk' => ['application/x-desktop', 'application/x-gnome-app-info'],
+ 'kexi' => ['application/x-kexiproject-sqlite', 'application/x-kexiproject-sqlite2', 'application/x-kexiproject-sqlite3', 'application/x-vnd.kde.kexi'],
+ 'kexic' => ['application/x-kexi-connectiondata'],
+ 'kexis' => ['application/x-kexiproject-shortcut'],
+ 'key' => ['application/vnd.apple.keynote', 'application/x-iwork-keynote-sffkey'],
+ 'kfo' => ['application/vnd.kde.kformula', 'application/x-kformula'],
+ 'kia' => ['application/vnd.kidspiration'],
+ 'kil' => ['application/x-killustrator'],
+ 'kino' => ['application/smil', 'application/smil+xml'],
+ 'kml' => ['application/vnd.google-earth.kml+xml'],
+ 'kmz' => ['application/vnd.google-earth.kmz'],
+ 'kne' => ['application/vnd.kinar'],
+ 'knp' => ['application/vnd.kinar'],
+ 'kon' => ['application/vnd.kde.kontour', 'application/x-kontour'],
+ 'kpm' => ['application/x-kpovmodeler'],
+ 'kpr' => ['application/vnd.kde.kpresenter', 'application/x-kpresenter'],
+ 'kpt' => ['application/vnd.kde.kpresenter', 'application/x-kpresenter'],
+ 'kpxx' => ['application/vnd.ds-keypoint'],
+ 'kra' => ['application/x-krita'],
+ 'ks' => ['application/x-java-keystore'],
+ 'ksp' => ['application/vnd.kde.kspread', 'application/x-kspread'],
+ 'ktr' => ['application/vnd.kahootz'],
+ 'ktx' => ['image/ktx'],
+ 'ktz' => ['application/vnd.kahootz'],
+ 'kud' => ['application/x-kugar'],
+ 'kwd' => ['application/vnd.kde.kword', 'application/x-kword'],
+ 'kwt' => ['application/vnd.kde.kword', 'application/x-kword'],
+ 'la' => ['application/x-shared-library-la'],
+ 'lasxml' => ['application/vnd.las.las+xml'],
+ 'latex' => ['application/x-latex', 'application/x-tex', 'text/x-tex'],
+ 'lbd' => ['application/vnd.llamagraphics.life-balance.desktop'],
+ 'lbe' => ['application/vnd.llamagraphics.life-balance.exchange+xml'],
+ 'lbm' => ['image/x-iff', 'image/x-ilbm'],
+ 'ldif' => ['text/x-ldif'],
+ 'les' => ['application/vnd.hhe.lesson-player'],
+ 'lha' => ['application/x-lha', 'application/x-lzh-compressed'],
+ 'lhs' => ['text/x-literate-haskell'],
+ 'lhz' => ['application/x-lhz'],
+ 'link66' => ['application/vnd.route66.link66+xml'],
+ 'list' => ['text/plain'],
+ 'list3820' => ['application/vnd.ibm.modcap'],
+ 'listafp' => ['application/vnd.ibm.modcap'],
+ 'lnk' => ['application/x-ms-shortcut'],
+ 'lnx' => ['application/x-atari-lynx-rom'],
+ 'loas' => ['audio/usac'],
+ 'log' => ['text/plain', 'text/x-log'],
+ 'lostxml' => ['application/lost+xml'],
+ 'lrf' => ['application/octet-stream'],
+ 'lrm' => ['application/vnd.ms-lrm'],
+ 'lrv' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'],
+ 'lrz' => ['application/x-lrzip'],
+ 'ltf' => ['application/vnd.frogans.ltf'],
+ 'ltx' => ['application/x-tex', 'text/x-tex'],
+ 'lua' => ['text/x-lua'],
+ 'lvp' => ['audio/vnd.lucent.voice'],
+ 'lwo' => ['image/x-lwo'],
+ 'lwob' => ['image/x-lwo'],
+ 'lwp' => ['application/vnd.lotus-wordpro'],
+ 'lws' => ['image/x-lws'],
+ 'ly' => ['text/x-lilypond'],
+ 'lyx' => ['application/x-lyx', 'text/x-lyx'],
+ 'lz' => ['application/x-lzip'],
+ 'lz4' => ['application/x-lz4'],
+ 'lzh' => ['application/x-lha', 'application/x-lzh-compressed'],
+ 'lzma' => ['application/x-lzma'],
+ 'lzo' => ['application/x-lzop'],
+ 'm' => ['text/x-matlab', 'text/x-objcsrc', 'text/x-octave'],
+ 'm13' => ['application/x-msmediaview'],
+ 'm14' => ['application/x-msmediaview'],
+ 'm15' => ['audio/x-mod'],
+ 'm1u' => ['video/vnd.mpegurl', 'video/x-mpegurl'],
+ 'm1v' => ['video/mpeg'],
+ 'm21' => ['application/mp21'],
+ 'm2a' => ['audio/mpeg'],
+ 'm2t' => ['video/mp2t'],
+ 'm2ts' => ['video/mp2t'],
+ 'm2v' => ['video/mpeg'],
+ 'm3a' => ['audio/mpeg'],
+ 'm3u' => ['audio/x-mpegurl', 'application/m3u', 'application/vnd.apple.mpegurl', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist'],
+ 'm3u8' => ['application/m3u', 'application/vnd.apple.mpegurl', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist', 'audio/x-mpegurl'],
+ 'm4' => ['application/x-m4'],
+ 'm4a' => ['audio/mp4', 'audio/m4a', 'audio/x-m4a'],
+ 'm4b' => ['audio/x-m4b'],
+ 'm4r' => ['audio/x-m4r'],
+ 'm4u' => ['video/vnd.mpegurl', 'video/x-mpegurl'],
+ 'm4v' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'],
+ 'm7' => ['application/x-thomson-cartridge-memo7'],
+ 'ma' => ['application/mathematica'],
+ 'mab' => ['application/x-markaby'],
+ 'mads' => ['application/mads+xml'],
+ 'mag' => ['application/vnd.ecowin.chart'],
+ 'mak' => ['text/x-makefile'],
+ 'maker' => ['application/vnd.framemaker'],
+ 'man' => ['application/x-troff-man', 'text/troff'],
+ 'manifest' => ['text/cache-manifest'],
+ 'mar' => ['application/octet-stream'],
+ 'markdown' => ['text/markdown', 'text/x-markdown'],
+ 'mathml' => ['application/mathml+xml'],
+ 'mb' => ['application/mathematica'],
+ 'mbk' => ['application/vnd.mobius.mbk'],
+ 'mbox' => ['application/mbox'],
+ 'mc1' => ['application/vnd.medcalcdata'],
+ 'mcd' => ['application/vnd.mcd'],
+ 'mcurl' => ['text/vnd.curl.mcurl'],
+ 'md' => ['text/markdown', 'text/x-markdown'],
+ 'mdb' => ['application/x-msaccess', 'application/mdb', 'application/msaccess', 'application/vnd.ms-access', 'application/vnd.msaccess', 'application/x-mdb', 'zz-application/zz-winassoc-mdb'],
+ 'mdi' => ['image/vnd.ms-modi'],
+ 'mdx' => ['application/x-genesis-32x-rom'],
+ 'me' => ['text/troff', 'text/x-troff-me'],
+ 'med' => ['audio/x-mod'],
+ 'mesh' => ['model/mesh'],
+ 'meta4' => ['application/metalink4+xml'],
+ 'metalink' => ['application/metalink+xml'],
+ 'mets' => ['application/mets+xml'],
+ 'mfm' => ['application/vnd.mfmp'],
+ 'mft' => ['application/rpki-manifest'],
+ 'mgp' => ['application/vnd.osgeo.mapguide.package', 'application/x-magicpoint'],
+ 'mgz' => ['application/vnd.proteus.magazine'],
+ 'mht' => ['application/x-mimearchive'],
+ 'mhtml' => ['application/x-mimearchive'],
+ 'mid' => ['audio/midi', 'audio/x-midi'],
+ 'midi' => ['audio/midi', 'audio/x-midi'],
+ 'mie' => ['application/x-mie'],
+ 'mif' => ['application/vnd.mif', 'application/x-mif'],
+ 'mime' => ['message/rfc822'],
+ 'minipsf' => ['audio/x-minipsf'],
+ 'mj2' => ['video/mj2'],
+ 'mjp2' => ['video/mj2'],
+ 'mjpeg' => ['video/x-mjpeg'],
+ 'mjpg' => ['video/x-mjpeg'],
+ 'mjs' => ['application/javascript', 'application/x-javascript', 'text/javascript'],
+ 'mk' => ['text/x-makefile'],
+ 'mk3d' => ['video/x-matroska', 'video/x-matroska-3d'],
+ 'mka' => ['audio/x-matroska'],
+ 'mkd' => ['text/markdown', 'text/x-markdown'],
+ 'mks' => ['video/x-matroska'],
+ 'mkv' => ['video/x-matroska'],
+ 'ml' => ['text/x-ocaml'],
+ 'mli' => ['text/x-ocaml'],
+ 'mlp' => ['application/vnd.dolby.mlp'],
+ 'mm' => ['text/x-troff-mm'],
+ 'mmd' => ['application/vnd.chipnuts.karaoke-mmd'],
+ 'mmf' => ['application/vnd.smaf', 'application/x-smaf'],
+ 'mml' => ['application/mathml+xml', 'text/mathml'],
+ 'mmr' => ['image/vnd.fujixerox.edmics-mmr'],
+ 'mng' => ['video/x-mng'],
+ 'mny' => ['application/x-msmoney'],
+ 'mo' => ['application/x-gettext-translation', 'text/x-modelica'],
+ 'mo3' => ['audio/x-mo3'],
+ 'mobi' => ['application/x-mobipocket-ebook'],
+ 'moc' => ['text/x-moc'],
+ 'mod' => ['audio/x-mod'],
+ 'mods' => ['application/mods+xml'],
+ 'mof' => ['text/x-mof'],
+ 'moov' => ['video/quicktime'],
+ 'mount' => ['text/x-systemd-unit'],
+ 'mov' => ['video/quicktime'],
+ 'movie' => ['video/x-sgi-movie'],
+ 'mp+' => ['audio/x-musepack'],
+ 'mp2' => ['audio/mp2', 'audio/mpeg', 'audio/x-mp2', 'video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'],
+ 'mp21' => ['application/mp21'],
+ 'mp2a' => ['audio/mpeg'],
+ 'mp3' => ['audio/mpeg', 'audio/mp3', 'audio/x-mp3', 'audio/x-mpeg', 'audio/x-mpg'],
+ 'mp4' => ['video/mp4', 'video/mp4v-es', 'video/x-m4v'],
+ 'mp4a' => ['audio/mp4'],
+ 'mp4s' => ['application/mp4'],
+ 'mp4v' => ['video/mp4'],
+ 'mpc' => ['application/vnd.mophun.certificate', 'audio/x-musepack'],
+ 'mpe' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'],
+ 'mpeg' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'],
+ 'mpg' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2'],
+ 'mpg4' => ['video/mp4'],
+ 'mpga' => ['audio/mp3', 'audio/mpeg', 'audio/x-mp3', 'audio/x-mpeg', 'audio/x-mpg'],
+ 'mpkg' => ['application/vnd.apple.installer+xml'],
+ 'mpl' => ['video/mp2t'],
+ 'mpls' => ['video/mp2t'],
+ 'mpm' => ['application/vnd.blueice.multipass'],
+ 'mpn' => ['application/vnd.mophun.application'],
+ 'mpp' => ['application/vnd.ms-project', 'audio/x-musepack'],
+ 'mpt' => ['application/vnd.ms-project'],
+ 'mpy' => ['application/vnd.ibm.minipay'],
+ 'mqy' => ['application/vnd.mobius.mqy'],
+ 'mrc' => ['application/marc'],
+ 'mrcx' => ['application/marcxml+xml'],
+ 'mrl' => ['text/x-mrml'],
+ 'mrml' => ['text/x-mrml'],
+ 'mrw' => ['image/x-minolta-mrw'],
+ 'ms' => ['text/troff', 'text/x-troff-ms'],
+ 'mscml' => ['application/mediaservercontrol+xml'],
+ 'mseed' => ['application/vnd.fdsn.mseed'],
+ 'mseq' => ['application/vnd.mseq'],
+ 'msf' => ['application/vnd.epson.msf'],
+ 'msh' => ['model/mesh'],
+ 'msi' => ['application/x-msdownload', 'application/x-msi'],
+ 'msl' => ['application/vnd.mobius.msl'],
+ 'msod' => ['image/x-msod'],
+ 'msty' => ['application/vnd.muvee.style'],
+ 'msx' => ['application/x-msx-rom'],
+ 'mtm' => ['audio/x-mod'],
+ 'mts' => ['model/vnd.mts', 'video/mp2t'],
+ 'mup' => ['text/x-mup'],
+ 'mus' => ['application/vnd.musician'],
+ 'musicxml' => ['application/vnd.recordare.musicxml+xml'],
+ 'mvb' => ['application/x-msmediaview'],
+ 'mwf' => ['application/vnd.mfer'],
+ 'mxf' => ['application/mxf'],
+ 'mxl' => ['application/vnd.recordare.musicxml'],
+ 'mxml' => ['application/xv+xml'],
+ 'mxs' => ['application/vnd.triscape.mxs'],
+ 'mxu' => ['video/vnd.mpegurl', 'video/x-mpegurl'],
+ 'n-gage' => ['application/vnd.nokia.n-gage.symbian.install'],
+ 'n3' => ['text/n3'],
+ 'n64' => ['application/x-n64-rom'],
+ 'nb' => ['application/mathematica', 'application/x-mathematica'],
+ 'nbp' => ['application/vnd.wolfram.player'],
+ 'nc' => ['application/x-netcdf'],
+ 'ncx' => ['application/x-dtbncx+xml'],
+ 'nds' => ['application/x-nintendo-ds-rom'],
+ 'nef' => ['image/x-nikon-nef'],
+ 'nes' => ['application/x-nes-rom'],
+ 'nez' => ['application/x-nes-rom'],
+ 'nfo' => ['text/x-nfo'],
+ 'ngc' => ['application/x-neo-geo-pocket-color-rom'],
+ 'ngdat' => ['application/vnd.nokia.n-gage.data'],
+ 'ngp' => ['application/x-neo-geo-pocket-rom'],
+ 'nitf' => ['application/vnd.nitf'],
+ 'nlu' => ['application/vnd.neurolanguage.nlu'],
+ 'nml' => ['application/vnd.enliven'],
+ 'nnd' => ['application/vnd.noblenet-directory'],
+ 'nns' => ['application/vnd.noblenet-sealer'],
+ 'nnw' => ['application/vnd.noblenet-web'],
+ 'not' => ['text/x-mup'],
+ 'npx' => ['image/vnd.net-fpx'],
+ 'nsc' => ['application/x-conference', 'application/x-netshow-channel'],
+ 'nsf' => ['application/vnd.lotus-notes'],
+ 'nsv' => ['video/x-nsv'],
+ 'ntf' => ['application/vnd.nitf'],
+ 'nzb' => ['application/x-nzb'],
+ 'o' => ['application/x-object'],
+ 'oa2' => ['application/vnd.fujitsu.oasys2'],
+ 'oa3' => ['application/vnd.fujitsu.oasys3'],
+ 'oas' => ['application/vnd.fujitsu.oasys'],
+ 'obd' => ['application/x-msbinder'],
+ 'obj' => ['application/x-tgif'],
+ 'ocl' => ['text/x-ocl'],
+ 'oda' => ['application/oda'],
+ 'odb' => ['application/vnd.oasis.opendocument.database', 'application/vnd.sun.xml.base'],
+ 'odc' => ['application/vnd.oasis.opendocument.chart'],
+ 'odf' => ['application/vnd.oasis.opendocument.formula'],
+ 'odft' => ['application/vnd.oasis.opendocument.formula-template'],
+ 'odg' => ['application/vnd.oasis.opendocument.graphics'],
+ 'odi' => ['application/vnd.oasis.opendocument.image'],
+ 'odm' => ['application/vnd.oasis.opendocument.text-master'],
+ 'odp' => ['application/vnd.oasis.opendocument.presentation'],
+ 'ods' => ['application/vnd.oasis.opendocument.spreadsheet'],
+ 'odt' => ['application/vnd.oasis.opendocument.text'],
+ 'oga' => ['audio/ogg', 'audio/vorbis', 'audio/x-flac+ogg', 'audio/x-ogg', 'audio/x-oggflac', 'audio/x-speex+ogg', 'audio/x-vorbis', 'audio/x-vorbis+ogg'],
+ 'ogg' => ['audio/ogg', 'audio/vorbis', 'audio/x-flac+ogg', 'audio/x-ogg', 'audio/x-oggflac', 'audio/x-speex+ogg', 'audio/x-vorbis', 'audio/x-vorbis+ogg', 'video/ogg', 'video/x-ogg', 'video/x-theora', 'video/x-theora+ogg'],
+ 'ogm' => ['video/x-ogm', 'video/x-ogm+ogg'],
+ 'ogv' => ['video/ogg', 'video/x-ogg'],
+ 'ogx' => ['application/ogg', 'application/x-ogg'],
+ 'old' => ['application/x-trash'],
+ 'oleo' => ['application/x-oleo'],
+ 'omdoc' => ['application/omdoc+xml'],
+ 'onepkg' => ['application/onenote'],
+ 'onetmp' => ['application/onenote'],
+ 'onetoc' => ['application/onenote'],
+ 'onetoc2' => ['application/onenote'],
+ 'ooc' => ['text/x-ooc'],
+ 'opf' => ['application/oebps-package+xml'],
+ 'opml' => ['text/x-opml', 'text/x-opml+xml'],
+ 'oprc' => ['application/vnd.palm', 'application/x-palm-database'],
+ 'opus' => ['audio/ogg', 'audio/x-ogg', 'audio/x-opus+ogg'],
+ 'ora' => ['image/openraster'],
+ 'orf' => ['image/x-olympus-orf'],
+ 'org' => ['application/vnd.lotus-organizer'],
+ 'osf' => ['application/vnd.yamaha.openscoreformat'],
+ 'osfpvg' => ['application/vnd.yamaha.openscoreformat.osfpvg+xml'],
+ 'otc' => ['application/vnd.oasis.opendocument.chart-template'],
+ 'otf' => ['application/vnd.oasis.opendocument.formula-template', 'application/x-font-otf', 'font/otf'],
+ 'otg' => ['application/vnd.oasis.opendocument.graphics-template'],
+ 'oth' => ['application/vnd.oasis.opendocument.text-web'],
+ 'oti' => ['application/vnd.oasis.opendocument.image-template'],
+ 'otp' => ['application/vnd.oasis.opendocument.presentation-template'],
+ 'ots' => ['application/vnd.oasis.opendocument.spreadsheet-template'],
+ 'ott' => ['application/vnd.oasis.opendocument.text-template'],
+ 'owl' => ['application/rdf+xml', 'text/rdf'],
+ 'owx' => ['application/owl+xml'],
+ 'oxps' => ['application/oxps', 'application/vnd.ms-xpsdocument', 'application/xps'],
+ 'oxt' => ['application/vnd.openofficeorg.extension'],
+ 'p' => ['text/x-pascal'],
+ 'p10' => ['application/pkcs10'],
+ 'p12' => ['application/pkcs12', 'application/x-pkcs12'],
+ 'p65' => ['application/x-pagemaker'],
+ 'p7b' => ['application/x-pkcs7-certificates'],
+ 'p7c' => ['application/pkcs7-mime'],
+ 'p7m' => ['application/pkcs7-mime'],
+ 'p7r' => ['application/x-pkcs7-certreqresp'],
+ 'p7s' => ['application/pkcs7-signature'],
+ 'p8' => ['application/pkcs8'],
+ 'p8e' => ['application/pkcs8-encrypted'],
+ 'pack' => ['application/x-java-pack200'],
+ 'pak' => ['application/x-pak'],
+ 'par2' => ['application/x-par2'],
+ 'part' => ['application/x-partial-download'],
+ 'pas' => ['text/x-pascal'],
+ 'pat' => ['image/x-gimp-pat'],
+ 'patch' => ['text/x-diff', 'text/x-patch'],
+ 'path' => ['text/x-systemd-unit'],
+ 'paw' => ['application/vnd.pawaafile'],
+ 'pbd' => ['application/vnd.powerbuilder6'],
+ 'pbm' => ['image/x-portable-bitmap'],
+ 'pcap' => ['application/pcap', 'application/vnd.tcpdump.pcap', 'application/x-pcap'],
+ 'pcd' => ['image/x-photo-cd'],
+ 'pce' => ['application/x-pc-engine-rom'],
+ 'pcf' => ['application/x-cisco-vpn-settings', 'application/x-font-pcf'],
+ 'pcf.Z' => ['application/x-font-pcf'],
+ 'pcf.gz' => ['application/x-font-pcf'],
+ 'pcl' => ['application/vnd.hp-pcl'],
+ 'pclxl' => ['application/vnd.hp-pclxl'],
+ 'pct' => ['image/x-pict'],
+ 'pcurl' => ['application/vnd.curl.pcurl'],
+ 'pcx' => ['image/vnd.zbrush.pcx', 'image/x-pcx'],
+ 'pdb' => ['application/vnd.palm', 'application/x-aportisdoc', 'application/x-palm-database'],
+ 'pdc' => ['application/x-aportisdoc'],
+ 'pdf' => ['application/pdf', 'application/acrobat', 'application/nappdf', 'application/x-pdf', 'image/pdf'],
+ 'pdf.bz2' => ['application/x-bzpdf'],
+ 'pdf.gz' => ['application/x-gzpdf'],
+ 'pdf.lz' => ['application/x-lzpdf'],
+ 'pdf.xz' => ['application/x-xzpdf'],
+ 'pef' => ['image/x-pentax-pef'],
+ 'pem' => ['application/x-x509-ca-cert'],
+ 'perl' => ['application/x-perl', 'text/x-perl'],
+ 'pfa' => ['application/x-font-type1'],
+ 'pfb' => ['application/x-font-type1'],
+ 'pfm' => ['application/x-font-type1'],
+ 'pfr' => ['application/font-tdpfr'],
+ 'pfx' => ['application/pkcs12', 'application/x-pkcs12'],
+ 'pgm' => ['image/x-portable-graymap'],
+ 'pgn' => ['application/vnd.chess-pgn', 'application/x-chess-pgn'],
+ 'pgp' => ['application/pgp', 'application/pgp-encrypted', 'application/pgp-keys', 'application/pgp-signature'],
+ 'php' => ['application/x-php'],
+ 'php3' => ['application/x-php'],
+ 'php4' => ['application/x-php'],
+ 'php5' => ['application/x-php'],
+ 'phps' => ['application/x-php'],
+ 'pic' => ['image/x-pict'],
+ 'pict' => ['image/x-pict'],
+ 'pict1' => ['image/x-pict'],
+ 'pict2' => ['image/x-pict'],
+ 'pk' => ['application/x-tex-pk'],
+ 'pkg' => ['application/octet-stream', 'application/x-xar'],
+ 'pki' => ['application/pkixcmp'],
+ 'pkipath' => ['application/pkix-pkipath'],
+ 'pkr' => ['application/pgp-keys'],
+ 'pl' => ['application/x-perl', 'text/x-perl'],
+ 'pla' => ['audio/x-iriver-pla'],
+ 'plb' => ['application/vnd.3gpp.pic-bw-large'],
+ 'plc' => ['application/vnd.mobius.plc'],
+ 'plf' => ['application/vnd.pocketlearn'],
+ 'pln' => ['application/x-planperfect'],
+ 'pls' => ['application/pls', 'application/pls+xml', 'audio/scpls', 'audio/x-scpls'],
+ 'pm' => ['application/x-pagemaker', 'application/x-perl', 'text/x-perl'],
+ 'pm6' => ['application/x-pagemaker'],
+ 'pmd' => ['application/x-pagemaker'],
+ 'pml' => ['application/vnd.ctc-posml'],
+ 'png' => ['image/png'],
+ 'pnm' => ['image/x-portable-anymap'],
+ 'pntg' => ['image/x-macpaint'],
+ 'po' => ['application/x-gettext', 'text/x-gettext-translation', 'text/x-po'],
+ 'pod' => ['application/x-perl', 'text/x-perl'],
+ 'por' => ['application/x-spss-por'],
+ 'portpkg' => ['application/vnd.macports.portpkg'],
+ 'pot' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint', 'text/x-gettext-translation-template', 'text/x-pot'],
+ 'potm' => ['application/vnd.ms-powerpoint.template.macroenabled.12'],
+ 'potx' => ['application/vnd.openxmlformats-officedocument.presentationml.template'],
+ 'ppam' => ['application/vnd.ms-powerpoint.addin.macroenabled.12'],
+ 'ppd' => ['application/vnd.cups-ppd'],
+ 'ppm' => ['image/x-portable-pixmap'],
+ 'pps' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint'],
+ 'ppsm' => ['application/vnd.ms-powerpoint.slideshow.macroenabled.12'],
+ 'ppsx' => ['application/vnd.openxmlformats-officedocument.presentationml.slideshow'],
+ 'ppt' => ['application/vnd.ms-powerpoint', 'application/mspowerpoint', 'application/powerpoint', 'application/x-mspowerpoint'],
+ 'pptm' => ['application/vnd.ms-powerpoint.presentation.macroenabled.12'],
+ 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
+ 'ppz' => ['application/mspowerpoint', 'application/powerpoint', 'application/vnd.ms-powerpoint', 'application/x-mspowerpoint'],
+ 'pqa' => ['application/vnd.palm', 'application/x-palm-database'],
+ 'prc' => ['application/vnd.palm', 'application/x-mobipocket-ebook', 'application/x-palm-database'],
+ 'pre' => ['application/vnd.lotus-freelance'],
+ 'prf' => ['application/pics-rules'],
+ 'ps' => ['application/postscript'],
+ 'ps.bz2' => ['application/x-bzpostscript'],
+ 'ps.gz' => ['application/x-gzpostscript'],
+ 'psb' => ['application/vnd.3gpp.pic-bw-small'],
+ 'psd' => ['application/photoshop', 'application/x-photoshop', 'image/photoshop', 'image/psd', 'image/vnd.adobe.photoshop', 'image/x-photoshop', 'image/x-psd'],
+ 'psf' => ['application/x-font-linux-psf', 'audio/x-psf'],
+ 'psf.gz' => ['application/x-gz-font-linux-psf'],
+ 'psflib' => ['audio/x-psflib'],
+ 'psid' => ['audio/prs.sid'],
+ 'pskcxml' => ['application/pskc+xml'],
+ 'psw' => ['application/x-pocket-word'],
+ 'ptid' => ['application/vnd.pvi.ptid1'],
+ 'pub' => ['application/vnd.ms-publisher', 'application/x-mspublisher'],
+ 'pvb' => ['application/vnd.3gpp.pic-bw-var'],
+ 'pw' => ['application/x-pw'],
+ 'pwn' => ['application/vnd.3m.post-it-notes'],
+ 'py' => ['text/x-python', 'text/x-python3'],
+ 'py3' => ['text/x-python3'],
+ 'py3x' => ['text/x-python3'],
+ 'pya' => ['audio/vnd.ms-playready.media.pya'],
+ 'pyc' => ['application/x-python-bytecode'],
+ 'pyo' => ['application/x-python-bytecode'],
+ 'pyv' => ['video/vnd.ms-playready.media.pyv'],
+ 'pyx' => ['text/x-python'],
+ 'qam' => ['application/vnd.epson.quickanime'],
+ 'qbo' => ['application/vnd.intu.qbo'],
+ 'qd' => ['application/x-fd-file', 'application/x-raw-floppy-disk-image'],
+ 'qfx' => ['application/vnd.intu.qfx'],
+ 'qif' => ['application/x-qw', 'image/x-quicktime'],
+ 'qml' => ['text/x-qml'],
+ 'qmlproject' => ['text/x-qml'],
+ 'qmltypes' => ['text/x-qml'],
+ 'qp' => ['application/x-qpress'],
+ 'qps' => ['application/vnd.publishare-delta-tree'],
+ 'qt' => ['video/quicktime'],
+ 'qti' => ['application/x-qtiplot'],
+ 'qti.gz' => ['application/x-qtiplot'],
+ 'qtif' => ['image/x-quicktime'],
+ 'qtl' => ['application/x-quicktime-media-link', 'application/x-quicktimeplayer'],
+ 'qtvr' => ['video/quicktime'],
+ 'qwd' => ['application/vnd.quark.quarkxpress'],
+ 'qwt' => ['application/vnd.quark.quarkxpress'],
+ 'qxb' => ['application/vnd.quark.quarkxpress'],
+ 'qxd' => ['application/vnd.quark.quarkxpress'],
+ 'qxl' => ['application/vnd.quark.quarkxpress'],
+ 'qxt' => ['application/vnd.quark.quarkxpress'],
+ 'ra' => ['audio/vnd.m-realaudio', 'audio/vnd.rn-realaudio', 'audio/x-pn-realaudio'],
+ 'raf' => ['image/x-fuji-raf'],
+ 'ram' => ['application/ram', 'audio/x-pn-realaudio'],
+ 'raml' => ['application/raml+yaml'],
+ 'rar' => ['application/x-rar-compressed', 'application/vnd.rar', 'application/x-rar'],
+ 'ras' => ['image/x-cmu-raster'],
+ 'raw' => ['image/x-panasonic-raw', 'image/x-panasonic-rw'],
+ 'raw-disk-image' => ['application/x-raw-disk-image'],
+ 'raw-disk-image.xz' => ['application/x-raw-disk-image-xz-compressed'],
+ 'rax' => ['audio/vnd.m-realaudio', 'audio/vnd.rn-realaudio', 'audio/x-pn-realaudio'],
+ 'rb' => ['application/x-ruby'],
+ 'rcprofile' => ['application/vnd.ipunplugged.rcprofile'],
+ 'rdf' => ['application/rdf+xml', 'text/rdf'],
+ 'rdfs' => ['application/rdf+xml', 'text/rdf'],
+ 'rdz' => ['application/vnd.data-vision.rdz'],
+ 'reg' => ['text/x-ms-regedit'],
+ 'rej' => ['application/x-reject', 'text/x-reject'],
+ 'rep' => ['application/vnd.businessobjects'],
+ 'res' => ['application/x-dtbresource+xml'],
+ 'rgb' => ['image/x-rgb'],
+ 'rif' => ['application/reginfo+xml'],
+ 'rip' => ['audio/vnd.rip'],
+ 'ris' => ['application/x-research-info-systems'],
+ 'rl' => ['application/resource-lists+xml'],
+ 'rlc' => ['image/vnd.fujixerox.edmics-rlc'],
+ 'rld' => ['application/resource-lists-diff+xml'],
+ 'rle' => ['image/rle'],
+ 'rm' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rmi' => ['audio/midi'],
+ 'rmj' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rmm' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rmp' => ['audio/x-pn-realaudio-plugin'],
+ 'rms' => ['application/vnd.jcp.javame.midlet-rms', 'application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rmvb' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rmx' => ['application/vnd.rn-realmedia', 'application/vnd.rn-realmedia-vbr'],
+ 'rnc' => ['application/relax-ng-compact-syntax', 'application/x-rnc'],
+ 'rng' => ['application/xml', 'text/xml'],
+ 'roa' => ['application/rpki-roa'],
+ 'roff' => ['application/x-troff', 'text/troff', 'text/x-troff'],
+ 'rp' => ['image/vnd.rn-realpix'],
+ 'rp9' => ['application/vnd.cloanto.rp9'],
+ 'rpm' => ['application/x-redhat-package-manager', 'application/x-rpm'],
+ 'rpss' => ['application/vnd.nokia.radio-presets'],
+ 'rpst' => ['application/vnd.nokia.radio-preset'],
+ 'rq' => ['application/sparql-query'],
+ 'rs' => ['application/rls-services+xml', 'text/rust'],
+ 'rsd' => ['application/rsd+xml'],
+ 'rss' => ['application/rss+xml', 'text/rss'],
+ 'rt' => ['text/vnd.rn-realtext'],
+ 'rtf' => ['application/rtf', 'text/rtf'],
+ 'rtx' => ['text/richtext'],
+ 'rv' => ['video/vnd.rn-realvideo', 'video/x-real-video'],
+ 'rvx' => ['video/vnd.rn-realvideo', 'video/x-real-video'],
+ 'rw2' => ['image/x-panasonic-raw2', 'image/x-panasonic-rw2'],
+ 's' => ['text/x-asm'],
+ 's3m' => ['audio/s3m', 'audio/x-s3m'],
+ 'saf' => ['application/vnd.yamaha.smaf-audio'],
+ 'sam' => ['application/x-amipro'],
+ 'sami' => ['application/x-sami'],
+ 'sap' => ['application/x-sap-file', 'application/x-thomson-sap-image'],
+ 'sass' => ['text/x-sass'],
+ 'sav' => ['application/x-spss-sav', 'application/x-spss-savefile'],
+ 'sbml' => ['application/sbml+xml'],
+ 'sc' => ['application/vnd.ibm.secure-container'],
+ 'scala' => ['text/x-scala'],
+ 'scd' => ['application/x-msschedule'],
+ 'scm' => ['application/vnd.lotus-screencam', 'text/x-scheme'],
+ 'scope' => ['text/x-systemd-unit'],
+ 'scq' => ['application/scvp-cv-request'],
+ 'scs' => ['application/scvp-cv-response'],
+ 'scss' => ['text/x-scss'],
+ 'scurl' => ['text/vnd.curl.scurl'],
+ 'sda' => ['application/vnd.stardivision.draw'],
+ 'sdc' => ['application/vnd.stardivision.calc'],
+ 'sdd' => ['application/vnd.stardivision.impress'],
+ 'sdkd' => ['application/vnd.solent.sdkm+xml'],
+ 'sdkm' => ['application/vnd.solent.sdkm+xml'],
+ 'sdp' => ['application/sdp', 'application/vnd.sdp', 'application/vnd.stardivision.impress', 'application/x-sdp'],
+ 'sds' => ['application/vnd.stardivision.chart'],
+ 'sdw' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'],
+ 'see' => ['application/vnd.seemail'],
+ 'seed' => ['application/vnd.fdsn.seed'],
+ 'sema' => ['application/vnd.sema'],
+ 'semd' => ['application/vnd.semd'],
+ 'semf' => ['application/vnd.semf'],
+ 'ser' => ['application/java-serialized-object'],
+ 'service' => ['text/x-dbus-service', 'text/x-systemd-unit'],
+ 'setpay' => ['application/set-payment-initiation'],
+ 'setreg' => ['application/set-registration-initiation'],
+ 'sfc' => ['application/vnd.nintendo.snes.rom', 'application/x-snes-rom'],
+ 'sfd-hdstx' => ['application/vnd.hydrostatix.sof-data'],
+ 'sfs' => ['application/vnd.spotfire.sfs'],
+ 'sfv' => ['text/x-sfv'],
+ 'sg' => ['application/x-sg1000-rom'],
+ 'sgb' => ['application/x-gameboy-rom'],
+ 'sgf' => ['application/x-go-sgf'],
+ 'sgi' => ['image/sgi', 'image/x-sgi'],
+ 'sgl' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'],
+ 'sgm' => ['text/sgml'],
+ 'sgml' => ['text/sgml'],
+ 'sh' => ['application/x-sh', 'application/x-shellscript', 'text/x-sh'],
+ 'shape' => ['application/x-dia-shape'],
+ 'shar' => ['application/x-shar'],
+ 'shf' => ['application/shf+xml'],
+ 'shn' => ['application/x-shorten', 'audio/x-shorten'],
+ 'siag' => ['application/x-siag'],
+ 'sid' => ['audio/prs.sid', 'image/x-mrsid-image'],
+ 'sig' => ['application/pgp-signature'],
+ 'sik' => ['application/x-trash'],
+ 'sil' => ['audio/silk'],
+ 'silo' => ['model/mesh'],
+ 'sis' => ['application/vnd.symbian.install'],
+ 'sisx' => ['application/vnd.symbian.install', 'x-epoc/x-sisx-app'],
+ 'sit' => ['application/x-stuffit', 'application/stuffit', 'application/x-sit'],
+ 'sitx' => ['application/x-stuffitx'],
+ 'siv' => ['application/sieve'],
+ 'sk' => ['image/x-skencil'],
+ 'sk1' => ['image/x-skencil'],
+ 'skd' => ['application/vnd.koan'],
+ 'skm' => ['application/vnd.koan'],
+ 'skp' => ['application/vnd.koan'],
+ 'skr' => ['application/pgp-keys'],
+ 'skt' => ['application/vnd.koan'],
+ 'sldm' => ['application/vnd.ms-powerpoint.slide.macroenabled.12'],
+ 'sldx' => ['application/vnd.openxmlformats-officedocument.presentationml.slide'],
+ 'slice' => ['text/x-systemd-unit'],
+ 'slk' => ['text/spreadsheet'],
+ 'slt' => ['application/vnd.epson.salt'],
+ 'sm' => ['application/vnd.stepmania.stepchart'],
+ 'smaf' => ['application/vnd.smaf', 'application/x-smaf'],
+ 'smc' => ['application/vnd.nintendo.snes.rom', 'application/x-snes-rom'],
+ 'smd' => ['application/vnd.stardivision.mail', 'application/x-genesis-rom'],
+ 'smf' => ['application/vnd.stardivision.math'],
+ 'smi' => ['application/smil', 'application/smil+xml', 'application/x-sami'],
+ 'smil' => ['application/smil', 'application/smil+xml'],
+ 'sml' => ['application/smil', 'application/smil+xml'],
+ 'sms' => ['application/x-sms-rom'],
+ 'smv' => ['video/x-smv'],
+ 'smzip' => ['application/vnd.stepmania.package'],
+ 'snap' => ['application/vnd.snap'],
+ 'snd' => ['audio/basic'],
+ 'snf' => ['application/x-font-snf'],
+ 'so' => ['application/octet-stream', 'application/x-sharedlib'],
+ 'socket' => ['text/x-systemd-unit'],
+ 'spc' => ['application/x-pkcs7-certificates'],
+ 'spd' => ['application/x-font-speedo'],
+ 'spec' => ['text/x-rpm-spec'],
+ 'spf' => ['application/vnd.yamaha.smaf-phrase'],
+ 'spl' => ['application/futuresplash', 'application/vnd.adobe.flash.movie', 'application/x-futuresplash', 'application/x-shockwave-flash'],
+ 'spm' => ['application/x-source-rpm'],
+ 'spot' => ['text/vnd.in3d.spot'],
+ 'spp' => ['application/scvp-vp-response'],
+ 'spq' => ['application/scvp-vp-request'],
+ 'spx' => ['audio/ogg', 'audio/x-speex'],
+ 'sql' => ['application/sql', 'application/x-sql', 'text/x-sql'],
+ 'sqlite2' => ['application/x-sqlite2'],
+ 'sqlite3' => ['application/vnd.sqlite3', 'application/x-sqlite3'],
+ 'sqsh' => ['application/vnd.squashfs'],
+ 'sr2' => ['image/x-sony-sr2'],
+ 'src' => ['application/x-wais-source'],
+ 'src.rpm' => ['application/x-source-rpm'],
+ 'srf' => ['image/x-sony-srf'],
+ 'srt' => ['application/x-srt', 'application/x-subrip'],
+ 'sru' => ['application/sru+xml'],
+ 'srx' => ['application/sparql-results+xml'],
+ 'ss' => ['text/x-scheme'],
+ 'ssa' => ['text/x-ssa'],
+ 'ssdl' => ['application/ssdl+xml'],
+ 'sse' => ['application/vnd.kodak-descriptor'],
+ 'ssf' => ['application/vnd.epson.ssf'],
+ 'ssml' => ['application/ssml+xml'],
+ 'st' => ['application/vnd.sailingtracker.track'],
+ 'stc' => ['application/vnd.sun.xml.calc.template'],
+ 'std' => ['application/vnd.sun.xml.draw.template'],
+ 'stf' => ['application/vnd.wt.stf'],
+ 'sti' => ['application/vnd.sun.xml.impress.template'],
+ 'stk' => ['application/hyperstudio'],
+ 'stl' => ['application/vnd.ms-pki.stl', 'model/stl', 'model/x.stl-ascii', 'model/x.stl-binary'],
+ 'stm' => ['audio/x-stm'],
+ 'str' => ['application/vnd.pg.format'],
+ 'stw' => ['application/vnd.sun.xml.writer.template'],
+ 'sty' => ['application/x-tex', 'text/x-tex'],
+ 'sub' => ['image/vnd.dvb.subtitle', 'text/vnd.dvb.subtitle', 'text/x-microdvd', 'text/x-mpsub', 'text/x-subviewer'],
+ 'sun' => ['image/x-sun-raster'],
+ 'sus' => ['application/vnd.sus-calendar'],
+ 'susp' => ['application/vnd.sus-calendar'],
+ 'sv' => ['text/x-svsrc'],
+ 'sv4cpio' => ['application/x-sv4cpio'],
+ 'sv4crc' => ['application/x-sv4crc'],
+ 'svc' => ['application/vnd.dvb.service'],
+ 'svd' => ['application/vnd.svd'],
+ 'svg' => ['image/svg+xml', 'image/svg'],
+ 'svgz' => ['image/svg+xml', 'image/svg+xml-compressed'],
+ 'svh' => ['text/x-svhdr'],
+ 'swa' => ['application/x-director'],
+ 'swap' => ['text/x-systemd-unit'],
+ 'swf' => ['application/futuresplash', 'application/vnd.adobe.flash.movie', 'application/x-shockwave-flash'],
+ 'swi' => ['application/vnd.aristanetworks.swi'],
+ 'swm' => ['application/x-ms-wim'],
+ 'sxc' => ['application/vnd.sun.xml.calc'],
+ 'sxd' => ['application/vnd.sun.xml.draw'],
+ 'sxg' => ['application/vnd.sun.xml.writer.global'],
+ 'sxi' => ['application/vnd.sun.xml.impress'],
+ 'sxm' => ['application/vnd.sun.xml.math'],
+ 'sxw' => ['application/vnd.sun.xml.writer'],
+ 'sylk' => ['text/spreadsheet'],
+ 't' => ['application/x-perl', 'application/x-troff', 'text/troff', 'text/x-perl', 'text/x-troff'],
+ 't2t' => ['text/x-txt2tags'],
+ 't3' => ['application/x-t3vm-image'],
+ 'taglet' => ['application/vnd.mynfc'],
+ 'tao' => ['application/vnd.tao.intent-module-archive'],
+ 'tar' => ['application/x-tar', 'application/x-gtar'],
+ 'tar.Z' => ['application/x-tarz'],
+ 'tar.bz' => ['application/x-bzip-compressed-tar'],
+ 'tar.bz2' => ['application/x-bzip-compressed-tar'],
+ 'tar.gz' => ['application/x-compressed-tar'],
+ 'tar.lrz' => ['application/x-lrzip-compressed-tar'],
+ 'tar.lz' => ['application/x-lzip-compressed-tar'],
+ 'tar.lz4' => ['application/x-lz4-compressed-tar'],
+ 'tar.lzma' => ['application/x-lzma-compressed-tar'],
+ 'tar.lzo' => ['application/x-tzo'],
+ 'tar.xz' => ['application/x-xz-compressed-tar'],
+ 'target' => ['text/x-systemd-unit'],
+ 'taz' => ['application/x-tarz'],
+ 'tb2' => ['application/x-bzip-compressed-tar'],
+ 'tbz' => ['application/x-bzip-compressed-tar'],
+ 'tbz2' => ['application/x-bzip-compressed-tar'],
+ 'tcap' => ['application/vnd.3gpp2.tcap'],
+ 'tcl' => ['application/x-tcl', 'text/x-tcl'],
+ 'teacher' => ['application/vnd.smart.teacher'],
+ 'tei' => ['application/tei+xml'],
+ 'teicorpus' => ['application/tei+xml'],
+ 'tex' => ['application/x-tex', 'text/x-tex'],
+ 'texi' => ['application/x-texinfo', 'text/x-texinfo'],
+ 'texinfo' => ['application/x-texinfo', 'text/x-texinfo'],
+ 'text' => ['text/plain'],
+ 'tfi' => ['application/thraud+xml'],
+ 'tfm' => ['application/x-tex-tfm'],
+ 'tga' => ['image/x-icb', 'image/x-tga'],
+ 'tgz' => ['application/x-compressed-tar'],
+ 'theme' => ['application/x-theme'],
+ 'themepack' => ['application/x-windows-themepack'],
+ 'thmx' => ['application/vnd.ms-officetheme'],
+ 'tif' => ['image/tiff'],
+ 'tiff' => ['image/tiff'],
+ 'timer' => ['text/x-systemd-unit'],
+ 'tk' => ['text/x-tcl'],
+ 'tlrz' => ['application/x-lrzip-compressed-tar'],
+ 'tlz' => ['application/x-lzma-compressed-tar'],
+ 'tmo' => ['application/vnd.tmobile-livetv'],
+ 'tnef' => ['application/ms-tnef', 'application/vnd.ms-tnef'],
+ 'tnf' => ['application/ms-tnef', 'application/vnd.ms-tnef'],
+ 'toc' => ['application/x-cdrdao-toc'],
+ 'torrent' => ['application/x-bittorrent'],
+ 'tpic' => ['image/x-icb', 'image/x-tga'],
+ 'tpl' => ['application/vnd.groove-tool-template'],
+ 'tpt' => ['application/vnd.trid.tpt'],
+ 'tr' => ['application/x-troff', 'text/troff', 'text/x-troff'],
+ 'tra' => ['application/vnd.trueapp'],
+ 'trig' => ['application/trig', 'application/x-trig'],
+ 'trm' => ['application/x-msterminal'],
+ 'ts' => ['application/x-linguist', 'text/vnd.qt.linguist', 'text/vnd.trolltech.linguist', 'video/mp2t'],
+ 'tsd' => ['application/timestamped-data'],
+ 'tsv' => ['text/tab-separated-values'],
+ 'tta' => ['audio/tta', 'audio/x-tta'],
+ 'ttc' => ['font/collection'],
+ 'ttf' => ['application/x-font-truetype', 'application/x-font-ttf', 'font/ttf'],
+ 'ttl' => ['text/turtle'],
+ 'ttx' => ['application/x-font-ttx'],
+ 'twd' => ['application/vnd.simtech-mindmapper'],
+ 'twds' => ['application/vnd.simtech-mindmapper'],
+ 'twig' => ['text/x-twig'],
+ 'txd' => ['application/vnd.genomatix.tuxedo'],
+ 'txf' => ['application/vnd.mobius.txf'],
+ 'txt' => ['text/plain'],
+ 'txz' => ['application/x-xz-compressed-tar'],
+ 'tzo' => ['application/x-tzo'],
+ 'u32' => ['application/x-authorware-bin'],
+ 'udeb' => ['application/vnd.debian.binary-package', 'application/x-deb', 'application/x-debian-package'],
+ 'ufd' => ['application/vnd.ufdl'],
+ 'ufdl' => ['application/vnd.ufdl'],
+ 'ufraw' => ['application/x-ufraw'],
+ 'ui' => ['application/x-designer', 'application/x-gtk-builder'],
+ 'uil' => ['text/x-uil'],
+ 'ult' => ['audio/x-mod'],
+ 'ulx' => ['application/x-glulx'],
+ 'umj' => ['application/vnd.umajin'],
+ 'unf' => ['application/x-nes-rom'],
+ 'uni' => ['audio/x-mod'],
+ 'unif' => ['application/x-nes-rom'],
+ 'unityweb' => ['application/vnd.unity'],
+ 'uoml' => ['application/vnd.uoml+xml'],
+ 'uri' => ['text/uri-list'],
+ 'uris' => ['text/uri-list'],
+ 'url' => ['application/x-mswinurl'],
+ 'urls' => ['text/uri-list'],
+ 'ustar' => ['application/x-ustar'],
+ 'utz' => ['application/vnd.uiq.theme'],
+ 'uu' => ['text/x-uuencode'],
+ 'uue' => ['text/x-uuencode', 'zz-application/zz-winassoc-uu'],
+ 'uva' => ['audio/vnd.dece.audio'],
+ 'uvd' => ['application/vnd.dece.data'],
+ 'uvf' => ['application/vnd.dece.data'],
+ 'uvg' => ['image/vnd.dece.graphic'],
+ 'uvh' => ['video/vnd.dece.hd'],
+ 'uvi' => ['image/vnd.dece.graphic'],
+ 'uvm' => ['video/vnd.dece.mobile'],
+ 'uvp' => ['video/vnd.dece.pd'],
+ 'uvs' => ['video/vnd.dece.sd'],
+ 'uvt' => ['application/vnd.dece.ttml+xml'],
+ 'uvu' => ['video/vnd.uvvu.mp4'],
+ 'uvv' => ['video/vnd.dece.video'],
+ 'uvva' => ['audio/vnd.dece.audio'],
+ 'uvvd' => ['application/vnd.dece.data'],
+ 'uvvf' => ['application/vnd.dece.data'],
+ 'uvvg' => ['image/vnd.dece.graphic'],
+ 'uvvh' => ['video/vnd.dece.hd'],
+ 'uvvi' => ['image/vnd.dece.graphic'],
+ 'uvvm' => ['video/vnd.dece.mobile'],
+ 'uvvp' => ['video/vnd.dece.pd'],
+ 'uvvs' => ['video/vnd.dece.sd'],
+ 'uvvt' => ['application/vnd.dece.ttml+xml'],
+ 'uvvu' => ['video/vnd.uvvu.mp4'],
+ 'uvvv' => ['video/vnd.dece.video'],
+ 'uvvx' => ['application/vnd.dece.unspecified'],
+ 'uvvz' => ['application/vnd.dece.zip'],
+ 'uvx' => ['application/vnd.dece.unspecified'],
+ 'uvz' => ['application/vnd.dece.zip'],
+ 'v' => ['text/x-verilog'],
+ 'v64' => ['application/x-n64-rom'],
+ 'vala' => ['text/x-vala'],
+ 'vapi' => ['text/x-vala'],
+ 'vb' => ['application/x-virtual-boy-rom'],
+ 'vcard' => ['text/directory', 'text/vcard', 'text/x-vcard'],
+ 'vcd' => ['application/x-cdlink'],
+ 'vcf' => ['text/x-vcard', 'text/directory', 'text/vcard'],
+ 'vcg' => ['application/vnd.groove-vcard'],
+ 'vcs' => ['application/ics', 'text/calendar', 'text/x-vcalendar'],
+ 'vct' => ['text/directory', 'text/vcard', 'text/x-vcard'],
+ 'vcx' => ['application/vnd.vcx'],
+ 'vda' => ['image/x-icb', 'image/x-tga'],
+ 'vhd' => ['text/x-vhdl'],
+ 'vhdl' => ['text/x-vhdl'],
+ 'vis' => ['application/vnd.visionary'],
+ 'viv' => ['video/vivo', 'video/vnd.vivo'],
+ 'vivo' => ['video/vivo', 'video/vnd.vivo'],
+ 'vlc' => ['application/m3u', 'audio/m3u', 'audio/mpegurl', 'audio/x-m3u', 'audio/x-mp3-playlist', 'audio/x-mpegurl'],
+ 'vob' => ['video/mpeg', 'video/mpeg-system', 'video/x-mpeg', 'video/x-mpeg-system', 'video/x-mpeg2', 'video/x-ms-vob'],
+ 'voc' => ['audio/x-voc'],
+ 'vor' => ['application/vnd.stardivision.writer', 'application/vnd.stardivision.writer-global'],
+ 'vox' => ['application/x-authorware-bin'],
+ 'vrm' => ['model/vrml'],
+ 'vrml' => ['model/vrml'],
+ 'vsd' => ['application/vnd.visio'],
+ 'vsdm' => ['application/vnd.ms-visio.drawing.macroenabled.main+xml'],
+ 'vsdx' => ['application/vnd.ms-visio.drawing.main+xml'],
+ 'vsf' => ['application/vnd.vsf'],
+ 'vss' => ['application/vnd.visio'],
+ 'vssm' => ['application/vnd.ms-visio.stencil.macroenabled.main+xml'],
+ 'vssx' => ['application/vnd.ms-visio.stencil.main+xml'],
+ 'vst' => ['application/vnd.visio', 'image/x-icb', 'image/x-tga'],
+ 'vstm' => ['application/vnd.ms-visio.template.macroenabled.main+xml'],
+ 'vstx' => ['application/vnd.ms-visio.template.main+xml'],
+ 'vsw' => ['application/vnd.visio'],
+ 'vtt' => ['text/vtt'],
+ 'vtu' => ['model/vnd.vtu'],
+ 'vxml' => ['application/voicexml+xml'],
+ 'w3d' => ['application/x-director'],
+ 'wad' => ['application/x-doom', 'application/x-doom-wad', 'application/x-wii-wad'],
+ 'wav' => ['audio/wav', 'audio/vnd.wave', 'audio/x-wav'],
+ 'wax' => ['application/x-ms-asx', 'audio/x-ms-asx', 'audio/x-ms-wax', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'],
+ 'wb1' => ['application/x-quattropro'],
+ 'wb2' => ['application/x-quattropro'],
+ 'wb3' => ['application/x-quattropro'],
+ 'wbmp' => ['image/vnd.wap.wbmp'],
+ 'wbs' => ['application/vnd.criticaltools.wbs+xml'],
+ 'wbxml' => ['application/vnd.wap.wbxml'],
+ 'wcm' => ['application/vnd.ms-works'],
+ 'wdb' => ['application/vnd.ms-works'],
+ 'wdp' => ['image/vnd.ms-photo'],
+ 'weba' => ['audio/webm'],
+ 'webm' => ['video/webm'],
+ 'webp' => ['image/webp'],
+ 'wg' => ['application/vnd.pmi.widget'],
+ 'wgt' => ['application/widget'],
+ 'wim' => ['application/x-ms-wim'],
+ 'wk1' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'],
+ 'wk3' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'],
+ 'wk4' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'],
+ 'wkdownload' => ['application/x-partial-download'],
+ 'wks' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/vnd.ms-works', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'],
+ 'wm' => ['video/x-ms-wm'],
+ 'wma' => ['audio/x-ms-wma', 'audio/wma'],
+ 'wmd' => ['application/x-ms-wmd'],
+ 'wmf' => ['application/wmf', 'application/x-msmetafile', 'application/x-wmf', 'image/wmf', 'image/x-win-metafile', 'image/x-wmf'],
+ 'wml' => ['text/vnd.wap.wml'],
+ 'wmlc' => ['application/vnd.wap.wmlc'],
+ 'wmls' => ['text/vnd.wap.wmlscript'],
+ 'wmlsc' => ['application/vnd.wap.wmlscriptc'],
+ 'wmv' => ['audio/x-ms-wmv', 'video/x-ms-wmv'],
+ 'wmx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'],
+ 'wmz' => ['application/x-ms-wmz', 'application/x-msmetafile'],
+ 'woff' => ['application/font-woff', 'application/x-font-woff', 'font/woff'],
+ 'woff2' => ['font/woff', 'font/woff2'],
+ 'wp' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wp4' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wp5' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wp6' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wpd' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wpg' => ['application/x-wpg'],
+ 'wpl' => ['application/vnd.ms-wpl'],
+ 'wpp' => ['application/vnd.wordperfect', 'application/wordperfect', 'application/x-wordperfect'],
+ 'wps' => ['application/vnd.ms-works'],
+ 'wqd' => ['application/vnd.wqd'],
+ 'wri' => ['application/x-mswrite'],
+ 'wrl' => ['model/vrml'],
+ 'ws' => ['application/x-wonderswan-rom'],
+ 'wsc' => ['application/x-wonderswan-color-rom'],
+ 'wsdl' => ['application/wsdl+xml'],
+ 'wsgi' => ['text/x-python'],
+ 'wspolicy' => ['application/wspolicy+xml'],
+ 'wtb' => ['application/vnd.webturbo'],
+ 'wv' => ['audio/x-wavpack'],
+ 'wvc' => ['audio/x-wavpack-correction'],
+ 'wvp' => ['audio/x-wavpack'],
+ 'wvx' => ['application/x-ms-asx', 'audio/x-ms-asx', 'video/x-ms-wax', 'video/x-ms-wmx', 'video/x-ms-wvx'],
+ 'wwf' => ['application/wwf', 'application/x-wwf'],
+ 'x32' => ['application/x-authorware-bin'],
+ 'x3d' => ['model/x3d+xml'],
+ 'x3db' => ['model/x3d+binary'],
+ 'x3dbz' => ['model/x3d+binary'],
+ 'x3dv' => ['model/x3d+vrml'],
+ 'x3dvz' => ['model/x3d+vrml'],
+ 'x3dz' => ['model/x3d+xml'],
+ 'x3f' => ['image/x-sigma-x3f'],
+ 'xac' => ['application/x-gnucash'],
+ 'xaml' => ['application/xaml+xml'],
+ 'xap' => ['application/x-silverlight-app'],
+ 'xar' => ['application/vnd.xara', 'application/x-xar'],
+ 'xbap' => ['application/x-ms-xbap'],
+ 'xbd' => ['application/vnd.fujixerox.docuworks.binder'],
+ 'xbel' => ['application/x-xbel'],
+ 'xbl' => ['application/xml', 'text/xml'],
+ 'xbm' => ['image/x-xbitmap'],
+ 'xcf' => ['image/x-xcf'],
+ 'xcf.bz2' => ['image/x-compressed-xcf'],
+ 'xcf.gz' => ['image/x-compressed-xcf'],
+ 'xdf' => ['application/xcap-diff+xml'],
+ 'xdgapp' => ['application/vnd.flatpak', 'application/vnd.xdgapp'],
+ 'xdm' => ['application/vnd.syncml.dm+xml'],
+ 'xdp' => ['application/vnd.adobe.xdp+xml'],
+ 'xdssc' => ['application/dssc+xml'],
+ 'xdw' => ['application/vnd.fujixerox.docuworks'],
+ 'xenc' => ['application/xenc+xml'],
+ 'xer' => ['application/patch-ops-error+xml'],
+ 'xfdf' => ['application/vnd.adobe.xfdf'],
+ 'xfdl' => ['application/vnd.xfdl'],
+ 'xhe' => ['audio/usac'],
+ 'xht' => ['application/xhtml+xml'],
+ 'xhtml' => ['application/xhtml+xml'],
+ 'xhvml' => ['application/xv+xml'],
+ 'xi' => ['audio/x-xi'],
+ 'xif' => ['image/vnd.xiff'],
+ 'xla' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xlam' => ['application/vnd.ms-excel.addin.macroenabled.12'],
+ 'xlc' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xld' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xlf' => ['application/x-xliff', 'application/x-xliff+xml', 'application/xliff+xml'],
+ 'xliff' => ['application/x-xliff', 'application/xliff+xml'],
+ 'xll' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xlm' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xlr' => ['application/vnd.ms-works'],
+ 'xls' => ['application/vnd.ms-excel', 'application/msexcel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xlsb' => ['application/vnd.ms-excel.sheet.binary.macroenabled.12'],
+ 'xlsm' => ['application/vnd.ms-excel.sheet.macroenabled.12'],
+ 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
+ 'xlt' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xltm' => ['application/vnd.ms-excel.template.macroenabled.12'],
+ 'xltx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.template'],
+ 'xlw' => ['application/msexcel', 'application/vnd.ms-excel', 'application/x-msexcel', 'zz-application/zz-winassoc-xls'],
+ 'xm' => ['audio/x-xm', 'audio/xm'],
+ 'xmf' => ['audio/mobile-xmf', 'audio/x-xmf', 'audio/xmf'],
+ 'xmi' => ['text/x-xmi'],
+ 'xml' => ['application/xml', 'text/xml'],
+ 'xo' => ['application/vnd.olpc-sugar'],
+ 'xop' => ['application/xop+xml'],
+ 'xpi' => ['application/x-xpinstall'],
+ 'xpl' => ['application/xproc+xml'],
+ 'xpm' => ['image/x-xpixmap', 'image/x-xpm'],
+ 'xpr' => ['application/vnd.is-xpr'],
+ 'xps' => ['application/oxps', 'application/vnd.ms-xpsdocument', 'application/xps'],
+ 'xpw' => ['application/vnd.intercon.formnet'],
+ 'xpx' => ['application/vnd.intercon.formnet'],
+ 'xsd' => ['application/xml', 'text/xml'],
+ 'xsl' => ['application/xml', 'application/xslt+xml'],
+ 'xslfo' => ['text/x-xslfo'],
+ 'xslt' => ['application/xslt+xml'],
+ 'xsm' => ['application/vnd.syncml+xml'],
+ 'xspf' => ['application/x-xspf+xml', 'application/xspf+xml'],
+ 'xul' => ['application/vnd.mozilla.xul+xml'],
+ 'xvm' => ['application/xv+xml'],
+ 'xvml' => ['application/xv+xml'],
+ 'xwd' => ['image/x-xwindowdump'],
+ 'xyz' => ['chemical/x-xyz'],
+ 'xz' => ['application/x-xz'],
+ 'yaml' => ['application/x-yaml', 'text/x-yaml', 'text/yaml'],
+ 'yang' => ['application/yang'],
+ 'yin' => ['application/yin+xml'],
+ 'yml' => ['application/x-yaml', 'text/x-yaml', 'text/yaml'],
+ 'yt' => ['application/vnd.youtube.yt'],
+ 'z1' => ['application/x-zmachine'],
+ 'z2' => ['application/x-zmachine'],
+ 'z3' => ['application/x-zmachine'],
+ 'z4' => ['application/x-zmachine'],
+ 'z5' => ['application/x-zmachine'],
+ 'z6' => ['application/x-zmachine'],
+ 'z64' => ['application/x-n64-rom'],
+ 'z7' => ['application/x-zmachine'],
+ 'z8' => ['application/x-zmachine'],
+ 'zabw' => ['application/x-abiword'],
+ 'zaz' => ['application/vnd.zzazz.deck+xml'],
+ 'zip' => ['application/zip', 'application/x-zip', 'application/x-zip-compressed'],
+ 'zir' => ['application/vnd.zul'],
+ 'zirz' => ['application/vnd.zul'],
+ 'zmm' => ['application/vnd.handheld-entertainment+xml'],
+ 'zoo' => ['application/x-zoo'],
+ 'zsav' => ['application/x-spss-sav', 'application/x-spss-savefile'],
+ 'zz' => ['application/zlib'],
+ '123' => ['application/lotus123', 'application/vnd.lotus-1-2-3', 'application/wk1', 'application/x-123', 'application/x-lotus123', 'zz-application/zz-winassoc-123'],
+ '602' => ['application/x-t602'],
+ '669' => ['audio/x-mod'],
+ ];
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/MimeTypesInterface.php b/lam/lib/3rdParty/composer/symfony/mime/MimeTypesInterface.php
new file mode 100644
index 00000000..9fbd2cc2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/MimeTypesInterface.php
@@ -0,0 +1,32 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+/**
+ * @author Fabien Potencier
+ */
+interface MimeTypesInterface extends MimeTypeGuesserInterface
+{
+ /**
+ * Gets the extensions for the given MIME type.
+ *
+ * @return string[] an array of extensions (first one is the preferred one)
+ */
+ public function getExtensions(string $mimeType): array;
+
+ /**
+ * Gets the MIME types for the given extension.
+ *
+ * @return string[] an array of MIME types (first one is the preferred one)
+ */
+ public function getMimeTypes(string $ext): array;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractMultipartPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractMultipartPart.php
new file mode 100644
index 00000000..48b86202
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractMultipartPart.php
@@ -0,0 +1,99 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Header\Headers;
+
+/**
+ * @author Fabien Potencier
+ */
+abstract class AbstractMultipartPart extends AbstractPart
+{
+ private $boundary;
+ private $parts = [];
+
+ public function __construct(AbstractPart ...$parts)
+ {
+ parent::__construct();
+
+ foreach ($parts as $part) {
+ $this->parts[] = $part;
+ }
+ }
+
+ /**
+ * @return AbstractPart[]
+ */
+ public function getParts(): array
+ {
+ return $this->parts;
+ }
+
+ public function getMediaType(): string
+ {
+ return 'multipart';
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = parent::getPreparedHeaders();
+ $headers->setHeaderParameter('Content-Type', 'boundary', $this->getBoundary());
+
+ return $headers;
+ }
+
+ public function bodyToString(): string
+ {
+ $parts = $this->getParts();
+ $string = '';
+ foreach ($parts as $part) {
+ $string .= '--'.$this->getBoundary()."\r\n".$part->toString()."\r\n";
+ }
+ $string .= '--'.$this->getBoundary()."--\r\n";
+
+ return $string;
+ }
+
+ public function bodyToIterable(): iterable
+ {
+ $parts = $this->getParts();
+ foreach ($parts as $part) {
+ yield '--'.$this->getBoundary()."\r\n";
+ yield from $part->toIterable();
+ yield "\r\n";
+ }
+ yield '--'.$this->getBoundary()."--\r\n";
+ }
+
+ public function asDebugString(): string
+ {
+ $str = parent::asDebugString();
+ foreach ($this->getParts() as $part) {
+ $lines = explode("\n", $part->asDebugString());
+ $str .= "\n └ ".array_shift($lines);
+ foreach ($lines as $line) {
+ $str .= "\n |".$line;
+ }
+ }
+
+ return $str;
+ }
+
+ private function getBoundary(): string
+ {
+ if (null === $this->boundary) {
+ $this->boundary = '_=_symfony_'.time().'_'.bin2hex(random_bytes(16)).'_=_';
+ }
+
+ return $this->boundary;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractPart.php
new file mode 100644
index 00000000..93892d9d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/AbstractPart.php
@@ -0,0 +1,65 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Header\Headers;
+
+/**
+ * @author Fabien Potencier
+ */
+abstract class AbstractPart
+{
+ private $headers;
+
+ public function __construct()
+ {
+ $this->headers = new Headers();
+ }
+
+ public function getHeaders(): Headers
+ {
+ return $this->headers;
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = clone $this->headers;
+ $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype());
+
+ return $headers;
+ }
+
+ public function toString(): string
+ {
+ return $this->getPreparedHeaders()->toString()."\r\n".$this->bodyToString();
+ }
+
+ public function toIterable(): iterable
+ {
+ yield $this->getPreparedHeaders()->toString();
+ yield "\r\n";
+ yield from $this->bodyToIterable();
+ }
+
+ public function asDebugString(): string
+ {
+ return $this->getMediaType().'/'.$this->getMediaSubtype();
+ }
+
+ abstract public function bodyToString(): string;
+
+ abstract public function bodyToIterable(): iterable;
+
+ abstract public function getMediaType(): string;
+
+ abstract public function getMediaSubtype(): string;
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/DataPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/DataPart.php
new file mode 100644
index 00000000..423185fe
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/DataPart.php
@@ -0,0 +1,161 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Header\Headers;
+use Symfony\Component\Mime\MimeTypes;
+
+/**
+ * @author Fabien Potencier
+ */
+class DataPart extends TextPart
+{
+ private static $mimeTypes;
+
+ private $filename;
+ private $mediaType;
+ private $cid;
+ private $handle;
+
+ /**
+ * @param resource|string $body
+ */
+ public function __construct($body, string $filename = null, string $contentType = null, string $encoding = null)
+ {
+ if (null === $contentType) {
+ $contentType = 'application/octet-stream';
+ }
+ list($this->mediaType, $subtype) = explode('/', $contentType);
+
+ parent::__construct($body, null, $subtype, $encoding);
+
+ $this->filename = $filename;
+ $this->setName($filename);
+ $this->setDisposition('attachment');
+ }
+
+ public static function fromPath(string $path, string $name = null, string $contentType = null): self
+ {
+ // FIXME: if file is not readable, exception?
+
+ if (null === $contentType) {
+ $ext = strtolower(substr($path, strrpos($path, '.') + 1));
+ if (null === self::$mimeTypes) {
+ self::$mimeTypes = new MimeTypes();
+ }
+ $contentType = self::$mimeTypes->getMimeTypes($ext)[0] ?? 'application/octet-stream';
+ }
+
+ if (false === $handle = @fopen($path, 'r', false)) {
+ throw new InvalidArgumentException(sprintf('Unable to open path "%s"', $path));
+ }
+ $p = new self($handle, $name ?: basename($path), $contentType);
+ $p->handle = $handle;
+
+ return $p;
+ }
+
+ /**
+ * @return $this
+ */
+ public function asInline()
+ {
+ return $this->setDisposition('inline');
+ }
+
+ public function getContentId(): string
+ {
+ return $this->cid ?: $this->cid = $this->generateContentId();
+ }
+
+ public function hasContentId(): bool
+ {
+ return null !== $this->cid;
+ }
+
+ public function getMediaType(): string
+ {
+ return $this->mediaType;
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = parent::getPreparedHeaders();
+
+ if (null !== $this->cid) {
+ $headers->setHeaderBody('Id', 'Content-ID', $this->cid);
+ }
+
+ if (null !== $this->filename) {
+ $headers->setHeaderParameter('Content-Disposition', 'filename', $this->filename);
+ }
+
+ return $headers;
+ }
+
+ public function asDebugString(): string
+ {
+ $str = parent::asDebugString();
+ if (null !== $this->filename) {
+ $str .= ' filename: '.$this->filename;
+ }
+
+ return $str;
+ }
+
+ private function generateContentId(): string
+ {
+ return bin2hex(random_bytes(16)).'@symfony';
+ }
+
+ public function __destruct()
+ {
+ if (null !== $this->handle && \is_resource($this->handle)) {
+ fclose($this->handle);
+ }
+ }
+
+ /**
+ * @return array
+ */
+ public function __sleep()
+ {
+ // converts the body to a string
+ parent::__sleep();
+
+ $this->_parent = [];
+ foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) {
+ $r = new \ReflectionProperty(TextPart::class, $name);
+ $r->setAccessible(true);
+ $this->_parent[$name] = $r->getValue($this);
+ }
+ $this->_headers = $this->getHeaders();
+
+ return ['_headers', '_parent', 'filename', 'mediaType'];
+ }
+
+ public function __wakeup()
+ {
+ $r = new \ReflectionProperty(AbstractPart::class, 'headers');
+ $r->setAccessible(true);
+ $r->setValue($this, $this->_headers);
+ unset($this->_headers);
+
+ foreach (['body', 'charset', 'subtype', 'disposition', 'name', 'encoding'] as $name) {
+ $r = new \ReflectionProperty(TextPart::class, $name);
+ $r->setAccessible(true);
+ $r->setValue($this, $this->_parent[$name]);
+ }
+ unset($this->_parent);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/MessagePart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/MessagePart.php
new file mode 100644
index 00000000..1b5c23e2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/MessagePart.php
@@ -0,0 +1,62 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Message;
+use Symfony\Component\Mime\RawMessage;
+
+/**
+ * @final
+ *
+ * @author Fabien Potencier
+ */
+class MessagePart extends DataPart
+{
+ private $message;
+
+ public function __construct(RawMessage $message)
+ {
+ if ($message instanceof Message) {
+ $name = $message->getHeaders()->getHeaderBody('Subject').'.eml';
+ } else {
+ $name = 'email.eml';
+ }
+ parent::__construct('', $name);
+
+ $this->message = $message;
+ }
+
+ public function getMediaType(): string
+ {
+ return 'message';
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return 'rfc822';
+ }
+
+ public function getBody(): string
+ {
+ return $this->message->toString();
+ }
+
+ public function bodyToString(): string
+ {
+ return $this->getBody();
+ }
+
+ public function bodyToIterable(): iterable
+ {
+ return $this->message->toIterable();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/AlternativePart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/AlternativePart.php
new file mode 100644
index 00000000..fd754234
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/AlternativePart.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part\Multipart;
+
+use Symfony\Component\Mime\Part\AbstractMultipartPart;
+
+/**
+ * @author Fabien Potencier
+ */
+final class AlternativePart extends AbstractMultipartPart
+{
+ public function getMediaSubtype(): string
+ {
+ return 'alternative';
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/DigestPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/DigestPart.php
new file mode 100644
index 00000000..27537f15
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/DigestPart.php
@@ -0,0 +1,31 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part\Multipart;
+
+use Symfony\Component\Mime\Part\AbstractMultipartPart;
+use Symfony\Component\Mime\Part\MessagePart;
+
+/**
+ * @author Fabien Potencier
+ */
+final class DigestPart extends AbstractMultipartPart
+{
+ public function __construct(MessagePart ...$parts)
+ {
+ parent::__construct(...$parts);
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return 'digest';
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/FormDataPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/FormDataPart.php
new file mode 100644
index 00000000..88aa1a31
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/FormDataPart.php
@@ -0,0 +1,94 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part\Multipart;
+
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Part\AbstractMultipartPart;
+use Symfony\Component\Mime\Part\DataPart;
+use Symfony\Component\Mime\Part\TextPart;
+
+/**
+ * Implements RFC 7578.
+ *
+ * @author Fabien Potencier
+ */
+final class FormDataPart extends AbstractMultipartPart
+{
+ private $fields = [];
+
+ /**
+ * @param (string|array|DataPart)[] $fields
+ */
+ public function __construct(array $fields = [])
+ {
+ parent::__construct();
+
+ foreach ($fields as $name => $value) {
+ if (!\is_string($value) && !\is_array($value) && !$value instanceof TextPart) {
+ throw new InvalidArgumentException(sprintf('A form field value can only be a string, an array, or an instance of TextPart ("%s" given).', \is_object($value) ? \get_class($value) : \gettype($value)));
+ }
+
+ $this->fields[$name] = $value;
+ }
+ // HTTP does not support \r\n in header values
+ $this->getHeaders()->setMaxLineLength(PHP_INT_MAX);
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return 'form-data';
+ }
+
+ public function getParts(): array
+ {
+ return $this->prepareFields($this->fields);
+ }
+
+ private function prepareFields(array $fields): array
+ {
+ $values = [];
+ array_walk_recursive($fields, function ($item, $key) use (&$values) {
+ if (!\is_array($item)) {
+ $values[] = $this->preparePart($key, $item);
+ }
+ });
+
+ return $values;
+ }
+
+ private function preparePart(string $name, $value): TextPart
+ {
+ if (\is_string($value)) {
+ return $this->configurePart($name, new TextPart($value, 'utf-8', 'plain', '8bit'));
+ }
+
+ return $this->configurePart($name, $value);
+ }
+
+ private function configurePart(string $name, TextPart $part): TextPart
+ {
+ static $r;
+
+ if (null === $r) {
+ $r = new \ReflectionProperty(TextPart::class, 'encoding');
+ $r->setAccessible(true);
+ }
+
+ $part->setDisposition('form-data');
+ $part->setName($name);
+ // HTTP does not support \r\n in header values
+ $part->getHeaders()->setMaxLineLength(PHP_INT_MAX);
+ $r->setValue($part, '8bit');
+
+ return $part;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/MixedPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/MixedPart.php
new file mode 100644
index 00000000..c8d7028c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/MixedPart.php
@@ -0,0 +1,25 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part\Multipart;
+
+use Symfony\Component\Mime\Part\AbstractMultipartPart;
+
+/**
+ * @author Fabien Potencier
+ */
+final class MixedPart extends AbstractMultipartPart
+{
+ public function getMediaSubtype(): string
+ {
+ return 'mixed';
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/RelatedPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/RelatedPart.php
new file mode 100644
index 00000000..08fdd5fa
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/Multipart/RelatedPart.php
@@ -0,0 +1,55 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part\Multipart;
+
+use Symfony\Component\Mime\Part\AbstractMultipartPart;
+use Symfony\Component\Mime\Part\AbstractPart;
+
+/**
+ * @author Fabien Potencier
+ */
+final class RelatedPart extends AbstractMultipartPart
+{
+ private $mainPart;
+
+ public function __construct(AbstractPart $mainPart, AbstractPart $part, AbstractPart ...$parts)
+ {
+ $this->mainPart = $mainPart;
+ $this->prepareParts($part, ...$parts);
+
+ parent::__construct($part, ...$parts);
+ }
+
+ public function getParts(): array
+ {
+ return array_merge([$this->mainPart], parent::getParts());
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return 'related';
+ }
+
+ private function generateContentId(): string
+ {
+ return bin2hex(random_bytes(16)).'@symfony';
+ }
+
+ private function prepareParts(AbstractPart ...$parts): void
+ {
+ foreach ($parts as $part) {
+ if (!$part->getHeaders()->has('Content-ID')) {
+ $part->getHeaders()->setHeaderBody('Id', 'Content-ID', $this->generateContentId());
+ }
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/SMimePart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/SMimePart.php
new file mode 100644
index 00000000..1dfc1aef
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/SMimePart.php
@@ -0,0 +1,116 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Header\Headers;
+
+/**
+ * @author Sebastiaan Stok
+ */
+class SMimePart extends AbstractPart
+{
+ private $body;
+ private $type;
+ private $subtype;
+ private $parameters;
+
+ /**
+ * @param iterable|string $body
+ */
+ public function __construct($body, string $type, string $subtype, array $parameters)
+ {
+ parent::__construct();
+
+ if (!\is_string($body) && !is_iterable($body)) {
+ throw new \TypeError(sprintf('The body of "%s" must be a string or a iterable (got "%s").', self::class, \is_object($body) ? \get_class($body) : \gettype($body)));
+ }
+
+ $this->body = $body;
+ $this->type = $type;
+ $this->subtype = $subtype;
+ $this->parameters = $parameters;
+ }
+
+ public function getMediaType(): string
+ {
+ return $this->type;
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return $this->subtype;
+ }
+
+ public function bodyToString(): string
+ {
+ if (\is_string($this->body)) {
+ return $this->body;
+ }
+
+ $body = '';
+ foreach ($this->body as $chunk) {
+ $body .= $chunk;
+ }
+ $this->body = $body;
+
+ return $body;
+ }
+
+ public function bodyToIterable(): iterable
+ {
+ if (\is_string($this->body)) {
+ yield $this->body;
+
+ return;
+ }
+
+ $body = '';
+ foreach ($this->body as $chunk) {
+ $body .= $chunk;
+ yield $chunk;
+ }
+ $this->body = $body;
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = clone parent::getHeaders();
+
+ $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype());
+
+ foreach ($this->parameters as $name => $value) {
+ $headers->setHeaderParameter('Content-Type', $name, $value);
+ }
+
+ return $headers;
+ }
+
+ public function __sleep(): array
+ {
+ // convert iterables to strings for serialization
+ if (is_iterable($this->body)) {
+ $this->body = $this->bodyToString();
+ }
+
+ $this->_headers = $this->getHeaders();
+
+ return ['_headers', 'body', 'type', 'subtype', 'parameters'];
+ }
+
+ public function __wakeup(): void
+ {
+ $r = new \ReflectionProperty(AbstractPart::class, 'headers');
+ $r->setAccessible(true);
+ $r->setValue($this, $this->_headers);
+ unset($this->_headers);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Part/TextPart.php b/lam/lib/3rdParty/composer/symfony/mime/Part/TextPart.php
new file mode 100644
index 00000000..a41d91dd
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Part/TextPart.php
@@ -0,0 +1,204 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Part;
+
+use Symfony\Component\Mime\Encoder\Base64ContentEncoder;
+use Symfony\Component\Mime\Encoder\ContentEncoderInterface;
+use Symfony\Component\Mime\Encoder\EightBitContentEncoder;
+use Symfony\Component\Mime\Encoder\QpContentEncoder;
+use Symfony\Component\Mime\Exception\InvalidArgumentException;
+use Symfony\Component\Mime\Header\Headers;
+
+/**
+ * @author Fabien Potencier
+ */
+class TextPart extends AbstractPart
+{
+ private static $encoders = [];
+
+ private $body;
+ private $charset;
+ private $subtype;
+ private $disposition;
+ private $name;
+ private $encoding;
+
+ /**
+ * @param resource|string $body
+ */
+ public function __construct($body, ?string $charset = 'utf-8', $subtype = 'plain', string $encoding = null)
+ {
+ parent::__construct();
+
+ if (!\is_string($body) && !\is_resource($body)) {
+ throw new \TypeError(sprintf('The body of "%s" must be a string or a resource (got "%s").', self::class, \is_object($body) ? \get_class($body) : \gettype($body)));
+ }
+
+ $this->body = $body;
+ $this->charset = $charset;
+ $this->subtype = $subtype;
+
+ if (null === $encoding) {
+ $this->encoding = $this->chooseEncoding();
+ } else {
+ if ('quoted-printable' !== $encoding && 'base64' !== $encoding && '8bit' !== $encoding) {
+ throw new InvalidArgumentException(sprintf('The encoding must be one of "quoted-printable", "base64", or "8bit" ("%s" given).', $encoding));
+ }
+ $this->encoding = $encoding;
+ }
+ }
+
+ public function getMediaType(): string
+ {
+ return 'text';
+ }
+
+ public function getMediaSubtype(): string
+ {
+ return $this->subtype;
+ }
+
+ /**
+ * @param string $disposition one of attachment, inline, or form-data
+ *
+ * @return $this
+ */
+ public function setDisposition(string $disposition)
+ {
+ $this->disposition = $disposition;
+
+ return $this;
+ }
+
+ /**
+ * Sets the name of the file (used by FormDataPart).
+ *
+ * @return $this
+ */
+ public function setName($name)
+ {
+ $this->name = $name;
+
+ return $this;
+ }
+
+ public function getBody(): string
+ {
+ if (!\is_resource($this->body)) {
+ return $this->body;
+ }
+
+ if (stream_get_meta_data($this->body)['seekable'] ?? false) {
+ rewind($this->body);
+ }
+
+ return stream_get_contents($this->body) ?: '';
+ }
+
+ public function bodyToString(): string
+ {
+ return $this->getEncoder()->encodeString($this->getBody(), $this->charset);
+ }
+
+ public function bodyToIterable(): iterable
+ {
+ if (\is_resource($this->body)) {
+ if (stream_get_meta_data($this->body)['seekable'] ?? false) {
+ rewind($this->body);
+ }
+ yield from $this->getEncoder()->encodeByteStream($this->body);
+ } else {
+ yield $this->getEncoder()->encodeString($this->body);
+ }
+ }
+
+ public function getPreparedHeaders(): Headers
+ {
+ $headers = parent::getPreparedHeaders();
+
+ $headers->setHeaderBody('Parameterized', 'Content-Type', $this->getMediaType().'/'.$this->getMediaSubtype());
+ if ($this->charset) {
+ $headers->setHeaderParameter('Content-Type', 'charset', $this->charset);
+ }
+ if ($this->name) {
+ $headers->setHeaderParameter('Content-Type', 'name', $this->name);
+ }
+ $headers->setHeaderBody('Text', 'Content-Transfer-Encoding', $this->encoding);
+
+ if (!$headers->has('Content-Disposition') && null !== $this->disposition) {
+ $headers->setHeaderBody('Parameterized', 'Content-Disposition', $this->disposition);
+ if ($this->name) {
+ $headers->setHeaderParameter('Content-Disposition', 'name', $this->name);
+ }
+ }
+
+ return $headers;
+ }
+
+ public function asDebugString(): string
+ {
+ $str = parent::asDebugString();
+ if (null !== $this->charset) {
+ $str .= ' charset: '.$this->charset;
+ }
+ if (null !== $this->disposition) {
+ $str .= ' disposition: '.$this->disposition;
+ }
+
+ return $str;
+ }
+
+ private function getEncoder(): ContentEncoderInterface
+ {
+ if ('8bit' === $this->encoding) {
+ return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new EightBitContentEncoder());
+ }
+
+ if ('quoted-printable' === $this->encoding) {
+ return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new QpContentEncoder());
+ }
+
+ return self::$encoders[$this->encoding] ?? (self::$encoders[$this->encoding] = new Base64ContentEncoder());
+ }
+
+ private function chooseEncoding(): string
+ {
+ if (null === $this->charset) {
+ return 'base64';
+ }
+
+ return 'quoted-printable';
+ }
+
+ /**
+ * @return array
+ */
+ public function __sleep()
+ {
+ // convert resources to strings for serialization
+ if (\is_resource($this->body)) {
+ $this->body = $this->getBody();
+ }
+
+ $this->_headers = $this->getHeaders();
+
+ return ['_headers', 'body', 'charset', 'subtype', 'disposition', 'name', 'encoding'];
+ }
+
+ public function __wakeup()
+ {
+ $r = new \ReflectionProperty(AbstractPart::class, 'headers');
+ $r->setAccessible(true);
+ $r->setValue($this, $this->_headers);
+ unset($this->_headers);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/README.md b/lam/lib/3rdParty/composer/symfony/mime/README.md
new file mode 100644
index 00000000..4d565c92
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/README.md
@@ -0,0 +1,13 @@
+MIME Component
+==============
+
+The MIME component allows manipulating MIME messages.
+
+Resources
+---------
+
+ * [Documentation](https://symfony.com/doc/current/components/mime.html)
+ * [Contributing](https://symfony.com/doc/current/contributing/index.html)
+ * [Report issues](https://github.com/symfony/symfony/issues) and
+ [send Pull Requests](https://github.com/symfony/symfony/pulls)
+ in the [main Symfony repository](https://github.com/symfony/symfony)
diff --git a/lam/lib/3rdParty/composer/symfony/mime/RawMessage.php b/lam/lib/3rdParty/composer/symfony/mime/RawMessage.php
new file mode 100644
index 00000000..79a27e9f
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/RawMessage.php
@@ -0,0 +1,88 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime;
+
+use Symfony\Component\Mime\Exception\LogicException;
+
+/**
+ * @author Fabien Potencier
+ */
+class RawMessage implements \Serializable
+{
+ private $message;
+
+ /**
+ * @param iterable|string $message
+ */
+ public function __construct($message)
+ {
+ $this->message = $message;
+ }
+
+ public function toString(): string
+ {
+ if (\is_string($this->message)) {
+ return $this->message;
+ }
+
+ return $this->message = implode('', iterator_to_array($this->message, false));
+ }
+
+ public function toIterable(): iterable
+ {
+ if (\is_string($this->message)) {
+ yield $this->message;
+
+ return;
+ }
+
+ $message = '';
+ foreach ($this->message as $chunk) {
+ $message .= $chunk;
+ yield $chunk;
+ }
+ $this->message = $message;
+ }
+
+ /**
+ * @throws LogicException if the message is not valid
+ */
+ public function ensureValidity()
+ {
+ }
+
+ /**
+ * @internal
+ */
+ final public function serialize(): string
+ {
+ return serialize($this->__serialize());
+ }
+
+ /**
+ * @internal
+ */
+ final public function unserialize($serialized)
+ {
+ $this->__unserialize(unserialize($serialized));
+ }
+
+ public function __serialize(): array
+ {
+ return [$this->message];
+ }
+
+ public function __unserialize(array $data): void
+ {
+ [$this->message] = $data;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Resources/bin/update_mime_types.php b/lam/lib/3rdParty/composer/symfony/mime/Resources/bin/update_mime_types.php
new file mode 100644
index 00000000..74a9449c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Resources/bin/update_mime_types.php
@@ -0,0 +1,166 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+// load new map
+$data = file_get_contents('https://svn.apache.org/repos/asf/httpd/httpd/trunk/docs/conf/mime.types');
+$new = [];
+foreach (explode("\n", $data) as $line) {
+ if (!$line || '#' == $line[0]) {
+ continue;
+ }
+ $mimeType = substr($line, 0, strpos($line, "\t"));
+ $extensions = explode(' ', substr($line, strrpos($line, "\t") + 1));
+ $new[$mimeType] = $extensions;
+}
+
+$xml = simplexml_load_string(file_get_contents('https://raw.github.com/minad/mimemagic/master/script/freedesktop.org.xml'));
+foreach ($xml as $node) {
+ $exts = [];
+ foreach ($node->glob as $glob) {
+ $pattern = (string) $glob['pattern'];
+ if ('*' != $pattern[0] || '.' != $pattern[1]) {
+ continue;
+ }
+
+ $exts[] = substr($pattern, 2);
+ }
+
+ if (!$exts) {
+ continue;
+ }
+
+ $mt = strtolower((string) $node['type']);
+ $new[$mt] = array_merge($new[$mt] ?? [], $exts);
+ foreach ($node->alias as $alias) {
+ $mt = strtolower((string) $alias['type']);
+ $new[$mt] = array_merge($new[$mt] ?? [], $exts);
+ }
+}
+
+// load current map
+$data = file_get_contents($output = __DIR__.'/../../MimeTypes.php');
+$current = [];
+$pre = '';
+$post = '';
+foreach (explode("\n", $data) as $line) {
+ if (!preg_match("{^ '([^']+/[^']+)' => \['(.+)'\],$}", $line, $matches)) {
+ if (!$current) {
+ $pre .= $line."\n";
+ } else {
+ $post .= $line."\n";
+ }
+ continue;
+ }
+ $current[$matches[1]] = explode("', '", $matches[2]);
+}
+
+// we merge the 2 maps (we never remove old mime types)
+$map = array_replace_recursive($current, $new);
+ksort($map);
+
+$data = $pre;
+foreach ($map as $mimeType => $exts) {
+ $data .= sprintf(" '%s' => ['%s'],\n", $mimeType, implode("', '", array_unique($exts)));
+}
+$data .= $post;
+
+// reverse map
+// we prefill the extensions with some preferences for content-types
+$exts = [
+ 'aif' => ['audio/x-aiff'],
+ 'aiff' => ['audio/x-aiff'],
+ 'aps' => ['application/postscript'],
+ 'avi' => ['video/avi'],
+ 'bmp' => ['image/bmp'],
+ 'bz2' => ['application/x-bz2'],
+ 'css' => ['text/css'],
+ 'csv' => ['text/csv'],
+ 'dmg' => ['application/x-apple-diskimage'],
+ 'doc' => ['application/msword'],
+ 'docx' => ['application/vnd.openxmlformats-officedocument.wordprocessingml.document'],
+ 'eml' => ['message/rfc822'],
+ 'exe' => ['application/x-ms-dos-executable'],
+ 'flv' => ['video/x-flv'],
+ 'gif' => ['image/gif'],
+ 'gz' => ['application/x-gzip'],
+ 'hqx' => ['application/stuffit'],
+ 'htm' => ['text/html'],
+ 'html' => ['text/html'],
+ 'jar' => ['application/x-java-archive'],
+ 'jpeg' => ['image/jpeg'],
+ 'jpg' => ['image/jpeg'],
+ 'js' => ['text/javascript'],
+ 'm3u' => ['audio/x-mpegurl'],
+ 'm4a' => ['audio/mp4'],
+ 'mdb' => ['application/x-msaccess'],
+ 'mid' => ['audio/midi'],
+ 'midi' => ['audio/midi'],
+ 'mov' => ['video/quicktime'],
+ 'mp3' => ['audio/mpeg'],
+ 'mp4' => ['video/mp4'],
+ 'mpeg' => ['video/mpeg'],
+ 'mpg' => ['video/mpeg'],
+ 'ogg' => ['audio/ogg'],
+ 'pdf' => ['application/pdf'],
+ 'php' => ['application/x-php'],
+ 'php3' => ['application/x-php'],
+ 'php4' => ['application/x-php'],
+ 'php5' => ['application/x-php'],
+ 'png' => ['image/png'],
+ 'ppt' => ['application/vnd.ms-powerpoint'],
+ 'pptx' => ['application/vnd.openxmlformats-officedocument.presentationml.presentation'],
+ 'ps' => ['application/postscript'],
+ 'rar' => ['application/x-rar-compressed'],
+ 'rtf' => ['application/rtf'],
+ 'sit' => ['application/x-stuffit'],
+ 'svg' => ['image/svg+xml'],
+ 'tar' => ['application/x-tar'],
+ 'tif' => ['image/tiff'],
+ 'tiff' => ['image/tiff'],
+ 'ttf' => ['application/x-font-truetype'],
+ 'txt' => ['text/plain'],
+ 'vcf' => ['text/x-vcard'],
+ 'wav' => ['audio/wav'],
+ 'wma' => ['audio/x-ms-wma'],
+ 'wmv' => ['audio/x-ms-wmv'],
+ 'xls' => ['application/vnd.ms-excel'],
+ 'xlsx' => ['application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'],
+ 'xml' => ['application/xml'],
+ 'zip' => ['application/zip'],
+];
+foreach ($map as $mimeType => $extensions) {
+ foreach ($extensions as $extension) {
+ $exts[$extension][] = $mimeType;
+ }
+}
+ksort($exts);
+
+$updated = '';
+$state = 0;
+foreach (explode("\n", $data) as $line) {
+ if (!preg_match("{^ '([^'/]+)' => \['(.+)'\],$}", $line, $matches)) {
+ if (1 === $state) {
+ $state = 2;
+ foreach ($exts as $ext => $mimeTypes) {
+ $updated .= sprintf(" '%s' => ['%s'],\n", $ext, implode("', '", array_unique($mimeTypes)));
+ }
+ }
+ $updated .= $line."\n";
+ continue;
+ }
+ $state = 1;
+}
+
+$updated = preg_replace('{Updated from upstream on .+?\.}', 'Updated from upstream on '.date('Y-m-d'), $updated, -1);
+
+file_put_contents($output, rtrim($updated, "\n")."\n");
+
+echo "Done.\n";
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAddressContains.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAddressContains.php
new file mode 100644
index 00000000..58ef360c
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAddressContains.php
@@ -0,0 +1,74 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\Mime\Header\MailboxHeader;
+use Symfony\Component\Mime\Header\MailboxListHeader;
+use Symfony\Component\Mime\RawMessage;
+
+final class EmailAddressContains extends Constraint
+{
+ private $headerName;
+ private $expectedValue;
+
+ public function __construct(string $headerName, string $expectedValue)
+ {
+ $this->headerName = $headerName;
+ $this->expectedValue = $expectedValue;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('contains address "%s" with value "%s"', $this->headerName, $this->expectedValue);
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message address on a RawMessage instance.');
+ }
+
+ $header = $message->getHeaders()->get($this->headerName);
+ if ($header instanceof MailboxHeader) {
+ return $this->expectedValue === $header->Address()->getAddress();
+ } elseif ($header instanceof MailboxListHeader) {
+ foreach ($header->getAddresses() as $address) {
+ if ($this->expectedValue === $address->getAddress()) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ throw new \LogicException(sprintf('Unable to test a message address on a non-address header.'));
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($message): string
+ {
+ return sprintf('the Email %s (value is %s)', $this->toString(), $message->getHeaders()->get($this->headerName)->getBodyAsString());
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAttachmentCount.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAttachmentCount.php
new file mode 100644
index 00000000..b219f28b
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailAttachmentCount.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\Mime\RawMessage;
+
+final class EmailAttachmentCount extends Constraint
+{
+ private $expectedValue;
+ private $transport;
+
+ public function __construct(int $expectedValue, string $transport = null)
+ {
+ $this->expectedValue = $expectedValue;
+ $this->transport = $transport;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has sent "%d" attachment(s)', $this->expectedValue);
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message attachment on a RawMessage or Message instance.');
+ }
+
+ return $this->expectedValue === \count($message->getAttachments());
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($message): string
+ {
+ return 'the Email '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHasHeader.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHasHeader.php
new file mode 100644
index 00000000..a29f835f
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHasHeader.php
@@ -0,0 +1,57 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\Mime\RawMessage;
+
+final class EmailHasHeader extends Constraint
+{
+ private $headerName;
+
+ public function __construct(string $headerName)
+ {
+ $this->headerName = $headerName;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has header "%s"', $this->headerName);
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message header on a RawMessage instance.');
+ }
+
+ return $message->getHeaders()->has($this->headerName);
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($message): string
+ {
+ return 'the Email '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHeaderSame.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHeaderSame.php
new file mode 100644
index 00000000..bc7e330e
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHeaderSame.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+use Symfony\Component\Mime\RawMessage;
+
+final class EmailHeaderSame extends Constraint
+{
+ private $headerName;
+ private $expectedValue;
+
+ public function __construct(string $headerName, string $expectedValue)
+ {
+ $this->headerName = $headerName;
+ $this->expectedValue = $expectedValue;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('has header "%s" with value "%s"', $this->headerName, $this->expectedValue);
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message header on a RawMessage instance.');
+ }
+
+ return $this->expectedValue === $message->getHeaders()->get($this->headerName)->getBodyAsString();
+ }
+
+ /**
+ * @param RawMessage $message
+ *
+ * {@inheritdoc}
+ */
+ protected function failureDescription($message): string
+ {
+ return sprintf('the Email %s (value is %s)', $this->toString(), $message->getHeaders()->get($this->headerName)->getBodyAsString());
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php
new file mode 100644
index 00000000..89651951
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailHtmlBodyContains.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+
+final class EmailHtmlBodyContains extends Constraint
+{
+ private $expectedText;
+
+ public function __construct(string $expectedText)
+ {
+ $this->expectedText = $expectedText;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('contains "%s"', $this->expectedText);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param RawMessage $message
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message HTML body on a RawMessage or Message instance.');
+ }
+
+ return false !== mb_strpos($message->getHtmlBody(), $this->expectedText);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param RawMessage $message
+ */
+ protected function failureDescription($message): string
+ {
+ return 'the Email HTML body '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailTextBodyContains.php b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailTextBodyContains.php
new file mode 100644
index 00000000..b5e87f96
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/Test/Constraint/EmailTextBodyContains.php
@@ -0,0 +1,56 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Mime\Test\Constraint;
+
+use PHPUnit\Framework\Constraint\Constraint;
+
+final class EmailTextBodyContains extends Constraint
+{
+ private $expectedText;
+
+ public function __construct(string $expectedText)
+ {
+ $this->expectedText = $expectedText;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function toString(): string
+ {
+ return sprintf('contains "%s"', $this->expectedText);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param RawMessage $message
+ */
+ protected function matches($message): bool
+ {
+ if (RawMessage::class === \get_class($message) || Message::class === \get_class($message)) {
+ throw new \LogicException('Unable to test a message text body on a RawMessage or Message instance.');
+ }
+
+ return false !== mb_strpos($message->getTextBody(), $this->expectedText);
+ }
+
+ /**
+ * {@inheritdoc}
+ *
+ * @param RawMessage $message
+ */
+ protected function failureDescription($message): string
+ {
+ return 'the Email text body '.$this->toString();
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/mime/composer.json b/lam/lib/3rdParty/composer/symfony/mime/composer.json
new file mode 100644
index 00000000..c3f26261
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/mime/composer.json
@@ -0,0 +1,42 @@
+{
+ "name": "symfony/mime",
+ "type": "library",
+ "description": "A library to manipulate MIME messages",
+ "keywords": ["mime", "mime-type"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": "^7.2.5",
+ "symfony/polyfill-intl-idn": "^1.10",
+ "symfony/polyfill-mbstring": "^1.0"
+ },
+ "require-dev": {
+ "egulias/email-validator": "^2.1.10",
+ "symfony/dependency-injection": "^4.4|^5.0"
+ },
+ "conflict": {
+ "symfony/mailer": "<4.4"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Component\\Mime\\": "" },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "5.0-dev"
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-ctype/Ctype.php b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/Ctype.php
new file mode 100644
index 00000000..58414dc7
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/Ctype.php
@@ -0,0 +1,227 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Ctype;
+
+/**
+ * Ctype implementation through regex.
+ *
+ * @internal
+ *
+ * @author Gert de Pagter
+ */
+final class Ctype
+{
+ /**
+ * Returns TRUE if every character in text is either a letter or a digit, FALSE otherwise.
+ *
+ * @see https://php.net/ctype-alnum
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_alnum($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z0-9]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is a letter, FALSE otherwise.
+ *
+ * @see https://php.net/ctype-alpha
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_alpha($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^A-Za-z]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is a control character from the current locale, FALSE otherwise.
+ *
+ * @see https://php.net/ctype-cntrl
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_cntrl($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^\x00-\x1f\x7f]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in the string text is a decimal digit, FALSE otherwise.
+ *
+ * @see https://php.net/ctype-digit
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_digit($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^0-9]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is printable and actually creates visible output (no white space), FALSE otherwise.
+ *
+ * @see https://php.net/ctype-graph
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_graph($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^!-~]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is a lowercase letter.
+ *
+ * @see https://php.net/ctype-lower
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_lower($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^a-z]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text will actually create output (including blanks). Returns FALSE if text contains control characters or characters that do not have any output or control function at all.
+ *
+ * @see https://php.net/ctype-print
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_print($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^ -~]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is printable, but neither letter, digit or blank, FALSE otherwise.
+ *
+ * @see https://php.net/ctype-punct
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_punct($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^!-\/\:-@\[-`\{-~]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text creates some sort of white space, FALSE otherwise. Besides the blank character this also includes tab, vertical tab, line feed, carriage return and form feed characters.
+ *
+ * @see https://php.net/ctype-space
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_space($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^\s]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is an uppercase letter.
+ *
+ * @see https://php.net/ctype-upper
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_upper($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^A-Z]/', $text);
+ }
+
+ /**
+ * Returns TRUE if every character in text is a hexadecimal 'digit', that is a decimal digit or a character from [A-Fa-f] , FALSE otherwise.
+ *
+ * @see https://php.net/ctype-xdigit
+ *
+ * @param string|int $text
+ *
+ * @return bool
+ */
+ public static function ctype_xdigit($text)
+ {
+ $text = self::convert_int_to_char_for_ctype($text);
+
+ return \is_string($text) && '' !== $text && !preg_match('/[^A-Fa-f0-9]/', $text);
+ }
+
+ /**
+ * Converts integers to their char versions according to normal ctype behaviour, if needed.
+ *
+ * If an integer between -128 and 255 inclusive is provided,
+ * it is interpreted as the ASCII value of a single character
+ * (negative values have 256 added in order to allow characters in the Extended ASCII range).
+ * Any other integer is interpreted as a string containing the decimal digits of the integer.
+ *
+ * @param string|int $int
+ *
+ * @return mixed
+ */
+ private static function convert_int_to_char_for_ctype($int)
+ {
+ if (!\is_int($int)) {
+ return $int;
+ }
+
+ if ($int < -128 || $int > 255) {
+ return (string) $int;
+ }
+
+ if ($int < 0) {
+ $int += 256;
+ }
+
+ return \chr($int);
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-ctype/LICENSE b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/LICENSE
new file mode 100644
index 00000000..3f853aaf
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-ctype/README.md b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/README.md
new file mode 100644
index 00000000..8add1ab0
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/README.md
@@ -0,0 +1,12 @@
+Symfony Polyfill / Ctype
+========================
+
+This component provides `ctype_*` functions to users who run php versions without the ctype extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-ctype/bootstrap.php b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/bootstrap.php
new file mode 100644
index 00000000..14d1d0fa
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/bootstrap.php
@@ -0,0 +1,26 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Polyfill\Ctype as p;
+
+if (!function_exists('ctype_alnum')) {
+ function ctype_alnum($text) { return p\Ctype::ctype_alnum($text); }
+ function ctype_alpha($text) { return p\Ctype::ctype_alpha($text); }
+ function ctype_cntrl($text) { return p\Ctype::ctype_cntrl($text); }
+ function ctype_digit($text) { return p\Ctype::ctype_digit($text); }
+ function ctype_graph($text) { return p\Ctype::ctype_graph($text); }
+ function ctype_lower($text) { return p\Ctype::ctype_lower($text); }
+ function ctype_print($text) { return p\Ctype::ctype_print($text); }
+ function ctype_punct($text) { return p\Ctype::ctype_punct($text); }
+ function ctype_space($text) { return p\Ctype::ctype_space($text); }
+ function ctype_upper($text) { return p\Ctype::ctype_upper($text); }
+ function ctype_xdigit($text) { return p\Ctype::ctype_xdigit($text); }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-ctype/composer.json b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/composer.json
new file mode 100644
index 00000000..2a2ea044
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-ctype/composer.json
@@ -0,0 +1,34 @@
+{
+ "name": "symfony/polyfill-ctype",
+ "type": "library",
+ "description": "Symfony polyfill for ctype functions",
+ "keywords": ["polyfill", "compatibility", "portable", "ctype"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Polyfill\\Ctype\\": "" },
+ "files": [ "bootstrap.php" ]
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.13-dev"
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/Idn.php b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/Idn.php
new file mode 100644
index 00000000..adb718d2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/Idn.php
@@ -0,0 +1,283 @@
+
+ * @author Sebastian Kroczek
+ * @author Dmitry Lukashin
+ * @author Laurent Bassin
+ *
+ * @internal
+ */
+final class Idn
+{
+ const INTL_IDNA_VARIANT_2003 = 0;
+ const INTL_IDNA_VARIANT_UTS46 = 1;
+
+ private static $encodeTable = array(
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
+ 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ );
+
+ private static $decodeTable = array(
+ 'a' => 0, 'b' => 1, 'c' => 2, 'd' => 3, 'e' => 4, 'f' => 5,
+ 'g' => 6, 'h' => 7, 'i' => 8, 'j' => 9, 'k' => 10, 'l' => 11,
+ 'm' => 12, 'n' => 13, 'o' => 14, 'p' => 15, 'q' => 16, 'r' => 17,
+ 's' => 18, 't' => 19, 'u' => 20, 'v' => 21, 'w' => 22, 'x' => 23,
+ 'y' => 24, 'z' => 25, '0' => 26, '1' => 27, '2' => 28, '3' => 29,
+ '4' => 30, '5' => 31, '6' => 32, '7' => 33, '8' => 34, '9' => 35,
+ );
+
+ public static function idn_to_ascii($domain, $options, $variant, &$idna_info = array())
+ {
+ if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
+ @trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
+ }
+
+ if (self::INTL_IDNA_VARIANT_UTS46 === $variant) {
+ $domain = mb_strtolower($domain, 'utf-8');
+ }
+
+ $parts = explode('.', $domain);
+
+ foreach ($parts as $i => &$part) {
+ if ('' === $part && \count($parts) > 1 + $i) {
+ return false;
+ }
+ if (false === $part = self::encodePart($part)) {
+ return false;
+ }
+ }
+
+ $output = implode('.', $parts);
+
+ $idna_info = array(
+ 'result' => \strlen($output) > 255 ? false : $output,
+ 'isTransitionalDifferent' => false,
+ 'errors' => 0,
+ );
+
+ return $idna_info['result'];
+ }
+
+ public static function idn_to_utf8($domain, $options, $variant, &$idna_info = array())
+ {
+ if (\PHP_VERSION_ID >= 70200 && self::INTL_IDNA_VARIANT_2003 === $variant) {
+ @trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
+ }
+
+ $parts = explode('.', $domain);
+
+ foreach ($parts as &$part) {
+ $length = \strlen($part);
+ if ($length < 1 || 63 < $length) {
+ continue;
+ }
+ if (0 !== strpos($part, 'xn--')) {
+ continue;
+ }
+
+ $part = substr($part, 4);
+ $part = self::decodePart($part);
+ }
+
+ $output = implode('.', $parts);
+
+ $idna_info = array(
+ 'result' => \strlen($output) > 255 ? false : $output,
+ 'isTransitionalDifferent' => false,
+ 'errors' => 0,
+ );
+
+ return $idna_info['result'];
+ }
+
+ private static function encodePart($input)
+ {
+ $codePoints = self::listCodePoints($input);
+
+ $n = 128;
+ $bias = 72;
+ $delta = 0;
+ $h = $b = \count($codePoints['basic']);
+
+ $output = '';
+ foreach ($codePoints['basic'] as $code) {
+ $output .= mb_chr($code, 'utf-8');
+ }
+ if ($input === $output) {
+ return $output;
+ }
+ if ($b > 0) {
+ $output .= '-';
+ }
+
+ $codePoints['nonBasic'] = array_unique($codePoints['nonBasic']);
+ sort($codePoints['nonBasic']);
+
+ $i = 0;
+ $length = mb_strlen($input, 'utf-8');
+ while ($h < $length) {
+ $m = $codePoints['nonBasic'][$i++];
+ $delta += ($m - $n) * ($h + 1);
+ $n = $m;
+
+ foreach ($codePoints['all'] as $c) {
+ if ($c < $n || $c < 128) {
+ ++$delta;
+ }
+ if ($c === $n) {
+ $q = $delta;
+ for ($k = 36;; $k += 36) {
+ $t = self::calculateThreshold($k, $bias);
+ if ($q < $t) {
+ break;
+ }
+
+ $code = $t + (($q - $t) % (36 - $t));
+ $output .= self::$encodeTable[$code];
+
+ $q = ($q - $t) / (36 - $t);
+ }
+
+ $output .= self::$encodeTable[$q];
+ $bias = self::adapt($delta, $h + 1, ($h === $b));
+ $delta = 0;
+ ++$h;
+ }
+ }
+
+ ++$delta;
+ ++$n;
+ }
+
+ $output = 'xn--'.$output;
+
+ return \strlen($output) < 1 || 63 < \strlen($output) ? false : strtolower($output);
+ }
+
+ private static function listCodePoints($input)
+ {
+ $codePoints = array(
+ 'all' => array(),
+ 'basic' => array(),
+ 'nonBasic' => array(),
+ );
+
+ $length = mb_strlen($input, 'utf-8');
+ for ($i = 0; $i < $length; ++$i) {
+ $char = mb_substr($input, $i, 1, 'utf-8');
+ $code = mb_ord($char, 'utf-8');
+ if ($code < 128) {
+ $codePoints['all'][] = $codePoints['basic'][] = $code;
+ } else {
+ $codePoints['all'][] = $codePoints['nonBasic'][] = $code;
+ }
+ }
+
+ return $codePoints;
+ }
+
+ private static function calculateThreshold($k, $bias)
+ {
+ if ($k <= $bias + 1) {
+ return 1;
+ }
+ if ($k >= $bias + 26) {
+ return 26;
+ }
+
+ return $k - $bias;
+ }
+
+ private static function adapt($delta, $numPoints, $firstTime)
+ {
+ $delta = (int) ($firstTime ? $delta / 700 : $delta / 2);
+ $delta += (int) ($delta / $numPoints);
+
+ $k = 0;
+ while ($delta > 35 * 13) {
+ $delta = (int) ($delta / 35);
+ $k = $k + 36;
+ }
+
+ return $k + (int) (36 * $delta / ($delta + 38));
+ }
+
+ private static function decodePart($input)
+ {
+ $n = 128;
+ $i = 0;
+ $bias = 72;
+ $output = '';
+
+ $pos = strrpos($input, '-');
+ if (false !== $pos) {
+ $output = substr($input, 0, $pos++);
+ } else {
+ $pos = 0;
+ }
+
+ $outputLength = \strlen($output);
+ $inputLength = \strlen($input);
+
+ while ($pos < $inputLength) {
+ $oldi = $i;
+ $w = 1;
+
+ for ($k = 36;; $k += 36) {
+ $digit = self::$decodeTable[$input[$pos++]];
+ $i += $digit * $w;
+ $t = self::calculateThreshold($k, $bias);
+
+ if ($digit < $t) {
+ break;
+ }
+
+ $w *= 36 - $t;
+ }
+
+ $bias = self::adapt($i - $oldi, ++$outputLength, 0 === $oldi);
+ $n = $n + (int) ($i / $outputLength);
+ $i = $i % $outputLength;
+ $output = mb_substr($output, 0, $i, 'utf-8').mb_chr($n, 'utf-8').mb_substr($output, $i, $outputLength - 1, 'utf-8');
+
+ ++$i;
+ }
+
+ return $output;
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/LICENSE b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/LICENSE
new file mode 100644
index 00000000..3f853aaf
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/README.md b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/README.md
new file mode 100644
index 00000000..5fd8c6e9
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/README.md
@@ -0,0 +1,12 @@
+Symfony Polyfill / Intl: Idn
+============================
+
+This component provides `idn_to_ascii` and `idn_to_utf8` functions to users who run php versions without the intl extension.
+
+More information can be found in the
+[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md).
+
+License
+=======
+
+This library is released under the [MIT license](LICENSE).
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/bootstrap.php b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/bootstrap.php
new file mode 100644
index 00000000..c6e3921d
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/bootstrap.php
@@ -0,0 +1,59 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+use Symfony\Polyfill\Intl\Idn as p;
+
+if (!function_exists('idn_to_ascii')) {
+ define('U_IDNA_PROHIBITED_ERROR', 66560);
+ define('U_IDNA_ERROR_START', 66560);
+ define('U_IDNA_UNASSIGNED_ERROR', 66561);
+ define('U_IDNA_CHECK_BIDI_ERROR', 66562);
+ define('U_IDNA_STD3_ASCII_RULES_ERROR', 66563);
+ define('U_IDNA_ACE_PREFIX_ERROR', 66564);
+ define('U_IDNA_VERIFICATION_ERROR', 66565);
+ define('U_IDNA_LABEL_TOO_LONG_ERROR', 66566);
+ define('U_IDNA_ZERO_LENGTH_LABEL_ERROR', 66567);
+ define('U_IDNA_DOMAIN_NAME_TOO_LONG_ERROR', 66568);
+ define('U_IDNA_ERROR_LIMIT', 66569);
+ define('U_STRINGPREP_PROHIBITED_ERROR', 66560);
+ define('U_STRINGPREP_UNASSIGNED_ERROR', 66561);
+ define('U_STRINGPREP_CHECK_BIDI_ERROR', 66562);
+ define('IDNA_DEFAULT', 0);
+ define('IDNA_ALLOW_UNASSIGNED', 1);
+ define('IDNA_USE_STD3_RULES', 2);
+ define('IDNA_CHECK_BIDI', 4);
+ define('IDNA_CHECK_CONTEXTJ', 8);
+ define('IDNA_NONTRANSITIONAL_TO_ASCII', 16);
+ define('IDNA_NONTRANSITIONAL_TO_UNICODE', 32);
+ define('INTL_IDNA_VARIANT_2003', 0);
+ define('INTL_IDNA_VARIANT_UTS46', 1);
+ define('IDNA_ERROR_EMPTY_LABEL', 1);
+ define('IDNA_ERROR_LABEL_TOO_LONG', 2);
+ define('IDNA_ERROR_DOMAIN_NAME_TOO_LONG', 4);
+ define('IDNA_ERROR_LEADING_HYPHEN', 8);
+ define('IDNA_ERROR_TRAILING_HYPHEN', 16);
+ define('IDNA_ERROR_HYPHEN_3_4', 32);
+ define('IDNA_ERROR_LEADING_COMBINING_MARK', 64);
+ define('IDNA_ERROR_DISALLOWED', 128);
+ define('IDNA_ERROR_PUNYCODE', 256);
+ define('IDNA_ERROR_LABEL_HAS_DOT', 512);
+ define('IDNA_ERROR_INVALID_ACE_LABEL', 1024);
+ define('IDNA_ERROR_BIDI', 2048);
+ define('IDNA_ERROR_CONTEXTJ', 4096);
+
+ if (PHP_VERSION_ID < 70400) {
+ function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); }
+ function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_2003, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); }
+ } else {
+ function idn_to_ascii($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_ascii($domain, $options, $variant, $idna_info); }
+ function idn_to_utf8($domain, $options = IDNA_DEFAULT, $variant = INTL_IDNA_VARIANT_UTS46, &$idna_info = array()) { return p\Idn::idn_to_utf8($domain, $options, $variant, $idna_info); }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/composer.json b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/composer.json
new file mode 100644
index 00000000..428c13e6
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-intl-idn/composer.json
@@ -0,0 +1,36 @@
+{
+ "name": "symfony/polyfill-intl-idn",
+ "type": "library",
+ "description": "Symfony polyfill for intl's idn_to_ascii and idn_to_utf8 functions",
+ "keywords": ["polyfill", "shim", "compatibility", "portable", "intl", "idn"],
+ "homepage": "https://symfony.com",
+ "license": "MIT",
+ "authors": [
+ {
+ "name": "Laurent Bassin",
+ "email": "laurent@bassin.info"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "require": {
+ "php": ">=5.3.3",
+ "symfony/polyfill-mbstring": "^1.3",
+ "symfony/polyfill-php72": "^1.9"
+ },
+ "autoload": {
+ "psr-4": { "Symfony\\Polyfill\\Intl\\Idn\\": "" },
+ "files": [ "bootstrap.php" ]
+ },
+ "suggest": {
+ "ext-intl": "For best performance"
+ },
+ "minimum-stability": "dev",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.13-dev"
+ }
+ }
+}
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/LICENSE b/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/LICENSE
new file mode 100644
index 00000000..4cd8bdd3
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2015-2019 Fabien Potencier
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/Mbstring.php b/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/Mbstring.php
new file mode 100644
index 00000000..bf882ba2
--- /dev/null
+++ b/lam/lib/3rdParty/composer/symfony/polyfill-mbstring/Mbstring.php
@@ -0,0 +1,840 @@
+
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Polyfill\Mbstring;
+
+/**
+ * Partial mbstring implementation in PHP, iconv based, UTF-8 centric.
+ *
+ * Implemented:
+ * - mb_chr - Returns a specific character from its Unicode code point
+ * - mb_convert_encoding - Convert character encoding
+ * - mb_convert_variables - Convert character code in variable(s)
+ * - mb_decode_mimeheader - Decode string in MIME header field
+ * - mb_encode_mimeheader - Encode string for MIME header XXX NATIVE IMPLEMENTATION IS REALLY BUGGED
+ * - mb_decode_numericentity - Decode HTML numeric string reference to character
+ * - mb_encode_numericentity - Encode character to HTML numeric string reference
+ * - mb_convert_case - Perform case folding on a string
+ * - mb_detect_encoding - Detect character encoding
+ * - mb_get_info - Get internal settings of mbstring
+ * - mb_http_input - Detect HTTP input character encoding
+ * - mb_http_output - Set/Get HTTP output character encoding
+ * - mb_internal_encoding - Set/Get internal character encoding
+ * - mb_list_encodings - Returns an array of all supported encodings
+ * - mb_ord - Returns the Unicode code point of a character
+ * - mb_output_handler - Callback function converts character encoding in output buffer
+ * - mb_scrub - Replaces ill-formed byte sequences with substitute characters
+ * - mb_strlen - Get string length
+ * - mb_strpos - Find position of first occurrence of string in a string
+ * - mb_strrpos - Find position of last occurrence of a string in a string
+ * - mb_str_split - Convert a string to an array
+ * - mb_strtolower - Make a string lowercase
+ * - mb_strtoupper - Make a string uppercase
+ * - mb_substitute_character - Set/Get substitution character
+ * - mb_substr - Get part of string
+ * - mb_stripos - Finds position of first occurrence of a string within another, case insensitive
+ * - mb_stristr - Finds first occurrence of a string within another, case insensitive
+ * - mb_strrchr - Finds the last occurrence of a character in a string within another
+ * - mb_strrichr - Finds the last occurrence of a character in a string within another, case insensitive
+ * - mb_strripos - Finds position of last occurrence of a string within another, case insensitive
+ * - mb_strstr - Finds first occurrence of a string within another
+ * - mb_strwidth - Return width of string
+ * - mb_substr_count - Count the number of substring occurrences
+ *
+ * Not implemented:
+ * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more)
+ * - mb_ereg_* - Regular expression with multibyte support
+ * - mb_parse_str - Parse GET/POST/COOKIE data and set global variable
+ * - mb_preferred_mime_name - Get MIME charset string
+ * - mb_regex_encoding - Returns current encoding for multibyte regex as string
+ * - mb_regex_set_options - Set/Get the default options for mbregex functions
+ * - mb_send_mail - Send encoded mail
+ * - mb_split - Split multibyte string using regular expression
+ * - mb_strcut - Get part of string
+ * - mb_strimwidth - Get truncated string with specified width
+ *
+ * @author Nicolas Grekas