git subrepo commit (merge) mailcow/src/mailcow-dockerized

subrepo: subdir:   "mailcow/src/mailcow-dockerized"
  merged:   "02ae5285"
upstream: origin:   "https://github.com/mailcow/mailcow-dockerized.git"
  branch:   "master"
  commit:   "649a5c01"
git-subrepo: version:  "0.4.3"
  origin:   "???"
  commit:   "???"
Change-Id: I870ad468fba026cc5abf3c5699ed1e12ff28b32b
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/ArrayLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/ArrayLoader.php
new file mode 100644
index 0000000..0758da8
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/ArrayLoader.php
@@ -0,0 +1,58 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * ArrayLoader loads translations from a PHP array.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class ArrayLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        $resource = $this->flatten($resource);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($resource, $domain);
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an nested array of translations.
+     *
+     * The scheme used is:
+     *   'key' => ['key2' => ['key3' => 'value']]
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     */
+    private function flatten(array $messages): array
+    {
+        $result = [];
+        foreach ($messages as $key => $value) {
+            if (\is_array($value)) {
+                foreach ($this->flatten($value) as $k => $v) {
+                    $result[$key.'.'.$k] = $v;
+                }
+            } else {
+                $result[$key] = $value;
+            }
+        }
+
+        return $result;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/CsvFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/CsvFileLoader.php
new file mode 100644
index 0000000..8d5d4db
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/CsvFileLoader.php
@@ -0,0 +1,65 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * CsvFileLoader loads translations from CSV files.
+ *
+ * @author Saša Stamenković <umpirsky@gmail.com>
+ */
+class CsvFileLoader extends FileLoader
+{
+    private $delimiter = ';';
+    private $enclosure = '"';
+    private $escape = '\\';
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        $messages = [];
+
+        try {
+            $file = new \SplFileObject($resource, 'rb');
+        } catch (\RuntimeException $e) {
+            throw new NotFoundResourceException(sprintf('Error opening file "%s".', $resource), 0, $e);
+        }
+
+        $file->setFlags(\SplFileObject::READ_CSV | \SplFileObject::SKIP_EMPTY);
+        $file->setCsvControl($this->delimiter, $this->enclosure, $this->escape);
+
+        foreach ($file as $data) {
+            if (false === $data) {
+                continue;
+            }
+
+            if ('#' !== substr($data[0], 0, 1) && isset($data[1]) && 2 === \count($data)) {
+                $messages[$data[0]] = $data[1];
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Sets the delimiter, enclosure, and escape character for CSV.
+     */
+    public function setCsvControl(string $delimiter = ';', string $enclosure = '"', string $escape = '\\')
+    {
+        $this->delimiter = $delimiter;
+        $this->enclosure = $enclosure;
+        $this->escape = $escape;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/FileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/FileLoader.php
new file mode 100644
index 0000000..4725ea6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/FileLoader.php
@@ -0,0 +1,63 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+
+/**
+ * @author Abdellatif Ait boudad <a.aitboudad@gmail.com>
+ */
+abstract class FileLoader extends ArrayLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        $messages = $this->loadResource($resource);
+
+        // empty resource
+        if (null === $messages) {
+            $messages = [];
+        }
+
+        // not an array
+        if (!\is_array($messages)) {
+            throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
+        }
+
+        $catalogue = parent::load($messages, $locale, $domain);
+
+        if (class_exists(FileResource::class)) {
+            $catalogue->addResource(new FileResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    /**
+     * @return array
+     *
+     * @throws InvalidResourceException if stream content has an invalid format
+     */
+    abstract protected function loadResource(string $resource);
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuDatFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuDatFileLoader.php
new file mode 100644
index 0000000..2a1aecc
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuDatFileLoader.php
@@ -0,0 +1,61 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuDatFileLoader extends IcuResFileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        if (!stream_is_local($resource.'.dat')) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource.'.dat')) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $rb = new \ResourceBundle($locale, $resource);
+        } catch (\Exception $e) {
+            $rb = null;
+        }
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+
+        if (class_exists(FileResource::class)) {
+            $catalogue->addResource(new FileResource($resource.'.dat'));
+        }
+
+        return $catalogue;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuResFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuResFileLoader.php
new file mode 100644
index 0000000..64bbd3e
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IcuResFileLoader.php
@@ -0,0 +1,91 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\DirectoryResource;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * IcuResFileLoader loads translations from a resource bundle.
+ *
+ * @author stealth35
+ */
+class IcuResFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!is_dir($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $rb = new \ResourceBundle($locale, $resource);
+        } catch (\Exception $e) {
+            $rb = null;
+        }
+
+        if (!$rb) {
+            throw new InvalidResourceException(sprintf('Cannot load resource "%s".', $resource));
+        } elseif (intl_is_failure($rb->getErrorCode())) {
+            throw new InvalidResourceException($rb->getErrorMessage(), $rb->getErrorCode());
+        }
+
+        $messages = $this->flatten($rb);
+        $catalogue = new MessageCatalogue($locale);
+        $catalogue->add($messages, $domain);
+
+        if (class_exists(DirectoryResource::class)) {
+            $catalogue->addResource(new DirectoryResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    /**
+     * Flattens an ResourceBundle.
+     *
+     * The scheme used is:
+     *   key { key2 { key3 { "value" } } }
+     * Becomes:
+     *   'key.key2.key3' => 'value'
+     *
+     * This function takes an array by reference and will modify it
+     *
+     * @param \ResourceBundle $rb       The ResourceBundle that will be flattened
+     * @param array           $messages Used internally for recursive calls
+     * @param string          $path     Current path being parsed, used internally for recursive calls
+     *
+     * @return array the flattened ResourceBundle
+     */
+    protected function flatten(\ResourceBundle $rb, array &$messages = [], string $path = null)
+    {
+        foreach ($rb as $key => $value) {
+            $nodePath = $path ? $path.'.'.$key : $key;
+            if ($value instanceof \ResourceBundle) {
+                $this->flatten($value, $messages, $nodePath);
+            } else {
+                $messages[$nodePath] = $value;
+            }
+        }
+
+        return $messages;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IniFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IniFileLoader.php
new file mode 100644
index 0000000..7398f77
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/IniFileLoader.php
@@ -0,0 +1,28 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * IniFileLoader loads translations from an ini file.
+ *
+ * @author stealth35
+ */
+class IniFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        return parse_ini_file($resource, true);
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/JsonFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/JsonFileLoader.php
new file mode 100644
index 0000000..5aefba0
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/JsonFileLoader.php
@@ -0,0 +1,60 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * JsonFileLoader loads translations from an json file.
+ *
+ * @author singles
+ */
+class JsonFileLoader extends FileLoader
+{
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        $messages = [];
+        if ($data = file_get_contents($resource)) {
+            $messages = json_decode($data, true);
+
+            if (0 < $errorCode = json_last_error()) {
+                throw new InvalidResourceException('Error parsing JSON: '.$this->getJSONErrorMessage($errorCode));
+            }
+        }
+
+        return $messages;
+    }
+
+    /**
+     * Translates JSON_ERROR_* constant into meaningful message.
+     */
+    private function getJSONErrorMessage(int $errorCode): string
+    {
+        switch ($errorCode) {
+            case \JSON_ERROR_DEPTH:
+                return 'Maximum stack depth exceeded';
+            case \JSON_ERROR_STATE_MISMATCH:
+                return 'Underflow or the modes mismatch';
+            case \JSON_ERROR_CTRL_CHAR:
+                return 'Unexpected control character found';
+            case \JSON_ERROR_SYNTAX:
+                return 'Syntax error, malformed JSON';
+            case \JSON_ERROR_UTF8:
+                return 'Malformed UTF-8 characters, possibly incorrectly encoded';
+            default:
+                return 'Unknown error';
+        }
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/LoaderInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/LoaderInterface.php
new file mode 100644
index 0000000..2073f2b
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/LoaderInterface.php
@@ -0,0 +1,38 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * LoaderInterface is the interface implemented by all translation loaders.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+interface LoaderInterface
+{
+    /**
+     * Loads a locale.
+     *
+     * @param mixed  $resource A resource
+     * @param string $locale   A locale
+     * @param string $domain   The domain
+     *
+     * @return MessageCatalogue A MessageCatalogue instance
+     *
+     * @throws NotFoundResourceException when the resource cannot be found
+     * @throws InvalidResourceException  when the resource cannot be loaded
+     */
+    public function load($resource, string $locale, string $domain = 'messages');
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/MoFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/MoFileLoader.php
new file mode 100644
index 0000000..0ff6549
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/MoFileLoader.php
@@ -0,0 +1,140 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD http://union-of-rad.org (http://lithify.me/)
+ */
+class MoFileLoader extends FileLoader
+{
+    /**
+     * Magic used for validating the format of an MO file as well as
+     * detecting if the machine used to create that file was little endian.
+     */
+    public const MO_LITTLE_ENDIAN_MAGIC = 0x950412de;
+
+    /**
+     * Magic used for validating the format of an MO file as well as
+     * detecting if the machine used to create that file was big endian.
+     */
+    public const MO_BIG_ENDIAN_MAGIC = 0xde120495;
+
+    /**
+     * The size of the header of an MO file in bytes.
+     */
+    public const MO_HEADER_SIZE = 28;
+
+    /**
+     * Parses machine object (MO) format, independent of the machine's endian it
+     * was created on. Both 32bit and 64bit systems are supported.
+     *
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $stat = fstat($stream);
+
+        if ($stat['size'] < self::MO_HEADER_SIZE) {
+            throw new InvalidResourceException('MO stream content has an invalid format.');
+        }
+        $magic = unpack('V1', fread($stream, 4));
+        $magic = hexdec(substr(dechex(current($magic)), -8));
+
+        if (self::MO_LITTLE_ENDIAN_MAGIC == $magic) {
+            $isBigEndian = false;
+        } elseif (self::MO_BIG_ENDIAN_MAGIC == $magic) {
+            $isBigEndian = true;
+        } else {
+            throw new InvalidResourceException('MO stream content has an invalid format.');
+        }
+
+        // formatRevision
+        $this->readLong($stream, $isBigEndian);
+        $count = $this->readLong($stream, $isBigEndian);
+        $offsetId = $this->readLong($stream, $isBigEndian);
+        $offsetTranslated = $this->readLong($stream, $isBigEndian);
+        // sizeHashes
+        $this->readLong($stream, $isBigEndian);
+        // offsetHashes
+        $this->readLong($stream, $isBigEndian);
+
+        $messages = [];
+
+        for ($i = 0; $i < $count; ++$i) {
+            $pluralId = null;
+            $translated = null;
+
+            fseek($stream, $offsetId + $i * 8);
+
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            if ($length < 1) {
+                continue;
+            }
+
+            fseek($stream, $offset);
+            $singularId = fread($stream, $length);
+
+            if (str_contains($singularId, "\000")) {
+                [$singularId, $pluralId] = explode("\000", $singularId);
+            }
+
+            fseek($stream, $offsetTranslated + $i * 8);
+            $length = $this->readLong($stream, $isBigEndian);
+            $offset = $this->readLong($stream, $isBigEndian);
+
+            if ($length < 1) {
+                continue;
+            }
+
+            fseek($stream, $offset);
+            $translated = fread($stream, $length);
+
+            if (str_contains($translated, "\000")) {
+                $translated = explode("\000", $translated);
+            }
+
+            $ids = ['singular' => $singularId, 'plural' => $pluralId];
+            $item = compact('ids', 'translated');
+
+            if (!empty($item['ids']['singular'])) {
+                $id = $item['ids']['singular'];
+                if (isset($item['ids']['plural'])) {
+                    $id .= '|'.$item['ids']['plural'];
+                }
+                $messages[$id] = stripcslashes(implode('|', (array) $item['translated']));
+            }
+        }
+
+        fclose($stream);
+
+        return array_filter($messages);
+    }
+
+    /**
+     * Reads an unsigned long from stream respecting endianness.
+     *
+     * @param resource $stream
+     */
+    private function readLong($stream, bool $isBigEndian): int
+    {
+        $result = unpack($isBigEndian ? 'N1' : 'V1', fread($stream, 4));
+        $result = current($result);
+
+        return (int) substr($result, -8);
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PhpFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PhpFileLoader.php
new file mode 100644
index 0000000..85f1090
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PhpFileLoader.php
@@ -0,0 +1,42 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * PhpFileLoader loads translations from PHP files returning an array of translations.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class PhpFileLoader extends FileLoader
+{
+    private static $cache = [];
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        if ([] === self::$cache && \function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN) && (!\in_array(\PHP_SAPI, ['cli', 'phpdbg'], true) || filter_var(ini_get('opcache.enable_cli'), \FILTER_VALIDATE_BOOLEAN))) {
+            self::$cache = null;
+        }
+
+        if (null === self::$cache) {
+            return require $resource;
+        }
+
+        if (isset(self::$cache[$resource])) {
+            return self::$cache[$resource];
+        }
+
+        return self::$cache[$resource] = require $resource;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PoFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PoFileLoader.php
new file mode 100644
index 0000000..ee143e2
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/PoFileLoader.php
@@ -0,0 +1,149 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+/**
+ * @copyright Copyright (c) 2010, Union of RAD https://github.com/UnionOfRAD/lithium
+ * @copyright Copyright (c) 2012, Clemens Tolboom
+ */
+class PoFileLoader extends FileLoader
+{
+    /**
+     * Parses portable object (PO) format.
+     *
+     * From https://www.gnu.org/software/gettext/manual/gettext.html#PO-Files
+     * we should be able to parse files having:
+     *
+     * white-space
+     * #  translator-comments
+     * #. extracted-comments
+     * #: reference...
+     * #, flag...
+     * #| msgid previous-untranslated-string
+     * msgid untranslated-string
+     * msgstr translated-string
+     *
+     * extra or different lines are:
+     *
+     * #| msgctxt previous-context
+     * #| msgid previous-untranslated-string
+     * msgctxt context
+     *
+     * #| msgid previous-untranslated-string-singular
+     * #| msgid_plural previous-untranslated-string-plural
+     * msgid untranslated-string-singular
+     * msgid_plural untranslated-string-plural
+     * msgstr[0] translated-string-case-0
+     * ...
+     * msgstr[N] translated-string-case-n
+     *
+     * The definition states:
+     * - white-space and comments are optional.
+     * - msgid "" that an empty singleline defines a header.
+     *
+     * This parser sacrifices some features of the reference implementation the
+     * differences to that implementation are as follows.
+     * - No support for comments spanning multiple lines.
+     * - Translator and extracted comments are treated as being the same type.
+     * - Message IDs are allowed to have other encodings as just US-ASCII.
+     *
+     * Items with an empty id are ignored.
+     *
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        $stream = fopen($resource, 'r');
+
+        $defaults = [
+            'ids' => [],
+            'translated' => null,
+        ];
+
+        $messages = [];
+        $item = $defaults;
+        $flags = [];
+
+        while ($line = fgets($stream)) {
+            $line = trim($line);
+
+            if ('' === $line) {
+                // Whitespace indicated current item is done
+                if (!\in_array('fuzzy', $flags)) {
+                    $this->addMessage($messages, $item);
+                }
+                $item = $defaults;
+                $flags = [];
+            } elseif ('#,' === substr($line, 0, 2)) {
+                $flags = array_map('trim', explode(',', substr($line, 2)));
+            } elseif ('msgid "' === substr($line, 0, 7)) {
+                // We start a new msg so save previous
+                // TODO: this fails when comments or contexts are added
+                $this->addMessage($messages, $item);
+                $item = $defaults;
+                $item['ids']['singular'] = substr($line, 7, -1);
+            } elseif ('msgstr "' === substr($line, 0, 8)) {
+                $item['translated'] = substr($line, 8, -1);
+            } elseif ('"' === $line[0]) {
+                $continues = isset($item['translated']) ? 'translated' : 'ids';
+
+                if (\is_array($item[$continues])) {
+                    end($item[$continues]);
+                    $item[$continues][key($item[$continues])] .= substr($line, 1, -1);
+                } else {
+                    $item[$continues] .= substr($line, 1, -1);
+                }
+            } elseif ('msgid_plural "' === substr($line, 0, 14)) {
+                $item['ids']['plural'] = substr($line, 14, -1);
+            } elseif ('msgstr[' === substr($line, 0, 7)) {
+                $size = strpos($line, ']');
+                $item['translated'][(int) substr($line, 7, 1)] = substr($line, $size + 3, -1);
+            }
+        }
+        // save last item
+        if (!\in_array('fuzzy', $flags)) {
+            $this->addMessage($messages, $item);
+        }
+        fclose($stream);
+
+        return $messages;
+    }
+
+    /**
+     * Save a translation item to the messages.
+     *
+     * A .po file could contain by error missing plural indexes. We need to
+     * fix these before saving them.
+     */
+    private function addMessage(array &$messages, array $item)
+    {
+        if (!empty($item['ids']['singular'])) {
+            $id = stripcslashes($item['ids']['singular']);
+            if (isset($item['ids']['plural'])) {
+                $id .= '|'.stripcslashes($item['ids']['plural']);
+            }
+
+            $translated = (array) $item['translated'];
+            // PO are by definition indexed so sort by index.
+            ksort($translated);
+            // Make sure every index is filled.
+            end($translated);
+            $count = key($translated);
+            // Fill missing spots with '-'.
+            $empties = array_fill(0, $count + 1, '-');
+            $translated += $empties;
+            ksort($translated);
+
+            $messages[$id] = stripcslashes(implode('|', $translated));
+        }
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/QtFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/QtFileLoader.php
new file mode 100644
index 0000000..9cf2fe9
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/QtFileLoader.php
@@ -0,0 +1,82 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+
+/**
+ * QtFileLoader loads translations from QT Translations XML files.
+ *
+ * @author Benjamin Eberlei <kontakt@beberlei.de>
+ */
+class QtFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        if (!class_exists(XmlUtils::class)) {
+            throw new RuntimeException('Loading translations from the QT format requires the Symfony Config component.');
+        }
+
+        if (!stream_is_local($resource)) {
+            throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+        }
+
+        if (!file_exists($resource)) {
+            throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+        }
+
+        try {
+            $dom = XmlUtils::loadFile($resource);
+        } catch (\InvalidArgumentException $e) {
+            throw new InvalidResourceException(sprintf('Unable to load "%s".', $resource), $e->getCode(), $e);
+        }
+
+        $internalErrors = libxml_use_internal_errors(true);
+        libxml_clear_errors();
+
+        $xpath = new \DOMXPath($dom);
+        $nodes = $xpath->evaluate('//TS/context/name[text()="'.$domain.'"]');
+
+        $catalogue = new MessageCatalogue($locale);
+        if (1 == $nodes->length) {
+            $translations = $nodes->item(0)->nextSibling->parentNode->parentNode->getElementsByTagName('message');
+            foreach ($translations as $translation) {
+                $translationValue = (string) $translation->getElementsByTagName('translation')->item(0)->nodeValue;
+
+                if (!empty($translationValue)) {
+                    $catalogue->set(
+                        (string) $translation->getElementsByTagName('source')->item(0)->nodeValue,
+                        $translationValue,
+                        $domain
+                    );
+                }
+                $translation = $translation->nextSibling;
+            }
+
+            if (class_exists(FileResource::class)) {
+                $catalogue->addResource(new FileResource($resource));
+            }
+        }
+
+        libxml_use_internal_errors($internalErrors);
+
+        return $catalogue;
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/XliffFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/XliffFileLoader.php
new file mode 100644
index 0000000..35ad33e
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/XliffFileLoader.php
@@ -0,0 +1,232 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\Config\Util\Exception\InvalidXmlException;
+use Symfony\Component\Config\Util\Exception\XmlParsingException;
+use Symfony\Component\Config\Util\XmlUtils;
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\NotFoundResourceException;
+use Symfony\Component\Translation\Exception\RuntimeException;
+use Symfony\Component\Translation\MessageCatalogue;
+use Symfony\Component\Translation\Util\XliffUtils;
+
+/**
+ * XliffFileLoader loads translations from XLIFF files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class XliffFileLoader implements LoaderInterface
+{
+    /**
+     * {@inheritdoc}
+     */
+    public function load($resource, string $locale, string $domain = 'messages')
+    {
+        if (!class_exists(XmlUtils::class)) {
+            throw new RuntimeException('Loading translations from the Xliff format requires the Symfony Config component.');
+        }
+
+        if (!$this->isXmlString($resource)) {
+            if (!stream_is_local($resource)) {
+                throw new InvalidResourceException(sprintf('This is not a local file "%s".', $resource));
+            }
+
+            if (!file_exists($resource)) {
+                throw new NotFoundResourceException(sprintf('File "%s" not found.', $resource));
+            }
+
+            if (!is_file($resource)) {
+                throw new InvalidResourceException(sprintf('This is neither a file nor an XLIFF string "%s".', $resource));
+            }
+        }
+
+        try {
+            if ($this->isXmlString($resource)) {
+                $dom = XmlUtils::parse($resource);
+            } else {
+                $dom = XmlUtils::loadFile($resource);
+            }
+        } catch (\InvalidArgumentException | XmlParsingException | InvalidXmlException $e) {
+            throw new InvalidResourceException(sprintf('Unable to load "%s": ', $resource).$e->getMessage(), $e->getCode(), $e);
+        }
+
+        if ($errors = XliffUtils::validateSchema($dom)) {
+            throw new InvalidResourceException(sprintf('Invalid resource provided: "%s"; Errors: ', $resource).XliffUtils::getErrorsAsString($errors));
+        }
+
+        $catalogue = new MessageCatalogue($locale);
+        $this->extract($dom, $catalogue, $domain);
+
+        if (is_file($resource) && class_exists(FileResource::class)) {
+            $catalogue->addResource(new FileResource($resource));
+        }
+
+        return $catalogue;
+    }
+
+    private function extract(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+    {
+        $xliffVersion = XliffUtils::getVersionNumber($dom);
+
+        if ('1.2' === $xliffVersion) {
+            $this->extractXliff1($dom, $catalogue, $domain);
+        }
+
+        if ('2.0' === $xliffVersion) {
+            $this->extractXliff2($dom, $catalogue, $domain);
+        }
+    }
+
+    /**
+     * Extract messages and metadata from DOMDocument into a MessageCatalogue.
+     */
+    private function extractXliff1(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+    {
+        $xml = simplexml_import_dom($dom);
+        $encoding = $dom->encoding ? strtoupper($dom->encoding) : null;
+
+        $namespace = 'urn:oasis:names:tc:xliff:document:1.2';
+        $xml->registerXPathNamespace('xliff', $namespace);
+
+        foreach ($xml->xpath('//xliff:file') as $file) {
+            $fileAttributes = $file->attributes();
+
+            $file->registerXPathNamespace('xliff', $namespace);
+
+            foreach ($file->xpath('.//xliff:trans-unit') as $translation) {
+                $attributes = $translation->attributes();
+
+                if (!(isset($attributes['resname']) || isset($translation->source))) {
+                    continue;
+                }
+
+                $source = isset($attributes['resname']) && $attributes['resname'] ? $attributes['resname'] : $translation->source;
+                // If the xlf file has another encoding specified, try to convert it because
+                // simple_xml will always return utf-8 encoded values
+                $target = $this->utf8ToCharset((string) ($translation->target ?? $translation->source), $encoding);
+
+                $catalogue->set((string) $source, $target, $domain);
+
+                $metadata = [
+                    'source' => (string) $translation->source,
+                    'file' => [
+                        'original' => (string) $fileAttributes['original'],
+                    ],
+                ];
+                if ($notes = $this->parseNotesMetadata($translation->note, $encoding)) {
+                    $metadata['notes'] = $notes;
+                }
+
+                if (isset($translation->target) && $translation->target->attributes()) {
+                    $metadata['target-attributes'] = [];
+                    foreach ($translation->target->attributes() as $key => $value) {
+                        $metadata['target-attributes'][$key] = (string) $value;
+                    }
+                }
+
+                if (isset($attributes['id'])) {
+                    $metadata['id'] = (string) $attributes['id'];
+                }
+
+                $catalogue->setMetadata((string) $source, $metadata, $domain);
+            }
+        }
+    }
+
+    private function extractXliff2(\DOMDocument $dom, MessageCatalogue $catalogue, string $domain)
+    {
+        $xml = simplexml_import_dom($dom);
+        $encoding = $dom->encoding ? strtoupper($dom->encoding) : null;
+
+        $xml->registerXPathNamespace('xliff', 'urn:oasis:names:tc:xliff:document:2.0');
+
+        foreach ($xml->xpath('//xliff:unit') as $unit) {
+            foreach ($unit->segment as $segment) {
+                $attributes = $unit->attributes();
+                $source = $attributes['name'] ?? $segment->source;
+
+                // If the xlf file has another encoding specified, try to convert it because
+                // simple_xml will always return utf-8 encoded values
+                $target = $this->utf8ToCharset((string) ($segment->target ?? $segment->source), $encoding);
+
+                $catalogue->set((string) $source, $target, $domain);
+
+                $metadata = [];
+                if (isset($segment->target) && $segment->target->attributes()) {
+                    $metadata['target-attributes'] = [];
+                    foreach ($segment->target->attributes() as $key => $value) {
+                        $metadata['target-attributes'][$key] = (string) $value;
+                    }
+                }
+
+                if (isset($unit->notes)) {
+                    $metadata['notes'] = [];
+                    foreach ($unit->notes->note as $noteNode) {
+                        $note = [];
+                        foreach ($noteNode->attributes() as $key => $value) {
+                            $note[$key] = (string) $value;
+                        }
+                        $note['content'] = (string) $noteNode;
+                        $metadata['notes'][] = $note;
+                    }
+                }
+
+                $catalogue->setMetadata((string) $source, $metadata, $domain);
+            }
+        }
+    }
+
+    /**
+     * Convert a UTF8 string to the specified encoding.
+     */
+    private function utf8ToCharset(string $content, string $encoding = null): string
+    {
+        if ('UTF-8' !== $encoding && !empty($encoding)) {
+            return mb_convert_encoding($content, $encoding, 'UTF-8');
+        }
+
+        return $content;
+    }
+
+    private function parseNotesMetadata(\SimpleXMLElement $noteElement = null, string $encoding = null): array
+    {
+        $notes = [];
+
+        if (null === $noteElement) {
+            return $notes;
+        }
+
+        /** @var \SimpleXMLElement $xmlNote */
+        foreach ($noteElement as $xmlNote) {
+            $noteAttributes = $xmlNote->attributes();
+            $note = ['content' => $this->utf8ToCharset((string) $xmlNote, $encoding)];
+            if (isset($noteAttributes['priority'])) {
+                $note['priority'] = (int) $noteAttributes['priority'];
+            }
+
+            if (isset($noteAttributes['from'])) {
+                $note['from'] = (string) $noteAttributes['from'];
+            }
+
+            $notes[] = $note;
+        }
+
+        return $notes;
+    }
+
+    private function isXmlString(string $resource): bool
+    {
+        return 0 === strpos($resource, '<?xml');
+    }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/YamlFileLoader.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/YamlFileLoader.php
new file mode 100644
index 0000000..8588e18
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/symfony/translation/Loader/YamlFileLoader.php
@@ -0,0 +1,54 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Symfony\Component\Translation\Loader;
+
+use Symfony\Component\Translation\Exception\InvalidResourceException;
+use Symfony\Component\Translation\Exception\LogicException;
+use Symfony\Component\Yaml\Exception\ParseException;
+use Symfony\Component\Yaml\Parser as YamlParser;
+use Symfony\Component\Yaml\Yaml;
+
+/**
+ * YamlFileLoader loads translations from Yaml files.
+ *
+ * @author Fabien Potencier <fabien@symfony.com>
+ */
+class YamlFileLoader extends FileLoader
+{
+    private $yamlParser;
+
+    /**
+     * {@inheritdoc}
+     */
+    protected function loadResource(string $resource)
+    {
+        if (null === $this->yamlParser) {
+            if (!class_exists(\Symfony\Component\Yaml\Parser::class)) {
+                throw new LogicException('Loading translations from the YAML format requires the Symfony Yaml component.');
+            }
+
+            $this->yamlParser = new YamlParser();
+        }
+
+        try {
+            $messages = $this->yamlParser->parseFile($resource, Yaml::PARSE_CONSTANT);
+        } catch (ParseException $e) {
+            throw new InvalidResourceException(sprintf('The file "%s" does not contain valid YAML: ', $resource).$e->getMessage(), 0, $e);
+        }
+
+        if (null !== $messages && !\is_array($messages)) {
+            throw new InvalidResourceException(sprintf('Unable to load file "%s".', $resource));
+        }
+
+        return $messages ?: [];
+    }
+}