whoami7 - Manager
:
/
home
/
kckglobal
/
www
/
portal
/
vendor
/
webklex
/
php-imap
/
src
/
Upload File:
files >> //home/kckglobal/www/portal/vendor/webklex/php-imap/src/Message.php
<?php /* * File: Message.php * Category: - * Author: M. Goldenbaum * Created: 19.01.17 22:21 * Updated: - * * Description: * - */ namespace Webklex\PHPIMAP; use ReflectionClass; use ReflectionException; use Webklex\PHPIMAP\Exceptions\AuthFailedException; use Webklex\PHPIMAP\Exceptions\ConnectionFailedException; use Webklex\PHPIMAP\Exceptions\EventNotFoundException; use Webklex\PHPIMAP\Exceptions\FolderFetchingException; use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException; use Webklex\PHPIMAP\Exceptions\ImapBadRequestException; use Webklex\PHPIMAP\Exceptions\ImapServerErrorException; use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException; use Webklex\PHPIMAP\Exceptions\MaskNotFoundException; use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException; use Webklex\PHPIMAP\Exceptions\MessageFlagException; use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException; use Webklex\PHPIMAP\Exceptions\MessageNotFoundException; use Webklex\PHPIMAP\Exceptions\MessageSizeFetchingException; use Webklex\PHPIMAP\Exceptions\MethodNotFoundException; use Webklex\PHPIMAP\Exceptions\ResponseException; use Webklex\PHPIMAP\Exceptions\RuntimeException; use Webklex\PHPIMAP\Support\AttachmentCollection; use Webklex\PHPIMAP\Support\FlagCollection; use Webklex\PHPIMAP\Support\Masks\MessageMask; use Illuminate\Support\Str; use Webklex\PHPIMAP\Support\MessageCollection; use Webklex\PHPIMAP\Traits\HasEvents; /** * Class Message * * @package Webklex\PHPIMAP * * @property integer msglist * @property integer uid * @property integer msgn * @property integer size * @property Attribute subject * @property Attribute message_id * @property Attribute message_no * @property Attribute references * @property Attribute date * @property Attribute from * @property Attribute to * @property Attribute cc * @property Attribute bcc * @property Attribute reply_to * @property Attribute in_reply_to * @property Attribute sender * * @method integer getMsglist() * @method integer setMsglist($msglist) * @method integer getUid() * @method integer getMsgn() * @method integer getSize() * @method Attribute getPriority() * @method Attribute getSubject() * @method Attribute getMessageId() * @method Attribute getMessageNo() * @method Attribute getReferences() * @method Attribute getDate() * @method Attribute getFrom() * @method Attribute getTo() * @method Attribute getCc() * @method Attribute getBcc() * @method Attribute getReplyTo() * @method Attribute getInReplyTo() * @method Attribute getSender() */ class Message { use HasEvents; /** * Client instance * * @var ?Client */ private ?Client $client = null; /** * Default mask * * @var string $mask */ protected string $mask = MessageMask::class; /** * Used config * * @var array $config */ protected array $config = []; /** * Attribute holder * * @var Attribute[]|array $attributes */ protected array $attributes = []; /** * The message folder path * * @var string $folder_path */ protected string $folder_path; /** * Fetch body options * * @var ?integer */ public ?int $fetch_options = null; /** * @var integer */ protected int $sequence = IMAP::NIL; /** * Fetch body options * * @var bool */ public bool $fetch_body = true; /** * Fetch flags options * * @var bool */ public bool $fetch_flags = true; /** * @var ?Header $header */ public ?Header $header = null; /** * Raw message body * * @var string $raw_body */ protected string $raw_body = ""; /** * Message structure * * @var ?Structure $structure */ protected ?Structure $structure = null; /** * Message body components * * @var array $bodies */ public array $bodies = []; /** @var AttachmentCollection $attachments */ public AttachmentCollection $attachments; /** @var FlagCollection $flags */ public FlagCollection $flags; /** * A list of all available and supported flags * * @var ?array $available_flags */ private ?array $available_flags = null; /** * Message constructor. * @param integer $uid * @param integer|null $msglist * @param Client $client * @param integer|null $fetch_options * @param boolean $fetch_body * @param boolean $fetch_flags * @param integer|null $sequence * * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws MessageHeaderFetchingException * @throws RuntimeException * @throws ResponseException */ public function __construct(int $uid, ?int $msglist, Client $client, int $fetch_options = null, bool $fetch_body = false, bool $fetch_flags = false, int $sequence = null) { $this->boot(); $default_mask = $client->getDefaultMessageMask(); if ($default_mask != null) { $this->mask = $default_mask; } $this->events["message"] = $client->getDefaultEvents("message"); $this->events["flag"] = $client->getDefaultEvents("flag"); $this->folder_path = $client->getFolderPath(); $this->setSequence($sequence); $this->setFetchOption($fetch_options); $this->setFetchBodyOption($fetch_body); $this->setFetchFlagsOption($fetch_flags); $this->client = $client; $this->client->openFolder($this->folder_path); $this->setSequenceId($uid, $msglist); if ($this->fetch_options == IMAP::FT_PEEK) { $this->parseFlags(); } $this->parseHeader(); if ($this->getFetchBodyOption() === true) { $this->parseBody(); } if ($this->getFetchFlagsOption() === true && $this->fetch_options !== IMAP::FT_PEEK) { $this->parseFlags(); } } /** * Create a new instance without fetching the message header and providing them raw instead * @param int $uid * @param int|null $msglist * @param Client $client * @param string $raw_header * @param string $raw_body * @param array $raw_flags * @param null $fetch_options * @param null $sequence * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws ReflectionException * @throws RuntimeException * @throws ResponseException */ public static function make(int $uid, ?int $msglist, Client $client, string $raw_header, string $raw_body, array $raw_flags, $fetch_options = null, $sequence = null): Message { $reflection = new ReflectionClass(self::class); /** @var Message $instance */ $instance = $reflection->newInstanceWithoutConstructor(); $instance->boot(); $default_mask = $client->getDefaultMessageMask(); if ($default_mask != null) { $instance->setMask($default_mask); } $instance->setEvents([ "message" => $client->getDefaultEvents("message"), "flag" => $client->getDefaultEvents("flag"), ]); $instance->setFolderPath($client->getFolderPath()); $instance->setSequence($sequence); $instance->setFetchOption($fetch_options); $instance->setClient($client); $instance->setSequenceId($uid, $msglist); $instance->parseRawHeader($raw_header); $instance->parseRawFlags($raw_flags); $instance->parseRawBody($raw_body); $instance->peek(); return $instance; } /** * Create a new message instance by reading and loading a file or remote location * * @throws RuntimeException * @throws MessageContentFetchingException * @throws ResponseException * @throws ImapBadRequestException * @throws InvalidMessageDateException * @throws ConnectionFailedException * @throws ImapServerErrorException * @throws ReflectionException * @throws AuthFailedException * @throws MaskNotFoundException */ public static function fromFile($filename): Message { $blob = file_get_contents($filename); if ($blob === false) { throw new RuntimeException("Unable to read file"); } return self::fromString($blob); } /** * Create a new message instance by reading and loading a string * @param string $blob * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MaskNotFoundException * @throws MessageContentFetchingException * @throws ReflectionException * @throws ResponseException * @throws RuntimeException */ public static function fromString(string $blob): Message { $reflection = new ReflectionClass(self::class); /** @var Message $instance */ $instance = $reflection->newInstanceWithoutConstructor(); $instance->boot(); $default_mask = ClientManager::getMask("message"); if($default_mask != ""){ $instance->setMask($default_mask); }else{ throw new MaskNotFoundException("Unknown message mask provided"); } if(!str_contains($blob, "\r\n")){ $blob = str_replace("\n", "\r\n", $blob); } $raw_header = substr($blob, 0, strpos($blob, "\r\n\r\n")); $raw_body = substr($blob, strlen($raw_header)+4); $instance->parseRawHeader($raw_header); $instance->parseRawBody($raw_body); $instance->setUid(0); return $instance; } /** * Boot a new instance */ public function boot(): void { $this->attributes = []; $this->config = ClientManager::get('options'); $this->available_flags = ClientManager::get('flags'); $this->attachments = AttachmentCollection::make([]); $this->flags = FlagCollection::make([]); } /** * Call dynamic attribute setter and getter methods * @param string $method * @param array $arguments * * @return mixed * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageNotFoundException * @throws MethodNotFoundException * @throws MessageSizeFetchingException * @throws RuntimeException * @throws ResponseException */ public function __call(string $method, array $arguments) { if (strtolower(substr($method, 0, 3)) === 'get') { $name = Str::snake(substr($method, 3)); return $this->get($name); } elseif (strtolower(substr($method, 0, 3)) === 'set') { $name = Str::snake(substr($method, 3)); if (in_array($name, array_keys($this->attributes))) { return $this->__set($name, array_pop($arguments)); } } throw new MethodNotFoundException("Method " . self::class . '::' . $method . '() is not supported'); } /** * Magic setter * @param $name * @param $value * * @return mixed */ public function __set($name, $value) { $this->attributes[$name] = $value; return $this->attributes[$name]; } /** * Magic getter * @param $name * * @return Attribute|mixed|null * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageNotFoundException * @throws MessageSizeFetchingException * @throws RuntimeException * @throws ResponseException */ public function __get($name) { return $this->get($name); } /** * Get an available message or message header attribute * @param $name * * @return Attribute|mixed|null * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageNotFoundException * @throws RuntimeException * @throws ResponseException * @throws MessageSizeFetchingException */ public function get($name): mixed { if (isset($this->attributes[$name]) && $this->attributes[$name] !== null) { return $this->attributes[$name]; } switch ($name){ case "uid": $this->attributes[$name] = $this->client->getConnection()->getUid($this->msgn)->validate()->integer(); return $this->attributes[$name]; case "msgn": $this->attributes[$name] = $this->client->getConnection()->getMessageNumber($this->uid)->validate()->integer(); return $this->attributes[$name]; case "size": if (!isset($this->attributes[$name])) { $this->fetchSize(); } return $this->attributes[$name]; } return $this->header->get($name); } /** * Check if the Message has a text body * * @return bool */ public function hasTextBody(): bool { return isset($this->bodies['text']) && $this->bodies['text'] !== ""; } /** * Get the Message text body * * @return string */ public function getTextBody(): string { if (!isset($this->bodies['text'])) { return ""; } return $this->bodies['text']; } /** * Check if the Message has a html body * * @return bool */ public function hasHTMLBody(): bool { return isset($this->bodies['html']) && $this->bodies['html'] !== ""; } /** * Get the Message html body * * @return string */ public function getHTMLBody(): string { if (!isset($this->bodies['html'])) { return ""; } return $this->bodies['html']; } /** * Parse all defined headers * * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws InvalidMessageDateException * @throws MessageHeaderFetchingException * @throws ResponseException */ private function parseHeader(): void { $sequence_id = $this->getSequenceId(); $headers = $this->client->getConnection()->headers([$sequence_id], "RFC822", $this->sequence)->validatedData(); if (!isset($headers[$sequence_id])) { throw new MessageHeaderFetchingException("no headers found", 0); } $this->parseRawHeader($headers[$sequence_id]); } /** * @param string $raw_header * * @throws InvalidMessageDateException */ public function parseRawHeader(string $raw_header): void { $this->header = new Header($raw_header); } /** * Parse additional raw flags * @param array $raw_flags */ public function parseRawFlags(array $raw_flags): void { $this->flags = FlagCollection::make([]); foreach ($raw_flags as $flag) { if (str_starts_with($flag, "\\")) { $flag = substr($flag, 1); } $flag_key = strtolower($flag); if ($this->available_flags === null || in_array($flag_key, $this->available_flags)) { $this->flags->put($flag_key, $flag); } } } /** * Parse additional flags * * @return void * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ private function parseFlags(): void { $this->client->openFolder($this->folder_path); $this->flags = FlagCollection::make([]); $sequence_id = $this->getSequenceId(); try { $flags = $this->client->getConnection()->flags([$sequence_id], $this->sequence)->validatedData(); } catch (Exceptions\RuntimeException $e) { throw new MessageFlagException("flag could not be fetched", 0, $e); } if (isset($flags[$sequence_id])) { $this->parseRawFlags($flags[$sequence_id]); } } /** * Parse the Message body * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function parseBody(): Message { $this->client->openFolder($this->folder_path); $sequence_id = $this->getSequenceId(); try { $contents = $this->client->getConnection()->content([$sequence_id], "RFC822", $this->sequence)->validatedData(); } catch (Exceptions\RuntimeException $e) { throw new MessageContentFetchingException("failed to fetch content", 0); } if (!isset($contents[$sequence_id])) { throw new MessageContentFetchingException("no content found", 0); } $content = $contents[$sequence_id]; $body = $this->parseRawBody($content); $this->peek(); return $body; } /** * Fetches the size for this message. * * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageSizeFetchingException * @throws ResponseException * @throws RuntimeException */ private function fetchSize(): void { $sequence_id = $this->getSequenceId(); $sizes = $this->client->getConnection()->sizes([$sequence_id], $this->sequence)->validatedData(); if (!isset($sizes[$sequence_id])) { throw new MessageSizeFetchingException("sizes did not set an array entry for the supplied sequence_id", 0); } $this->attributes["size"] = $sizes[$sequence_id]; } /** * Handle auto "Seen" flag handling * * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function peek(): void { if ($this->fetch_options == IMAP::FT_PEEK) { if ($this->getFlags()->get("seen") == null) { $this->unsetFlag("Seen"); } } elseif ($this->getFlags()->get("seen") == null) { $this->setFlag("Seen"); } } /** * Parse a given message body * @param string $raw_body * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws RuntimeException * @throws ResponseException */ public function parseRawBody(string $raw_body): Message { $this->structure = new Structure($raw_body, $this->header); $this->fetchStructure($this->structure); return $this; } /** * Fetch the Message structure * @param Structure $structure * * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ private function fetchStructure(Structure $structure): void { $this->client?->openFolder($this->folder_path); foreach ($structure->parts as $part) { $this->fetchPart($part); } } /** * Fetch a given part * @param Part $part */ private function fetchPart(Part $part): void { if ($part->isAttachment()) { $this->fetchAttachment($part); } else { $encoding = $this->getEncoding($part); $content = $this->decodeString($part->content, $part->encoding); // We don't need to do convertEncoding() if charset is ASCII (us-ascii): // ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded // https://stackoverflow.com/a/11303410 // // us-ascii is the same as ASCII: // ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA) // prefers the updated name US-ASCII, which clarifies that this system was developed in the US and // based on the typographical symbols predominantly in use there. // https://en.wikipedia.org/wiki/ASCII // // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken. if ($encoding != 'us-ascii') { $content = $this->convertEncoding($content, $encoding); } $this->addBody($part->subtype ?? '', $content); } } /** * Add a body to the message * @param string $subtype * @param string $content * * @return void */ protected function addBody(string $subtype, string $content): void { $subtype = strtolower($subtype); $subtype = $subtype == "plain" || $subtype == "" ? "text" : $subtype; if (isset($this->bodies[$subtype]) && $this->bodies[$subtype] !== null && $this->bodies[$subtype] !== "") { if ($content !== "") { $this->bodies[$subtype] .= "\n".$content; } } else { $this->bodies[$subtype] = $content; } } /** * Fetch the Message attachment * @param Part $part */ protected function fetchAttachment(Part $part): void { $oAttachment = new Attachment($this, $part); if ($oAttachment->getSize() > 0) { if ($oAttachment->getId() !== null && $this->attachments->offsetExists($oAttachment->getId())) { $this->attachments->put($oAttachment->getId(), $oAttachment); } else { $this->attachments->push($oAttachment); } } } /** * Fail proof setter for $fetch_option * @param $option * * @return Message */ public function setFetchOption($option): Message { if (is_long($option) === true) { $this->fetch_options = $option; } elseif (is_null($option) === true) { $config = ClientManager::get('options.fetch', IMAP::FT_UID); $this->fetch_options = is_long($config) ? $config : 1; } return $this; } /** * Set the sequence type * @param int|null $sequence * * @return Message */ public function setSequence(?int $sequence): Message { if (is_long($sequence)) { $this->sequence = $sequence; } elseif (is_null($sequence)) { $config = ClientManager::get('options.sequence', IMAP::ST_MSGN); $this->sequence = is_long($config) ? $config : IMAP::ST_MSGN; } return $this; } /** * Fail proof setter for $fetch_body * @param $option * * @return Message */ public function setFetchBodyOption($option): Message { if (is_bool($option)) { $this->fetch_body = $option; } elseif (is_null($option)) { $config = ClientManager::get('options.fetch_body', true); $this->fetch_body = is_bool($config) ? $config : true; } return $this; } /** * Fail proof setter for $fetch_flags * @param $option * * @return Message */ public function setFetchFlagsOption($option): Message { if (is_bool($option)) { $this->fetch_flags = $option; } elseif (is_null($option)) { $config = ClientManager::get('options.fetch_flags', true); $this->fetch_flags = is_bool($config) ? $config : true; } return $this; } /** * Decode a given string * @param $string * @param $encoding * * @return string */ public function decodeString($string, $encoding): string { switch ($encoding) { case IMAP::MESSAGE_ENC_BINARY: if (extension_loaded('imap')) { return base64_decode(\imap_binary($string)); } return base64_decode($string); case IMAP::MESSAGE_ENC_BASE64: return base64_decode($string); case IMAP::MESSAGE_ENC_QUOTED_PRINTABLE: return quoted_printable_decode($string); case IMAP::MESSAGE_ENC_8BIT: case IMAP::MESSAGE_ENC_7BIT: case IMAP::MESSAGE_ENC_OTHER: default: return $string; } } /** * Convert the encoding * @param $str * @param string $from * @param string $to * * @return mixed|string */ public function convertEncoding($str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed { $from = EncodingAliases::get($from); $to = EncodingAliases::get($to); if ($from === $to) { return $str; } // We don't need to do convertEncoding() if charset is ASCII (us-ascii): // ASCII is a subset of UTF-8, so all ASCII files are already UTF-8 encoded // https://stackoverflow.com/a/11303410 // // us-ascii is the same as ASCII: // ASCII is the traditional name for the encoding system; the Internet Assigned Numbers Authority (IANA) // prefers the updated name US-ASCII, which clarifies that this system was developed in the US and // based on the typographical symbols predominantly in use there. // https://en.wikipedia.org/wiki/ASCII // // convertEncoding() function basically means convertToUtf8(), so when we convert ASCII string into UTF-8 it gets broken. if (strtolower($from ?? '') == 'us-ascii' && $to == 'UTF-8') { return $str; } if (function_exists('iconv') && !EncodingAliases::isUtf7($from) && !EncodingAliases::isUtf7($to)) { try { return iconv($from, $to.'//IGNORE', $str); } catch (\Exception $e) { return @iconv($from, $to, $str); } } else { if (!$from) { return mb_convert_encoding($str, $to); } return mb_convert_encoding($str, $to, $from); } } /** * Get the encoding of a given abject * @param object|string $structure * * @return string */ public function getEncoding(object|string $structure): string { if (property_exists($structure, 'parameters')) { foreach ($structure->parameters as $parameter) { if (strtolower($parameter->attribute) == "charset") { return EncodingAliases::get($parameter->value, "ISO-8859-2"); } } } elseif (property_exists($structure, 'charset')) { return EncodingAliases::get($structure->charset, "ISO-8859-2"); } elseif (is_string($structure) === true) { return EncodingAliases::detectEncoding($structure); } return 'UTF-8'; } /** * Get the messages folder * * @return ?Folder * @throws AuthFailedException * @throws ConnectionFailedException * @throws FolderFetchingException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ public function getFolder(): ?Folder { return $this->client->getFolderByPath($this->folder_path); } /** * Create a message thread based on the current message * @param Folder|null $sent_folder * @param MessageCollection|null $thread * @param Folder|null $folder * * @return MessageCollection * @throws AuthFailedException * @throws ConnectionFailedException * @throws FolderFetchingException * @throws GetMessagesFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ public function thread(Folder $sent_folder = null, MessageCollection &$thread = null, Folder $folder = null): MessageCollection { $thread = $thread ?: MessageCollection::make([]); $folder = $folder ?: $this->getFolder(); $sent_folder = $sent_folder ?: $this->client->getFolderByPath(ClientManager::get("options.common_folders.sent", "INBOX/Sent")); /** @var Message $message */ foreach ($thread as $message) { if ($message->message_id->first() == $this->message_id->first()) { return $thread; } } $thread->push($this); $this->fetchThreadByInReplyTo($thread, $this->message_id, $folder, $folder, $sent_folder); $this->fetchThreadByInReplyTo($thread, $this->message_id, $sent_folder, $folder, $sent_folder); foreach ($this->in_reply_to->all() as $in_reply_to) { $this->fetchThreadByMessageId($thread, $in_reply_to, $folder, $folder, $sent_folder); $this->fetchThreadByMessageId($thread, $in_reply_to, $sent_folder, $folder, $sent_folder); } return $thread; } /** * Fetch a partial thread by message id * @param MessageCollection $thread * @param string $in_reply_to * @param Folder $primary_folder * @param Folder $secondary_folder * @param Folder $sent_folder * * @throws AuthFailedException * @throws ConnectionFailedException * @throws FolderFetchingException * @throws GetMessagesFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ protected function fetchThreadByInReplyTo(MessageCollection &$thread, string $in_reply_to, Folder $primary_folder, Folder $secondary_folder, Folder $sent_folder): void { $primary_folder->query()->inReplyTo($in_reply_to) ->setFetchBody($this->getFetchBodyOption()) ->leaveUnread()->get()->each(function($message) use (&$thread, $secondary_folder, $sent_folder) { /** @var Message $message */ $message->thread($sent_folder, $thread, $secondary_folder); }); } /** * Fetch a partial thread by message id * @param MessageCollection $thread * @param string $message_id * @param Folder $primary_folder * @param Folder $secondary_folder * @param Folder $sent_folder * * @throws AuthFailedException * @throws ConnectionFailedException * @throws GetMessagesFailedException * @throws FolderFetchingException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ protected function fetchThreadByMessageId(MessageCollection &$thread, string $message_id, Folder $primary_folder, Folder $secondary_folder, Folder $sent_folder): void { $primary_folder->query()->messageId($message_id) ->setFetchBody($this->getFetchBodyOption()) ->leaveUnread()->get()->each(function($message) use (&$thread, $secondary_folder, $sent_folder) { /** @var Message $message */ $message->thread($sent_folder, $thread, $secondary_folder); }); } /** * Copy the current Messages to a mailbox * @param string $folder_path * @param boolean $expunge * * @return null|Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws FolderFetchingException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws MessageHeaderFetchingException * @throws MessageNotFoundException * @throws RuntimeException * @throws ResponseException */ public function copy(string $folder_path, bool $expunge = false): ?Message { $this->client->openFolder($folder_path); $status = $this->client->getConnection()->examineFolder($folder_path)->validatedData(); if (isset($status["uidnext"])) { $next_uid = $status["uidnext"]; if ((int)$next_uid <= 0) { return null; } /** @var Folder $folder */ $folder = $this->client->getFolderByPath($folder_path); $this->client->openFolder($this->folder_path); if ($this->client->getConnection()->copyMessage($folder->path, $this->getSequenceId(), null, $this->sequence)->validatedData()) { return $this->fetchNewMail($folder, $next_uid, "copied", $expunge); } } return null; } /** * Move the current Messages to a mailbox * @param string $folder_path * @param boolean $expunge * * @return Message|null * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws FolderFetchingException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws MessageHeaderFetchingException * @throws MessageNotFoundException * @throws RuntimeException * @throws ResponseException */ public function move(string $folder_path, bool $expunge = false): ?Message { $this->client->openFolder($folder_path); $status = $this->client->getConnection()->examineFolder($folder_path)->validatedData(); if (isset($status["uidnext"])) { $next_uid = $status["uidnext"]; if ((int)$next_uid <= 0) { return null; } /** @var Folder $folder */ $folder = $this->client->getFolderByPath($folder_path); $this->client->openFolder($this->folder_path); if ($this->client->getConnection()->moveMessage($folder->path, $this->getSequenceId(), null, $this->sequence)->validatedData()) { return $this->fetchNewMail($folder, $next_uid, "moved", $expunge); } } return null; } /** * Fetch a new message and fire a given event * @param Folder $folder * @param int $next_uid * @param string $event * @param boolean $expunge * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws MessageHeaderFetchingException * @throws MessageNotFoundException * @throws RuntimeException * @throws ResponseException */ protected function fetchNewMail(Folder $folder, int $next_uid, string $event, bool $expunge): Message { if ($expunge) $this->client->expunge(); $this->client->openFolder($folder->path); if ($this->sequence === IMAP::ST_UID) { $sequence_id = $next_uid; } else { $sequence_id = $this->client->getConnection()->getMessageNumber($next_uid)->validatedData(); } $message = $folder->query()->getMessage($sequence_id, null, $this->sequence); $event = $this->getEvent("message", $event); $event::dispatch($this, $message); return $message; } /** * Delete the current Message * @param bool $expunge * @param string|null $trash_path * @param boolean $force_move * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws FolderFetchingException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws InvalidMessageDateException * @throws MessageContentFetchingException * @throws MessageFlagException * @throws MessageHeaderFetchingException * @throws MessageNotFoundException * @throws RuntimeException * @throws ResponseException */ public function delete(bool $expunge = true, string $trash_path = null, bool $force_move = false): bool { $status = $this->setFlag("Deleted"); if ($force_move) { $trash_path = $trash_path === null ? $this->config["common_folders"]["trash"] : $trash_path; $this->move($trash_path); } if ($expunge) $this->client->expunge(); $event = $this->getEvent("message", "deleted"); $event::dispatch($this); return $status; } /** * Restore a deleted Message * @param boolean $expunge * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function restore(bool $expunge = true): bool { $status = $this->unsetFlag("Deleted"); if ($expunge) $this->client->expunge(); $event = $this->getEvent("message", "restored"); $event::dispatch($this); return $status; } /** * Set a given flag * @param array|string $flag * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function setFlag(array|string $flag): bool { $this->client->openFolder($this->folder_path); $flag = "\\" . trim(is_array($flag) ? implode(" \\", $flag) : $flag); $sequence_id = $this->getSequenceId(); try { $status = $this->client->getConnection()->store([$flag], $sequence_id, $sequence_id, "+", true, $this->sequence)->validatedData(); } catch (Exceptions\RuntimeException $e) { throw new MessageFlagException("flag could not be set", 0, $e); } $this->parseFlags(); $event = $this->getEvent("flag", "new"); $event::dispatch($this, $flag); return (bool)$status; } /** * Unset a given flag * @param array|string $flag * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function unsetFlag(array|string $flag): bool { $this->client->openFolder($this->folder_path); $flag = "\\" . trim(is_array($flag) ? implode(" \\", $flag) : $flag); $sequence_id = $this->getSequenceId(); try { $status = $this->client->getConnection()->store([$flag], $sequence_id, $sequence_id, "-", true, $this->sequence)->validatedData(); } catch (Exceptions\RuntimeException $e) { throw new MessageFlagException("flag could not be removed", 0, $e); } $this->parseFlags(); $event = $this->getEvent("flag", "deleted"); $event::dispatch($this, $flag); return (bool)$status; } /** * Set a given flag * @param array|string $flag * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function addFlag(array|string $flag): bool { return $this->setFlag($flag); } /** * Unset a given flag * @param array|string $flag * * @return bool * @throws AuthFailedException * @throws ConnectionFailedException * @throws EventNotFoundException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws MessageFlagException * @throws RuntimeException * @throws ResponseException */ public function removeFlag(array|string $flag): bool { return $this->unsetFlag($flag); } /** * Get all message attachments. * * @return AttachmentCollection */ public function getAttachments(): AttachmentCollection { return $this->attachments; } /** * Get all message attachments. * * @return AttachmentCollection */ public function attachments(): AttachmentCollection { return $this->getAttachments(); } /** * Checks if there are any attachments present * * @return boolean */ public function hasAttachments(): bool { return $this->attachments->isEmpty() === false; } /** * Get the raw body * * @return string */ public function getRawBody(): string { if ($this->raw_body === "") { $this->raw_body = $this->structure->raw; } return $this->raw_body; } /** * Get the message header * * @return ?Header */ public function getHeader(): ?Header { return $this->header; } /** * Get the current client * * @return ?Client */ public function getClient(): ?Client { return $this->client; } /** * Get the used fetch option * * @return ?integer */ public function getFetchOptions(): ?int { return $this->fetch_options; } /** * Get the used fetch body option * * @return boolean */ public function getFetchBodyOption(): bool { return $this->fetch_body; } /** * Get the used fetch flags option * * @return boolean */ public function getFetchFlagsOption(): bool { return $this->fetch_flags; } /** * Get all available bodies * * @return array */ public function getBodies(): array { return $this->bodies; } /** * Get all set flags * * @return FlagCollection */ public function getFlags(): FlagCollection { return $this->flags; } /** * Get all set flags * * @return FlagCollection */ public function flags(): FlagCollection { return $this->getFlags(); } /** * Check if a flag is set * * @param string $flag * @return boolean */ public function hasFlag(string $flag): bool { $flag = str_replace("\\", "", strtolower($flag)); return $this->getFlags()->has($flag); } /** * Get the fetched structure * * @return Structure|null */ public function getStructure(): ?Structure { return $this->structure; } /** * Check if a message matches another by comparing basic attributes * * @param null|Message $message * @return boolean */ public function is(Message $message = null): bool { if (is_null($message)) { return false; } return $this->uid == $message->uid && $this->message_id->first() == $message->message_id->first() && $this->subject->first() == $message->subject->first() && $this->date->toDate()->eq($message->date->toDate()); } /** * Get all message attributes * * @return array */ public function getAttributes(): array { return array_merge($this->attributes, $this->header->getAttributes()); } /** * Set the message mask * @param $mask * * @return Message */ public function setMask($mask): Message { if (class_exists($mask)) { $this->mask = $mask; } return $this; } /** * Get the used message mask * * @return string */ public function getMask(): string { return $this->mask; } /** * Get a masked instance by providing a mask name * @param mixed|null $mask * * @return mixed * @throws MaskNotFoundException */ public function mask(mixed $mask = null): mixed { $mask = $mask !== null ? $mask : $this->mask; if (class_exists($mask)) { return new $mask($this); } throw new MaskNotFoundException("Unknown mask provided: " . $mask); } /** * Get the message path aka folder path * * @return string */ public function getFolderPath(): string { return $this->folder_path; } /** * Set the message path aka folder path * @param $folder_path * * @return Message */ public function setFolderPath($folder_path): Message { $this->folder_path = $folder_path; return $this; } /** * Set the config * @param array $config * * @return Message */ public function setConfig(array $config): Message { $this->config = $config; return $this; } /** * Get the config * * @return array */ public function getConfig(): array { return $this->config; } /** * Set the available flags * @param $available_flags * * @return Message */ public function setAvailableFlags($available_flags): Message { $this->available_flags = $available_flags; return $this; } /** * Get the available flags * * @return array */ public function getAvailableFlags(): array { return $this->available_flags; } /** * Set the attachment collection * @param $attachments * * @return Message */ public function setAttachments($attachments): Message { $this->attachments = $attachments; return $this; } /** * Set the flag collection * @param $flags * * @return Message */ public function setFlags($flags): Message { $this->flags = $flags; return $this; } /** * Set the client * @param $client * * @return Message * @throws AuthFailedException * @throws ConnectionFailedException * @throws ImapBadRequestException * @throws ImapServerErrorException * @throws RuntimeException * @throws ResponseException */ public function setClient($client): Message { $this->client = $client; $this->client?->openFolder($this->folder_path); return $this; } /** * Set the message number * @param int $uid * * @return Message */ public function setUid(int $uid): Message { $this->uid = $uid; $this->msgn = null; $this->msglist = null; return $this; } /** * Set the message number * @param int $msgn * @param int|null $msglist * * @return Message */ public function setMsgn(int $msgn, int $msglist = null): Message { $this->msgn = $msgn; $this->msglist = $msglist; $this->uid = null; return $this; } /** * Get the current sequence type * * @return int */ public function getSequence(): int { return $this->sequence; } /** * Get the current sequence id (either a UID or a message number!) * * @return int */ public function getSequenceId(): int { return $this->sequence === IMAP::ST_UID ? $this->uid : $this->msgn; } /** * Set the sequence id * @param $uid * @param int|null $msglist */ public function setSequenceId($uid, int $msglist = null): void { if ($this->getSequence() === IMAP::ST_UID) { $this->setUid($uid); $this->setMsglist($msglist); } else { $this->setMsgn($uid, $msglist); } } /** * Safe the entire message in a file * @param $filename * * @return bool|int */ public function save($filename): bool|int { return file_put_contents($filename, $this->header->raw."\r\n\r\n".$this->structure->raw); } }
Copyright ©2021 || Defacer Indonesia