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/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;
+    }
+}