git subrepo clone https://github.com/mailcow/mailcow-dockerized.git mailcow/src/mailcow-dockerized
subrepo: subdir: "mailcow/src/mailcow-dockerized"
merged: "a832becb"
upstream: origin: "https://github.com/mailcow/mailcow-dockerized.git"
branch: "master"
commit: "a832becb"
git-subrepo: version: "0.4.3"
origin: "???"
commit: "???"
Change-Id: If5be2d621a211e164c9b6577adaa7884449f16b5
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Connection.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Connection.php
new file mode 100644
index 0000000..902c7ce
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Connection.php
@@ -0,0 +1,234 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use Ddeboer\Imap\Exception\CreateMailboxException;
+use Ddeboer\Imap\Exception\DeleteMailboxException;
+use Ddeboer\Imap\Exception\ImapGetmailboxesException;
+use Ddeboer\Imap\Exception\ImapNumMsgException;
+use Ddeboer\Imap\Exception\ImapQuotaException;
+use Ddeboer\Imap\Exception\InvalidResourceException;
+use Ddeboer\Imap\Exception\MailboxDoesNotExistException;
+
+/**
+ * A connection to an IMAP server that is authenticated for a user.
+ */
+final class Connection implements ConnectionInterface
+{
+ /**
+ * @var ImapResourceInterface
+ */
+ private $resource;
+
+ /**
+ * @var string
+ */
+ private $server;
+
+ /**
+ * @var null|array
+ */
+ private $mailboxes;
+
+ /**
+ * @var null|array
+ */
+ private $mailboxNames;
+
+ /**
+ * Constructor.
+ *
+ * @throws \InvalidArgumentException
+ */
+ public function __construct(ImapResourceInterface $resource, string $server)
+ {
+ $this->resource = $resource;
+ $this->server = $server;
+ }
+
+ /**
+ * Get IMAP resource.
+ */
+ public function getResource(): ImapResourceInterface
+ {
+ return $this->resource;
+ }
+
+ /**
+ * Delete all messages marked for deletion.
+ */
+ public function expunge(): bool
+ {
+ return \imap_expunge($this->resource->getStream());
+ }
+
+ /**
+ * Close connection.
+ */
+ public function close(int $flag = 0): bool
+ {
+ $this->resource->clearLastMailboxUsedCache();
+
+ return \imap_close($this->resource->getStream(), $flag);
+ }
+
+ /**
+ * Get Mailbox quota.
+ */
+ public function getQuota(string $root = 'INBOX'): array
+ {
+ $errorMessage = null;
+ $errorNumber = 0;
+ \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
+ $errorMessage = $message;
+ $errorNumber = $nr;
+
+ return true;
+ });
+
+ $return = \imap_get_quotaroot($this->resource->getStream(), $root);
+
+ \restore_error_handler();
+
+ if (false === $return || null !== $errorMessage) {
+ throw new ImapQuotaException(
+ \sprintf(
+ 'IMAP Quota request failed for "%s"%s',
+ $root,
+ null !== $errorMessage ? ': ' . $errorMessage : ''
+ ),
+ $errorNumber
+ );
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get a list of mailboxes (also known as folders).
+ *
+ * @return MailboxInterface[]
+ */
+ public function getMailboxes(): array
+ {
+ $this->initMailboxNames();
+
+ if (null === $this->mailboxes) {
+ $this->mailboxes = [];
+ foreach ($this->mailboxNames as $mailboxName => $mailboxInfo) {
+ $this->mailboxes[(string) $mailboxName] = $this->getMailbox((string) $mailboxName);
+ }
+ }
+
+ return $this->mailboxes;
+ }
+
+ /**
+ * Check that a mailbox with the given name exists.
+ *
+ * @param string $name Mailbox name
+ */
+ public function hasMailbox(string $name): bool
+ {
+ $this->initMailboxNames();
+
+ return isset($this->mailboxNames[$name]);
+ }
+
+ /**
+ * Get a mailbox by its name.
+ *
+ * @param string $name Mailbox name
+ *
+ * @throws MailboxDoesNotExistException If mailbox does not exist
+ */
+ public function getMailbox(string $name): MailboxInterface
+ {
+ if (false === $this->hasMailbox($name)) {
+ throw new MailboxDoesNotExistException(\sprintf('Mailbox name "%s" does not exist', $name));
+ }
+
+ return new Mailbox($this->resource, $name, $this->mailboxNames[$name]);
+ }
+
+ /**
+ * Count number of messages not in any mailbox.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ $return = \imap_num_msg($this->resource->getStream());
+
+ if (false === $return) {
+ throw new ImapNumMsgException('imap_num_msg failed');
+ }
+
+ return $return;
+ }
+
+ /**
+ * Check if the connection is still active.
+ *
+ * @throws InvalidResourceException If connection was closed
+ */
+ public function ping(): bool
+ {
+ return \imap_ping($this->resource->getStream());
+ }
+
+ /**
+ * Create mailbox.
+ *
+ * @throws CreateMailboxException
+ */
+ public function createMailbox(string $name): MailboxInterface
+ {
+ if (false === \imap_createmailbox($this->resource->getStream(), $this->server . \mb_convert_encoding($name, 'UTF7-IMAP', 'UTF-8'))) {
+ throw new CreateMailboxException(\sprintf('Can not create "%s" mailbox at "%s"', $name, $this->server));
+ }
+
+ $this->mailboxNames = $this->mailboxes = null;
+ $this->resource->clearLastMailboxUsedCache();
+
+ return $this->getMailbox($name);
+ }
+
+ /**
+ * Create mailbox.
+ *
+ * @throws DeleteMailboxException
+ */
+ public function deleteMailbox(MailboxInterface $mailbox): void
+ {
+ if (false === \imap_deletemailbox($this->resource->getStream(), $mailbox->getFullEncodedName())) {
+ throw new DeleteMailboxException(\sprintf('Mailbox "%s" could not be deleted', $mailbox->getName()));
+ }
+
+ $this->mailboxes = $this->mailboxNames = null;
+ $this->resource->clearLastMailboxUsedCache();
+ }
+
+ /**
+ * Get mailbox names.
+ */
+ private function initMailboxNames(): void
+ {
+ if (null !== $this->mailboxNames) {
+ return;
+ }
+
+ $this->mailboxNames = [];
+ $mailboxesInfo = \imap_getmailboxes($this->resource->getStream(), $this->server, '*');
+ if (!\is_array($mailboxesInfo)) {
+ throw new ImapGetmailboxesException('imap_getmailboxes failed');
+ }
+
+ foreach ($mailboxesInfo as $mailboxInfo) {
+ $name = \mb_convert_encoding(\str_replace($this->server, '', $mailboxInfo->name), 'UTF-8', 'UTF7-IMAP');
+ $this->mailboxNames[$name] = $mailboxInfo;
+ }
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ConnectionInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ConnectionInterface.php
new file mode 100644
index 0000000..e76077f
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ConnectionInterface.php
@@ -0,0 +1,67 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+/**
+ * A connection to an IMAP server that is authenticated for a user.
+ */
+interface ConnectionInterface extends \Countable
+{
+ /**
+ * Get IMAP resource.
+ */
+ public function getResource(): ImapResourceInterface;
+
+ /**
+ * Delete all messages marked for deletion.
+ */
+ public function expunge(): bool;
+
+ /**
+ * Close connection.
+ */
+ public function close(int $flag = 0): bool;
+
+ /**
+ * Check if the connection is still active.
+ */
+ public function ping(): bool;
+
+ /**
+ * Get Mailbox quota.
+ */
+ public function getQuota(string $root = 'INBOX'): array;
+
+ /**
+ * Get a list of mailboxes (also known as folders).
+ *
+ * @return MailboxInterface[]
+ */
+ public function getMailboxes(): array;
+
+ /**
+ * Check that a mailbox with the given name exists.
+ *
+ * @param string $name Mailbox name
+ */
+ public function hasMailbox(string $name): bool;
+
+ /**
+ * Get a mailbox by its name.
+ *
+ * @param string $name Mailbox name
+ */
+ public function getMailbox(string $name): MailboxInterface;
+
+ /**
+ * Create mailbox.
+ */
+ public function createMailbox(string $name): MailboxInterface;
+
+ /**
+ * Delete mailbox.
+ */
+ public function deleteMailbox(MailboxInterface $mailbox): void;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AbstractException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AbstractException.php
new file mode 100644
index 0000000..26c1e58
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AbstractException.php
@@ -0,0 +1,57 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+abstract class AbstractException extends \RuntimeException
+{
+ /**
+ * @var array
+ */
+ private static $errorLabels = [
+ \E_ERROR => 'E_ERROR',
+ \E_WARNING => 'E_WARNING',
+ \E_PARSE => 'E_PARSE',
+ \E_NOTICE => 'E_NOTICE',
+ \E_CORE_ERROR => 'E_CORE_ERROR',
+ \E_CORE_WARNING => 'E_CORE_WARNING',
+ \E_COMPILE_ERROR => 'E_COMPILE_ERROR',
+ \E_COMPILE_WARNING => 'E_COMPILE_WARNING',
+ \E_USER_ERROR => 'E_USER_ERROR',
+ \E_USER_WARNING => 'E_USER_WARNING',
+ \E_USER_NOTICE => 'E_USER_NOTICE',
+ \E_STRICT => 'E_STRICT',
+ \E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
+ \E_DEPRECATED => 'E_DEPRECATED',
+ \E_USER_DEPRECATED => 'E_USER_DEPRECATED',
+ ];
+
+ /**
+ * @param string $message The exception message
+ * @param int $code The exception code
+ * @param \Throwable $previous The previous exception
+ */
+ final public function __construct(string $message, int $code = 0, \Throwable $previous = null)
+ {
+ $errorType = '';
+ if (isset(self::$errorLabels[$code])) {
+ $errorType = \sprintf('[%s] ', self::$errorLabels[$code]);
+ }
+
+ $joinString = "\n- ";
+ $alerts = \imap_alerts();
+ $errors = \imap_errors();
+ $completeMessage = \sprintf(
+ "%s%s\nimap_alerts (%s):%s\nimap_errors (%s):%s",
+ $errorType,
+ $message,
+ false !== $alerts ? \count($alerts) : 0,
+ false !== $alerts ? $joinString . \implode($joinString, $alerts) : '',
+ false !== $errors ? \count($errors) : 0,
+ false !== $errors ? $joinString . \implode($joinString, $errors) : ''
+ );
+
+ parent::__construct($completeMessage, $code, $previous);
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AuthenticationFailedException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AuthenticationFailedException.php
new file mode 100644
index 0000000..c0e93d0
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/AuthenticationFailedException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class AuthenticationFailedException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/CreateMailboxException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/CreateMailboxException.php
new file mode 100644
index 0000000..55bb3a9
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/CreateMailboxException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class CreateMailboxException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/DeleteMailboxException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/DeleteMailboxException.php
new file mode 100644
index 0000000..694c3a6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/DeleteMailboxException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class DeleteMailboxException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchbodyException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchbodyException.php
new file mode 100644
index 0000000..0e2f30f
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchbodyException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapFetchbodyException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchheaderException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchheaderException.php
new file mode 100644
index 0000000..8a8387f
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapFetchheaderException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapFetchheaderException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapGetmailboxesException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapGetmailboxesException.php
new file mode 100644
index 0000000..7e68326
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapGetmailboxesException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapGetmailboxesException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapMsgnoException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapMsgnoException.php
new file mode 100644
index 0000000..c9de4e4
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapMsgnoException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapMsgnoException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapNumMsgException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapNumMsgException.php
new file mode 100644
index 0000000..37a55ee
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapNumMsgException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapNumMsgException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapQuotaException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapQuotaException.php
new file mode 100644
index 0000000..5a73e67
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapQuotaException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapQuotaException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapStatusException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapStatusException.php
new file mode 100644
index 0000000..ca808cd
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ImapStatusException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ImapStatusException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidDateHeaderException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidDateHeaderException.php
new file mode 100644
index 0000000..05401e2
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidDateHeaderException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class InvalidDateHeaderException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidHeadersException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidHeadersException.php
new file mode 100644
index 0000000..9554527
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidHeadersException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class InvalidHeadersException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidResourceException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidResourceException.php
new file mode 100644
index 0000000..f3cbf5b
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidResourceException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class InvalidResourceException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidSearchCriteriaException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidSearchCriteriaException.php
new file mode 100644
index 0000000..783c6ba
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/InvalidSearchCriteriaException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class InvalidSearchCriteriaException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MailboxDoesNotExistException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MailboxDoesNotExistException.php
new file mode 100644
index 0000000..0cdf28a
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MailboxDoesNotExistException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MailboxDoesNotExistException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageCopyException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageCopyException.php
new file mode 100644
index 0000000..aa1ab68
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageCopyException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageCopyException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDeleteException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDeleteException.php
new file mode 100644
index 0000000..38d88ff
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDeleteException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageDeleteException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDoesNotExistException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDoesNotExistException.php
new file mode 100644
index 0000000..1485a63
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageDoesNotExistException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageDoesNotExistException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageMoveException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageMoveException.php
new file mode 100644
index 0000000..2f0cd0c
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageMoveException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageMoveException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageStructureException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageStructureException.php
new file mode 100644
index 0000000..a0dd436
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageStructureException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageStructureException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageUndeleteException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageUndeleteException.php
new file mode 100644
index 0000000..a25f45c
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/MessageUndeleteException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class MessageUndeleteException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/NotEmbeddedMessageException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/NotEmbeddedMessageException.php
new file mode 100644
index 0000000..12afdec
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/NotEmbeddedMessageException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class NotEmbeddedMessageException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/OutOfBoundsException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/OutOfBoundsException.php
new file mode 100644
index 0000000..55528c1
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/OutOfBoundsException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class OutOfBoundsException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ReopenMailboxException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ReopenMailboxException.php
new file mode 100644
index 0000000..945c654
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ReopenMailboxException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ReopenMailboxException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ResourceCheckFailureException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ResourceCheckFailureException.php
new file mode 100644
index 0000000..2751c71
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/ResourceCheckFailureException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class ResourceCheckFailureException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnexpectedEncodingException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnexpectedEncodingException.php
new file mode 100644
index 0000000..7ec3943
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnexpectedEncodingException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class UnexpectedEncodingException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnsupportedCharsetException.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnsupportedCharsetException.php
new file mode 100644
index 0000000..5743679
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Exception/UnsupportedCharsetException.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Exception;
+
+final class UnsupportedCharsetException extends AbstractException
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResource.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResource.php
new file mode 100644
index 0000000..60c1617
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResource.php
@@ -0,0 +1,107 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use Ddeboer\Imap\Exception\InvalidResourceException;
+use Ddeboer\Imap\Exception\ReopenMailboxException;
+
+/**
+ * An imap resource stream.
+ */
+final class ImapResource implements ImapResourceInterface
+{
+ /**
+ * @var mixed
+ */
+ private $resource;
+
+ /**
+ * @var null|MailboxInterface
+ */
+ private $mailbox;
+
+ /**
+ * @var null|string
+ */
+ private static $lastMailboxUsedCache;
+
+ /**
+ * Constructor.
+ *
+ * @param resource $resource
+ */
+ public function __construct($resource, MailboxInterface $mailbox = null)
+ {
+ $this->resource = $resource;
+ $this->mailbox = $mailbox;
+ }
+
+ /**
+ * Get IMAP resource stream.
+ *
+ * @throws InvalidResourceException
+ *
+ * @return resource
+ */
+ public function getStream()
+ {
+ if (false === \is_resource($this->resource) || 'imap' !== \get_resource_type($this->resource)) {
+ throw new InvalidResourceException('Supplied resource is not a valid imap resource');
+ }
+
+ $this->initMailbox();
+
+ return $this->resource;
+ }
+
+ /**
+ * Clear last mailbox used cache.
+ */
+ public function clearLastMailboxUsedCache(): void
+ {
+ self::$lastMailboxUsedCache = null;
+ }
+
+ /**
+ * If connection is not currently in this mailbox, switch it to this mailbox.
+ */
+ private function initMailbox(): void
+ {
+ if (null === $this->mailbox || self::isMailboxOpen($this->mailbox, $this->resource)) {
+ return;
+ }
+
+ \imap_reopen($this->resource, $this->mailbox->getFullEncodedName());
+
+ if (self::isMailboxOpen($this->mailbox, $this->resource)) {
+ return;
+ }
+
+ throw new ReopenMailboxException(\sprintf('Cannot reopen mailbox "%s"', $this->mailbox->getName()));
+ }
+
+ /**
+ * Check whether the current mailbox is open.
+ *
+ * @param mixed $resource
+ */
+ private static function isMailboxOpen(MailboxInterface $mailbox, $resource): bool
+ {
+ $currentMailboxName = $mailbox->getFullEncodedName();
+ if ($currentMailboxName === self::$lastMailboxUsedCache) {
+ return true;
+ }
+
+ self::$lastMailboxUsedCache = null;
+ $check = \imap_check($resource);
+ $return = false !== $check && $check->Mailbox === $currentMailboxName;
+
+ if (true === $return) {
+ self::$lastMailboxUsedCache = $currentMailboxName;
+ }
+
+ return $return;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResourceInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResourceInterface.php
new file mode 100644
index 0000000..03c16f7
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ImapResourceInterface.php
@@ -0,0 +1,20 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+interface ImapResourceInterface
+{
+ /**
+ * Get IMAP resource stream.
+ *
+ * @return resource
+ */
+ public function getStream();
+
+ /**
+ * Clear last mailbox used cache.
+ */
+ public function clearLastMailboxUsedCache(): void;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Mailbox.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Mailbox.php
new file mode 100644
index 0000000..d4e1bf4
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Mailbox.php
@@ -0,0 +1,321 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use DateTimeInterface;
+use Ddeboer\Imap\Exception\ImapNumMsgException;
+use Ddeboer\Imap\Exception\ImapStatusException;
+use Ddeboer\Imap\Exception\InvalidSearchCriteriaException;
+use Ddeboer\Imap\Exception\MessageCopyException;
+use Ddeboer\Imap\Exception\MessageMoveException;
+use Ddeboer\Imap\Search\ConditionInterface;
+use Ddeboer\Imap\Search\LogicalOperator\All;
+
+/**
+ * An IMAP mailbox (commonly referred to as a 'folder').
+ */
+final class Mailbox implements MailboxInterface
+{
+ /**
+ * @var ImapResourceInterface
+ */
+ private $resource;
+
+ /**
+ * @var string
+ */
+ private $name;
+
+ /**
+ * @var \stdClass
+ */
+ private $info;
+
+ /**
+ * Constructor.
+ *
+ * @param ImapResourceInterface $resource IMAP resource
+ * @param string $name Mailbox decoded name
+ * @param \stdClass $info Mailbox info
+ */
+ public function __construct(ImapResourceInterface $resource, string $name, \stdClass $info)
+ {
+ $this->resource = new ImapResource($resource->getStream(), $this);
+ $this->name = $name;
+ $this->info = $info;
+ }
+
+ /**
+ * Get mailbox decoded name.
+ */
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * Get mailbox encoded path.
+ */
+ public function getEncodedName(): string
+ {
+ /** @var string $name */
+ $name = $this->info->name;
+
+ return (string) \preg_replace('/^{.+}/', '', $name);
+ }
+
+ /**
+ * Get mailbox encoded full name.
+ */
+ public function getFullEncodedName(): string
+ {
+ return $this->info->name;
+ }
+
+ /**
+ * Get mailbox attributes.
+ */
+ public function getAttributes(): int
+ {
+ return $this->info->attributes;
+ }
+
+ /**
+ * Get mailbox delimiter.
+ */
+ public function getDelimiter(): string
+ {
+ return $this->info->delimiter;
+ }
+
+ /**
+ * Get number of messages in this mailbox.
+ *
+ * @return int
+ */
+ public function count()
+ {
+ $return = \imap_num_msg($this->resource->getStream());
+
+ if (false === $return) {
+ throw new ImapNumMsgException('imap_num_msg failed');
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get Mailbox status.
+ */
+ public function getStatus(int $flags = null): \stdClass
+ {
+ $return = \imap_status($this->resource->getStream(), $this->getFullEncodedName(), $flags ?? \SA_ALL);
+
+ if (false === $return) {
+ throw new ImapStatusException('imap_status failed');
+ }
+
+ return $return;
+ }
+
+ /**
+ * Bulk Set Flag for Messages.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ * @param array|MessageIterator|string $numbers Message numbers
+ */
+ public function setFlag(string $flag, $numbers): bool
+ {
+ return \imap_setflag_full($this->resource->getStream(), $this->prepareMessageIds($numbers), $flag, \ST_UID);
+ }
+
+ /**
+ * Bulk Clear Flag for Messages.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ * @param array|MessageIterator|string $numbers Message numbers
+ */
+ public function clearFlag(string $flag, $numbers): bool
+ {
+ return \imap_clearflag_full($this->resource->getStream(), $this->prepareMessageIds($numbers), $flag, \ST_UID);
+ }
+
+ /**
+ * Get message ids.
+ *
+ * @param ConditionInterface $search Search expression (optional)
+ */
+ public function getMessages(ConditionInterface $search = null, int $sortCriteria = null, bool $descending = false, string $charset = null): MessageIteratorInterface
+ {
+ if (null === $search) {
+ $search = new All();
+ }
+ $query = $search->toString();
+
+ // We need to clear the stack to know whether imap_last_error()
+ // is related to this imap_search
+ \imap_errors();
+
+ if (null !== $sortCriteria) {
+ $params = [
+ $this->resource->getStream(),
+ $sortCriteria,
+ $descending ? 1 : 0,
+ \SE_UID,
+ $query,
+ ];
+ if (null !== $charset) {
+ $params[] = $charset;
+ }
+ $messageNumbers = \imap_sort(...$params);
+ } else {
+ $params = [
+ $this->resource->getStream(),
+ $query,
+ \SE_UID,
+ ];
+ if (null !== $charset) {
+ $params[] = $charset;
+ }
+ $messageNumbers = \imap_search(...$params);
+ }
+ if (false === $messageNumbers) {
+ if (false !== \imap_last_error()) {
+ throw new InvalidSearchCriteriaException(\sprintf('Invalid search criteria [%s]', $query));
+ }
+
+ // imap_search can also return false
+ $messageNumbers = [];
+ }
+
+ return new MessageIterator($this->resource, $messageNumbers);
+ }
+
+ /**
+ * Get message iterator for a sequence.
+ *
+ * @param string $sequence Message numbers
+ */
+ public function getMessageSequence(string $sequence): MessageIteratorInterface
+ {
+ \imap_errors();
+
+ $overview = \imap_fetch_overview($this->resource->getStream(), $sequence, \FT_UID);
+ if (\is_array($overview) && [] !== $overview) {
+ $messageNumbers = \array_column($overview, 'uid');
+ } else {
+ if (false !== \imap_last_error()) {
+ throw new InvalidSearchCriteriaException(\sprintf('Invalid sequence [%s]', $sequence));
+ }
+
+ $messageNumbers = [];
+ }
+
+ return new MessageIterator($this->resource, $messageNumbers);
+ }
+
+ /**
+ * Get a message by message number.
+ *
+ * @param int $number Message number
+ */
+ public function getMessage(int $number): MessageInterface
+ {
+ return new Message($this->resource, $number);
+ }
+
+ /**
+ * Get messages in this mailbox.
+ */
+ public function getIterator(): MessageIteratorInterface
+ {
+ return $this->getMessages();
+ }
+
+ /**
+ * Add a message to the mailbox.
+ */
+ public function addMessage(string $message, string $options = null, DateTimeInterface $internalDate = null): bool
+ {
+ $arguments = [
+ $this->resource->getStream(),
+ $this->getFullEncodedName(),
+ $message,
+ ];
+ if (null !== $options) {
+ $arguments[] = $options;
+ if (null !== $internalDate) {
+ $arguments[] = $internalDate->format('d-M-Y H:i:s O');
+ }
+ }
+
+ return \imap_append(...$arguments);
+ }
+
+ /**
+ * Returns a tree of threaded message for the current Mailbox.
+ */
+ public function getThread(): array
+ {
+ \set_error_handler(static function (): bool {
+ return true;
+ });
+
+ /** @var array|false $tree */
+ $tree = \imap_thread($this->resource->getStream());
+
+ \restore_error_handler();
+
+ return false !== $tree ? $tree : [];
+ }
+
+ /**
+ * Bulk move messages.
+ *
+ * @param array|MessageIterator|string $numbers Message numbers
+ * @param MailboxInterface $mailbox Destination Mailbox to move the messages to
+ *
+ * @throws \Ddeboer\Imap\Exception\MessageMoveException
+ */
+ public function move($numbers, MailboxInterface $mailbox): void
+ {
+ if (!\imap_mail_move($this->resource->getStream(), $this->prepareMessageIds($numbers), $mailbox->getEncodedName(), \CP_UID)) {
+ throw new MessageMoveException(\sprintf('Messages cannot be moved to "%s"', $mailbox->getName()));
+ }
+ }
+
+ /**
+ * Bulk copy messages.
+ *
+ * @param array|MessageIterator|string $numbers Message numbers
+ * @param MailboxInterface $mailbox Destination Mailbox to copy the messages to
+ *
+ * @throws \Ddeboer\Imap\Exception\MessageCopyException
+ */
+ public function copy($numbers, MailboxInterface $mailbox): void
+ {
+ if (!\imap_mail_copy($this->resource->getStream(), $this->prepareMessageIds($numbers), $mailbox->getEncodedName(), \CP_UID)) {
+ throw new MessageCopyException(\sprintf('Messages cannot be copied to "%s"', $mailbox->getName()));
+ }
+ }
+
+ /**
+ * Prepare message ids for the use with bulk functions.
+ *
+ * @param array|MessageIterator|string $messageIds Message numbers
+ */
+ private function prepareMessageIds($messageIds): string
+ {
+ if ($messageIds instanceof MessageIterator) {
+ $messageIds = $messageIds->getArrayCopy();
+ }
+
+ if (\is_array($messageIds)) {
+ $messageIds = \implode(',', $messageIds);
+ }
+
+ return $messageIds;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MailboxInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MailboxInterface.php
new file mode 100644
index 0000000..efe432d
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MailboxInterface.php
@@ -0,0 +1,116 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use DateTimeInterface;
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * An IMAP mailbox (commonly referred to as a 'folder').
+ */
+interface MailboxInterface extends \Countable, \IteratorAggregate
+{
+ /**
+ * Get mailbox decoded name.
+ */
+ public function getName(): string;
+
+ /**
+ * Get mailbox encoded path.
+ */
+ public function getEncodedName(): string;
+
+ /**
+ * Get mailbox encoded full name.
+ */
+ public function getFullEncodedName(): string;
+
+ /**
+ * Get mailbox attributes.
+ */
+ public function getAttributes(): int;
+
+ /**
+ * Get mailbox delimiter.
+ */
+ public function getDelimiter(): string;
+
+ /**
+ * Get Mailbox status.
+ */
+ public function getStatus(int $flags = null): \stdClass;
+
+ /**
+ * Bulk Set Flag for Messages.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ * @param array|MessageIterator|string $numbers Message numbers
+ */
+ public function setFlag(string $flag, $numbers): bool;
+
+ /**
+ * Bulk Clear Flag for Messages.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ * @param array|MessageIterator|string $numbers Message numbers
+ */
+ public function clearFlag(string $flag, $numbers): bool;
+
+ /**
+ * Get message ids.
+ *
+ * @param ConditionInterface $search Search expression (optional)
+ */
+ public function getMessages(ConditionInterface $search = null, int $sortCriteria = null, bool $descending = false, string $charset = null): MessageIteratorInterface;
+
+ /**
+ * Get message iterator for a sequence.
+ *
+ * @param string $sequence Message numbers
+ */
+ public function getMessageSequence(string $sequence): MessageIteratorInterface;
+
+ /**
+ * Get a message by message number.
+ *
+ * @param int $number Message number
+ */
+ public function getMessage(int $number): MessageInterface;
+
+ /**
+ * Get messages in this mailbox.
+ */
+ public function getIterator(): MessageIteratorInterface;
+
+ /**
+ * Add a message to the mailbox.
+ */
+ public function addMessage(string $message, string $options = null, DateTimeInterface $internalDate = null): bool;
+
+ /**
+ * Returns a tree of threaded message for the current Mailbox.
+ */
+ public function getThread(): array;
+
+ /**
+ * Bulk move messages.
+ *
+ * @param array|MessageIterator|string $numbers Message numbers
+ * @param MailboxInterface $mailbox Destination Mailbox to move the messages to
+ *
+ * @throws \Ddeboer\Imap\Exception\MessageMoveException
+ */
+ public function move($numbers, self $mailbox): void;
+
+ /**
+ * Bulk copy messages.
+ *
+ * @param array|MessageIterator|string $numbers Message numbers
+ * @param MailboxInterface $mailbox Destination Mailbox to copy the messages to
+ *
+ * @throws \Ddeboer\Imap\Exception\MessageCopyException
+ */
+ public function copy($numbers, self $mailbox): void;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message.php
new file mode 100644
index 0000000..7b50777
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message.php
@@ -0,0 +1,355 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use Ddeboer\Imap\Exception\ImapFetchheaderException;
+use Ddeboer\Imap\Exception\InvalidHeadersException;
+use Ddeboer\Imap\Exception\MessageCopyException;
+use Ddeboer\Imap\Exception\MessageDeleteException;
+use Ddeboer\Imap\Exception\MessageDoesNotExistException;
+use Ddeboer\Imap\Exception\MessageMoveException;
+use Ddeboer\Imap\Exception\MessageStructureException;
+use Ddeboer\Imap\Exception\MessageUndeleteException;
+
+/**
+ * An IMAP message (e-mail).
+ */
+final class Message extends Message\AbstractMessage implements MessageInterface
+{
+ /**
+ * @var bool
+ */
+ private $messageNumberVerified = false;
+
+ /**
+ * @var int
+ */
+ private $imapMsgNo = 0;
+
+ /**
+ * @var bool
+ */
+ private $structureLoaded = false;
+
+ /**
+ * @var null|Message\Headers
+ */
+ private $headers;
+
+ /**
+ * @var null|string
+ */
+ private $rawHeaders;
+
+ /**
+ * @var null|string
+ */
+ private $rawMessage;
+
+ /**
+ * Constructor.
+ *
+ * @param ImapResourceInterface $resource IMAP resource
+ * @param int $messageNumber Message number
+ */
+ public function __construct(ImapResourceInterface $resource, int $messageNumber)
+ {
+ parent::__construct($resource, $messageNumber, '1', new \stdClass());
+ }
+
+ /**
+ * Lazy load structure.
+ */
+ protected function lazyLoadStructure(): void
+ {
+ if (true === $this->structureLoaded) {
+ return;
+ }
+ $this->structureLoaded = true;
+
+ $messageNumber = $this->getNumber();
+
+ $errorMessage = null;
+ $errorNumber = 0;
+ \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
+ $errorMessage = $message;
+ $errorNumber = $nr;
+
+ return true;
+ });
+
+ $structure = \imap_fetchstructure(
+ $this->resource->getStream(),
+ $messageNumber,
+ \FT_UID
+ );
+
+ \restore_error_handler();
+
+ if (!$structure instanceof \stdClass) {
+ throw new MessageStructureException(\sprintf(
+ 'Message "%s" structure is empty: %s',
+ $messageNumber,
+ $errorMessage
+ ), $errorNumber);
+ }
+
+ $this->setStructure($structure);
+ }
+
+ /**
+ * Ensure message exists.
+ */
+ protected function assertMessageExists(int $messageNumber): void
+ {
+ if (true === $this->messageNumberVerified) {
+ return;
+ }
+ $this->messageNumberVerified = true;
+
+ $msgno = \imap_msgno($this->resource->getStream(), $messageNumber);
+ if (\is_numeric($msgno) && $msgno > 0) {
+ $this->imapMsgNo = $msgno;
+
+ return;
+ }
+
+ throw new MessageDoesNotExistException(\sprintf(
+ 'Message "%s" does not exist',
+ $messageNumber
+ ));
+ }
+
+ private function getMsgNo(): int
+ {
+ // Triggers assertMessageExists()
+ $this->getNumber();
+
+ return $this->imapMsgNo;
+ }
+
+ /**
+ * Get raw message headers.
+ */
+ public function getRawHeaders(): string
+ {
+ if (null === $this->rawHeaders) {
+ $rawHeaders = \imap_fetchheader($this->resource->getStream(), $this->getNumber(), \FT_UID);
+
+ if (false === $rawHeaders) {
+ throw new ImapFetchheaderException('imap_fetchheader failed');
+ }
+
+ $this->rawHeaders = $rawHeaders;
+ }
+
+ return $this->rawHeaders;
+ }
+
+ /**
+ * Get the raw message, including all headers, parts, etc. unencoded and unparsed.
+ *
+ * @return string the raw message
+ */
+ public function getRawMessage(): string
+ {
+ if (null === $this->rawMessage) {
+ $this->rawMessage = $this->doGetContent('');
+ }
+
+ return $this->rawMessage;
+ }
+
+ /**
+ * Get message headers.
+ */
+ public function getHeaders(): Message\Headers
+ {
+ if (null === $this->headers) {
+ // imap_headerinfo is much faster than imap_fetchheader
+ // imap_headerinfo returns only a subset of all mail headers,
+ // but it does include the message flags.
+ $headers = \imap_headerinfo($this->resource->getStream(), $this->getMsgNo());
+ if (false === $headers) {
+ // @see https://github.com/ddeboer/imap/issues/358
+ throw new InvalidHeadersException(\sprintf('Message "%s" has invalid headers', $this->getNumber()));
+ }
+ $this->headers = new Message\Headers($headers);
+ }
+
+ return $this->headers;
+ }
+
+ /**
+ * Clearmessage headers.
+ */
+ private function clearHeaders(): void
+ {
+ $this->headers = null;
+ }
+
+ /**
+ * Get message recent flag value (from headers).
+ */
+ public function isRecent(): ?string
+ {
+ return $this->getHeaders()->get('recent');
+ }
+
+ /**
+ * Get message unseen flag value (from headers).
+ */
+ public function isUnseen(): bool
+ {
+ return 'U' === $this->getHeaders()->get('unseen');
+ }
+
+ /**
+ * Get message flagged flag value (from headers).
+ */
+ public function isFlagged(): bool
+ {
+ return 'F' === $this->getHeaders()->get('flagged');
+ }
+
+ /**
+ * Get message answered flag value (from headers).
+ */
+ public function isAnswered(): bool
+ {
+ return 'A' === $this->getHeaders()->get('answered');
+ }
+
+ /**
+ * Get message deleted flag value (from headers).
+ */
+ public function isDeleted(): bool
+ {
+ return 'D' === $this->getHeaders()->get('deleted');
+ }
+
+ /**
+ * Get message draft flag value (from headers).
+ */
+ public function isDraft(): bool
+ {
+ return 'X' === $this->getHeaders()->get('draft');
+ }
+
+ /**
+ * Has the message been marked as read?
+ */
+ public function isSeen(): bool
+ {
+ return 'N' !== $this->getHeaders()->get('recent') && 'U' !== $this->getHeaders()->get('unseen');
+ }
+
+ /**
+ * Mark message as seen.
+ *
+ * @deprecated since version 1.1, to be removed in 2.0
+ */
+ public function maskAsSeen(): bool
+ {
+ \trigger_error(\sprintf('%s is deprecated and will be removed in 2.0. Use %s::markAsSeen instead.', __METHOD__, __CLASS__), \E_USER_DEPRECATED);
+
+ return $this->markAsSeen();
+ }
+
+ /**
+ * Mark message as seen.
+ */
+ public function markAsSeen(): bool
+ {
+ return $this->setFlag('\\Seen');
+ }
+
+ /**
+ * Move message to another mailbox.
+ *
+ * @throws MessageCopyException
+ */
+ public function copy(MailboxInterface $mailbox): void
+ {
+ // 'deleted' header changed, force to reload headers, would be better to set deleted flag to true on header
+ $this->clearHeaders();
+
+ if (!\imap_mail_copy($this->resource->getStream(), (string) $this->getNumber(), $mailbox->getEncodedName(), \CP_UID)) {
+ throw new MessageCopyException(\sprintf('Message "%s" cannot be copied to "%s"', $this->getNumber(), $mailbox->getName()));
+ }
+ }
+
+ /**
+ * Move message to another mailbox.
+ *
+ * @throws MessageMoveException
+ */
+ public function move(MailboxInterface $mailbox): void
+ {
+ // 'deleted' header changed, force to reload headers, would be better to set deleted flag to true on header
+ $this->clearHeaders();
+
+ if (!\imap_mail_move($this->resource->getStream(), (string) $this->getNumber(), $mailbox->getEncodedName(), \CP_UID)) {
+ throw new MessageMoveException(\sprintf('Message "%s" cannot be moved to "%s"', $this->getNumber(), $mailbox->getName()));
+ }
+ }
+
+ /**
+ * Delete message.
+ *
+ * @throws MessageDeleteException
+ */
+ public function delete(): void
+ {
+ // 'deleted' header changed, force to reload headers, would be better to set deleted flag to true on header
+ $this->clearHeaders();
+
+ if (!\imap_delete($this->resource->getStream(), $this->getNumber(), \FT_UID)) {
+ throw new MessageDeleteException(\sprintf('Message "%s" cannot be deleted', $this->getNumber()));
+ }
+ }
+
+ /**
+ * Undelete message.
+ *
+ * @throws MessageUndeleteException
+ */
+ public function undelete(): void
+ {
+ // 'deleted' header changed, force to reload headers, would be better to set deleted flag to false on header
+ $this->clearHeaders();
+ if (!\imap_undelete($this->resource->getStream(), $this->getNumber(), \FT_UID)) {
+ throw new MessageUndeleteException(\sprintf('Message "%s" cannot be undeleted', $this->getNumber()));
+ }
+ }
+
+ /**
+ * Set Flag Message.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ */
+ public function setFlag(string $flag): bool
+ {
+ $result = \imap_setflag_full($this->resource->getStream(), (string) $this->getNumber(), $flag, \ST_UID);
+
+ $this->clearHeaders();
+
+ return $result;
+ }
+
+ /**
+ * Clear Flag Message.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ */
+ public function clearFlag(string $flag): bool
+ {
+ $result = \imap_clearflag_full($this->resource->getStream(), (string) $this->getNumber(), $flag, \ST_UID);
+
+ $this->clearHeaders();
+
+ return $result;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractMessage.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractMessage.php
new file mode 100644
index 0000000..9187c38
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractMessage.php
@@ -0,0 +1,266 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+use Ddeboer\Imap\Exception\InvalidDateHeaderException;
+
+abstract class AbstractMessage extends AbstractPart
+{
+ /**
+ * @var null|array
+ */
+ private $attachments;
+
+ /**
+ * Get message headers.
+ */
+ abstract public function getHeaders(): Headers;
+
+ /**
+ * Get message id.
+ *
+ * A unique message id in the form <...>
+ */
+ final public function getId(): ?string
+ {
+ return $this->getHeaders()->get('message_id');
+ }
+
+ /**
+ * Get message sender (from headers).
+ */
+ final public function getFrom(): ?EmailAddress
+ {
+ $from = $this->getHeaders()->get('from');
+
+ return null !== $from ? $this->decodeEmailAddress($from[0]) : null;
+ }
+
+ /**
+ * Get To recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no To: recipients
+ */
+ final public function getTo(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('to') ?: []);
+ }
+
+ /**
+ * Get Cc recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no CC: recipients
+ */
+ final public function getCc(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('cc') ?: []);
+ }
+
+ /**
+ * Get Bcc recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no BCC: recipients
+ */
+ final public function getBcc(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('bcc') ?: []);
+ }
+
+ /**
+ * Get Reply-To recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no Reply-To: recipients
+ */
+ final public function getReplyTo(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('reply_to') ?: []);
+ }
+
+ /**
+ * Get Sender.
+ *
+ * @return EmailAddress[] Empty array in case message has no Sender: recipients
+ */
+ final public function getSender(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('sender') ?: []);
+ }
+
+ /**
+ * Get Return-Path.
+ *
+ * @return EmailAddress[] Empty array in case message has no Return-Path: recipients
+ */
+ final public function getReturnPath(): array
+ {
+ return $this->decodeEmailAddresses($this->getHeaders()->get('return_path') ?: []);
+ }
+
+ /**
+ * Get date (from headers).
+ */
+ final public function getDate(): ?\DateTimeImmutable
+ {
+ /** @var null|string $dateHeader */
+ $dateHeader = $this->getHeaders()->get('date');
+ if (null === $dateHeader) {
+ return null;
+ }
+
+ $alteredValue = $dateHeader;
+ $alteredValue = \str_replace(',', '', $alteredValue);
+ $alteredValue = (string) \preg_replace('/^[a-zA-Z]+ ?/', '', $alteredValue);
+ $alteredValue = (string) \preg_replace('/\(.*\)/', '', $alteredValue);
+ $alteredValue = (string) \preg_replace('/\bUT\b/', 'UTC', $alteredValue);
+ if (0 === \preg_match('/\d\d:\d\d:\d\d.* [\+\-]\d\d:?\d\d/', $alteredValue)) {
+ $alteredValue .= ' +0000';
+ }
+ // Handle numeric months
+ $alteredValue = (string) \preg_replace('/^(\d\d) (\d\d) (\d\d(?:\d\d)?) /', '$3-$2-$1 ', $alteredValue);
+
+ try {
+ $date = new \DateTimeImmutable($alteredValue);
+ } catch (\Throwable $ex) {
+ throw new InvalidDateHeaderException(\sprintf('Invalid Date header found: "%s"', $dateHeader), 0, $ex);
+ }
+
+ return $date;
+ }
+
+ /**
+ * Get message size (from headers).
+ *
+ * @return null|int|string
+ */
+ final public function getSize()
+ {
+ return $this->getHeaders()->get('size');
+ }
+
+ /**
+ * Get message subject (from headers).
+ */
+ final public function getSubject(): ?string
+ {
+ return $this->getHeaders()->get('subject');
+ }
+
+ /**
+ * Get message In-Reply-To (from headers).
+ */
+ final public function getInReplyTo(): array
+ {
+ $inReplyTo = $this->getHeaders()->get('in_reply_to');
+
+ return null !== $inReplyTo ? \explode(' ', $inReplyTo) : [];
+ }
+
+ /**
+ * Get message References (from headers).
+ */
+ final public function getReferences(): array
+ {
+ $references = $this->getHeaders()->get('references');
+
+ return null !== $references ? \explode(' ', $references) : [];
+ }
+
+ /**
+ * Get body HTML.
+ */
+ final public function getBodyHtml(): ?string
+ {
+ $iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
+ foreach ($iterator as $part) {
+ if (self::SUBTYPE_HTML === $part->getSubtype()) {
+ return $part->getDecodedContent();
+ }
+ }
+
+ // If message has no parts and is HTML, return content of message itself.
+ if (self::SUBTYPE_HTML === $this->getSubtype()) {
+ return $this->getDecodedContent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get body text.
+ */
+ final public function getBodyText(): ?string
+ {
+ $iterator = new \RecursiveIteratorIterator($this, \RecursiveIteratorIterator::SELF_FIRST);
+ foreach ($iterator as $part) {
+ if (self::SUBTYPE_PLAIN === $part->getSubtype()) {
+ return $part->getDecodedContent();
+ }
+ }
+
+ // If message has no parts, return content of message itself.
+ if (self::SUBTYPE_PLAIN === $this->getSubtype()) {
+ return $this->getDecodedContent();
+ }
+
+ return null;
+ }
+
+ /**
+ * Get attachments (if any) linked to this e-mail.
+ *
+ * @return AttachmentInterface[]
+ */
+ final public function getAttachments(): array
+ {
+ if (null === $this->attachments) {
+ $this->attachments = self::gatherAttachments($this);
+ }
+
+ return $this->attachments;
+ }
+
+ private static function gatherAttachments(PartInterface $part): array
+ {
+ $attachments = [];
+ foreach ($part->getParts() as $childPart) {
+ if ($childPart instanceof Attachment) {
+ $attachments[] = $childPart;
+ }
+ if ($childPart->hasChildren()) {
+ $attachments = \array_merge($attachments, self::gatherAttachments($childPart));
+ }
+ }
+
+ return $attachments;
+ }
+
+ /**
+ * Does this message have attachments?
+ */
+ final public function hasAttachments(): bool
+ {
+ return \count($this->getAttachments()) > 0;
+ }
+
+ /**
+ * @param \stdClass[] $addresses
+ */
+ private function decodeEmailAddresses(array $addresses): array
+ {
+ $return = [];
+ foreach ($addresses as $address) {
+ if (isset($address->mailbox)) {
+ $return[] = $this->decodeEmailAddress($address);
+ }
+ }
+
+ return $return;
+ }
+
+ private function decodeEmailAddress(\stdClass $value): EmailAddress
+ {
+ return new EmailAddress($value->mailbox, $value->host, $value->personal);
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractPart.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractPart.php
new file mode 100644
index 0000000..923f300
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AbstractPart.php
@@ -0,0 +1,572 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+use Ddeboer\Imap\Exception\ImapFetchbodyException;
+use Ddeboer\Imap\Exception\UnexpectedEncodingException;
+use Ddeboer\Imap\ImapResourceInterface;
+use Ddeboer\Imap\Message;
+
+/**
+ * A message part.
+ */
+abstract class AbstractPart implements PartInterface
+{
+ /**
+ * @var ImapResourceInterface
+ */
+ protected $resource;
+
+ /**
+ * @var bool
+ */
+ private $structureParsed = false;
+
+ /**
+ * @var array
+ */
+ private $parts = [];
+
+ /**
+ * @var string
+ */
+ private $partNumber;
+
+ /**
+ * @var int
+ */
+ private $messageNumber;
+
+ /**
+ * @var \stdClass
+ */
+ private $structure;
+
+ /**
+ * @var Parameters
+ */
+ private $parameters;
+
+ /**
+ * @var null|string
+ */
+ private $type;
+
+ /**
+ * @var null|string
+ */
+ private $subtype;
+
+ /**
+ * @var null|string
+ */
+ private $encoding;
+
+ /**
+ * @var null|string
+ */
+ private $disposition;
+
+ /**
+ * @var null|string
+ */
+ private $description;
+
+ /**
+ * @var null|string
+ */
+ private $bytes;
+
+ /**
+ * @var null|string
+ */
+ private $lines;
+
+ /**
+ * @var null|string
+ */
+ private $content;
+
+ /**
+ * @var null|string
+ */
+ private $decodedContent;
+
+ /**
+ * @var int
+ */
+ private $key = 0;
+
+ /**
+ * @var array
+ */
+ private static $typesMap = [
+ \TYPETEXT => self::TYPE_TEXT,
+ \TYPEMULTIPART => self::TYPE_MULTIPART,
+ \TYPEMESSAGE => self::TYPE_MESSAGE,
+ \TYPEAPPLICATION => self::TYPE_APPLICATION,
+ \TYPEAUDIO => self::TYPE_AUDIO,
+ \TYPEIMAGE => self::TYPE_IMAGE,
+ \TYPEVIDEO => self::TYPE_VIDEO,
+ \TYPEMODEL => self::TYPE_MODEL,
+ \TYPEOTHER => self::TYPE_OTHER,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $encodingsMap = [
+ \ENC7BIT => self::ENCODING_7BIT,
+ \ENC8BIT => self::ENCODING_8BIT,
+ \ENCBINARY => self::ENCODING_BINARY,
+ \ENCBASE64 => self::ENCODING_BASE64,
+ \ENCQUOTEDPRINTABLE => self::ENCODING_QUOTED_PRINTABLE,
+ ];
+
+ /**
+ * @var array
+ */
+ private static $attachmentKeys = [
+ 'name' => true,
+ 'filename' => true,
+ 'name*' => true,
+ 'filename*' => true,
+ ];
+
+ /**
+ * Constructor.
+ *
+ * @param ImapResourceInterface $resource IMAP resource
+ * @param int $messageNumber Message number
+ * @param string $partNumber Part number
+ * @param \stdClass $structure Part structure
+ */
+ public function __construct(
+ ImapResourceInterface $resource,
+ int $messageNumber,
+ string $partNumber,
+ \stdClass $structure
+ ) {
+ $this->resource = $resource;
+ $this->messageNumber = $messageNumber;
+ $this->partNumber = $partNumber;
+ $this->setStructure($structure);
+ }
+
+ /**
+ * Get message number (from headers).
+ */
+ final public function getNumber(): int
+ {
+ $this->assertMessageExists($this->messageNumber);
+
+ return $this->messageNumber;
+ }
+
+ /**
+ * Ensure message exists.
+ */
+ protected function assertMessageExists(int $messageNumber): void
+ {
+ }
+
+ /**
+ * @param \stdClass $structure Part structure
+ */
+ final protected function setStructure(\stdClass $structure): void
+ {
+ $this->structure = $structure;
+ }
+
+ /**
+ * Part structure.
+ */
+ final public function getStructure(): \stdClass
+ {
+ $this->lazyLoadStructure();
+
+ return $this->structure;
+ }
+
+ /**
+ * Lazy load structure.
+ */
+ protected function lazyLoadStructure(): void
+ {
+ }
+
+ /**
+ * Part parameters.
+ */
+ final public function getParameters(): Parameters
+ {
+ $this->lazyParseStructure();
+
+ return $this->parameters;
+ }
+
+ /**
+ * Part charset.
+ */
+ final public function getCharset(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->parameters->get('charset') ?: null;
+ }
+
+ /**
+ * Part type.
+ */
+ final public function getType(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->type;
+ }
+
+ /**
+ * Part subtype.
+ */
+ final public function getSubtype(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->subtype;
+ }
+
+ /**
+ * Part encoding.
+ */
+ final public function getEncoding(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->encoding;
+ }
+
+ /**
+ * Part disposition.
+ */
+ final public function getDisposition(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->disposition;
+ }
+
+ /**
+ * Part description.
+ */
+ final public function getDescription(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->description;
+ }
+
+ /**
+ * Part bytes.
+ *
+ * @return null|int|string
+ */
+ final public function getBytes()
+ {
+ $this->lazyParseStructure();
+
+ return $this->bytes;
+ }
+
+ /**
+ * Part lines.
+ */
+ final public function getLines(): ?string
+ {
+ $this->lazyParseStructure();
+
+ return $this->lines;
+ }
+
+ /**
+ * Get raw part content.
+ */
+ final public function getContent(): string
+ {
+ if (null === $this->content) {
+ $this->content = $this->doGetContent($this->getContentPartNumber());
+ }
+
+ return $this->content;
+ }
+
+ /**
+ * Get content part number.
+ */
+ protected function getContentPartNumber(): string
+ {
+ return $this->partNumber;
+ }
+
+ /**
+ * Get part number.
+ */
+ final public function getPartNumber(): string
+ {
+ return $this->partNumber;
+ }
+
+ /**
+ * Get decoded part content.
+ */
+ final public function getDecodedContent(): string
+ {
+ if (null === $this->decodedContent) {
+ if (self::ENCODING_UNKNOWN === $this->getEncoding()) {
+ throw new UnexpectedEncodingException('Cannot decode a content with an uknown encoding');
+ }
+
+ $content = $this->getContent();
+ if (self::ENCODING_BASE64 === $this->getEncoding()) {
+ $content = \base64_decode($content, false);
+ } elseif (self::ENCODING_QUOTED_PRINTABLE === $this->getEncoding()) {
+ $content = \quoted_printable_decode($content);
+ }
+
+ if (false === $content) {
+ throw new UnexpectedEncodingException('Cannot decode content');
+ }
+
+ // If this part is a text part, convert its charset to UTF-8.
+ // We don't want to decode an attachment's charset.
+ if (!$this instanceof Attachment && null !== $this->getCharset() && self::TYPE_TEXT === $this->getType()) {
+ $content = Transcoder::decode($content, $this->getCharset());
+ }
+
+ $this->decodedContent = $content;
+ }
+
+ return $this->decodedContent;
+ }
+
+ /**
+ * Get raw message content.
+ */
+ final protected function doGetContent(string $partNumber): string
+ {
+ $return = \imap_fetchbody(
+ $this->resource->getStream(),
+ $this->getNumber(),
+ $partNumber,
+ \FT_UID | \FT_PEEK
+ );
+
+ if (false === $return) {
+ throw new ImapFetchbodyException('imap_fetchbody failed');
+ }
+
+ return $return;
+ }
+
+ /**
+ * Get an array of all parts for this message.
+ *
+ * @return PartInterface[]
+ */
+ final public function getParts(): array
+ {
+ $this->lazyParseStructure();
+
+ return $this->parts;
+ }
+
+ /**
+ * Get current child part.
+ *
+ * @return mixed
+ */
+ final public function current()
+ {
+ $this->lazyParseStructure();
+
+ return $this->parts[$this->key];
+ }
+
+ /**
+ * Get current child part.
+ *
+ * @return \RecursiveIterator
+ */
+ final public function getChildren()
+ {
+ return $this->current();
+ }
+
+ /**
+ * Get current child part.
+ *
+ * @return bool
+ */
+ final public function hasChildren()
+ {
+ $this->lazyParseStructure();
+
+ return \count($this->parts) > 0;
+ }
+
+ /**
+ * Get current part key.
+ *
+ * @return int
+ */
+ final public function key()
+ {
+ return $this->key;
+ }
+
+ /**
+ * Move to next part.
+ *
+ * @return void
+ */
+ final public function next()
+ {
+ ++$this->key;
+ }
+
+ /**
+ * Reset part key.
+ *
+ * @return void
+ */
+ final public function rewind()
+ {
+ $this->key = 0;
+ }
+
+ /**
+ * Check if current part is a valid one.
+ *
+ * @return bool
+ */
+ final public function valid()
+ {
+ $this->lazyParseStructure();
+
+ return isset($this->parts[$this->key]);
+ }
+
+ /**
+ * Parse part structure.
+ */
+ private function lazyParseStructure(): void
+ {
+ if (true === $this->structureParsed) {
+ return;
+ }
+ $this->structureParsed = true;
+
+ $this->lazyLoadStructure();
+
+ $this->type = self::$typesMap[$this->structure->type] ?? self::TYPE_UNKNOWN;
+
+ // In our context, \ENCOTHER is as useful as an uknown encoding
+ $this->encoding = self::$encodingsMap[$this->structure->encoding] ?? self::ENCODING_UNKNOWN;
+ $this->subtype = $this->structure->subtype;
+
+ if (isset($this->structure->bytes)) {
+ $this->bytes = $this->structure->bytes;
+ }
+ if ($this->structure->ifdisposition) {
+ $this->disposition = $this->structure->disposition;
+ }
+ if ($this->structure->ifdescription) {
+ $this->description = $this->structure->description;
+ }
+
+ $this->parameters = new Parameters();
+ if ($this->structure->ifparameters) {
+ $this->parameters->add($this->structure->parameters);
+ }
+
+ if ($this->structure->ifdparameters) {
+ $this->parameters->add($this->structure->dparameters);
+ }
+
+ // When the message is not multipart and the body is the attachment content
+ // Prevents infinite recursion
+ if (self::isAttachment($this->structure) && !$this instanceof Attachment) {
+ $this->parts[] = new Attachment($this->resource, $this->getNumber(), '1', $this->structure);
+ }
+
+ if (isset($this->structure->parts)) {
+ $parts = $this->structure->parts;
+ // https://secure.php.net/manual/en/function.imap-fetchbody.php#89002
+ if ($this instanceof Attachment && $this->isEmbeddedMessage() && 1 === \count($parts) && \TYPEMULTIPART === $parts[0]->type) {
+ $parts = $parts[0]->parts;
+ }
+ foreach ($parts as $key => $partStructure) {
+ $partNumber = (!$this instanceof Message) ? $this->partNumber . '.' : '';
+ $partNumber .= (string) ($key + 1);
+
+ $newPartClass = self::isAttachment($partStructure)
+ ? Attachment::class
+ : SimplePart::class
+ ;
+
+ $this->parts[] = new $newPartClass($this->resource, $this->getNumber(), $partNumber, $partStructure);
+ }
+ }
+ }
+
+ /**
+ * Check if the given part is an attachment.
+ */
+ private static function isAttachment(\stdClass $part): bool
+ {
+ if (isset(self::$typesMap[$part->type]) && self::TYPE_MULTIPART === self::$typesMap[$part->type]) {
+ return false;
+ }
+
+ // Attachment with correct Content-Disposition header
+ if ($part->ifdisposition) {
+ if ('attachment' === \strtolower($part->disposition)) {
+ return true;
+ }
+
+ if (
+ 'inline' === \strtolower($part->disposition)
+ && self::SUBTYPE_PLAIN !== \strtoupper($part->subtype)
+ && self::SUBTYPE_HTML !== \strtoupper($part->subtype)
+ ) {
+ return true;
+ }
+ }
+
+ // Attachment without Content-Disposition header
+ if ($part->ifparameters) {
+ foreach ($part->parameters as $parameter) {
+ if (isset(self::$attachmentKeys[\strtolower($parameter->attribute)])) {
+ return true;
+ }
+ }
+ }
+
+ /*
+ if ($part->ifdparameters) {
+ foreach ($part->dparameters as $parameter) {
+ if (isset(self::$attachmentKeys[\strtolower($parameter->attribute)])) {
+ return true;
+ }
+ }
+ }
+ */
+
+ if (self::SUBTYPE_RFC822 === \strtoupper($part->subtype)) {
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Attachment.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Attachment.php
new file mode 100644
index 0000000..bd76769
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Attachment.php
@@ -0,0 +1,63 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+use Ddeboer\Imap\Exception\NotEmbeddedMessageException;
+
+/**
+ * An e-mail attachment.
+ */
+final class Attachment extends AbstractPart implements AttachmentInterface
+{
+ /**
+ * Get attachment filename.
+ */
+ public function getFilename(): ?string
+ {
+ return $this->getParameters()->get('filename')
+ ?: $this->getParameters()->get('name');
+ }
+
+ /**
+ * Get attachment file size.
+ *
+ * @return null|int Number of bytes
+ */
+ public function getSize()
+ {
+ $size = $this->getParameters()->get('size');
+ if (\is_numeric($size)) {
+ $size = (int) $size;
+ }
+
+ return $size;
+ }
+
+ /**
+ * Is this attachment also an Embedded Message?
+ */
+ public function isEmbeddedMessage(): bool
+ {
+ return self::TYPE_MESSAGE === $this->getType();
+ }
+
+ /**
+ * Return embedded message.
+ *
+ * @throws NotEmbeddedMessageException
+ */
+ public function getEmbeddedMessage(): EmbeddedMessageInterface
+ {
+ if (!$this->isEmbeddedMessage()) {
+ throw new NotEmbeddedMessageException(\sprintf(
+ 'Attachment "%s" in message "%s" is not embedded message',
+ $this->getPartNumber(),
+ $this->getNumber()
+ ));
+ }
+
+ return new EmbeddedMessage($this->resource, $this->getNumber(), $this->getPartNumber(), $this->getStructure()->parts[0]);
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AttachmentInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AttachmentInterface.php
new file mode 100644
index 0000000..0d20f44
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/AttachmentInterface.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+/**
+ * An e-mail attachment.
+ */
+interface AttachmentInterface extends PartInterface
+{
+ /**
+ * Get attachment filename.
+ */
+ public function getFilename(): ?string;
+
+ /**
+ * Get attachment file size.
+ *
+ * @return null|int Number of bytes
+ */
+ public function getSize();
+
+ /**
+ * Is this attachment also an Embedded Message?
+ */
+ public function isEmbeddedMessage(): bool;
+
+ /**
+ * Return embedded message.
+ */
+ public function getEmbeddedMessage(): EmbeddedMessageInterface;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/BasicMessageInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/BasicMessageInterface.php
new file mode 100644
index 0000000..20e6b1a
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/BasicMessageInterface.php
@@ -0,0 +1,130 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+interface BasicMessageInterface extends PartInterface
+{
+ /**
+ * Get raw message headers.
+ */
+ public function getRawHeaders(): string;
+
+ /**
+ * Get the raw message, including all headers, parts, etc. unencoded and unparsed.
+ *
+ * @return string the raw message
+ */
+ public function getRawMessage(): string;
+
+ /**
+ * Get message headers.
+ */
+ public function getHeaders(): Headers;
+
+ /**
+ * Get message id.
+ *
+ * A unique message id in the form <...>
+ */
+ public function getId(): ?string;
+
+ /**
+ * Get message sender (from headers).
+ */
+ public function getFrom(): ?EmailAddress;
+
+ /**
+ * Get To recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no To: recipients
+ */
+ public function getTo(): array;
+
+ /**
+ * Get Cc recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no CC: recipients
+ */
+ public function getCc(): array;
+
+ /**
+ * Get Bcc recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no BCC: recipients
+ */
+ public function getBcc(): array;
+
+ /**
+ * Get Reply-To recipients.
+ *
+ * @return EmailAddress[] Empty array in case message has no Reply-To: recipients
+ */
+ public function getReplyTo(): array;
+
+ /**
+ * Get Sender.
+ *
+ * @return EmailAddress[] Empty array in case message has no Sender: recipients
+ */
+ public function getSender(): array;
+
+ /**
+ * Get Return-Path.
+ *
+ * @return EmailAddress[] Empty array in case message has no Return-Path: recipients
+ */
+ public function getReturnPath(): array;
+
+ /**
+ * Get date (from headers).
+ */
+ public function getDate(): ?\DateTimeImmutable;
+
+ /**
+ * Get message size (from headers).
+ *
+ * @return null|int|string
+ */
+ public function getSize();
+
+ /**
+ * Get message subject (from headers).
+ */
+ public function getSubject(): ?string;
+
+ /**
+ * Get message In-Reply-To (from headers).
+ */
+ public function getInReplyTo(): array;
+
+ /**
+ * Get message References (from headers).
+ */
+ public function getReferences(): array;
+
+ /**
+ * Get body HTML.
+ *
+ * @return null|string Null if message has no HTML message part
+ */
+ public function getBodyHtml(): ?string;
+
+ /**
+ * Get body text.
+ */
+ public function getBodyText(): ?string;
+
+ /**
+ * Get attachments (if any) linked to this e-mail.
+ *
+ * @return AttachmentInterface[]
+ */
+ public function getAttachments(): array;
+
+ /**
+ * Does this message have attachments?
+ */
+ public function hasAttachments(): bool;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmailAddress.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmailAddress.php
new file mode 100644
index 0000000..b88e0f9
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmailAddress.php
@@ -0,0 +1,84 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+/**
+ * An e-mail address.
+ */
+final class EmailAddress
+{
+ /**
+ * @var string
+ */
+ private $mailbox;
+
+ /**
+ * @var null|string
+ */
+ private $hostname;
+
+ /**
+ * @var null|string
+ */
+ private $name;
+
+ /**
+ * @var null|string
+ */
+ private $address;
+
+ public function __construct(string $mailbox, string $hostname = null, string $name = null)
+ {
+ $this->mailbox = $mailbox;
+ $this->hostname = $hostname;
+ $this->name = $name;
+
+ if (null !== $hostname) {
+ $this->address = $mailbox . '@' . $hostname;
+ }
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getAddress()
+ {
+ return $this->address;
+ }
+
+ /**
+ * Returns address with person name.
+ */
+ public function getFullAddress(): string
+ {
+ $address = \sprintf('%s@%s', $this->mailbox, $this->hostname);
+ if (null !== $this->name) {
+ $address = \sprintf('"%s" <%s>', \addcslashes($this->name, '"'), $address);
+ }
+
+ return $address;
+ }
+
+ public function getMailbox(): string
+ {
+ return $this->mailbox;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getHostname()
+ {
+ return $this->hostname;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getName()
+ {
+ return $this->name;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessage.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessage.php
new file mode 100644
index 0000000..243cff6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessage.php
@@ -0,0 +1,75 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+final class EmbeddedMessage extends AbstractMessage implements EmbeddedMessageInterface
+{
+ /**
+ * @var null|Headers
+ */
+ private $headers;
+
+ /**
+ * @var null|string
+ */
+ private $rawHeaders;
+
+ /**
+ * @var null|string
+ */
+ private $rawMessage;
+
+ /**
+ * Get message headers.
+ */
+ public function getHeaders(): Headers
+ {
+ if (null === $this->headers) {
+ $this->headers = new Headers(\imap_rfc822_parse_headers($this->getRawHeaders()));
+ }
+
+ return $this->headers;
+ }
+
+ /**
+ * Get raw message headers.
+ */
+ public function getRawHeaders(): string
+ {
+ if (null === $this->rawHeaders) {
+ $rawHeaders = \explode("\r\n\r\n", $this->getRawMessage(), 2);
+ $this->rawHeaders = \current($rawHeaders);
+ }
+
+ return $this->rawHeaders;
+ }
+
+ /**
+ * Get the raw message, including all headers, parts, etc. unencoded and unparsed.
+ *
+ * @return string the raw message
+ */
+ public function getRawMessage(): string
+ {
+ if (null === $this->rawMessage) {
+ $this->rawMessage = $this->doGetContent($this->getPartNumber());
+ }
+
+ return $this->rawMessage;
+ }
+
+ /**
+ * Get content part number.
+ */
+ protected function getContentPartNumber(): string
+ {
+ $partNumber = $this->getPartNumber();
+ if (0 === \count($this->getParts())) {
+ $partNumber .= '.1';
+ }
+
+ return $partNumber;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessageInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessageInterface.php
new file mode 100644
index 0000000..c685edf
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/EmbeddedMessageInterface.php
@@ -0,0 +1,9 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+interface EmbeddedMessageInterface extends BasicMessageInterface
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Headers.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Headers.php
new file mode 100644
index 0000000..f76fec3
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Headers.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+/**
+ * Collection of message headers.
+ */
+final class Headers extends Parameters
+{
+ /**
+ * Constructor.
+ */
+ public function __construct(\stdClass $headers)
+ {
+ parent::__construct();
+
+ // Store all headers as lowercase
+ $headers = \array_change_key_case((array) $headers);
+
+ foreach ($headers as $key => $value) {
+ $this[$key] = $this->parseHeader($key, $value);
+ }
+ }
+
+ /**
+ * Get header.
+ *
+ * @return mixed
+ */
+ public function get(string $key)
+ {
+ return parent::get(\strtolower($key));
+ }
+
+ /**
+ * Parse header.
+ *
+ * @param mixed $value
+ *
+ * @return mixed
+ */
+ private function parseHeader(string $key, $value)
+ {
+ switch ($key) {
+ case 'msgno':
+ return (int) $value;
+ case 'from':
+ case 'to':
+ case 'cc':
+ case 'bcc':
+ case 'reply_to':
+ case 'sender':
+ case 'return_path':
+ /** @var \stdClass $address */
+ foreach ($value as $address) {
+ if (isset($address->mailbox)) {
+ $address->host = $address->host ?? null;
+ $address->personal = isset($address->personal) ? $this->decode($address->personal) : null;
+ }
+ }
+
+ return $value;
+ case 'date':
+ case 'subject':
+ return $this->decode($value);
+ }
+
+ return $value;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Parameters.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Parameters.php
new file mode 100644
index 0000000..2f7d8a1
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Parameters.php
@@ -0,0 +1,74 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+class Parameters extends \ArrayIterator
+{
+ /**
+ * @var array
+ */
+ private static $attachmentCustomKeys = [
+ 'name*' => 'name',
+ 'filename*' => 'filename',
+ ];
+
+ public function __construct(array $parameters = [])
+ {
+ parent::__construct();
+
+ $this->add($parameters);
+ }
+
+ public function add(array $parameters = []): void
+ {
+ foreach ($parameters as $parameter) {
+ $key = \strtolower($parameter->attribute);
+ if (isset(self::$attachmentCustomKeys[$key])) {
+ $key = self::$attachmentCustomKeys[$key];
+ }
+ $value = $this->decode($parameter->value);
+ $this[$key] = $value;
+ }
+ }
+
+ /**
+ * @return mixed
+ */
+ public function get(string $key)
+ {
+ return $this[$key] ?? null;
+ }
+
+ /**
+ * Decode value.
+ */
+ final protected function decode(string $value): string
+ {
+ $parts = \imap_mime_header_decode($value);
+ if (!\is_array($parts)) {
+ return $value;
+ }
+
+ $decoded = '';
+ foreach ($parts as $part) {
+ $text = $part->text;
+ if ('default' !== $part->charset) {
+ $text = Transcoder::decode($text, $part->charset);
+ }
+ // RFC2231
+ if (1 === \preg_match('/^(?<encoding>[^\']+)\'[^\']*?\'(?<urltext>.+)$/', $text, $matches)) {
+ $hasInvalidChars = 1 === \preg_match('#[^%a-zA-Z0-9\-_\.\+]#', $matches['urltext']);
+ $hasEscapedChars = 1 === \preg_match('#%[a-zA-Z0-9]{2}#', $matches['urltext']);
+ if (!$hasInvalidChars && $hasEscapedChars) {
+ $text = Transcoder::decode(\urldecode($matches['urltext']), $matches['encoding']);
+ }
+ }
+
+ $decoded .= $text;
+ }
+
+ return $decoded;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/PartInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/PartInterface.php
new file mode 100644
index 0000000..70a83f2
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/PartInterface.php
@@ -0,0 +1,112 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+/**
+ * A message part.
+ */
+interface PartInterface extends \RecursiveIterator
+{
+ public const TYPE_TEXT = 'text';
+ public const TYPE_MULTIPART = 'multipart';
+ public const TYPE_MESSAGE = 'message';
+ public const TYPE_APPLICATION = 'application';
+ public const TYPE_AUDIO = 'audio';
+ public const TYPE_IMAGE = 'image';
+ public const TYPE_VIDEO = 'video';
+ public const TYPE_MODEL = 'model';
+ public const TYPE_OTHER = 'other';
+ public const TYPE_UNKNOWN = 'unknown';
+
+ public const ENCODING_7BIT = '7bit';
+ public const ENCODING_8BIT = '8bit';
+ public const ENCODING_BINARY = 'binary';
+ public const ENCODING_BASE64 = 'base64';
+ public const ENCODING_QUOTED_PRINTABLE = 'quoted-printable';
+ public const ENCODING_UNKNOWN = 'unknown';
+
+ public const SUBTYPE_PLAIN = 'PLAIN';
+ public const SUBTYPE_HTML = 'HTML';
+ public const SUBTYPE_RFC822 = 'RFC822';
+
+ /**
+ * Get message number (from headers).
+ */
+ public function getNumber(): int;
+
+ /**
+ * Part charset.
+ */
+ public function getCharset(): ?string;
+
+ /**
+ * Part type.
+ */
+ public function getType(): ?string;
+
+ /**
+ * Part subtype.
+ */
+ public function getSubtype(): ?string;
+
+ /**
+ * Part encoding.
+ */
+ public function getEncoding(): ?string;
+
+ /**
+ * Part disposition.
+ */
+ public function getDisposition(): ?string;
+
+ /**
+ * Part description.
+ */
+ public function getDescription(): ?string;
+
+ /**
+ * Part bytes.
+ *
+ * @return null|int|string
+ */
+ public function getBytes();
+
+ /**
+ * Part lines.
+ */
+ public function getLines(): ?string;
+
+ /**
+ * Part parameters.
+ */
+ public function getParameters(): Parameters;
+
+ /**
+ * Get raw part content.
+ */
+ public function getContent(): string;
+
+ /**
+ * Get decoded part content.
+ */
+ public function getDecodedContent(): string;
+
+ /**
+ * Part structure.
+ */
+ public function getStructure(): \stdClass;
+
+ /**
+ * Get part number.
+ */
+ public function getPartNumber(): string;
+
+ /**
+ * Get an array of all parts for this message.
+ *
+ * @return PartInterface[]
+ */
+ public function getParts(): array;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/SimplePart.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/SimplePart.php
new file mode 100644
index 0000000..3c6188d
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/SimplePart.php
@@ -0,0 +1,12 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+/**
+ * A message part.
+ */
+final class SimplePart extends AbstractPart
+{
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Transcoder.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Transcoder.php
new file mode 100644
index 0000000..15dfb87
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Message/Transcoder.php
@@ -0,0 +1,328 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Message;
+
+use Ddeboer\Imap\Exception\UnsupportedCharsetException;
+
+final class Transcoder
+{
+ /**
+ * @var array
+ *
+ * @see https://encoding.spec.whatwg.org/#encodings
+ * @see https://dxr.mozilla.org/mozilla-central/source/dom/encoding/labelsencodings.properties
+ * @see https://dxr.mozilla.org/mozilla1.9.1/source/intl/uconv/src/charsetalias.properties
+ * @see https://msdn.microsoft.com/en-us/library/cc194829.aspx
+ */
+ private static $charsetAliases = [
+ '128' => 'Shift_JIS',
+ '129' => 'EUC-KR',
+ '134' => 'GB2312',
+ '136' => 'Big5',
+ '161' => 'windows-1253',
+ '162' => 'windows-1254',
+ '177' => 'windows-1255',
+ '178' => 'windows-1256',
+ '186' => 'windows-1257',
+ '204' => 'windows-1251',
+ '222' => 'windows-874',
+ '238' => 'windows-1250',
+ '5601' => 'EUC-KR',
+ '646' => 'us-ascii',
+ '850' => 'IBM850',
+ '852' => 'IBM852',
+ '855' => 'IBM855',
+ '857' => 'IBM857',
+ '862' => 'IBM862',
+ '864' => 'IBM864',
+ '864i' => 'IBM864i',
+ '866' => 'IBM866',
+ 'ansi-1251' => 'windows-1251',
+ 'ansi_x3.4-1968' => 'us-ascii',
+ 'arabic' => 'ISO-8859-6',
+ 'ascii' => 'us-ascii',
+ 'asmo-708' => 'ISO-8859-6',
+ 'big5-hkscs' => 'Big5',
+ 'chinese' => 'GB2312',
+ 'cn-big5' => 'Big5',
+ 'cns11643' => 'x-euc-tw',
+ 'cp-866' => 'IBM866',
+ 'cp1250' => 'windows-1250',
+ 'cp1251' => 'windows-1251',
+ 'cp1252' => 'windows-1252',
+ 'cp1253' => 'windows-1253',
+ 'cp1254' => 'windows-1254',
+ 'cp1255' => 'windows-1255',
+ 'cp1256' => 'windows-1256',
+ 'cp1257' => 'windows-1257',
+ 'cp1258' => 'windows-1258',
+ 'cp819' => 'ISO-8859-1',
+ 'cp850' => 'IBM850',
+ 'cp852' => 'IBM852',
+ 'cp855' => 'IBM855',
+ 'cp857' => 'IBM857',
+ 'cp862' => 'IBM862',
+ 'cp864' => 'IBM864',
+ 'cp864i' => 'IBM864i',
+ 'cp866' => 'IBM866',
+ 'cp932' => 'Shift_JIS',
+ 'csbig5' => 'Big5',
+ 'cseucjpkdfmtjapanese' => 'EUC-JP',
+ 'cseuckr' => 'EUC-KR',
+ 'cseucpkdfmtjapanese' => 'EUC-JP',
+ 'csgb2312' => 'GB2312',
+ 'csibm850' => 'IBM850',
+ 'csibm852' => 'IBM852',
+ 'csibm855' => 'IBM855',
+ 'csibm857' => 'IBM857',
+ 'csibm862' => 'IBM862',
+ 'csibm864' => 'IBM864',
+ 'csibm864i' => 'IBM864i',
+ 'csibm866' => 'IBM866',
+ 'csiso103t618bit' => 'T.61-8bit',
+ 'csiso111ecmacyrillic' => 'ISO-IR-111',
+ 'csiso2022jp' => 'ISO-2022-JP',
+ 'csiso2022jp2' => 'ISO-2022-JP',
+ 'csiso2022kr' => 'ISO-2022-KR',
+ 'csiso58gb231280' => 'GB2312',
+ 'csiso88596e' => 'ISO-8859-6-E',
+ 'csiso88596i' => 'ISO-8859-6-I',
+ 'csiso88598e' => 'ISO-8859-8-E',
+ 'csiso88598i' => 'ISO-8859-8-I',
+ 'csisolatin1' => 'ISO-8859-1',
+ 'csisolatin2' => 'ISO-8859-2',
+ 'csisolatin3' => 'ISO-8859-3',
+ 'csisolatin4' => 'ISO-8859-4',
+ 'csisolatin5' => 'ISO-8859-9',
+ 'csisolatin6' => 'ISO-8859-10',
+ 'csisolatin9' => 'ISO-8859-15',
+ 'csisolatinarabic' => 'ISO-8859-6',
+ 'csisolatincyrillic' => 'ISO-8859-5',
+ 'csisolatingreek' => 'ISO-8859-7',
+ 'csisolatinhebrew' => 'ISO-8859-8',
+ 'cskoi8r' => 'KOI8-R',
+ 'csksc56011987' => 'EUC-KR',
+ 'csmacintosh' => 'x-mac-roman',
+ 'csshiftjis' => 'Shift_JIS',
+ 'csueckr' => 'EUC-KR',
+ 'csunicode' => 'UTF-16BE',
+ 'csunicode11' => 'UTF-16BE',
+ 'csunicode11utf7' => 'UTF-7',
+ 'csunicodeascii' => 'UTF-16BE',
+ 'csunicodelatin1' => 'UTF-16BE',
+ 'csviqr' => 'VIQR',
+ 'csviscii' => 'VISCII',
+ 'cyrillic' => 'ISO-8859-5',
+ 'dos-874' => 'windows-874',
+ 'ecma-114' => 'ISO-8859-6',
+ 'ecma-118' => 'ISO-8859-7',
+ 'ecma-cyrillic' => 'ISO-IR-111',
+ 'elot_928' => 'ISO-8859-7',
+ 'gb_2312' => 'GB2312',
+ 'gb_2312-80' => 'GB2312',
+ 'greek' => 'ISO-8859-7',
+ 'greek8' => 'ISO-8859-7',
+ 'hebrew' => 'ISO-8859-8',
+ 'ibm-864' => 'IBM864',
+ 'ibm-864i' => 'IBM864i',
+ 'ibm819' => 'ISO-8859-1',
+ 'ibm874' => 'windows-874',
+ 'iso-10646' => 'UTF-16BE',
+ 'iso-10646-j-1' => 'UTF-16BE',
+ 'iso-10646-ucs-2' => 'UTF-16BE',
+ 'iso-10646-ucs-4' => 'UTF-32BE',
+ 'iso-10646-ucs-basic' => 'UTF-16BE',
+ 'iso-10646-unicode-latin1' => 'UTF-16BE',
+ 'iso-2022-cn-ext' => 'ISO-2022-CN',
+ 'iso-2022-jp-2' => 'ISO-2022-JP',
+ 'iso-8859-8i' => 'ISO-8859-8-I',
+ 'iso-ir-100' => 'ISO-8859-1',
+ 'iso-ir-101' => 'ISO-8859-2',
+ 'iso-ir-103' => 'T.61-8bit',
+ 'iso-ir-109' => 'ISO-8859-3',
+ 'iso-ir-110' => 'ISO-8859-4',
+ 'iso-ir-126' => 'ISO-8859-7',
+ 'iso-ir-127' => 'ISO-8859-6',
+ 'iso-ir-138' => 'ISO-8859-8',
+ 'iso-ir-144' => 'ISO-8859-5',
+ 'iso-ir-148' => 'ISO-8859-9',
+ 'iso-ir-149' => 'EUC-KR',
+ 'iso-ir-157' => 'ISO-8859-10',
+ 'iso-ir-58' => 'GB2312',
+ 'iso8859-1' => 'ISO-8859-1',
+ 'iso8859-10' => 'ISO-8859-10',
+ 'iso8859-11' => 'ISO-8859-11',
+ 'iso8859-13' => 'ISO-8859-13',
+ 'iso8859-14' => 'ISO-8859-14',
+ 'iso8859-15' => 'ISO-8859-15',
+ 'iso8859-2' => 'ISO-8859-2',
+ 'iso8859-3' => 'ISO-8859-3',
+ 'iso8859-4' => 'ISO-8859-4',
+ 'iso8859-5' => 'ISO-8859-5',
+ 'iso8859-6' => 'ISO-8859-6',
+ 'iso8859-7' => 'ISO-8859-7',
+ 'iso8859-8' => 'ISO-8859-8',
+ 'iso8859-9' => 'ISO-8859-9',
+ 'iso88591' => 'ISO-8859-1',
+ 'iso885910' => 'ISO-8859-10',
+ 'iso885911' => 'ISO-8859-11',
+ 'iso885912' => 'ISO-8859-12',
+ 'iso885913' => 'ISO-8859-13',
+ 'iso885914' => 'ISO-8859-14',
+ 'iso885915' => 'ISO-8859-15',
+ 'iso88592' => 'ISO-8859-2',
+ 'iso88593' => 'ISO-8859-3',
+ 'iso88594' => 'ISO-8859-4',
+ 'iso88595' => 'ISO-8859-5',
+ 'iso88596' => 'ISO-8859-6',
+ 'iso88597' => 'ISO-8859-7',
+ 'iso88598' => 'ISO-8859-8',
+ 'iso88599' => 'ISO-8859-9',
+ 'iso_8859-1' => 'ISO-8859-1',
+ 'iso_8859-15' => 'ISO-8859-15',
+ 'iso_8859-1:1987' => 'ISO-8859-1',
+ 'iso_8859-2' => 'ISO-8859-2',
+ 'iso_8859-2:1987' => 'ISO-8859-2',
+ 'iso_8859-3' => 'ISO-8859-3',
+ 'iso_8859-3:1988' => 'ISO-8859-3',
+ 'iso_8859-4' => 'ISO-8859-4',
+ 'iso_8859-4:1988' => 'ISO-8859-4',
+ 'iso_8859-5' => 'ISO-8859-5',
+ 'iso_8859-5:1988' => 'ISO-8859-5',
+ 'iso_8859-6' => 'ISO-8859-6',
+ 'iso_8859-6:1987' => 'ISO-8859-6',
+ 'iso_8859-7' => 'ISO-8859-7',
+ 'iso_8859-7:1987' => 'ISO-8859-7',
+ 'iso_8859-8' => 'ISO-8859-8',
+ 'iso_8859-8:1988' => 'ISO-8859-8',
+ 'iso_8859-9' => 'ISO-8859-9',
+ 'iso_8859-9:1989' => 'ISO-8859-9',
+ 'koi' => 'KOI8-R',
+ 'koi8' => 'KOI8-R',
+ 'koi8-ru' => 'KOI8-U',
+ 'koi8_r' => 'KOI8-R',
+ 'korean' => 'EUC-KR',
+ 'ks_c_5601-1987' => 'EUC-KR',
+ 'ks_c_5601-1989' => 'EUC-KR',
+ 'ksc5601' => 'EUC-KR',
+ 'ksc_5601' => 'EUC-KR',
+ 'l1' => 'ISO-8859-1',
+ 'l2' => 'ISO-8859-2',
+ 'l3' => 'ISO-8859-3',
+ 'l4' => 'ISO-8859-4',
+ 'l5' => 'ISO-8859-9',
+ 'l6' => 'ISO-8859-10',
+ 'l9' => 'ISO-8859-15',
+ 'latin1' => 'ISO-8859-1',
+ 'latin2' => 'ISO-8859-2',
+ 'latin3' => 'ISO-8859-3',
+ 'latin4' => 'ISO-8859-4',
+ 'latin5' => 'ISO-8859-9',
+ 'latin6' => 'ISO-8859-10',
+ 'logical' => 'ISO-8859-8-I',
+ 'mac' => 'x-mac-roman',
+ 'macintosh' => 'x-mac-roman',
+ 'ms932' => 'Shift_JIS',
+ 'ms_kanji' => 'Shift_JIS',
+ 'shift-jis' => 'Shift_JIS',
+ 'sjis' => 'Shift_JIS',
+ 'sun_eu_greek' => 'ISO-8859-7',
+ 't.61' => 'T.61-8bit',
+ 'tis620' => 'TIS-620',
+ 'unicode-1-1-utf-7' => 'UTF-7',
+ 'unicode-1-1-utf-8' => 'UTF-8',
+ 'unicode-2-0-utf-7' => 'UTF-7',
+ 'visual' => 'ISO-8859-8',
+ 'windows-31j' => 'Shift_JIS',
+ 'windows-949' => 'EUC-KR',
+ 'x-cp1250' => 'windows-1250',
+ 'x-cp1251' => 'windows-1251',
+ 'x-cp1252' => 'windows-1252',
+ 'x-cp1253' => 'windows-1253',
+ 'x-cp1254' => 'windows-1254',
+ 'x-cp1255' => 'windows-1255',
+ 'x-cp1256' => 'windows-1256',
+ 'x-cp1257' => 'windows-1257',
+ 'x-cp1258' => 'windows-1258',
+ 'x-euc-jp' => 'EUC-JP',
+ 'x-gbk' => 'gbk',
+ 'x-iso-10646-ucs-2-be' => 'UTF-16BE',
+ 'x-iso-10646-ucs-2-le' => 'UTF-16LE',
+ 'x-iso-10646-ucs-4-be' => 'UTF-32BE',
+ 'x-iso-10646-ucs-4-le' => 'UTF-32LE',
+ 'x-sjis' => 'Shift_JIS',
+ 'x-unicode-2-0-utf-7' => 'UTF-7',
+ 'x-x-big5' => 'Big5',
+ 'zh_cn.euc' => 'GB2312',
+ 'zh_tw-big5' => 'Big5',
+ 'zh_tw-euc' => 'x-euc-tw',
+ ];
+
+ /**
+ * Decode text to UTF-8.
+ *
+ * @param string $text Text to decode
+ * @param string $fromCharset Original charset
+ */
+ public static function decode(string $text, string $fromCharset): string
+ {
+ static $utf8Aliases = [
+ 'unicode-1-1-utf-8' => true,
+ 'utf8' => true,
+ 'utf-8' => true,
+ 'UTF8' => true,
+ 'UTF-8' => true,
+ ];
+
+ if (isset($utf8Aliases[$fromCharset])) {
+ return $text;
+ }
+
+ $originalFromCharset = $fromCharset;
+ $lowercaseFromCharset = \strtolower($fromCharset);
+ if (isset(self::$charsetAliases[$lowercaseFromCharset])) {
+ $fromCharset = self::$charsetAliases[$lowercaseFromCharset];
+ }
+
+ \set_error_handler(static function (): bool {
+ return true;
+ });
+
+ $iconvDecodedText = \iconv($fromCharset, 'UTF-8', $text);
+ if (false === $iconvDecodedText) {
+ $iconvDecodedText = \iconv($originalFromCharset, 'UTF-8', $text);
+ }
+
+ \restore_error_handler();
+
+ if (false !== $iconvDecodedText) {
+ return $iconvDecodedText;
+ }
+
+ $errorMessage = null;
+ $errorNumber = 0;
+ \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
+ $errorMessage = $message;
+ $errorNumber = $nr;
+
+ return true;
+ });
+
+ $decodedText = \mb_convert_encoding($text, 'UTF-8', $fromCharset);
+
+ \restore_error_handler();
+
+ if (null !== $errorMessage) {
+ throw new UnsupportedCharsetException(\sprintf(
+ 'Unsupported charset "%s"%s: %s',
+ $originalFromCharset,
+ ($fromCharset !== $originalFromCharset) ? \sprintf(' (alias found: "%s")', $fromCharset) : '',
+ $errorMessage
+ ), $errorNumber);
+ }
+
+ return $decodedText;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageInterface.php
new file mode 100644
index 0000000..eda8ab6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageInterface.php
@@ -0,0 +1,97 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+/**
+ * An IMAP message (e-mail).
+ */
+interface MessageInterface extends Message\BasicMessageInterface
+{
+ /**
+ * Get raw part content.
+ */
+ public function getContent(): string;
+
+ /**
+ * Get message recent flag value (from headers).
+ */
+ public function isRecent(): ?string;
+
+ /**
+ * Get message unseen flag value (from headers).
+ */
+ public function isUnseen(): bool;
+
+ /**
+ * Get message flagged flag value (from headers).
+ */
+ public function isFlagged(): bool;
+
+ /**
+ * Get message answered flag value (from headers).
+ */
+ public function isAnswered(): bool;
+
+ /**
+ * Get message deleted flag value (from headers).
+ */
+ public function isDeleted(): bool;
+
+ /**
+ * Get message draft flag value (from headers).
+ */
+ public function isDraft(): bool;
+
+ /**
+ * Has the message been marked as read?
+ */
+ public function isSeen(): bool;
+
+ /**
+ * Mark message as seen.
+ *
+ * @deprecated since version 1.1, to be removed in 2.0
+ */
+ public function maskAsSeen(): bool;
+
+ /**
+ * Mark message as seen.
+ */
+ public function markAsSeen(): bool;
+
+ /**
+ * Move message to another mailbox.
+ */
+ public function copy(MailboxInterface $mailbox): void;
+
+ /**
+ * Move message to another mailbox.
+ */
+ public function move(MailboxInterface $mailbox): void;
+
+ /**
+ * Delete message.
+ */
+ public function delete(): void;
+
+ /**
+ * Undelete message.
+ */
+ public function undelete(): void;
+
+ /**
+ * Set Flag Message.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ */
+ public function setFlag(string $flag): bool;
+
+ /**
+ * Clear Flag Message.
+ *
+ * @param string $flag \Seen, \Answered, \Flagged, \Deleted, and \Draft
+ */
+ public function clearFlag(string $flag): bool;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIterator.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIterator.php
new file mode 100644
index 0000000..c617478
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIterator.php
@@ -0,0 +1,45 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+final class MessageIterator extends \ArrayIterator implements MessageIteratorInterface
+{
+ /**
+ * @var ImapResourceInterface
+ */
+ private $resource;
+
+ /**
+ * Constructor.
+ *
+ * @param ImapResourceInterface $resource IMAP resource
+ * @param array $messageNumbers Array of message numbers
+ */
+ public function __construct(ImapResourceInterface $resource, array $messageNumbers)
+ {
+ $this->resource = $resource;
+
+ parent::__construct($messageNumbers);
+ }
+
+ /**
+ * Get current message.
+ */
+ public function current(): MessageInterface
+ {
+ $current = parent::current();
+ if (!\is_int($current)) {
+ throw new Exception\OutOfBoundsException(\sprintf(
+ 'The current value "%s" isn\'t an integer and doesn\'t represent a message;'
+ . ' try to cycle this "%s" with a native php function like foreach or with the method getArrayCopy(),'
+ . ' or check it by calling the methods valid().',
+ \is_object($current) ? \get_class($current) : \gettype($current),
+ static::class
+ ));
+ }
+
+ return new Message($this->resource, $current);
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIteratorInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIteratorInterface.php
new file mode 100644
index 0000000..a9d7988
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/MessageIteratorInterface.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+interface MessageIteratorInterface extends \Iterator
+{
+ /**
+ * Get current message.
+ */
+ public function current(): MessageInterface;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractDate.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractDate.php
new file mode 100644
index 0000000..cdd7312
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractDate.php
@@ -0,0 +1,51 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search;
+
+use DateTimeInterface;
+
+/**
+ * Represents a date condition.
+ */
+abstract class AbstractDate implements ConditionInterface
+{
+ /**
+ * Format for dates to be sent to the IMAP server.
+ *
+ * @var string
+ */
+ private $dateFormat;
+
+ /**
+ * The date to be used for the condition.
+ *
+ * @var DateTimeInterface
+ */
+ private $date;
+
+ /**
+ * Constructor.
+ *
+ * @param DateTimeInterface $date optional date for the condition
+ */
+ public function __construct(DateTimeInterface $date, string $dateFormat = 'j-M-Y')
+ {
+ $this->date = $date;
+ $this->dateFormat = $dateFormat;
+ }
+
+ /**
+ * Converts the condition to a string that can be sent to the IMAP server.
+ */
+ final public function toString(): string
+ {
+ return \sprintf('%s "%s"', $this->getKeyword(), $this->date->format($this->dateFormat));
+ }
+
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ abstract protected function getKeyword(): string;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractText.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractText.php
new file mode 100644
index 0000000..69b2516
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/AbstractText.php
@@ -0,0 +1,42 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search;
+
+/**
+ * Represents a text based condition. Text based conditions use a contains
+ * restriction.
+ */
+abstract class AbstractText implements ConditionInterface
+{
+ /**
+ * Text to be used for the condition.
+ *
+ * @var string
+ */
+ private $text;
+
+ /**
+ * Constructor.
+ *
+ * @param string $text optional text for the condition
+ */
+ public function __construct(string $text)
+ {
+ $this->text = $text;
+ }
+
+ /**
+ * Converts the condition to a string that can be sent to the IMAP server.
+ */
+ final public function toString(): string
+ {
+ return \sprintf('%s "%s"', $this->getKeyword(), $this->text);
+ }
+
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ abstract protected function getKeyword(): string;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/ConditionInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/ConditionInterface.php
new file mode 100644
index 0000000..3aed28c
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/ConditionInterface.php
@@ -0,0 +1,16 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search;
+
+/**
+ * Represents a condition that can be used in a search expression.
+ */
+interface ConditionInterface
+{
+ /**
+ * Converts the condition to a string that can be sent to the IMAP server.
+ */
+ public function toString(): string;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Before.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Before.php
new file mode 100644
index 0000000..561f3f2
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Before.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Date;
+
+use Ddeboer\Imap\Search\AbstractDate;
+
+/**
+ * Represents a date before condition. Messages must have a date before the
+ * specified date in order to match the condition.
+ */
+final class Before extends AbstractDate
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'BEFORE';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/On.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/On.php
new file mode 100644
index 0000000..2f00dde
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/On.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Date;
+
+use Ddeboer\Imap\Search\AbstractDate;
+
+/**
+ * Represents a date on condition. Messages must have a date matching the
+ * specified date in order to match the condition.
+ */
+final class On extends AbstractDate
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'ON';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Since.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Since.php
new file mode 100644
index 0000000..31825f6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Date/Since.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Date;
+
+use Ddeboer\Imap\Search\AbstractDate;
+
+/**
+ * Represents a date after condition. Messages must have a date after the
+ * specified date in order to match the condition.
+ */
+final class Since extends AbstractDate
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'SINCE';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Bcc.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Bcc.php
new file mode 100644
index 0000000..d450973
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Bcc.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Email;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a "Bcc" email address condition. Messages must have been addressed
+ * to the specified recipient (along with any others) in order to match the
+ * condition.
+ */
+final class Bcc extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'BCC';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Cc.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Cc.php
new file mode 100644
index 0000000..72d0642
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/Cc.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Email;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a "Cc" email address condition. Messages must have been addressed
+ * to the specified recipient (along with any others) in order to match the
+ * condition.
+ */
+final class Cc extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'CC';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/From.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/From.php
new file mode 100644
index 0000000..f690121
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/From.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Email;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a "From" email address condition. Messages must have been sent
+ * from the specified email address in order to match the condition.
+ */
+final class From extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'FROM';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/To.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/To.php
new file mode 100644
index 0000000..5ff5c09
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Email/To.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Email;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a "To" email address condition. Messages must have been addressed
+ * to the specified recipient (along with any others) in order to match the
+ * condition.
+ */
+final class To extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'TO';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Answered.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Answered.php
new file mode 100644
index 0000000..fc671a3
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Answered.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an ANSWERED flag condition. Messages must have the \\ANSWERED flag
+ * set in order to match the condition.
+ */
+final class Answered implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'ANSWERED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Flagged.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Flagged.php
new file mode 100644
index 0000000..b1161cc
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Flagged.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents a FLAGGED flag condition. Messages must have the \\FLAGGED flag
+ * (i.e. urgent or important) set in order to match the condition.
+ */
+final class Flagged implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'FLAGGED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Recent.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Recent.php
new file mode 100644
index 0000000..ba0ba73
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Recent.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an RECENT flag condition. Messages must have the \\RECENT flag
+ * set in order to match the condition.
+ */
+final class Recent implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'RECENT';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Seen.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Seen.php
new file mode 100644
index 0000000..2a52a9d
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Seen.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an SEEN flag condition. Messages must have the \\SEEN flag
+ * set in order to match the condition.
+ */
+final class Seen implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'SEEN';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unanswered.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unanswered.php
new file mode 100644
index 0000000..e2c5717
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unanswered.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an UNANSWERED flag condition. Messages must not have the
+ * \\ANSWERED flag set in order to match the condition.
+ */
+final class Unanswered implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'UNANSWERED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unflagged.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unflagged.php
new file mode 100644
index 0000000..ca53c6d
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unflagged.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents a UNFLAGGED flag condition. Messages must no have the \\FLAGGED
+ * flag (i.e. urgent or important) set in order to match the condition.
+ */
+final class Unflagged implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'UNFLAGGED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unseen.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unseen.php
new file mode 100644
index 0000000..27db7d2
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Flag/Unseen.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Flag;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an UNSEEN flag condition. Messages must not have the \\SEEN flag
+ * set in order to match the condition.
+ */
+final class Unseen implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'UNSEEN';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/All.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/All.php
new file mode 100644
index 0000000..c867ec7
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/All.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\LogicalOperator;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an ALL operator. Messages must match all conditions following this
+ * operator in order to match the expression.
+ */
+final class All implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'ALL';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/OrConditions.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/OrConditions.php
new file mode 100644
index 0000000..bfebddd
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/LogicalOperator/OrConditions.php
@@ -0,0 +1,50 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\LogicalOperator;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an OR operator. Messages only need to match one of the conditions
+ * after this operator to match the expression.
+ */
+final class OrConditions implements ConditionInterface
+{
+ /**
+ * The conditions that together represent the expression.
+ *
+ * @var array
+ */
+ private $conditions = [];
+
+ public function __construct(array $conditions)
+ {
+ foreach ($conditions as $condition) {
+ $this->addCondition($condition);
+ }
+ }
+
+ /**
+ * Adds a new condition to the expression.
+ *
+ * @param ConditionInterface $condition the condition to be added
+ */
+ private function addCondition(ConditionInterface $condition)
+ {
+ $this->conditions[] = $condition;
+ }
+
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ $conditions = \array_map(static function (ConditionInterface $condition): string {
+ return $condition->toString();
+ }, $this->conditions);
+
+ return \sprintf('( %s )', \implode(' OR ', $conditions));
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/RawExpression.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/RawExpression.php
new file mode 100644
index 0000000..cc6a465
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/RawExpression.php
@@ -0,0 +1,31 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search;
+
+/**
+ * Represents a raw expression.
+ */
+final class RawExpression implements ConditionInterface
+{
+ /**
+ * Text to be used for the condition.
+ *
+ * @var string
+ */
+ private $expression;
+
+ /**
+ * @param string $expression text for the condition
+ */
+ public function __construct(string $expression)
+ {
+ $this->expression = $expression;
+ }
+
+ public function toString(): string
+ {
+ return $this->expression;
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Deleted.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Deleted.php
new file mode 100644
index 0000000..b74581e
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Deleted.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\State;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents a DELETED condition. Messages must have been marked for deletion
+ * but not yet expunged in order to match the condition.
+ */
+final class Deleted implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'DELETED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/NewMessage.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/NewMessage.php
new file mode 100644
index 0000000..98df4fb
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/NewMessage.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\State;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents a NEW condition. Only new messages will match this condition.
+ */
+final class NewMessage implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'NEW';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Old.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Old.php
new file mode 100644
index 0000000..396673f
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Old.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\State;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents an OLD condition. Only old messages will match this condition.
+ */
+final class Old implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'OLD';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Undeleted.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Undeleted.php
new file mode 100644
index 0000000..d6d2d36
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/State/Undeleted.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\State;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Represents a UNDELETED condition. Messages must not have been marked for
+ * deletion in order to match the condition.
+ */
+final class Undeleted implements ConditionInterface
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ public function toString(): string
+ {
+ return 'UNDELETED';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Body.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Body.php
new file mode 100644
index 0000000..6b6d531
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Body.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Text;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a body text contains condition. Messages must have a body
+ * containing the specified text in order to match the condition.
+ */
+final class Body extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'BODY';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Keyword.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Keyword.php
new file mode 100644
index 0000000..6bf1e11
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Keyword.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Text;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a keyword text contains condition. Messages must have a keyword
+ * matching the specified text in order to match the condition.
+ */
+final class Keyword extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'KEYWORD';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Subject.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Subject.php
new file mode 100644
index 0000000..6f5198f
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Subject.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Text;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a subject contains condition. Messages must have a subject
+ * containing the specified text in order to match the condition.
+ */
+final class Subject extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'SUBJECT';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Text.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Text.php
new file mode 100644
index 0000000..09c3017
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Text.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Text;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a message text contains condition. Messages must contain the
+ * specified text in order to match the condition.
+ */
+final class Text extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'TEXT';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Unkeyword.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Unkeyword.php
new file mode 100644
index 0000000..64a706d
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Search/Text/Unkeyword.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Search\Text;
+
+use Ddeboer\Imap\Search\AbstractText;
+
+/**
+ * Represents a keyword text does not contain condition. Messages must not have
+ * a keyword matching the specified text in order to match the condition.
+ */
+final class Unkeyword extends AbstractText
+{
+ /**
+ * Returns the keyword that the condition represents.
+ */
+ protected function getKeyword(): string
+ {
+ return 'UNKEYWORD';
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/SearchExpression.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/SearchExpression.php
new file mode 100644
index 0000000..e89d49e
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/SearchExpression.php
@@ -0,0 +1,44 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use Ddeboer\Imap\Search\ConditionInterface;
+
+/**
+ * Defines a search expression that can be used to look up email messages.
+ */
+final class SearchExpression implements ConditionInterface
+{
+ /**
+ * The conditions that together represent the expression.
+ *
+ * @var array
+ */
+ private $conditions = [];
+
+ /**
+ * Adds a new condition to the expression.
+ *
+ * @param ConditionInterface $condition the condition to be added
+ */
+ public function addCondition(ConditionInterface $condition): self
+ {
+ $this->conditions[] = $condition;
+
+ return $this;
+ }
+
+ /**
+ * Converts the expression to a string that can be sent to the IMAP server.
+ */
+ public function toString(): string
+ {
+ $conditions = \array_map(static function (ConditionInterface $condition): string {
+ return $condition->toString();
+ }, $this->conditions);
+
+ return \implode(' ', $conditions);
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Server.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Server.php
new file mode 100644
index 0000000..7412f00
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Server.php
@@ -0,0 +1,146 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+use Ddeboer\Imap\Exception\AuthenticationFailedException;
+use Ddeboer\Imap\Exception\ResourceCheckFailureException;
+
+/**
+ * An IMAP server.
+ */
+final class Server implements ServerInterface
+{
+ /**
+ * @var string Internet domain name or bracketed IP address of server
+ */
+ private $hostname;
+
+ /**
+ * @var string TCP port number
+ */
+ private $port;
+
+ /**
+ * @var string Optional flags
+ */
+ private $flags;
+
+ /**
+ * @var array
+ */
+ private $parameters;
+
+ /**
+ * @var int Connection options
+ */
+ private $options;
+
+ /**
+ * @var int Retries number
+ */
+ private $retries;
+
+ /**
+ * Constructor.
+ *
+ * @param string $hostname Internet domain name or bracketed IP address
+ * of server
+ * @param string $port TCP port number
+ * @param string $flags Optional flags
+ * @param array $parameters Connection parameters
+ * @param int $options Connection options
+ * @param int $retries Retries number
+ */
+ public function __construct(
+ string $hostname,
+ string $port = '993',
+ string $flags = '/imap/ssl/validate-cert',
+ array $parameters = [],
+ int $options = 0,
+ int $retries = 1
+ ) {
+ if (!\function_exists('imap_open')) {
+ throw new \RuntimeException('IMAP extension must be enabled');
+ }
+
+ $this->hostname = $hostname;
+ $this->port = $port;
+ $this->flags = '' !== $flags ? '/' . \ltrim($flags, '/') : '';
+ $this->parameters = $parameters;
+ $this->options = $options;
+ $this->retries = $retries;
+ }
+
+ /**
+ * Authenticate connection.
+ *
+ * @param string $username Username
+ * @param string $password Password
+ *
+ * @throws AuthenticationFailedException
+ */
+ public function authenticate(string $username, string $password): ConnectionInterface
+ {
+ $errorMessage = null;
+ $errorNumber = 0;
+ \set_error_handler(static function ($nr, $message) use (&$errorMessage, &$errorNumber): bool {
+ $errorMessage = $message;
+ $errorNumber = $nr;
+
+ return true;
+ });
+
+ $resource = \imap_open(
+ $this->getServerString(),
+ $username,
+ $password,
+ $this->options,
+ $this->retries,
+ $this->parameters
+ );
+
+ \restore_error_handler();
+
+ if (false === $resource || null !== $errorMessage) {
+ throw new AuthenticationFailedException(\sprintf(
+ 'Authentication failed for user "%s"%s',
+ $username,
+ null !== $errorMessage ? ': ' . $errorMessage : ''
+ ), $errorNumber);
+ }
+
+ $check = \imap_check($resource);
+
+ if (false === $check) {
+ throw new ResourceCheckFailureException('Resource check failure');
+ }
+
+ $mailbox = $check->Mailbox;
+ $connection = $mailbox;
+ $curlyPosition = \strpos($mailbox, '}');
+ if (false !== $curlyPosition) {
+ $connection = \substr($mailbox, 0, $curlyPosition + 1);
+ }
+
+ // These are necessary to get rid of PHP throwing IMAP errors
+ \imap_errors();
+ \imap_alerts();
+
+ return new Connection(new ImapResource($resource), $connection);
+ }
+
+ /**
+ * Glues hostname, port and flags and returns result.
+ */
+ private function getServerString(): string
+ {
+ return \sprintf(
+ '{%s%s%s}',
+ $this->hostname,
+ '' !== $this->port ? ':' . $this->port : '',
+ $this->flags
+ );
+ }
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ServerInterface.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ServerInterface.php
new file mode 100644
index 0000000..f7e95f9
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/ServerInterface.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap;
+
+/**
+ * An IMAP server.
+ */
+interface ServerInterface
+{
+ /**
+ * Authenticate connection.
+ *
+ * @param string $username Username
+ * @param string $password Password
+ */
+ public function authenticate(string $username, string $password): ConnectionInterface;
+}
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Test/RawMessageIterator.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Test/RawMessageIterator.php
new file mode 100644
index 0000000..08500b6
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/ddeboer/imap/src/Test/RawMessageIterator.php
@@ -0,0 +1,19 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Ddeboer\Imap\Test;
+
+use Ddeboer\Imap\MessageInterface;
+use Ddeboer\Imap\MessageIteratorInterface;
+
+/**
+ * A MessageIterator to be used in a mocked environment.
+ */
+final class RawMessageIterator extends \ArrayIterator implements MessageIteratorInterface
+{
+ public function current(): MessageInterface
+ {
+ return parent::current();
+ }
+}