added php download of membershipments

This commit is contained in:
Torsten Schulz
2023-12-27 10:40:24 +01:00
parent ea29b477f6
commit c622398357
2571 changed files with 350456 additions and 15 deletions

90
vendor/webklex/php-imap/src/Address.php vendored Normal file
View File

@@ -0,0 +1,90 @@
<?php
/*
* File: Address.php
* Category: -
* Author: M. Goldenbaum
* Created: 01.01.21 21:17
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
/**
* Class Address
*
* @package Webklex\PHPIMAP
*/
class Address {
/**
* Address attributes
* @var string $personal
* @var string $mailbox
* @var string $host
* @var string $mail
* @var string $full
*/
public $personal = "";
public $mailbox = "";
public $host = "";
public $mail = "";
public $full = "";
/**
* Address constructor.
* @param object $object
*/
public function __construct($object) {
if (property_exists($object, "personal")){ $this->personal = $object->personal; }
if (property_exists($object, "mailbox")){ $this->mailbox = $object->mailbox; }
if (property_exists($object, "host")){ $this->host = $object->host; }
if (property_exists($object, "mail")){ $this->mail = $object->mail; }
if (property_exists($object, "full")){ $this->full = $object->full; }
}
/**
* Return the stringified address
*
* @return string
*/
public function __toString() {
return $this->full ?: "";
}
/**
* Return the serialized address
*
* @return array
*/
public function __serialize(){
return [
"personal" => $this->personal,
"mailbox" => $this->mailbox,
"host" => $this->host,
"mail" => $this->mail,
"full" => $this->full,
];
}
/**
* Convert instance to array
*
* @return array
*/
public function toArray(): array {
return $this->__serialize();
}
/**
* Return the stringified attribute
*
* @return string
*/
public function toString(): string {
return $this->__toString();
}
}

353
vendor/webklex/php-imap/src/Attachment.php vendored Executable file
View File

@@ -0,0 +1,353 @@
<?php
/*
* File: Attachment.php
* Category: -
* Author: M. Goldenbaum
* Created: 16.03.18 19:37
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use Illuminate\Support\Str;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
/**
* Class Attachment
*
* @package Webklex\PHPIMAP
*
* @property integer part_number
* @property integer size
* @property string content
* @property string type
* @property string content_type
* @property string id
* @property string name
* @property string disposition
* @property string img_src
*
* @method integer getPartNumber()
* @method integer setPartNumber(integer $part_number)
* @method string getContent()
* @method string setContent(string $content)
* @method string getType()
* @method string setType(string $type)
* @method string getContentType()
* @method string setContentType(string $content_type)
* @method string getId()
* @method string setId(string $id)
* @method string getSize()
* @method string setSize(integer $size)
* @method string getName()
* @method string getDisposition()
* @method string setDisposition(string $disposition)
* @method string setImgSrc(string $img_src)
*/
class Attachment {
/**
* @var Message $oMessage
*/
protected $oMessage;
/**
* Used config
*
* @var array $config
*/
protected $config = [];
/** @var Part $part */
protected $part;
/**
* Attribute holder
*
* @var array $attributes
*/
protected $attributes = [
'content' => null,
'type' => null,
'part_number' => 0,
'content_type' => null,
'id' => null,
'name' => null,
'disposition' => null,
'img_src' => null,
'size' => null,
];
/**
* Default mask
*
* @var string $mask
*/
protected $mask = AttachmentMask::class;
/**
* Attachment constructor.
* @param Message $oMessage
* @param Part $part
*/
public function __construct(Message $oMessage, Part $part) {
$this->config = ClientManager::get('options');
$this->oMessage = $oMessage;
$this->part = $part;
$this->part_number = $part->part_number;
$default_mask = $this->oMessage->getClient()->getDefaultAttachmentMask();
if($default_mask != null) {
$this->mask = $default_mask;
}
$this->findType();
$this->fetch();
}
/**
* Call dynamic attribute setter and getter methods
* @param string $method
* @param array $arguments
*
* @return mixed
* @throws MethodNotFoundException
*/
public function __call(string $method, array $arguments) {
if(strtolower(substr($method, 0, 3)) === 'get') {
$name = Str::snake(substr($method, 3));
if(isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}elseif (strtolower(substr($method, 0, 3)) === 'set') {
$name = Str::snake(substr($method, 3));
$this->attributes[$name] = array_pop($arguments);
return $this->attributes[$name];
}
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 mixed|null
*/
public function __get($name) {
if(isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}
/**
* Determine the structure type
*/
protected function findType() {
switch ($this->part->type) {
case IMAP::ATTACHMENT_TYPE_MESSAGE:
$this->type = 'message';
break;
case IMAP::ATTACHMENT_TYPE_APPLICATION:
$this->type = 'application';
break;
case IMAP::ATTACHMENT_TYPE_AUDIO:
$this->type = 'audio';
break;
case IMAP::ATTACHMENT_TYPE_IMAGE:
$this->type = 'image';
break;
case IMAP::ATTACHMENT_TYPE_VIDEO:
$this->type = 'video';
break;
case IMAP::ATTACHMENT_TYPE_MODEL:
$this->type = 'model';
break;
case IMAP::ATTACHMENT_TYPE_TEXT:
$this->type = 'text';
break;
case IMAP::ATTACHMENT_TYPE_MULTIPART:
$this->type = 'multipart';
break;
default:
$this->type = 'other';
break;
}
}
/**
* Fetch the given attachment
*/
protected function fetch() {
$content = $this->part->content;
$this->content_type = $this->part->content_type;
$this->content = $this->oMessage->decodeString($content, $this->part->encoding);
if (($id = $this->part->id) !== null) {
$this->id = str_replace(['<', '>'], '', $id);
}else{
$this->id = hash("sha256", (string)microtime(true));
}
$this->size = $this->part->bytes;
$this->disposition = $this->part->disposition;
if (($filename = $this->part->filename) !== null) {
$this->setName($filename);
} elseif (($name = $this->part->name) !== null) {
$this->setName($name);
}else {
$this->setName("undefined");
}
if (IMAP::ATTACHMENT_TYPE_MESSAGE == $this->part->type) {
if ($this->part->ifdescription) {
$this->setName($this->part->description);
} else {
$this->setName($this->part->subtype);
}
}
}
/**
* Save the attachment content to your filesystem
* @param string $path
* @param string|null $filename
*
* @return boolean
*/
public function save(string $path, $filename = null): bool {
$filename = $filename ?: $this->getName();
return file_put_contents($path.$filename, $this->getContent()) !== false;
}
/**
* Set the attachment name and try to decode it
* @param $name
*/
public function setName($name) {
$decoder = $this->config['decoder']['attachment'];
if ($name !== null) {
if($decoder === 'utf-8' && extension_loaded('imap')) {
$this->name = \imap_utf8($name);
}else{
$this->name = mb_decode_mimeheader($name);
}
}
}
/**
* Get the attachment mime type
*
* @return string|null
*/
public function getMimeType(){
return (new \finfo())->buffer($this->getContent(), FILEINFO_MIME_TYPE);
}
/**
* Try to guess the attachment file extension
*
* @return string|null
*/
public function getExtension(){
$guesser = "\Symfony\Component\Mime\MimeTypes";
if (class_exists($guesser) !== false) {
/** @var Symfony\Component\Mime\MimeTypes $guesser */
$extensions = $guesser::getDefault()->getExtensions($this->getMimeType());
return $extensions[0] ?? null;
}
$deprecated_guesser = "\Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser";
if (class_exists($deprecated_guesser) !== false){
/** @var \Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser $deprecated_guesser */
return $deprecated_guesser::getInstance()->guess($this->getMimeType());
}
return null;
}
/**
* Get all attributes
*
* @return array
*/
public function getAttributes(): array {
return $this->attributes;
}
/**
* @return Message
*/
public function getMessage(): Message {
return $this->oMessage;
}
/**
* Set the default mask
* @param $mask
*
* @return $this
*/
public function setMask($mask): Attachment {
if(class_exists($mask)){
$this->mask = $mask;
}
return $this;
}
/**
* Get the used default mask
*
* @return string
*/
public function getMask(): string {
return $this->mask;
}
/**
* Get a masked instance by providing a mask name
* @param string|null $mask
*
* @return mixed
* @throws MaskNotFoundException
*/
public function mask($mask = null){
$mask = $mask !== null ? $mask : $this->mask;
if(class_exists($mask)){
return new $mask($this);
}
throw new MaskNotFoundException("Unknown mask provided: ".$mask);
}
}

View File

@@ -0,0 +1,262 @@
<?php
/*
* File: Attribute.php
* Category: -
* Author: M. Goldenbaum
* Created: 01.01.21 20:17
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use ArrayAccess;
use Carbon\Carbon;
use ReturnTypeWillChange;
/**
* Class Attribute
*
* @package Webklex\PHPIMAP
*/
class Attribute implements ArrayAccess {
/** @var string $name */
protected $name;
/**
* Value holder
*
* @var array $values
*/
protected $values = [];
/**
* Attribute constructor.
* @param string $name
* @param array|mixed $value
*/
public function __construct(string $name, $value = null) {
$this->setName($name);
$this->add($value);
}
/**
* Return the stringified attribute
*
* @return string
*/
public function __toString() {
return implode(", ", $this->values);
}
/**
* Return the stringified attribute
*
* @return string
*/
public function toString(): string {
return $this->__toString();
}
/**
* Convert instance to array
*
* @return array
*/
public function toArray(): array {
return $this->values;
}
/**
* Convert first value to a date object
*
* @return Carbon
*/
public function toDate(): Carbon {
$date = $this->first();
if ($date instanceof Carbon) return $date;
return Carbon::parse($date);
}
/**
* Determine if a value exists at an offset.
*
* @param mixed $offset
* @return bool
*/
public function offsetExists($offset): bool {
return array_key_exists($offset, $this->values);
}
/**
* Get a value at a given offset.
*
* @param mixed $offset
* @return mixed
*/
#[ReturnTypeWillChange]
public function offsetGet($offset) {
return $this->values[$offset];
}
/**
* Set the value at a given offset.
*
* @param mixed $offset
* @param mixed $value
* @return void
*/
#[ReturnTypeWillChange]
public function offsetSet($offset, $value) {
if (is_null($offset)) {
$this->values[] = $value;
} else {
$this->values[$offset] = $value;
}
}
/**
* Unset the value at a given offset.
*
* @param string $offset
* @return void
*/
#[ReturnTypeWillChange]
public function offsetUnset($offset) {
unset($this->values[$offset]);
}
/**
* Add one or more values to the attribute
* @param array|mixed $value
* @param boolean $strict
*
* @return Attribute
*/
public function add($value, bool $strict = false): Attribute {
if (is_array($value)) {
return $this->merge($value, $strict);
}elseif ($value !== null) {
$this->attach($value, $strict);
}
return $this;
}
/**
* Merge a given array of values with the current values array
* @param array $values
* @param boolean $strict
*
* @return Attribute
*/
public function merge(array $values, bool $strict = false): Attribute {
foreach ($values as $value) {
$this->attach($value, $strict);
}
return $this;
}
/**
* Check if the attribute contains the given value
* @param mixed $value
*
* @return bool
*/
public function contains($value): bool {
return in_array($value, $this->values, true);
}
/**
* Attach a given value to the current value array
* @param $value
* @param bool $strict
*/
public function attach($value, bool $strict = false) {
if ($strict === true) {
if ($this->contains($value) === false) {
$this->values[] = $value;
}
}else{
$this->values[] = $value;
}
}
/**
* Set the attribute name
* @param $name
*
* @return Attribute
*/
public function setName($name): Attribute {
$this->name = $name;
return $this;
}
/**
* Get the attribute name
*
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* Get all values
*
* @return array
*/
public function get(): array {
return $this->values;
}
/**
* Alias method for self::get()
*
* @return array
*/
public function all(): array {
return $this->get();
}
/**
* Get the first value if possible
*
* @return mixed|null
*/
public function first(){
if ($this->offsetExists(0)) {
return $this->values[0];
}
return null;
}
/**
* Get the last value if possible
*
* @return mixed|null
*/
public function last(){
if (($cnt = $this->count()) > 0) {
return $this->values[$cnt - 1];
}
return null;
}
/**
* Get the number of values
*
* @return int
*/
public function count(): int {
return count($this->values);
}
}

746
vendor/webklex/php-imap/src/Client.php vendored Executable file
View File

@@ -0,0 +1,746 @@
<?php
/*
* File: Client.php
* Category: -
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use ErrorException;
use Webklex\PHPIMAP\Connection\Protocols\ImapProtocol;
use Webklex\PHPIMAP\Connection\Protocols\LegacyProtocol;
use Webklex\PHPIMAP\Connection\Protocols\Protocol;
use Webklex\PHPIMAP\Connection\Protocols\ProtocolInterface;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\FolderFetchingException;
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
use Webklex\PHPIMAP\Exceptions\ProtocolNotSupportedException;
use Webklex\PHPIMAP\Support\FolderCollection;
use Webklex\PHPIMAP\Support\Masks\AttachmentMask;
use Webklex\PHPIMAP\Support\Masks\MessageMask;
use Webklex\PHPIMAP\Traits\HasEvents;
/**
* Class Client
*
* @package Webklex\PHPIMAP
*/
class Client {
use HasEvents;
/**
* Connection resource
*
* @var boolean|Protocol|ProtocolInterface
*/
public $connection = false;
/**
* Server hostname.
*
* @var string
*/
public $host;
/**
* Server port.
*
* @var int
*/
public $port;
/**
* Service protocol.
*
* @var int
*/
public $protocol;
/**
* Server encryption.
* Supported: none, ssl, tls, starttls or notls.
*
* @var string
*/
public $encryption;
/**
* If server has to validate cert.
*
* @var bool
*/
public $validate_cert = true;
/**
* Proxy settings
* @var array
*/
protected $proxy = [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
];
/**
* Connection timeout
* @var int $timeout
*/
public $timeout;
/**
* Account username/
*
* @var mixed
*/
public $username;
/**
* Account password.
*
* @var string
*/
public $password;
/**
* Additional data fetched from the server.
*
* @var string
*/
public $extensions;
/**
* Account authentication method.
*
* @var string
*/
public $authentication;
/**
* Active folder path.
*
* @var string
*/
protected $active_folder = null;
/**
* Default message mask
*
* @var string $default_message_mask
*/
protected $default_message_mask = MessageMask::class;
/**
* Default attachment mask
*
* @var string $default_attachment_mask
*/
protected $default_attachment_mask = AttachmentMask::class;
/**
* Used default account values
*
* @var array $default_account_config
*/
protected $default_account_config = [
'host' => 'localhost',
'port' => 993,
'protocol' => 'imap',
'encryption' => 'ssl',
'validate_cert' => true,
'username' => '',
'password' => '',
'authentication' => null,
"extensions" => [],
'proxy' => [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
],
"timeout" => 30
];
/**
* Client constructor.
* @param array $config
*
* @throws MaskNotFoundException
*/
public function __construct(array $config = []) {
$this->setConfig($config);
$this->setMaskFromConfig($config);
$this->setEventsFromConfig($config);
}
/**
* Client destructor
*/
public function __destruct() {
$this->disconnect();
}
/**
* Set the Client configuration
* @param array $config
*
* @return self
*/
public function setConfig(array $config): Client {
$default_account = ClientManager::get('default');
$default_config = ClientManager::get("accounts.$default_account");
foreach ($this->default_account_config as $key => $value) {
$this->setAccountConfig($key, $config, $default_config);
}
return $this;
}
/**
* Set a specific account config
* @param string $key
* @param array $config
* @param array $default_config
*/
private function setAccountConfig(string $key, array $config, array $default_config){
$value = $this->default_account_config[$key];
if(isset($config[$key])) {
$value = $config[$key];
}elseif(isset($default_config[$key])) {
$value = $default_config[$key];
}
$this->$key = $value;
}
/**
* Look for a possible events in any available config
* @param $config
*/
protected function setEventsFromConfig($config) {
$this->events = ClientManager::get("events");
if(isset($config['events'])){
foreach($config['events'] as $section => $events) {
$this->events[$section] = array_merge($this->events[$section], $events);
}
}
}
/**
* Look for a possible mask in any available config
* @param $config
*
* @throws MaskNotFoundException
*/
protected function setMaskFromConfig($config) {
$default_config = ClientManager::get("masks");
if(isset($config['masks'])){
if(isset($config['masks']['message'])) {
if(class_exists($config['masks']['message'])) {
$this->default_message_mask = $config['masks']['message'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['message']);
}
}else{
if(class_exists($default_config['message'])) {
$this->default_message_mask = $default_config['message'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
}
}
if(isset($config['masks']['attachment'])) {
if(class_exists($config['masks']['attachment'])) {
$this->default_attachment_mask = $config['masks']['attachment'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$config['masks']['attachment']);
}
}else{
if(class_exists($default_config['attachment'])) {
$this->default_attachment_mask = $default_config['attachment'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
}
}
}else{
if(class_exists($default_config['message'])) {
$this->default_message_mask = $default_config['message'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$default_config['message']);
}
if(class_exists($default_config['attachment'])) {
$this->default_attachment_mask = $default_config['attachment'];
}else{
throw new MaskNotFoundException("Unknown mask provided: ".$default_config['attachment']);
}
}
}
/**
* Get the current imap resource
*
* @return bool|Protocol|ProtocolInterface
* @throws ConnectionFailedException
*/
public function getConnection() {
$this->checkConnection();
return $this->connection;
}
/**
* Determine if connection was established.
*
* @return bool
*/
public function isConnected(): bool {
return $this->connection && $this->connection->connected();
}
/**
* Determine if connection was established and connect if not.
*
* @throws ConnectionFailedException
*/
public function checkConnection() {
if (!$this->isConnected()) {
$this->connect();
}
}
/**
* Force the connection to reconnect
*
* @throws ConnectionFailedException
*/
public function reconnect() {
if ($this->isConnected()) {
$this->disconnect();
}
$this->connect();
}
/**
* Connect to server.
*
* @return $this
* @throws ConnectionFailedException
*/
public function connect(): Client {
$this->disconnect();
$protocol = strtolower($this->protocol);
if (in_array($protocol, ['imap', 'imap4', 'imap4rev1'])) {
$this->connection = new ImapProtocol($this->validate_cert, $this->encryption);
$this->connection->setConnectionTimeout($this->timeout);
$this->connection->setProxy($this->proxy);
}else{
if (extension_loaded('imap') === false) {
throw new ConnectionFailedException("connection setup failed", 0, new ProtocolNotSupportedException($protocol." is an unsupported protocol"));
}
$this->connection = new LegacyProtocol($this->validate_cert, $this->encryption);
if (strpos($protocol, "legacy-") === 0) {
$protocol = substr($protocol, 7);
}
$this->connection->setProtocol($protocol);
}
if (ClientManager::get('options.debug')) {
$this->connection->enableDebug();
}
if (!ClientManager::get('options.uid_cache')) {
$this->connection->disableUidCache();
}
try {
$this->connection->connect($this->host, $this->port);
} catch (ErrorException $e) {
throw new ConnectionFailedException("connection setup failed", 0, $e);
} catch (Exceptions\RuntimeException $e) {
throw new ConnectionFailedException("connection setup failed", 0, $e);
}
$this->authenticate();
return $this;
}
/**
* Authenticate the current session
*
* @throws ConnectionFailedException
*/
protected function authenticate() {
try {
if ($this->authentication == "oauth") {
if (!$this->connection->authenticate($this->username, $this->password)) {
throw new AuthFailedException();
}
} elseif (!$this->connection->login($this->username, $this->password)) {
throw new AuthFailedException();
}
} catch (AuthFailedException $e) {
throw new ConnectionFailedException("connection setup failed", 0, $e);
}
}
/**
* Disconnect from server.
*
* @return $this
*/
public function disconnect(): Client {
if ($this->isConnected() && $this->connection !== false) {
$this->connection->logout();
}
$this->active_folder = null;
return $this;
}
/**
* Get a folder instance by a folder name
* @param string $folder_name
* @param string|bool|null $delimiter
*
* @return Folder|null
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function getFolder(string $folder_name, $delimiter = null) {
if ($delimiter !== false && $delimiter !== null) {
return $this->getFolderByPath($folder_name);
}
// Set delimiter to false to force selection via getFolderByName (maybe useful for uncommon folder names)
$delimiter = is_null($delimiter) ? ClientManager::get('options.delimiter', "/") : $delimiter;
if (strpos($folder_name, (string)$delimiter) !== false) {
return $this->getFolderByPath($folder_name);
}
return $this->getFolderByName($folder_name);
}
/**
* Get a folder instance by a folder name
* @param $folder_name
*
* @return Folder|null
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function getFolderByName($folder_name) {
return $this->getFolders(false)->where("name", $folder_name)->first();
}
/**
* Get a folder instance by a folder path
* @param $folder_path
*
* @return Folder|null
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function getFolderByPath($folder_path) {
return $this->getFolders(false)->where("path", $folder_path)->first();
}
/**
* Get folders list.
* If hierarchical order is set to true, it will make a tree of folders, otherwise it will return flat array.
*
* @param boolean $hierarchical
* @param string|null $parent_folder
*
* @return FolderCollection
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function getFolders(bool $hierarchical = true, string $parent_folder = null): FolderCollection {
$this->checkConnection();
$folders = FolderCollection::make([]);
$pattern = $parent_folder.($hierarchical ? '%' : '*');
$items = $this->connection->folders('', $pattern);
if(is_array($items)){
foreach ($items as $folder_name => $item) {
$folder = new Folder($this, $folder_name, $item["delimiter"], $item["flags"]);
if ($hierarchical && $folder->hasChildren()) {
$pattern = $folder->full_name.$folder->delimiter.'%';
$children = $this->getFolders(true, $pattern);
$folder->setChildren($children);
}
$folders->push($folder);
}
return $folders;
}else{
throw new FolderFetchingException("failed to fetch any folders");
}
}
/**
* Get folders list.
* If hierarchical order is set to true, it will make a tree of folders, otherwise it will return flat array.
*
* @param boolean $hierarchical
* @param string|null $parent_folder
*
* @return FolderCollection
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function getFoldersWithStatus(bool $hierarchical = true, string $parent_folder = null): FolderCollection {
$this->checkConnection();
$folders = FolderCollection::make([]);
$pattern = $parent_folder.($hierarchical ? '%' : '*');
$items = $this->connection->folders('', $pattern);
if(is_array($items)){
foreach ($items as $folder_name => $item) {
$folder = new Folder($this, $folder_name, $item["delimiter"], $item["flags"]);
if ($hierarchical && $folder->hasChildren()) {
$pattern = $folder->full_name.$folder->delimiter.'%';
$children = $this->getFoldersWithStatus(true, $pattern);
$folder->setChildren($children);
}
$folder->loadStatus();
$folders->push($folder);
}
return $folders;
}else{
throw new FolderFetchingException("failed to fetch any folders");
}
}
/**
* Open a given folder.
* @param string $folder_path
* @param boolean $force_select
*
* @return array|bool
* @throws ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function openFolder(string $folder_path, bool $force_select = false) {
if ($this->active_folder == $folder_path && $this->isConnected() && $force_select === false) {
return true;
}
$this->checkConnection();
$this->active_folder = $folder_path;
return $this->connection->selectFolder($folder_path);
}
/**
* Create a new Folder
* @param string $folder
* @param boolean $expunge
*
* @return Folder
* @throws ConnectionFailedException
* @throws FolderFetchingException
* @throws Exceptions\EventNotFoundException
* @throws Exceptions\RuntimeException
*/
public function createFolder(string $folder, bool $expunge = true): Folder {
$this->checkConnection();
$status = $this->connection->createFolder($folder);
if($expunge) $this->expunge();
$folder = $this->getFolderByPath($folder);
if($status && $folder) {
$event = $this->getEvent("folder", "new");
$event::dispatch($folder);
}
return $folder;
}
/**
* Check a given folder
* @param $folder
*
* @return array|bool
* @throws ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function checkFolder($folder) {
$this->checkConnection();
return $this->connection->examineFolder($folder);
}
/**
* Get the current active folder
*
* @return string
*/
public function getFolderPath(){
return $this->active_folder;
}
/**
* Exchange identification information
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
*
* @param array|null $ids
* @return array|bool|void|null
*
* @throws ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function Id(array $ids = null) {
$this->checkConnection();
return $this->connection->ID($ids);
}
/**
* Retrieve the quota level settings, and usage statics per mailbox
*
* @return array
* @throws ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function getQuota(): array {
$this->checkConnection();
return $this->connection->getQuota($this->username);
}
/**
* Retrieve the quota settings per user
* @param string $quota_root
*
* @return array
* @throws ConnectionFailedException
*/
public function getQuotaRoot(string $quota_root = 'INBOX'): array {
$this->checkConnection();
return $this->connection->getQuotaRoot($quota_root);
}
/**
* Delete all messages marked for deletion
*
* @return bool
* @throws ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function expunge(): bool {
$this->checkConnection();
return $this->connection->expunge();
}
/**
* Set the connection timeout
* @param integer $timeout
*
* @return Protocol
* @throws ConnectionFailedException
*/
public function setTimeout(int $timeout): Protocol {
$this->timeout = $timeout;
if ($this->isConnected()) {
$this->connection->setConnectionTimeout($timeout);
$this->reconnect();
}
return $this->connection;
}
/**
* Get the connection timeout
*
* @return int
* @throws ConnectionFailedException
*/
public function getTimeout(): int {
$this->checkConnection();
return $this->connection->getConnectionTimeout();
}
/**
* Get the default message mask
*
* @return string
*/
public function getDefaultMessageMask(): string {
return $this->default_message_mask;
}
/**
* Get the default events for a given section
* @param $section
*
* @return array
*/
public function getDefaultEvents($section): array {
if (isset($this->events[$section])) {
return is_array($this->events[$section]) ? $this->events[$section] : [];
}
return [];
}
/**
* Set the default message mask
* @param string $mask
*
* @return $this
* @throws MaskNotFoundException
*/
public function setDefaultMessageMask(string $mask): Client {
if(class_exists($mask)) {
$this->default_message_mask = $mask;
return $this;
}
throw new MaskNotFoundException("Unknown mask provided: ".$mask);
}
/**
* Get the default attachment mask
*
* @return string
*/
public function getDefaultAttachmentMask(): string {
return $this->default_attachment_mask;
}
/**
* Set the default attachment mask
* @param string $mask
*
* @return $this
* @throws MaskNotFoundException
*/
public function setDefaultAttachmentMask(string $mask): Client {
if(class_exists($mask)) {
$this->default_attachment_mask = $mask;
return $this;
}
throw new MaskNotFoundException("Unknown mask provided: ".$mask);
}
}

View File

@@ -0,0 +1,276 @@
<?php
/*
* File: ClientManager.php
* Category: -
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
/**
* Class ClientManager
*
* @package Webklex\IMAP
*
* @mixin Client
*/
class ClientManager {
/**
* All library config
*
* @var array $config
*/
public static $config = [];
/**
* @var array $accounts
*/
protected $accounts = [];
/**
* ClientManager constructor.
* @param array|string $config
*/
public function __construct($config = []) {
$this->setConfig($config);
}
/**
* Dynamically pass calls to the default account.
* @param string $method
* @param array $parameters
*
* @return mixed
* @throws Exceptions\MaskNotFoundException
*/
public function __call(string $method, array $parameters) {
$callable = [$this->account(), $method];
return call_user_func_array($callable, $parameters);
}
/**
* Safely create a new client instance which is not listed in accounts
* @param array $config
*
* @return Client
* @throws Exceptions\MaskNotFoundException
*/
public function make(array $config): Client {
return new Client($config);
}
/**
* Get a dotted config parameter
* @param string $key
* @param null $default
*
* @return mixed|null
*/
public static function get(string $key, $default = null) {
$parts = explode('.', $key);
$value = null;
foreach($parts as $part) {
if($value === null) {
if(isset(self::$config[$part])) {
$value = self::$config[$part];
}else{
break;
}
}else{
if(isset($value[$part])) {
$value = $value[$part];
}else{
break;
}
}
}
return $value === null ? $default : $value;
}
/**
* Resolve a account instance.
* @param string|null $name
*
* @return Client
* @throws Exceptions\MaskNotFoundException
*/
public function account(string $name = null): Client {
$name = $name ?: $this->getDefaultAccount();
// If the connection has not been resolved we will resolve it now as all
// the connections are resolved when they are actually needed, so we do
// not make any unnecessary connection to the various queue end-points.
if (!isset($this->accounts[$name])) {
$this->accounts[$name] = $this->resolve($name);
}
return $this->accounts[$name];
}
/**
* Resolve an account.
* @param string $name
*
* @return Client
* @throws Exceptions\MaskNotFoundException
*/
protected function resolve(string $name): Client {
$config = $this->getClientConfig($name);
return new Client($config);
}
/**
* Get the account configuration.
* @param string|null $name
*
* @return array
*/
protected function getClientConfig($name): array {
if ($name === null || $name === 'null') {
return ['driver' => 'null'];
}
return is_array(self::$config["accounts"][$name]) ? self::$config["accounts"][$name] : [];
}
/**
* Get the name of the default account.
*
* @return string
*/
public function getDefaultAccount(): string {
return self::$config['default'];
}
/**
* Set the name of the default account.
* @param string $name
*
* @return void
*/
public function setDefaultAccount(string $name) {
self::$config['default'] = $name;
}
/**
* Merge the vendor settings with the local config
*
* The default account identifier will be used as default for any missing account parameters.
* If however the default account is missing a parameter the package default account parameter will be used.
* This can be disabled by setting imap.default in your config file to 'false'
*
* @param array|string $config
*
* @return $this
*/
public function setConfig($config): ClientManager {
if(is_array($config) === false) {
$config = require $config;
}
$config_key = 'imap';
$path = __DIR__.'/config/'.$config_key.'.php';
$vendor_config = require $path;
$config = $this->array_merge_recursive_distinct($vendor_config, $config);
if(is_array($config)){
if(isset($config['default'])){
if(isset($config['accounts']) && $config['default']){
$default_config = $vendor_config['accounts']['default'];
if(isset($config['accounts'][$config['default']])){
$default_config = array_merge($default_config, $config['accounts'][$config['default']]);
}
if(is_array($config['accounts'])){
foreach($config['accounts'] as $account_key => $account){
$config['accounts'][$account_key] = array_merge($default_config, $account);
}
}
}
}
}
self::$config = $config;
return $this;
}
/**
* Marge arrays recursively and distinct
*
* Merges any number of arrays / parameters recursively, replacing
* entries with string keys with values from latter arrays.
* If the entry or the next value to be assigned is an array, then it
* automatically treats both arguments as an array.
* Numeric entries are appended, not replaced, but only if they are
* unique
*
* @return array|mixed
*
* @link http://www.php.net/manual/en/function.array-merge-recursive.php#96201
* @author Mark Roduner <mark.roduner@gmail.com>
*/
private function array_merge_recursive_distinct() {
$arrays = func_get_args();
$base = array_shift($arrays);
// From https://stackoverflow.com/a/173479
$isAssoc = function(array $arr) {
if (array() === $arr) return false;
return array_keys($arr) !== range(0, count($arr) - 1);
};
if(!is_array($base)) $base = empty($base) ? array() : array($base);
foreach($arrays as $append) {
if(!is_array($append)) $append = array($append);
foreach($append as $key => $value) {
if(!array_key_exists($key, $base) and !is_numeric($key)) {
$base[$key] = $value;
continue;
}
if(
(
is_array($value)
&& $isAssoc($value)
)
|| (
is_array($base[$key])
&& $isAssoc($base[$key])
)
) {
// If the arrays are not associates we don't want to array_merge_recursive_distinct
// else merging $baseConfig['dispositions'] = ['attachment', 'inline'] with $customConfig['dispositions'] = ['attachment']
// results in $resultConfig['dispositions'] = ['attachment', 'inline']
$base[$key] = $this->array_merge_recursive_distinct($base[$key], $value);
} else if(is_numeric($key)) {
if(!in_array($value, $base)) $base[] = $value;
} else {
$base[$key] = $value;
}
}
}
return $base;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,643 @@
<?php
/*
* File: LegacyProtocol.php
* Category: Protocol
* Author: M.Goldenbaum
* Created: 16.09.20 18:27
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Connection\Protocols;
use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\MethodNotSupportedException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\IMAP;
/**
* Class LegacyProtocol
*
* @package Webklex\PHPIMAP\Connection\Protocols
*/
class LegacyProtocol extends Protocol {
protected $protocol = "imap";
protected $host = null;
protected $port = null;
protected $encryption = null;
/**
* Imap constructor.
* @param bool $cert_validation set to false to skip SSL certificate validation
* @param mixed $encryption Connection encryption method
*/
public function __construct(bool $cert_validation = true, $encryption = false) {
$this->setCertValidation($cert_validation);
$this->encryption = $encryption;
}
/**
* Public destructor
*/
public function __destruct() {
$this->logout();
}
/**
* Save the information for a nw connection
* @param string $host
* @param null $port
*/
public function connect(string $host, $port = null) {
if ($this->encryption) {
$encryption = strtolower($this->encryption);
if ($encryption == "ssl") {
$port = $port === null ? 993 : $port;
}
}
$port = $port === null ? 143 : $port;
$this->host = $host;
$this->port = $port;
}
/**
* Login to a new session.
* @param string $user username
* @param string $password password
*
* @return bool
* @throws AuthFailedException
* @throws RuntimeException
*/
public function login(string $user, string $password): bool {
try {
$this->stream = \imap_open(
$this->getAddress(),
$user,
$password,
0,
$attempts = 3,
ClientManager::get('options.open')
);
} catch (\ErrorException $e) {
$errors = \imap_errors();
$message = $e->getMessage().'. '.implode("; ", (is_array($errors) ? $errors : array()));
throw new AuthFailedException($message);
}
if(!$this->stream) {
$errors = \imap_errors();
$message = implode("; ", (is_array($errors) ? $errors : array()));
throw new AuthFailedException($message);
}
$errors = \imap_errors();
if(is_array($errors)) {
$status = $this->examineFolder();
if($status['exists'] !== 0) {
$message = implode("; ", (is_array($errors) ? $errors : array()));
throw new RuntimeException($message);
}
}
return $this->stream !== false;
}
/**
* Authenticate your current session.
* @param string $user username
* @param string $token access token
*
* @return bool|resource
* @throws AuthFailedException|RuntimeException
*/
public function authenticate(string $user, string $token): bool {
return $this->login($user, $token);
}
/**
* Get full address of mailbox.
*
* @return string
*/
protected function getAddress(): string {
$address = "{".$this->host.":".$this->port."/".$this->protocol;
if (!$this->cert_validation) {
$address .= '/novalidate-cert';
}
if (in_array($this->encryption,['tls', 'notls', 'ssl'])) {
$address .= '/'.$this->encryption;
} elseif ($this->encryption === "starttls") {
$address .= '/tls';
}
$address .= '}';
return $address;
}
/**
* Logout of the current session
*
* @return bool success
*/
public function logout(): bool {
if ($this->stream) {
$result = \imap_close($this->stream, IMAP::CL_EXPUNGE);
$this->stream = false;
$this->uid_cache = null;
return $result;
}
return false;
}
/**
* Check if the current session is connected
*
* @return bool
*/
public function connected(): bool {
return boolval($this->stream);
}
/**
* Get an array of available capabilities
*
* @throws MethodNotSupportedException
*/
public function getCapabilities(): array {
throw new MethodNotSupportedException();
}
/**
* Change the current folder
* @param string $folder change to this folder
*
* @return bool|array see examineOrselect()
* @throws RuntimeException
*/
public function selectFolder(string $folder = 'INBOX') {
$flags = IMAP::OP_READONLY;
if (in_array($this->protocol, ["pop3", "nntp"])) {
$flags = IMAP::NIL;
}
if ($this->stream === false) {
throw new RuntimeException("failed to reopen stream.");
}
\imap_reopen($this->stream, $this->getAddress().$folder, $flags, 3);
$this->uid_cache = null;
return $this->examineFolder($folder);
}
/**
* Examine a given folder
* @param string $folder examine this folder
*
* @return bool|array
* @throws RuntimeException
*/
public function examineFolder(string $folder = 'INBOX') {
if (strpos($folder, ".") === 0) {
throw new RuntimeException("Segmentation fault prevented. Folders starts with an illegal char '.'.");
}
$folder = $this->getAddress().$folder;
$status = \imap_status($this->stream, $folder, IMAP::SA_ALL);
return [
"flags" => [],
"exists" => $status->messages,
"recent" => $status->recent,
"unseen" => $status->unseen,
"uidnext" => $status->uidnext,
];
}
/**
* Fetch message content
* @param array|int $uids
* @param string $rfc
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array
*/
public function content($uids, string $rfc = "RFC822", $uid = IMAP::ST_UID): array {
$result = [];
$uids = is_array($uids) ? $uids : [$uids];
foreach ($uids as $id) {
$result[$id] = \imap_fetchbody($this->stream, $id, "", $uid ? IMAP::ST_UID : IMAP::NIL);
}
return $result;
}
/**
* Fetch message headers
* @param array|int $uids
* @param string $rfc
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array
*/
public function headers($uids, string $rfc = "RFC822", $uid = IMAP::ST_UID): array {
$result = [];
$uids = is_array($uids) ? $uids : [$uids];
foreach ($uids as $id) {
$result[$id] = \imap_fetchheader($this->stream, $id, $uid ? IMAP::ST_UID : IMAP::NIL);
}
return $result;
}
/**
* Fetch message flags
* @param array|int $uids
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array
*/
public function flags($uids, $uid = IMAP::ST_UID): array {
$result = [];
$uids = is_array($uids) ? $uids : [$uids];
foreach ($uids as $id) {
$raw_flags = \imap_fetch_overview($this->stream, $id, $uid ? IMAP::ST_UID : IMAP::NIL);
$flags = [];
if (is_array($raw_flags) && isset($raw_flags[0])) {
$raw_flags = (array) $raw_flags[0];
foreach($raw_flags as $flag => $value) {
if ($value === 1 && in_array($flag, ["size", "uid", "msgno", "update"]) === false){
$flags[] = "\\".ucfirst($flag);
}
}
}
$result[$uid] = $flags;
}
return $result;
}
/**
* Get uid for a given id
* @param int|null $id message number
*
* @return array|string message number for given message or all messages as array
*/
public function getUid($id = null) {
if ($id === null) {
if ($this->enable_uid_cache && $this->uid_cache) {
return $this->uid_cache;
}
$overview = $this->overview("1:*");
$uids = [];
foreach($overview as $set){
$uids[$set->msgno] = $set->uid;
}
$this->setUidCache($uids);
return $uids;
}
return \imap_uid($this->stream, $id);
}
/**
* Get a message number for a uid
* @param string $id uid
*
* @return int message number
*/
public function getMessageNumber(string $id): int {
return \imap_msgno($this->stream, $id);
}
/**
* Get a message overview
* @param string $sequence uid sequence
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array
*/
public function overview(string $sequence, $uid = IMAP::ST_UID): array {
return \imap_fetch_overview($this->stream, $sequence,$uid ? IMAP::ST_UID : IMAP::NIL);
}
/**
* Get a list of available folders
* @param string $reference mailbox reference for list
* @param string $folder mailbox name match with wildcards
*
* @return array folders that matched $folder as array(name => array('delimiter' => .., 'flags' => ..))
* @throws RuntimeException
*/
public function folders(string $reference = '', string $folder = '*'): array {
$result = [];
$items = \imap_getmailboxes($this->stream, $this->getAddress(), $reference.$folder);
if(is_array($items)){
foreach ($items as $item) {
$name = $this->decodeFolderName($item->name);
$result[$name] = ['delimiter' => $item->delimiter, 'flags' => []];
}
}else{
throw new RuntimeException(\imap_last_error());
}
return $result;
}
/**
* Manage flags
* @param array $flags flags to set, add or remove - see $mode
* @param int $from message for items or start message if $to !== null
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
* @param bool $silent if false the return values are the new flags for the wanted messages
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
* @param null $item unused attribute
*
* @return bool|array new flags if $silent is false, else true or false depending on success
*/
public function store(array $flags, int $from, $to = null, $mode = null, bool $silent = true, $uid = IMAP::ST_UID, $item = null) {
$flag = trim(is_array($flags) ? implode(" ", $flags) : $flags);
if ($mode == "+"){
$status = \imap_setflag_full($this->stream, $from, $flag, $uid ? IMAP::ST_UID : IMAP::NIL);
}else{
$status = \imap_clearflag_full($this->stream, $from, $flag, $uid ? IMAP::ST_UID : IMAP::NIL);
}
if ($silent === true) {
return $status;
}
return $this->flags($from);
}
/**
* Append a new message to given folder
* @param string $folder name of target folder
* @param string $message full message content
* @param array|null $flags flags for new message
* @param string $date date for new message
*
* @return bool success
*/
public function appendMessage(string $folder, string $message, $flags = null, $date = null): bool {
if ($date != null) {
if ($date instanceof \Carbon\Carbon){
$date = $date->format('d-M-Y H:i:s O');
}
return \imap_append($this->stream, $folder, $message, $flags, $date);
}
return \imap_append($this->stream, $folder, $message, $flags);
}
/**
* Copy message set from current folder to other folder
* @param string $folder destination folder
* @param $from
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return bool success
*/
public function copyMessage(string $folder, $from, $to = null, $uid = IMAP::ST_UID): bool {
return \imap_mail_copy($this->stream, $from, $folder, $uid ? IMAP::ST_UID : IMAP::NIL);
}
/**
* Copy multiple messages to the target folder
* @param array $messages List of message identifiers
* @param string $folder Destination folder
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array|bool Tokens if operation successful, false if an error occurred
*/
public function copyManyMessages(array $messages, string $folder, $uid = IMAP::ST_UID) {
foreach($messages as $msg) {
if (!$this->copyMessage($folder, $msg, null, $uid)) {
return false;
}
}
return $messages;
}
/**
* Move a message set from current folder to another folder
* @param string $folder destination folder
* @param $from
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return bool success
*/
public function moveMessage(string $folder, $from, $to = null, $uid = IMAP::ST_UID): bool {
return \imap_mail_move($this->stream, $from, $folder, $uid ? IMAP::ST_UID : IMAP::NIL);
}
/**
* Move multiple messages to the target folder
* @param array $messages List of message identifiers
* @param string $folder Destination folder
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array|bool Tokens if operation successful, false if an error occurred
*/
public function moveManyMessages(array $messages, string $folder, $uid = IMAP::ST_UID) {
foreach($messages as $msg) {
if (!$this->moveMessage($folder, $msg, null, $uid)) {
return false;
}
}
return $messages;
}
/**
* Exchange identification information
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
*
* @param null $ids
* @return array|bool|void|null
*
* @throws MethodNotSupportedException
*/
public function ID($ids = null) {
throw new MethodNotSupportedException();
}
/**
* Create a new folder (and parent folders if needed)
* @param string $folder folder name
*
* @return bool success
*/
public function createFolder(string $folder): bool {
return \imap_createmailbox($this->stream, $folder);
}
/**
* Rename an existing folder
* @param string $old old name
* @param string $new new name
*
* @return bool success
*/
public function renameFolder(string $old, string $new): bool {
return \imap_renamemailbox($this->stream, $old, $new);
}
/**
* Delete a folder
* @param string $folder folder name
*
* @return bool success
*/
public function deleteFolder(string $folder): bool {
return \imap_deletemailbox($this->stream, $folder);
}
/**
* Subscribe to a folder
* @param string $folder folder name
*
* @throws MethodNotSupportedException
*/
public function subscribeFolder(string $folder): bool {
throw new MethodNotSupportedException();
}
/**
* Unsubscribe from a folder
* @param string $folder folder name
*
* @throws MethodNotSupportedException
*/
public function unsubscribeFolder(string $folder): bool {
throw new MethodNotSupportedException();
}
/**
* Apply session saved changes to the server
*
* @return bool success
*/
public function expunge(): bool {
return \imap_expunge($this->stream);
}
/**
* Send noop command
*
* @throws MethodNotSupportedException
*/
public function noop(): bool {
throw new MethodNotSupportedException();
}
/**
* Send idle command
*
* @throws MethodNotSupportedException
*/
public function idle() {
throw new MethodNotSupportedException();
}
/**
* Send done command
*
* @throws MethodNotSupportedException
*/
public function done() {
throw new MethodNotSupportedException();
}
/**
* Search for matching messages
* @param array $params
* @param int $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
*
* @return array message ids
*/
public function search(array $params, $uid = IMAP::ST_UID): array {
$result = \imap_search($this->stream, $params[0], $uid ? IMAP::ST_UID : IMAP::NIL);
if ($result === false) {
return [];
}
return $result;
}
/**
* Enable the debug mode
*/
public function enableDebug(){
$this->debug = true;
}
/**
* Disable the debug mode
*/
public function disableDebug(){
$this->debug = false;
}
/**
* Decode name.
* It converts UTF7-IMAP encoding to UTF-8.
*
* @param $name
*
* @return array|false|string|string[]|null
*/
protected function decodeFolderName($name) {
preg_match('#\{(.*)\}(.*)#', $name, $preg);
return mb_convert_encoding($preg[2], "UTF-8", "UTF7-IMAP");
}
/**
* @return string
*/
public function getProtocol(): string {
return $this->protocol;
}
/**
* Retrieve the quota level settings, and usage statics per mailbox
* @param $username
*
* @return array
*/
public function getQuota($username): array {
return \imap_get_quota($this->stream, 'user.'.$username);
}
/**
* Retrieve the quota settings per user
* @param string $quota_root
*
* @return array
*/
public function getQuotaRoot(string $quota_root = 'INBOX'): array {
return \imap_get_quotaroot($this->stream, $quota_root);
}
/**
* @param string $protocol
* @return LegacyProtocol
*/
public function setProtocol(string $protocol): LegacyProtocol {
if (($pos = strpos($protocol, "legacy")) > 0) {
$protocol = substr($protocol, 0, ($pos + 2) * -1);
}
$this->protocol = $protocol;
return $this;
}
}

View File

@@ -0,0 +1,285 @@
<?php
/*
* File: ImapProtocol.php
* Category: Protocol
* Author: M.Goldenbaum
* Created: 16.09.20 18:27
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Connection\Protocols;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\IMAP;
/**
* Class Protocol
*
* @package Webklex\PHPIMAP\Connection\Protocols
*/
abstract class Protocol implements ProtocolInterface {
/**
* Default connection timeout in seconds
*/
protected $connection_timeout = 30;
/**
* @var boolean
*/
protected $debug = false;
/**
* @var boolean
*/
protected $enable_uid_cache = true;
/**
* @var false|\IMAP\Connection|resource
*/
public $stream = false;
/**
* Connection encryption method
* @var mixed $encryption
*/
protected $encryption = false;
/**
* Set to false to ignore SSL certificate validation
* @var bool
*/
protected $cert_validation = true;
/**
* Proxy settings
* @var array
*/
protected $proxy = [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
];
/**
* Cache for uid of active folder.
*
* @var null|array
*/
protected $uid_cache = null;
/**
* Get an available cryptographic method
*
* @return int
*/
public function getCryptoMethod() {
// Allow the best TLS version(s) we can
$cryptoMethod = STREAM_CRYPTO_METHOD_TLS_CLIENT;
// PHP 5.6.7 dropped inclusion of TLS 1.1 and 1.2 in STREAM_CRYPTO_METHOD_TLS_CLIENT
// so add them back in manually if we can
if (defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT')) {
$cryptoMethod = STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT;
}elseif (defined('STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT')) {
$cryptoMethod = STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT;
}
return $cryptoMethod;
}
/**
* Enable SSL certificate validation
*
* @return $this
*/
public function enableCertValidation() {
$this->cert_validation = true;
return $this;
}
/**
* Disable SSL certificate validation
* @return $this
*/
public function disableCertValidation() {
$this->cert_validation = false;
return $this;
}
/**
* Set SSL certificate validation
* @var int $cert_validation
*
* @return $this
*/
public function setCertValidation($cert_validation) {
$this->cert_validation = $cert_validation;
return $this;
}
/**
* Should we validate SSL certificate?
*
* @return bool
*/
public function getCertValidation() {
return $this->cert_validation;
}
/**
* Set connection proxy settings
* @var array $options
*
* @return $this
*/
public function setProxy($options) {
foreach ($this->proxy as $key => $val) {
if (isset($options[$key])) {
$this->proxy[$key] = $options[$key];
}
}
return $this;
}
/**
* Get the current proxy settings
*
* @return array
*/
public function getProxy() {
return $this->proxy;
}
/**
* Prepare socket options
* @var string $transport
*
* @return array
*/
private function defaultSocketOptions($transport) {
$options = [];
if ($this->encryption != false) {
$options["ssl"] = [
'verify_peer_name' => $this->getCertValidation(),
'verify_peer' => $this->getCertValidation(),
];
}
if ($this->proxy["socket"] != null) {
$options[$transport]["proxy"] = $this->proxy["socket"];
$options[$transport]["request_fulluri"] = $this->proxy["request_fulluri"];
if ($this->proxy["username"] != null) {
$auth = base64_encode($this->proxy["username"].':'.$this->proxy["password"]);
$options[$transport]["header"] = [
"Proxy-Authorization: Basic $auth"
];
}
}
return $options;
}
/**
* Create a new resource stream
* @param $transport
* @param string $host hostname or IP address of IMAP server
* @param int $port of IMAP server, default is 143 (993 for ssl)
* @param int $timeout timeout in seconds for initiating session
*
* @return resource|boolean The socket created.
* @throws ConnectionFailedException
*/
protected function createStream($transport, $host, $port, $timeout) {
$socket = "$transport://$host:$port";
$stream = stream_socket_client($socket, $errno, $errstr, $timeout,
STREAM_CLIENT_CONNECT,
stream_context_create($this->defaultSocketOptions($transport))
);
if (!$stream) {
throw new ConnectionFailedException($errstr, $errno);
}
if (false === stream_set_timeout($stream, $timeout)) {
throw new ConnectionFailedException('Failed to set stream timeout');
}
return $stream;
}
/**
* @return int
*/
public function getConnectionTimeout() {
return $this->connection_timeout;
}
/**
* @param int $connection_timeout
* @return Protocol
*/
public function setConnectionTimeout($connection_timeout) {
if ($connection_timeout !== null) {
$this->connection_timeout = $connection_timeout;
}
return $this;
}
/**
* Get the UID key string
* @param int|string $uid
*
* @return string
*/
public function getUIDKey($uid) {
if ($uid == IMAP::ST_UID || $uid == IMAP::FT_UID) {
return "UID";
}
if (strlen($uid) > 0 && !is_numeric($uid)) {
return (string)$uid;
}
return "";
}
public function buildUIDCommand($command, $uid) {
return trim($this->getUIDKey($uid)." ".$command);
}
/**
* Set the uid cache of current active folder
*
* @param array|null $uids
*/
public function setUidCache($uids) {
if (is_null($uids)) {
$this->uid_cache = null;
return;
}
$messageNumber = 1;
$uid_cache = [];
foreach ($uids as $uid) {
$uid_cache[$messageNumber++] = $uid;
}
$this->uid_cache = $uid_cache;
}
public function enableUidCache() {
$this->enable_uid_cache = true;
}
public function disableUidCache() {
$this->enable_uid_cache = false;
}
}

View File

@@ -0,0 +1,408 @@
<?php
/*
* File: ImapProtocol.php
* Category: Protocol
* Author: M.Goldenbaum
* Created: 16.09.20 18:27
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Connection\Protocols;
use ErrorException;
use Webklex\PHPIMAP\Client;
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\IMAP;
/**
* Interface ProtocolInterface
*
* @package Webklex\PHPIMAP\Connection\Protocols
*/
interface ProtocolInterface {
/**
* Public destructor
*/
public function __destruct();
/**
* Open a new connection / session
* @param string $host hostname or IP address of IMAP server
* @param int|null $port of service server
*
* @throws ErrorException
* @throws ConnectionFailedException
* @throws RuntimeException
*/
public function connect(string $host, $port = null);
/**
* Login to a new session.
*
* @param string $user username
* @param string $password password
* @return bool success
* @throws AuthFailedException
*/
public function login(string $user, string $password): bool;
/**
* Authenticate your current session.
* @param string $user username
* @param string $token access token
*
* @return bool|mixed
* @throws AuthFailedException
*/
public function authenticate(string $user, string $token);
/**
* Logout of the current server session
*
* @return bool success
*/
public function logout(): bool;
/**
* Check if the current session is connected
*
* @return bool
*/
public function connected(): bool;
/**
* Get an array of available capabilities
*
* @return array list of capabilities
* @throws RuntimeException
*/
public function getCapabilities(): array;
/**
* Change the current folder
*
* @param string $folder change to this folder
* @return bool|array see examineOrSelect()
* @throws RuntimeException
*/
public function selectFolder(string $folder = 'INBOX');
/**
* Examine a given folder
*
* @param string $folder
* @return bool|array
* @throws RuntimeException
*/
public function examineFolder(string $folder = 'INBOX');
/**
* Fetch message headers
* @param array|int $uids
* @param string $rfc
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array
* @throws RuntimeException
*/
public function content($uids, string $rfc = "RFC822", $uid = IMAP::ST_UID): array;
/**
* Fetch message headers
* @param array|int $uids
* @param string $rfc
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array
* @throws RuntimeException
*/
public function headers($uids, string $rfc = "RFC822", $uid = IMAP::ST_UID): array;
/**
* Fetch message flags
* @param array|int $uids
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array
* @throws RuntimeException
*/
public function flags($uids, $uid = IMAP::ST_UID): array;
/**
* Get uid for a given id
* @param int|null $id message number
*
* @return array|string message number for given message or all messages as array
* @throws MessageNotFoundException
*/
public function getUid($id = null);
/**
* Get a message number for a uid
* @param string $id uid
*
* @return int message number
* @throws MessageNotFoundException
*/
public function getMessageNumber(string $id): int;
/**
* Get a list of available folders
* @param string $reference mailbox reference for list
* @param string $folder mailbox / folder name match with wildcards
*
* @return array mailboxes that matched $folder as array(globalName => array('delim' => .., 'flags' => ..))
* @throws RuntimeException
*/
public function folders(string $reference = '', string $folder = '*'): array;
/**
* Set message flags
* @param array $flags flags to set, add or remove
* @param int $from message for items or start message if $to !== null
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param string|null $mode '+' to add flags, '-' to remove flags, everything else sets the flags as given
* @param bool $silent if false the return values are the new flags for the wanted messages
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
* @param null|string $item command used to store a flag
*
* @return bool|array new flags if $silent is false, else true or false depending on success
* @throws RuntimeException
*/
public function store(array $flags, int $from, $to = null, $mode = null, bool $silent = true, $uid = IMAP::ST_UID, $item = null);
/**
* Append a new message to given folder
* @param string $folder name of target folder
* @param string $message full message content
* @param array|null $flags flags for new message
* @param string|null $date date for new message
*
* @return bool success
* @throws RuntimeException
*/
public function appendMessage(string $folder, string $message, $flags = null, $date = null): bool;
/**
* Copy message set from current folder to other folder
*
* @param string $folder destination folder
* @param $from
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return bool success
* @throws RuntimeException
*/
public function copyMessage(string $folder, $from, $to = null, $uid = IMAP::ST_UID): bool;
/**
* Copy multiple messages to the target folder
* @param array<string> $messages List of message identifiers
* @param string $folder Destination folder
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array|bool Tokens if operation successful, false if an error occurred
* @throws RuntimeException
*/
public function copyManyMessages(array $messages, string $folder, $uid = IMAP::ST_UID);
/**
* Move a message set from current folder to another folder
* @param string $folder destination folder
* @param $from
* @param int|null $to if null only one message ($from) is fetched, else it's the
* last message, INF means last message available
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return bool success
*/
public function moveMessage(string $folder, $from, $to = null, $uid = IMAP::ST_UID): bool;
/**
* Move multiple messages to the target folder
*
* @param array<string> $messages List of message identifiers
* @param string $folder Destination folder
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array|bool Tokens if operation successful, false if an error occurred
* @throws RuntimeException
*/
public function moveManyMessages(array $messages, string $folder, $uid = IMAP::ST_UID);
/**
* Exchange identification information
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
*
* @param null $ids
* @return array|bool|void|null
*
* @throws RuntimeException
*/
public function ID($ids = null);
/**
* Create a new folder
*
* @param string $folder folder name
* @return bool success
* @throws RuntimeException
*/
public function createFolder(string $folder): bool;
/**
* Rename an existing folder
*
* @param string $old old name
* @param string $new new name
* @return bool success
* @throws RuntimeException
*/
public function renameFolder(string $old, string $new): bool;
/**
* Delete a folder
*
* @param string $folder folder name
* @return bool success
* @throws RuntimeException
*/
public function deleteFolder(string $folder): bool;
/**
* Subscribe to a folder
*
* @param string $folder folder name
* @return bool success
* @throws RuntimeException
*/
public function subscribeFolder(string $folder): bool;
/**
* Unsubscribe from a folder
* @param string $folder folder name
*
* @return bool success
* @throws RuntimeException
*/
public function unsubscribeFolder(string $folder): bool;
/**
* Send idle command
*
* @throws RuntimeException
*/
public function idle();
/**
* Send done command
* @throws RuntimeException
*/
public function done();
/**
* Apply session saved changes to the server
*
* @return bool success
* @throws RuntimeException
*/
public function expunge(): bool;
/**
* Retrieve the quota level settings, and usage statics per mailbox
* @param $username
*
* @return array
* @throws RuntimeException
*/
public function getQuota($username): array;
/**
* Retrieve the quota settings per user
*
* @param string $quota_root
*
* @return array
* @throws ConnectionFailedException
*/
public function getQuotaRoot(string $quota_root = 'INBOX'): array;
/**
* Send noop command
*
* @return bool success
* @throws RuntimeException
*/
public function noop(): bool;
/**
* Do a search request
*
* @param array $params
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array message ids
* @throws RuntimeException
*/
public function search(array $params, $uid = IMAP::ST_UID): array;
/**
* Get a message overview
* @param string $sequence uid sequence
* @param int|string $uid set to IMAP::ST_UID or any string representing the UID - set to IMAP::ST_MSGN to use
* message numbers instead.
*
* @return array
* @throws RuntimeException
* @throws MessageNotFoundException
* @throws InvalidMessageDateException
*/
public function overview(string $sequence, $uid = IMAP::ST_UID): array;
/**
* Enable the debug mode
*/
public function enableDebug();
/**
* Disable the debug mode
*/
public function disableDebug();
/**
* Enable uid caching
*/
public function enableUidCache();
/**
* Disable uid caching
*/
public function disableUidCache();
/**
* Set the uid cache of current active folder
*
* @param array|null $uids
*/
public function setUidCache($uids);
}

View File

@@ -0,0 +1,482 @@
<?php
/*
* File: EncodingAliases.php
* Category: -
* Author: S. Todorov (https://github.com/todorowww)
* Created: 23.04.18 14:16
* Updated: -
*
* Description:
* Contains email encoding aliases, thta can occur when fetching emails. These sometimes can break icvon()
* This file attempts to correct this by using a list of aliases and their mappings to supported iconv() encodings
*/
namespace Webklex\PHPIMAP;
/**
* Class EncodingAliases
*
* @package Webklex\PHPIMAP
*/
class EncodingAliases {
/**
* Contains email encoding mappings
*
* @var array
*/
private static $aliases = [
/*
|--------------------------------------------------------------------------
| Email encoding aliases
|--------------------------------------------------------------------------
|
| Email encoding aliases used to convert to iconv supported charsets
|
|
| This Source Code Form is subject to the terms of the Mozilla Public
| License, v. 2.0. If a copy of the MPL was not distributed with this
| file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
| This Original Code has been modified by IBM Corporation.
| Modifications made by IBM described herein are
| Copyright (c) International Business Machines
| Corporation, 1999
|
| Modifications to Mozilla code or documentation
| identified per MPL Section 3.3
|
| Date Modified by Description of modification
| 12/09/1999 IBM Corp. Support for IBM codepages - 850,852,855,857,862,864
|
| Rule of this file:
| 1. key should always be in lower case ascii so we can do case insensitive
| comparison in the code faster.
| 2. value should be the one used in unicode converter
|
| 3. If the charset is not used for document charset, but font charset
| (e.g. XLFD charset- such as JIS x0201, JIS x0208), don't put here
|
*/
"ascii" => "us-ascii",
"us-ascii" => "us-ascii",
"ansi_x3.4-1968" => "us-ascii",
"646" => "us-ascii",
"iso-8859-1" => "ISO-8859-1",
"iso-8859-2" => "ISO-8859-2",
"iso-8859-3" => "ISO-8859-3",
"iso-8859-4" => "ISO-8859-4",
"iso-8859-5" => "ISO-8859-5",
"iso-8859-6" => "ISO-8859-6",
"iso-8859-6-i" => "ISO-8859-6-I",
"iso-8859-6-e" => "ISO-8859-6-E",
"iso-8859-7" => "ISO-8859-7",
"iso-8859-8" => "ISO-8859-8",
"iso-8859-8-i" => "ISO-8859-8-I",
"iso-8859-8-e" => "ISO-8859-8-E",
"iso-8859-9" => "ISO-8859-9",
"iso-8859-10" => "ISO-8859-10",
"iso-8859-11" => "ISO-8859-11",
"iso-8859-13" => "ISO-8859-13",
"iso-8859-14" => "ISO-8859-14",
"iso-8859-15" => "ISO-8859-15",
"iso-8859-16" => "ISO-8859-16",
"iso-ir-111" => "ISO-IR-111",
"iso-2022-cn" => "ISO-2022-CN",
"iso-2022-cn-ext" => "ISO-2022-CN",
"iso-2022-kr" => "ISO-2022-KR",
"iso-2022-jp" => "ISO-2022-JP",
"utf-16be" => "UTF-16BE",
"utf-16le" => "UTF-16LE",
"utf-16" => "UTF-16",
"windows-1250" => "windows-1250",
"windows-1251" => "windows-1251",
"windows-1252" => "windows-1252",
"windows-1253" => "windows-1253",
"windows-1254" => "windows-1254",
"windows-1255" => "windows-1255",
"windows-1256" => "windows-1256",
"windows-1257" => "windows-1257",
"windows-1258" => "windows-1258",
"ibm866" => "IBM866",
"ibm850" => "IBM850",
"ibm852" => "IBM852",
"ibm855" => "IBM855",
"ibm857" => "IBM857",
"ibm862" => "IBM862",
"ibm864" => "IBM864",
"utf-8" => "UTF-8",
"utf-7" => "UTF-7",
"shift_jis" => "Shift_JIS",
"big5" => "Big5",
"euc-jp" => "EUC-JP",
"euc-kr" => "EUC-KR",
"gb2312" => "GB2312",
"gb18030" => "gb18030",
"viscii" => "VISCII",
"koi8-r" => "KOI8-R",
"koi8_r" => "KOI8-R",
"cskoi8r" => "KOI8-R",
"koi" => "KOI8-R",
"koi8" => "KOI8-R",
"koi8-u" => "KOI8-U",
"tis-620" => "TIS-620",
"t.61-8bit" => "T.61-8bit",
"hz-gb-2312" => "HZ-GB-2312",
"big5-hkscs" => "Big5-HKSCS",
"gbk" => "gbk",
"cns11643" => "x-euc-tw",
//
// Aliases for ISO-8859-1
//
"latin1" => "ISO-8859-1",
"iso_8859-1" => "ISO-8859-1",
"iso8859-1" => "ISO-8859-1",
"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",
"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",
"iso_8859-1:1987" => "ISO-8859-1",
"iso-ir-100" => "ISO-8859-1",
"l1" => "ISO-8859-1",
"ibm819" => "ISO-8859-1",
"cp819" => "ISO-8859-1",
"csisolatin1" => "ISO-8859-1",
//
// Aliases for ISO-8859-2
//
"latin2" => "ISO-8859-2",
"iso_8859-2" => "ISO-8859-2",
"iso_8859-2:1987" => "ISO-8859-2",
"iso-ir-101" => "ISO-8859-2",
"l2" => "ISO-8859-2",
"csisolatin2" => "ISO-8859-2",
//
// Aliases for ISO-8859-3
//
"latin3" => "ISO-8859-3",
"iso_8859-3" => "ISO-8859-3",
"iso_8859-3:1988" => "ISO-8859-3",
"iso-ir-109" => "ISO-8859-3",
"l3" => "ISO-8859-3",
"csisolatin3" => "ISO-8859-3",
//
// Aliases for ISO-8859-4
//
"latin4" => "ISO-8859-4",
"iso_8859-4" => "ISO-8859-4",
"iso_8859-4:1988" => "ISO-8859-4",
"iso-ir-110" => "ISO-8859-4",
"l4" => "ISO-8859-4",
"csisolatin4" => "ISO-8859-4",
//
// Aliases for ISO-8859-5
//
"cyrillic" => "ISO-8859-5",
"iso_8859-5" => "ISO-8859-5",
"iso_8859-5:1988" => "ISO-8859-5",
"iso-ir-144" => "ISO-8859-5",
"csisolatincyrillic" => "ISO-8859-5",
//
// Aliases for ISO-8859-6
//
"arabic" => "ISO-8859-6",
"iso_8859-6" => "ISO-8859-6",
"iso_8859-6:1987" => "ISO-8859-6",
"iso-ir-127" => "ISO-8859-6",
"ecma-114" => "ISO-8859-6",
"asmo-708" => "ISO-8859-6",
"csisolatinarabic" => "ISO-8859-6",
//
// Aliases for ISO-8859-6-I
//
"csiso88596i" => "ISO-8859-6-I",
//
// Aliases for ISO-8859-6-E",
//
"csiso88596e" => "ISO-8859-6-E",
//
// Aliases for ISO-8859-7",
//
"greek" => "ISO-8859-7",
"greek8" => "ISO-8859-7",
"sun_eu_greek" => "ISO-8859-7",
"iso_8859-7" => "ISO-8859-7",
"iso_8859-7:1987" => "ISO-8859-7",
"iso-ir-126" => "ISO-8859-7",
"elot_928" => "ISO-8859-7",
"ecma-118" => "ISO-8859-7",
"csisolatingreek" => "ISO-8859-7",
//
// Aliases for ISO-8859-8",
//
"hebrew" => "ISO-8859-8",
"iso_8859-8" => "ISO-8859-8",
"visual" => "ISO-8859-8",
"iso_8859-8:1988" => "ISO-8859-8",
"iso-ir-138" => "ISO-8859-8",
"csisolatinhebrew" => "ISO-8859-8",
//
// Aliases for ISO-8859-8-I",
//
"csiso88598i" => "ISO-8859-8-I",
"iso-8859-8i" => "ISO-8859-8-I",
"logical" => "ISO-8859-8-I",
//
// Aliases for ISO-8859-8-E",
//
"csiso88598e" => "ISO-8859-8-E",
//
// Aliases for ISO-8859-9",
//
"latin5" => "ISO-8859-9",
"iso_8859-9" => "ISO-8859-9",
"iso_8859-9:1989" => "ISO-8859-9",
"iso-ir-148" => "ISO-8859-9",
"l5" => "ISO-8859-9",
"csisolatin5" => "ISO-8859-9",
//
// Aliases for UTF-8",
//
"unicode-1-1-utf-8" => "UTF-8",
// nl_langinfo(CODESET) in HP/UX returns 'utf8' under UTF-8 locales",
"utf8" => "UTF-8",
//
// Aliases for Shift_JIS",
//
"x-sjis" => "Shift_JIS",
"shift-jis" => "Shift_JIS",
"ms_kanji" => "Shift_JIS",
"csshiftjis" => "Shift_JIS",
"windows-31j" => "Shift_JIS",
"cp932" => "Shift_JIS",
"sjis" => "Shift_JIS",
//
// Aliases for EUC_JP",
//
"cseucpkdfmtjapanese" => "EUC-JP",
"x-euc-jp" => "EUC-JP",
//
// Aliases for ISO-2022-JP",
//
"csiso2022jp" => "ISO-2022-JP",
// The following are really not aliases ISO-2022-JP, but sharing the same decoder",
"iso-2022-jp-2" => "ISO-2022-JP",
"csiso2022jp2" => "ISO-2022-JP",
//
// Aliases for Big5",
//
"csbig5" => "Big5",
"cn-big5" => "Big5",
// x-x-big5 is not really a alias for Big5, add it only for MS FrontPage",
"x-x-big5" => "Big5",
// Sun Solaris",
"zh_tw-big5" => "Big5",
//
// Aliases for EUC-KR",
//
"cseuckr" => "EUC-KR",
"ks_c_5601-1987" => "EUC-KR",
"iso-ir-149" => "EUC-KR",
"ks_c_5601-1989" => "EUC-KR",
"ksc_5601" => "EUC-KR",
"ksc5601" => "EUC-KR",
"korean" => "EUC-KR",
"csksc56011987" => "EUC-KR",
"5601" => "EUC-KR",
"windows-949" => "EUC-KR",
//
// Aliases for GB2312",
//
// The following are really not aliases GB2312, add them only for MS FrontPage",
"gb_2312-80" => "GB2312",
"iso-ir-58" => "GB2312",
"chinese" => "GB2312",
"csiso58gb231280" => "GB2312",
"csgb2312" => "GB2312",
"zh_cn.euc" => "GB2312",
// Sun Solaris",
"gb_2312" => "GB2312",
//
// Aliases for windows-125x ",
//
"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",
//
// Aliases for windows-874 ",
//
"windows-874" => "windows-874",
"ibm874" => "windows-874",
"dos-874" => "windows-874",
//
// Aliases for macintosh",
//
"macintosh" => "macintosh",
"x-mac-roman" => "macintosh",
"mac" => "macintosh",
"csmacintosh" => "macintosh",
//
// Aliases for IBM866",
//
"cp866" => "IBM866",
"cp-866" => "IBM866",
"866" => "IBM866",
"csibm866" => "IBM866",
//
// Aliases for IBM850",
//
"cp850" => "IBM850",
"850" => "IBM850",
"csibm850" => "IBM850",
//
// Aliases for IBM852",
//
"cp852" => "IBM852",
"852" => "IBM852",
"csibm852" => "IBM852",
//
// Aliases for IBM855",
//
"cp855" => "IBM855",
"855" => "IBM855",
"csibm855" => "IBM855",
//
// Aliases for IBM857",
//
"cp857" => "IBM857",
"857" => "IBM857",
"csibm857" => "IBM857",
//
// Aliases for IBM862",
//
"cp862" => "IBM862",
"862" => "IBM862",
"csibm862" => "IBM862",
//
// Aliases for IBM864",
//
"cp864" => "IBM864",
"864" => "IBM864",
"csibm864" => "IBM864",
"ibm-864" => "IBM864",
//
// Aliases for T.61-8bit",
//
"t.61" => "T.61-8bit",
"iso-ir-103" => "T.61-8bit",
"csiso103t618bit" => "T.61-8bit",
//
// Aliases for UTF-7",
//
"x-unicode-2-0-utf-7" => "UTF-7",
"unicode-2-0-utf-7" => "UTF-7",
"unicode-1-1-utf-7" => "UTF-7",
"csunicode11utf7" => "UTF-7",
//
// Aliases for ISO-10646-UCS-2",
//
"csunicode" => "UTF-16BE",
"csunicode11" => "UTF-16BE",
"iso-10646-ucs-basic" => "UTF-16BE",
"csunicodeascii" => "UTF-16BE",
"iso-10646-unicode-latin1" => "UTF-16BE",
"csunicodelatin1" => "UTF-16BE",
"iso-10646" => "UTF-16BE",
"iso-10646-j-1" => "UTF-16BE",
//
// Aliases for ISO-8859-10",
//
"latin6" => "ISO-8859-10",
"iso-ir-157" => "ISO-8859-10",
"l6" => "ISO-8859-10",
// Currently .properties cannot handle : in key",
//iso_8859-10:1992" => "ISO-8859-10",
"csisolatin6" => "ISO-8859-10",
//
// Aliases for ISO-8859-15",
//
"iso_8859-15" => "ISO-8859-15",
"csisolatin9" => "ISO-8859-15",
"l9" => "ISO-8859-15",
//
// Aliases for ISO-IR-111",
//
"ecma-cyrillic" => "ISO-IR-111",
"csiso111ecmacyrillic" => "ISO-IR-111",
//
// Aliases for ISO-2022-KR",
//
"csiso2022kr" => "ISO-2022-KR",
//
// Aliases for VISCII",
//
"csviscii" => "VISCII",
//
// Aliases for x-euc-tw",
//
"zh_tw-euc" => "x-euc-tw",
//
// Following names appears in unix nl_langinfo(CODESET)",
// They can be compiled as platform specific if necessary",
// DONT put things here if it does not look generic enough (like hp15CN)",
//
"iso88591" => "ISO-8859-1",
"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",
"iso885910" => "ISO-8859-10",
"iso885911" => "ISO-8859-11",
"iso885912" => "ISO-8859-12",
"iso885913" => "ISO-8859-13",
"iso885914" => "ISO-8859-14",
"iso885915" => "ISO-8859-15",
"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",
"x-gbk" => "gbk",
"windows-936" => "gbk",
"ansi-1251" => "windows-1251",
];
/**
* Returns proper encoding mapping, if exsists. If it doesn't, return unchanged $encoding
* @param string|null $encoding
* @param string|null $fallback
*
* @return string
*/
public static function get($encoding, string $fallback = null): string {
if (isset(self::$aliases[strtolower($encoding ?? '')])) {
return self::$aliases[strtolower($encoding ?? '')];
}
return $fallback !== null ? $fallback : $encoding;
}
}

View File

@@ -0,0 +1,28 @@
<?php
/*
* File: Event.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class Event
*
* @package Webklex\PHPIMAP\Events
*/
abstract class Event {
/**
* Dispatch the event with the given arguments.
*/
public static function dispatch(): Event {
return new static(func_get_args());
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: FlagDeletedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class FlagDeletedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class FlagDeletedEvent extends FlagNewEvent {
}

View File

@@ -0,0 +1,39 @@
<?php
/*
* File: FlagNewEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
use Webklex\PHPIMAP\Message;
/**
* Class FlagNewEvent
*
* @package Webklex\PHPIMAP\Events
*/
class FlagNewEvent extends Event {
/** @var Message $message */
public $message;
/** @var string $flag */
public $flag;
/**
* Create a new event instance.
* @var mixed[] $arguments
* @return void
*/
public function __construct($arguments) {
$this->message = $arguments[0];
$this->flag = $arguments[1];
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: FolderDeletedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class FolderDeletedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class FolderDeletedEvent extends FolderNewEvent {
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* File: FolderMovedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
use Webklex\PHPIMAP\Folder;
/**
* Class FolderMovedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class FolderMovedEvent extends Event {
/** @var Folder $old_folder */
public $old_folder;
/** @var Folder $new_folder */
public $new_folder;
/**
* Create a new event instance.
* @var Folder[] $folders
* @return void
*/
public function __construct($folders) {
$this->old_folder = $folders[0];
$this->new_folder = $folders[1];
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* File: FolderNewEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
use Webklex\PHPIMAP\Folder;
/**
* Class FolderNewEvent
*
* @package Webklex\PHPIMAP\Events
*/
class FolderNewEvent extends Event {
/** @var Folder $folder */
public $folder;
/**
* Create a new event instance.
* @var Folder[] $folders
* @return void
*/
public function __construct($folders) {
$this->folder = $folders[0];
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: MessageCopiedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class MessageCopiedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class MessageCopiedEvent extends MessageMovedEvent {
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: MessageDeletedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class MessageDeletedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class MessageDeletedEvent extends MessageNewEvent {
}

View File

@@ -0,0 +1,38 @@
<?php
/*
* File: MessageMovedEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
use Webklex\PHPIMAP\Message;
/**
* Class MessageMovedEvent
*
* @package Webklex\PHPIMAP\Events
*/
class MessageMovedEvent extends Event {
/** @var Message $old_message */
public $old_message;
/** @var Message $new_message */
public $new_message;
/**
* Create a new event instance.
* @var Message[] $messages
* @return void
*/
public function __construct($messages) {
$this->old_message = $messages[0];
$this->new_message = $messages[1];
}
}

View File

@@ -0,0 +1,35 @@
<?php
/*
* File: MessageNewEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
use Webklex\PHPIMAP\Message;
/**
* Class MessageNewEvent
*
* @package Webklex\PHPIMAP\Events
*/
class MessageNewEvent extends Event {
/** @var Message $message */
public $message;
/**
* Create a new event instance.
* @var Message[] $messages
* @return void
*/
public function __construct($messages) {
$this->message = $messages[0];
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: MessageRestoredEvent.php
* Category: Event
* Author: M. Goldenbaum
* Created: 25.11.20 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Events;
/**
* Class MessageRestoredEvent
*
* @package Webklex\PHPIMAP\Events
*/
class MessageRestoredEvent extends MessageNewEvent {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: AuthFailedException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class AuthFailedException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class AuthFailedException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: ConnectionFailedException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class ConnectionFailedException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class ConnectionFailedException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: EventNotFoundException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class EventNotFoundException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class EventNotFoundException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: FolderFetchingException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class FolderFetchingException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class FolderFetchingException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: GetMessagesFailedException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class GetMessagesFailedException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class GetMessagesFailedException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: InvalidMessageDateException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 10.03.19 04:31
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class InvalidMessageDateException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class InvalidMessageDateException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: InvalidWhereQueryCriteriaException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 21.07.18 19:04
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class InvalidWhereQueryCriteriaException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class InvalidWhereQueryCriteriaException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MaskNotFoundException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MaskNotFoundException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MaskNotFoundException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MessageContentFetchingException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MessageContentFetchingException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MessageContentFetchingException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MessageFlagException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 02.01.21 02:47
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MessageFlagException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MessageFlagException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MessageHeaderFetchingException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MessageHeaderFetchingException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MessageHeaderFetchingException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MessageNotFoundException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 25.01.21 18:19
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MessageNotFoundException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MessageNotFoundException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MessageSearchValidationException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MessageSearchValidationException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MessageSearchValidationException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MethodNotFoundException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MethodNotFoundException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MethodNotFoundException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: MethodNotSupportedException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 05.03.18 23:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class MethodNotSupportedException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class MethodNotSupportedException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: RuntimeException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class NotSupportedCapabilityException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class NotSupportedCapabilityException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: ProtocolNotSupportedException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class ProtocolNotSupportedException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class ProtocolNotSupportedException extends Exception {
}

View File

@@ -0,0 +1,24 @@
<?php
/*
* File: RuntimeException.php
* Category: Exception
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Exceptions;
use \Exception;
/**
* Class RuntimeException
*
* @package Webklex\PHPIMAP\Exceptions
*/
class RuntimeException extends Exception {
}

475
vendor/webklex/php-imap/src/Folder.php vendored Executable file
View File

@@ -0,0 +1,475 @@
<?php
/*
* File: Folder.php
* Category: -
* Author: M. Goldenbaum
* Created: 19.01.17 22:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use Carbon\Carbon;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\NotSupportedCapabilityException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\Query\WhereQuery;
use Webklex\PHPIMAP\Support\FolderCollection;
use Webklex\PHPIMAP\Traits\HasEvents;
/**
* Class Folder
*
* @package Webklex\PHPIMAP
*/
class Folder {
use HasEvents;
/**
* Client instance
*
* @var Client
*/
protected $client;
/**
* Folder full path
*
* @var string
*/
public $path;
/**
* Folder name
*
* @var string
*/
public $name;
/**
* Folder fullname
*
* @var string
*/
public $full_name;
/**
* Children folders
*
* @var FolderCollection|array
*/
public $children = [];
/**
* Delimiter for folder
*
* @var string
*/
public $delimiter;
/**
* Indicates if folder can't containg any "children".
* CreateFolder won't work on this folder.
*
* @var boolean
*/
public $no_inferiors;
/**
* Indicates if folder is only container, not a mailbox - you can't open it.
*
* @var boolean
*/
public $no_select;
/**
* Indicates if folder is marked. This means that it may contain new messages since the last time it was checked.
* Not provided by all IMAP servers.
*
* @var boolean
*/
public $marked;
/**
* Indicates if folder containg any "children".
* Not provided by all IMAP servers.
*
* @var boolean
*/
public $has_children;
/**
* Indicates if folder refers to other.
* Not provided by all IMAP servers.
*
* @var boolean
*/
public $referral;
/** @var array */
public $status;
/**
* Folder constructor.
* @param Client $client
* @param string $folder_name
* @param string $delimiter
* @param string[] $attributes
*/
public function __construct(Client $client, string $folder_name, string $delimiter, array $attributes) {
$this->client = $client;
$this->events["message"] = $client->getDefaultEvents("message");
$this->events["folder"] = $client->getDefaultEvents("folder");
$this->setDelimiter($delimiter);
$this->path = $folder_name;
$this->full_name = $this->decodeName($folder_name);
$this->name = $this->getSimpleName($this->delimiter, $this->full_name);
$this->parseAttributes($attributes);
}
/**
* Get a new search query instance
* @param string[] $extensions
*
* @return WhereQuery
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function query(array $extensions = []): WhereQuery {
$this->getClient()->checkConnection();
$this->getClient()->openFolder($this->path);
$extensions = count($extensions) > 0 ? $extensions : $this->getClient()->extensions;
return new WhereQuery($this->getClient(), $extensions);
}
/**
* Get a new search query instance
* @param string[] $extensions
*
* @return WhereQuery
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function search(array $extensions = []): WhereQuery {
return $this->query($extensions);
}
/**
* Get a new search query instance
* @param string[] $extensions
*
* @return WhereQuery
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function messages(array $extensions = []): WhereQuery {
return $this->query($extensions);
}
/**
* Determine if folder has children.
*
* @return bool
*/
public function hasChildren(): bool {
return $this->has_children;
}
/**
* Set children.
* @param FolderCollection|array $children
*
* @return self
*/
public function setChildren($children = []): Folder {
$this->children = $children;
return $this;
}
/**
* Decode name.
* It converts UTF7-IMAP encoding to UTF-8.
* @param $name
*
* @return array|false|string|string[]|null
*/
protected function decodeName($name) {
return mb_convert_encoding($name, "UTF-8", "UTF7-IMAP");
}
/**
* Get simple name (without parent folders).
* @param $delimiter
* @param $full_name
*
* @return mixed
*/
protected function getSimpleName($delimiter, $full_name) {
$arr = explode($delimiter, $full_name);
return end($arr);
}
/**
* Parse attributes and set it to object properties.
* @param $attributes
*/
protected function parseAttributes($attributes) {
$this->no_inferiors = in_array('\NoInferiors', $attributes);
$this->no_select = in_array('\NoSelect', $attributes);
$this->marked = in_array('\Marked', $attributes);
$this->referral = in_array('\Referral', $attributes);
$this->has_children = in_array('\HasChildren', $attributes);
}
/**
* Move or rename the current folder
* @param string $new_name
* @param boolean $expunge
*
* @return bool
* @throws ConnectionFailedException
* @throws Exceptions\EventNotFoundException
* @throws Exceptions\FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function move(string $new_name, bool $expunge = true): bool {
$this->client->checkConnection();
$status = $this->client->getConnection()->renameFolder($this->full_name, $new_name);
if($expunge) $this->client->expunge();
$folder = $this->client->getFolder($new_name);
$event = $this->getEvent("folder", "moved");
$event::dispatch($this, $folder);
return $status;
}
/**
* Get a message overview
* @param string|null $sequence uid sequence
*
* @return array
* @throws ConnectionFailedException
* @throws Exceptions\InvalidMessageDateException
* @throws Exceptions\MessageNotFoundException
* @throws Exceptions\RuntimeException
*/
public function overview(string $sequence = null): array {
$this->client->openFolder($this->path);
$sequence = $sequence === null ? "1:*" : $sequence;
$uid = ClientManager::get('options.sequence', IMAP::ST_MSGN) == IMAP::ST_UID;
return $this->client->getConnection()->overview($sequence, $uid);
}
/**
* Append a string message to the current mailbox
* @param string $message
* @param array|null $options
* @param string|null|Carbon $internal_date
*
* @return bool
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function appendMessage(string $message, array $options = null, $internal_date = null): bool {
/**
* Check if $internal_date is parsed. If it is null it should not be set. Otherwise, the message can't be stored.
* If this parameter is set, it will set the INTERNALDATE on the appended message. The parameter should be a
* date string that conforms to the rfc2060 specifications for a date_time value or be a Carbon object.
*/
if ($internal_date instanceof Carbon){
$internal_date = $internal_date->format('d-M-Y H:i:s O');
}
return $this->client->getConnection()->appendMessage($this->path, $message, $options, $internal_date);
}
/**
* Rename the current folder
* @param string $new_name
* @param boolean $expunge
*
* @return bool
* @throws ConnectionFailedException
* @throws Exceptions\EventNotFoundException
* @throws Exceptions\FolderFetchingException
* @throws Exceptions\RuntimeException
*/
public function rename(string $new_name, bool $expunge = true): bool {
return $this->move($new_name, $expunge);
}
/**
* Delete the current folder
* @param boolean $expunge
*
* @return bool
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
* @throws Exceptions\EventNotFoundException
*/
public function delete(bool $expunge = true): bool {
$status = $this->client->getConnection()->deleteFolder($this->path);
if($expunge) $this->client->expunge();
$event = $this->getEvent("folder", "deleted");
$event::dispatch($this);
return $status;
}
/**
* Subscribe the current folder
*
* @return bool
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function subscribe(): bool {
$this->client->openFolder($this->path);
return $this->client->getConnection()->subscribeFolder($this->path);
}
/**
* Unsubscribe the current folder
*
* @return bool
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function unsubscribe(): bool {
$this->client->openFolder($this->path);
return $this->client->getConnection()->unsubscribeFolder($this->path);
}
/**
* Idle the current connection
* @param callable $callback
* @param integer $timeout max 1740 seconds - recommended by rfc2177 §3. Should not be lower than the servers "* OK Still here" message interval
* @param boolean $auto_reconnect try to reconnect on connection close (@deprecated is no longer required)
*
* @throws ConnectionFailedException
* @throws Exceptions\InvalidMessageDateException
* @throws Exceptions\MessageContentFetchingException
* @throws Exceptions\MessageHeaderFetchingException
* @throws Exceptions\RuntimeException
* @throws Exceptions\EventNotFoundException
* @throws Exceptions\MessageFlagException
* @throws Exceptions\MessageNotFoundException
* @throws Exceptions\NotSupportedCapabilityException
*/
public function idle(callable $callback, int $timeout = 300, bool $auto_reconnect = false) {
$this->client->setTimeout($timeout);
if (!in_array("IDLE", $this->client->getConnection()->getCapabilities())) {
throw new NotSupportedCapabilityException("IMAP server does not support IDLE");
}
$this->client->openFolder($this->path, true);
$connection = $this->client->getConnection();
$connection->idle();
$sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
while (true) {
try {
// This polymorphic call is fine - Protocol::idle() will throw an exception beforehand
$line = $connection->nextLine();
if (($pos = strpos($line, "EXISTS")) !== false) {
$connection->done();
$msgn = (int) substr($line, 2, $pos -2);
$this->client->openFolder($this->path, true);
$message = $this->query()->getMessageByMsgn($msgn);
$message->setSequence($sequence);
$callback($message);
$event = $this->getEvent("message", "new");
$event::dispatch($message);
$connection->idle();
} elseif (strpos($line, "OK") === false) {
$connection->done();
$connection->idle();
}
}catch (Exceptions\RuntimeException $e) {
if(strpos($e->getMessage(), "empty response") >= 0 && $connection->connected()) {
$connection->done();
$connection->idle();
continue;
}
if(strpos($e->getMessage(), "connection closed") === false) {
throw $e;
}
$this->client->reconnect();
$this->client->openFolder($this->path, true);
$connection = $this->client->getConnection();
$connection->idle();
}
}
}
/**
* Get folder status information
*
* @return array
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function getStatus(): array {
return $this->examine();
}
/**
* @throws RuntimeException
* @throws ConnectionFailedException
*/
public function loadStatus(): Folder
{
$this->status = $this->getStatus();
return $this;
}
/**
* Examine the current folder
*
* @return array
* @throws Exceptions\ConnectionFailedException
* @throws Exceptions\RuntimeException
*/
public function examine(): array {
$result = $this->client->getConnection()->examineFolder($this->path);
return is_array($result) ? $result : [];
}
/**
* Get the current Client instance
*
* @return Client
*/
public function getClient(): Client {
return $this->client;
}
/**
* Set the delimiter
* @param $delimiter
*/
public function setDelimiter($delimiter){
if(in_array($delimiter, [null, '', ' ', false]) === true) {
$delimiter = ClientManager::get('options.delimiter', '/');
}
$this->delimiter = $delimiter;
}
}

756
vendor/webklex/php-imap/src/Header.php vendored Normal file
View File

@@ -0,0 +1,756 @@
<?php
/*
* File: Header.php
* Category: -
* Author: M.Goldenbaum
* Created: 17.09.20 20:38
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use Carbon\Carbon;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
/**
* Class Header
*
* @package Webklex\PHPIMAP
*/
class Header {
/**
* Raw header
*
* @var string $raw
*/
public $raw = "";
/**
* Attribute holder
*
* @var Attribute[]|array $attributes
*/
protected $attributes = [];
/**
* Config holder
*
* @var array $config
*/
protected $config = [];
/**
* Fallback Encoding
*
* @var string
*/
public $fallback_encoding = 'UTF-8';
/**
* Convert parsed values to attributes
*
* @var bool
*/
protected $attributize = false;
/**
* Header constructor.
* @param string $raw_header
* @param boolean $attributize
*
* @throws InvalidMessageDateException
*/
public function __construct(string $raw_header, bool $attributize = true) {
$this->raw = $raw_header;
$this->config = ClientManager::get('options');
$this->attributize = $attributize;
$this->parse();
}
/**
* Call dynamic attribute setter and getter methods
* @param string $method
* @param array $arguments
*
* @return Attribute|mixed
* @throws MethodNotFoundException
*/
public function __call(string $method, array $arguments) {
if (strtolower(substr($method, 0, 3)) === 'get') {
$name = preg_replace('/(.)(?=[A-Z])/u', '$1_', substr(strtolower($method), 3));
if (in_array($name, array_keys($this->attributes))) {
return $this->attributes[$name];
}
}
throw new MethodNotFoundException("Method " . self::class . '::' . $method . '() is not supported');
}
/**
* Magic getter
* @param $name
*
* @return Attribute|null
*/
public function __get($name) {
return $this->get($name);
}
/**
* Get a specific header attribute
* @param $name
*
* @return Attribute|mixed
*/
public function get($name) {
if (isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}
/**
* Set a specific attribute
* @param string $name
* @param array|mixed $value
* @param boolean $strict
*
* @return Attribute
*/
public function set(string $name, $value, bool $strict = false) {
if (isset($this->attributes[$name]) && $strict === false) {
if ($this->attributize) {
$this->attributes[$name]->add($value, true);
} else {
if (isset($this->attributes[$name])) {
if (!is_array($this->attributes[$name])) {
$this->attributes[$name] = [$this->attributes[$name], $value];
} else {
$this->attributes[$name][] = $value;
}
} else {
$this->attributes[$name] = $value;
}
}
} elseif (!$this->attributize) {
$this->attributes[$name] = $value;
} else {
$this->attributes[$name] = new Attribute($name, $value);
}
return $this->attributes[$name];
}
/**
* Perform a regex match all on the raw header and return the first result
* @param $pattern
*
* @return mixed|null
*/
public function find($pattern) {
if (preg_match_all($pattern, $this->raw, $matches)) {
if (isset($matches[1])) {
if (count($matches[1]) > 0) {
return $matches[1][0];
}
}
}
return null;
}
/**
* Try to find a boundary if possible
*
* @return string|null
*/
public function getBoundary() {
$regex = $this->config["boundary"] ?? "/boundary=(.*?(?=;)|(.*))/i";
$boundary = $this->find($regex);
if ($boundary === null) {
return null;
}
return $this->clearBoundaryString($boundary);
}
/**
* Remove all unwanted chars from a given boundary
* @param string $str
*
* @return string
*/
private function clearBoundaryString(string $str): string {
return str_replace(['"', '\r', '\n', "\n", "\r", ";", "\s"], "", $str);
}
/**
* Parse the raw headers
*
* @throws InvalidMessageDateException
*/
protected function parse() {
$header = $this->rfc822_parse_headers($this->raw);
$this->extractAddresses($header);
if (property_exists($header, 'subject')) {
$sub = $this->decode($header->subject);
$this->set("subject", $sub);
}
if (property_exists($header, 'references')) {
$this->set("references", $this->decode($header->references));
}
if (property_exists($header, 'message_id')) {
$this->set("message_id", str_replace(['<', '>'], '', $header->message_id));
}
$this->parseDate($header);
foreach ($header as $key => $value) {
$key = trim(rtrim(strtolower($key)));
if (!isset($this->attributes[$key])) {
$this->set($key, $value);
}
}
$this->extractHeaderExtensions();
$this->findPriority();
}
/**
* Parse mail headers from a string
* @link https://php.net/manual/en/function.imap-rfc822-parse-headers.php
* @param $raw_headers
*
* @return object
*/
public function rfc822_parse_headers($raw_headers) {
$headers = [];
$imap_headers = [];
if (extension_loaded('imap') && $this->config["rfc822"]) {
$raw_imap_headers = (array)\imap_rfc822_parse_headers($this->raw);
foreach ($raw_imap_headers as $key => $values) {
$key = str_replace("-", "_", $key);
$imap_headers[$key] = $values;
}
}
$lines = explode("\r\n", preg_replace("/\r\n\s/", ' ', $raw_headers));
$prev_header = null;
foreach ($lines as $line) {
if (substr($line, 0, 1) === "\n") {
$line = substr($line, 1);
}
if (substr($line, 0, 1) === "\t") {
$line = substr($line, 1);
$line = trim(rtrim($line));
if ($prev_header !== null) {
$headers[$prev_header][] = $line;
}
} elseif (substr($line, 0, 1) === " ") {
$line = substr($line, 1);
$line = trim(rtrim($line));
if ($prev_header !== null) {
if (!isset($headers[$prev_header])) {
$headers[$prev_header] = "";
}
if (is_array($headers[$prev_header])) {
$headers[$prev_header][] = $line;
} else {
$headers[$prev_header] .= $line;
}
}
} else {
if (($pos = strpos($line, ":")) > 0) {
$key = trim(rtrim(strtolower(substr($line, 0, $pos))));
$key = str_replace("-", "_", $key);
$value = trim(rtrim(substr($line, $pos + 1)));
if (isset($headers[$key])) {
$headers[$key][] = $value;
} else {
$headers[$key] = [$value];
}
$prev_header = $key;
}
}
}
foreach ($headers as $key => $values) {
if (isset($imap_headers[$key])) continue;
$value = null;
switch ($key) {
case 'from':
case 'to':
case 'cc':
case 'bcc':
case 'reply_to':
case 'sender':
$value = $this->decodeAddresses($values);
$headers[$key . "address"] = implode(", ", $values);
break;
case 'subject':
$value = implode(" ", $values);
break;
default:
if (is_array($values)) {
foreach ($values as $k => $v) {
if ($v == "") {
unset($values[$k]);
}
}
$available_values = count($values);
if ($available_values === 1) {
$value = array_pop($values);
} elseif ($available_values === 2) {
$value = implode(" ", $values);
} elseif ($available_values > 2) {
$value = array_values($values);
} else {
$value = "";
}
}
break;
}
$headers[$key] = $value;
}
return (object)array_merge($headers, $imap_headers);
}
/**
* Decode MIME header elements
* @link https://php.net/manual/en/function.imap-mime-header-decode.php
* @param string $text The MIME text
*
* @return array The decoded elements are returned in an array of objects, where each
* object has two properties, charset and text.
*/
public function mime_header_decode(string $text): array {
if (extension_loaded('imap')) {
$result = \imap_mime_header_decode($text);
return is_array($result) ? $result : [];
}
$charset = $this->getEncoding($text);
return [(object)[
"charset" => $charset,
"text" => $this->convertEncoding($text, $charset)
]];
}
/**
* Check if a given pair of strings has been decoded
* @param $encoded
* @param $decoded
*
* @return bool
*/
private function notDecoded($encoded, $decoded): bool {
return 0 === strpos($decoded, '=?')
&& strlen($decoded) - 2 === strpos($decoded, '?=')
&& false !== strpos($encoded, $decoded);
}
/**
* Convert the encoding
* @param $str
* @param string $from
* @param string $to
*
* @return mixed|string
*/
public function convertEncoding($str, $from = "ISO-8859-2", $to = "UTF-8") {
$from = EncodingAliases::get($from, $this->fallback_encoding);
$to = EncodingAliases::get($to, $this->fallback_encoding);
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;
}
try {
if (function_exists('iconv') && $from != 'UTF-7' && $to != 'UTF-7') {
return iconv($from, $to, $str);
} else {
if (!$from) {
return mb_convert_encoding($str, $to);
}
return mb_convert_encoding($str, $to, $from);
}
} catch (\Exception $e) {
if (strstr($from, '-')) {
$from = str_replace('-', '', $from);
return $this->convertEncoding($str, $from, $to);
} else {
return $str;
}
}
}
/**
* Get the encoding of a given abject
* @param object|string $structure
*
* @return string
*/
public function getEncoding($structure): string {
if (property_exists($structure, 'parameters')) {
foreach ($structure->parameters as $parameter) {
if (strtolower($parameter->attribute) == "charset") {
return EncodingAliases::get($parameter->value, $this->fallback_encoding);
}
}
} elseif (property_exists($structure, 'charset')) {
return EncodingAliases::get($structure->charset, $this->fallback_encoding);
} elseif (is_string($structure) === true) {
$result = mb_detect_encoding($structure);
return $result === false ? $this->fallback_encoding : $result;
}
return $this->fallback_encoding;
}
/**
* Test if a given value is utf-8 encoded
* @param $value
*
* @return bool
*/
private function is_uft8($value): bool {
return strpos(strtolower($value), '=?utf-8?') === 0;
}
/**
* Try to decode a specific header
* @param mixed $value
*
* @return mixed
*/
public function decode($value) {
// Check if the input contains any encoding
if (!preg_match('/=\?([^?]+)\?([QB])\?([^?]+)\?=/i', $value)) {
return $value;
}
preg_match_all('/=\?([^?]+)\?([QB])\?([^?]+)\?=/i', $value, $matches, PREG_SET_ORDER);
$decoded = '';
foreach ($matches as $match) {
$encoding = $match[1];
$type = $match[2];
$encoded_string = $match[3];
// Decode based on encoding type
if (strtolower($type) == 'q') {
$decoded_part = quoted_printable_decode($encoded_string);
} elseif (strtolower($type) == 'b') {
$decoded_part = base64_decode($encoded_string);
}
if (strtolower($encoding) != 'utf-8') {
$decoded_part = iconv($encoding, 'UTF-8', $decoded_part);
}
$decoded .= str_replace('_', ' ', $decoded_part);
}
return $decoded;
}
/**
* Decode a given array
* @param array $values
*
* @return array
*/
private function decodeArray(array $values): array {
foreach ($values as $key => $value) {
$values[$key] = $this->decode($value);
}
return $values;
}
/**
* Try to extract the priority from a given raw header string
*/
private function findPriority() {
if (($priority = $this->get("x_priority")) === null) return;
switch ((int)"$priority") {
case IMAP::MESSAGE_PRIORITY_HIGHEST;
$priority = IMAP::MESSAGE_PRIORITY_HIGHEST;
break;
case IMAP::MESSAGE_PRIORITY_HIGH;
$priority = IMAP::MESSAGE_PRIORITY_HIGH;
break;
case IMAP::MESSAGE_PRIORITY_NORMAL;
$priority = IMAP::MESSAGE_PRIORITY_NORMAL;
break;
case IMAP::MESSAGE_PRIORITY_LOW;
$priority = IMAP::MESSAGE_PRIORITY_LOW;
break;
case IMAP::MESSAGE_PRIORITY_LOWEST;
$priority = IMAP::MESSAGE_PRIORITY_LOWEST;
break;
default:
$priority = IMAP::MESSAGE_PRIORITY_UNKNOWN;
break;
}
$this->set("priority", $priority);
}
/**
* Extract a given part as address array from a given header
* @param $values
*
* @return array
*/
private function decodeAddresses($values): array {
$addresses = [];
if (extension_loaded('mailparse') && $this->config["rfc822"]) {
foreach ($values as $address) {
foreach (\mailparse_rfc822_parse_addresses($address) as $parsed_address) {
if (isset($parsed_address['address'])) {
$mail_address = explode('@', $parsed_address['address']);
if (count($mail_address) == 2) {
$addresses[] = (object)[
"personal" => $parsed_address['display'] ?? '',
"mailbox" => $mail_address[0],
"host" => $mail_address[1],
];
}
}
}
}
return $addresses;
}
foreach ($values as $address) {
foreach (preg_split('/, (?=(?:[^"]*"[^"]*")*[^"]*$)/', $address) as $split_address) {
$split_address = trim(rtrim($split_address));
if (strpos($split_address, ",") == strlen($split_address) - 1) {
$split_address = substr($split_address, 0, -1);
}
if (preg_match(
'/^(?:(?P<name>.+)\s)?(?(name)<|<?)(?P<email>[^\s]+?)(?(name)>|>?)$/',
$split_address,
$matches
)) {
$name = trim(rtrim($matches["name"]));
$email = trim(rtrim($matches["email"]));
list($mailbox, $host) = array_pad(explode("@", $email), 2, null);
$addresses[] = (object)[
"personal" => $name,
"mailbox" => $mailbox,
"host" => $host,
];
}
}
}
return $addresses;
}
/**
* Extract a given part as address array from a given header
* @param object $header
*/
private function extractAddresses($header) {
foreach (['from', 'to', 'cc', 'bcc', 'reply_to', 'sender'] as $key) {
if (property_exists($header, $key)) {
$this->set($key, $this->parseAddresses($header->$key));
}
}
}
/**
* Parse Addresses
* @param $list
*
* @return array
*/
private function parseAddresses($list): array {
$addresses = [];
if (is_array($list) === false) {
return $addresses;
}
foreach ($list as $item) {
$address = (object)$item;
if (!property_exists($address, 'mailbox')) {
$address->mailbox = false;
}
if (!property_exists($address, 'host')) {
$address->host = false;
}
if (!property_exists($address, 'personal')) {
$address->personal = false;
} else {
$personalParts = $this->mime_header_decode($address->personal);
if (is_array($personalParts)) {
$address->personal = '';
foreach ($personalParts as $p) {
$address->personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
}
}
if (strpos($address->personal, "'") === 0) {
$address->personal = str_replace("'", "", $address->personal);
}
}
$address->mail = ($address->mailbox && $address->host) ? $address->mailbox . '@' . $address->host : false;
$address->full = ($address->personal) ? $address->personal . ' <' . $address->mail . '>' : $address->mail;
$addresses[] = new Address($address);
}
return $addresses;
}
/**
* Search and extract potential header extensions
*/
private function extractHeaderExtensions() {
foreach ($this->attributes as $key => $value) {
if (is_array($value)) {
$value = implode(", ", $value);
} else {
$value = (string)$value;
}
// Only parse strings and don't parse any attributes like the user-agent
if (($key == "user_agent") === false) {
if (($pos = strpos($value, ";")) !== false) {
$original = substr($value, 0, $pos);
$this->set($key, trim(rtrim($original)), true);
// Get all potential extensions
$extensions = explode(";", substr($value, $pos + 1));
foreach ($extensions as $extension) {
if (($pos = strpos($extension, "=")) !== false) {
$key = substr($extension, 0, $pos);
$key = trim(rtrim(strtolower($key)));
if (isset($this->attributes[$key]) === false) {
$value = substr($extension, $pos + 1);
$value = str_replace('"', "", $value);
$value = trim(rtrim($value));
$this->set($key, $value);
}
}
}
}
}
}
}
/**
* Exception handling for invalid dates
*
* Currently known invalid formats:
* ^ Datetime ^ Problem ^ Cause
* | Mon, 20 Nov 2017 20:31:31 +0800 (GMT+8:00) | Double timezone specification | A Windows feature
* | Thu, 8 Nov 2018 08:54:58 -0200 (-02) |
* | | and invalid timezone (max 6 char) |
* | 04 Jan 2018 10:12:47 UT | Missing letter "C" | Unknown
* | Thu, 31 May 2018 18:15:00 +0800 (added by) | Non-standard details added by the | Unknown
* | | mail server |
* | Sat, 31 Aug 2013 20:08:23 +0580 | Invalid timezone | PHPMailer bug https://sourceforge.net/p/phpmailer/mailman/message/6132703/
*
* Please report any new invalid timestamps to [#45](https://github.com/Webklex/php-imap/issues)
*
* @param object $header
*
* @throws InvalidMessageDateException
*/
private function parseDate($header) {
if (property_exists($header, 'date')) {
$date = $header->date;
if (preg_match('/\+0580/', $date)) {
$date = str_replace('+0580', '+0530', $date);
}
$date = trim(rtrim($date));
try {
if(strpos($date, '&nbsp;') !== false){
$date = str_replace('&nbsp;', ' ', $date);
}
$parsed_date = Carbon::parse($date);
} catch (\Exception $e) {
switch (true) {
case preg_match('/([0-9]{4}\.[0-9]{1,2}\.[0-9]{1,2}\-[0-9]{1,2}\.[0-9]{1,2}.[0-9]{1,2})+$/i', $date) > 0:
$date = Carbon::createFromFormat("Y.m.d-H.i.s", $date);
break;
case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ UT)+$/i', $date) > 0:
$date .= 'C';
break;
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}[\,]\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4})+$/i', $date) > 0:
$date = str_replace(',', '', $date);
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ \+[0-9]{2,4}\ \(\+[0-9]{1,2}\))+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}[\,|\ \,]\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}.*)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\,\ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
case preg_match('/([A-Z]{2,3}\, \ [0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{4}\ [0-9]{1,2}\:[0-9]{1,2}\:[0-9]{1,2}\ [\-|\+][0-9]{4}\ \(.*)\)+$/i', $date) > 0:
case preg_match('/([0-9]{1,2}\ [A-Z]{2,3}\ [0-9]{2,4}\ [0-9]{2}\:[0-9]{2}\:[0-9]{2}\ [A-Z]{2}\ \-[0-9]{2}\:[0-9]{2}\ \([A-Z]{2,3}\ \-[0-9]{2}:[0-9]{2}\))+$/i', $date) > 0:
$array = explode('(', $date);
$array = array_reverse($array);
$date = trim(array_pop($array));
break;
}
try {
$parsed_date = Carbon::parse($date);
} catch (\Exception $_e) {
if (!isset($this->config["fallback_date"])) {
throw new InvalidMessageDateException("Invalid message date. ID:" . $this->get("message_id") . " Date:" . $header->date . "/" . $date, 1100, $e);
} else {
$parsed_date = Carbon::parse($this->config["fallback_date"]);
}
}
}
$this->set("date", $parsed_date);
}
}
/**
* Get all available attributes
*
* @return array
*/
public function getAttributes(): array {
return $this->attributes;
}
}

375
vendor/webklex/php-imap/src/IMAP.php vendored Normal file
View File

@@ -0,0 +1,375 @@
<?php
/*
* File: IMAP.php
* Category: -
* Author: M.Goldenbaum
* Created: 14.03.19 18:22
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
/**
* Class IMAP
*
* Independent imap const holder
*/
class IMAP {
/**
* Message const
*
* @const integer TYPE_TEXT
* @const integer TYPE_MULTIPART
*
* @const integer ENC_7BIT
* @const integer ENC_8BIT
* @const integer ENC_BINARY
* @const integer ENC_BASE64
* @const integer ENC_QUOTED_PRINTABLE
* @const integer ENC_OTHER
*/
const MESSAGE_TYPE_TEXT = 0;
const MESSAGE_TYPE_MULTIPART = 1;
const MESSAGE_ENC_7BIT = 0;
const MESSAGE_ENC_8BIT = 1;
const MESSAGE_ENC_BINARY = 2;
const MESSAGE_ENC_BASE64 = 3;
const MESSAGE_ENC_QUOTED_PRINTABLE = 4;
const MESSAGE_ENC_OTHER = 5;
const MESSAGE_PRIORITY_UNKNOWN = 0;
const MESSAGE_PRIORITY_HIGHEST = 1;
const MESSAGE_PRIORITY_HIGH = 2;
const MESSAGE_PRIORITY_NORMAL = 3;
const MESSAGE_PRIORITY_LOW = 4;
const MESSAGE_PRIORITY_LOWEST = 5;
/**
* Attachment const
*
* @const integer TYPE_TEXT
* @const integer TYPE_MULTIPART
* @const integer TYPE_MESSAGE
* @const integer TYPE_APPLICATION
* @const integer TYPE_AUDIO
* @const integer TYPE_IMAGE
* @const integer TYPE_VIDEO
* @const integer TYPE_MODEL
* @const integer TYPE_OTHER
*/
const ATTACHMENT_TYPE_TEXT = 0;
const ATTACHMENT_TYPE_MULTIPART = 1;
const ATTACHMENT_TYPE_MESSAGE = 2;
const ATTACHMENT_TYPE_APPLICATION = 3;
const ATTACHMENT_TYPE_AUDIO = 4;
const ATTACHMENT_TYPE_IMAGE = 5;
const ATTACHMENT_TYPE_VIDEO = 6;
const ATTACHMENT_TYPE_MODEL = 7;
const ATTACHMENT_TYPE_OTHER = 8;
/**
* Client const
*
* @const integer CLIENT_OPENTIMEOUT
* @const integer CLIENT_READTIMEOUT
* @const integer CLIENT_WRITETIMEOUT
* @const integer CLIENT_CLOSETIMEOUT
*/
const CLIENT_OPENTIMEOUT = 1;
const CLIENT_READTIMEOUT = 2;
const CLIENT_WRITETIMEOUT = 3;
const CLIENT_CLOSETIMEOUT = 4;
/**
* Generic imap const
*
* @const integer NIL
* @const integer IMAP_OPENTIMEOUT
* @const integer IMAP_READTIMEOUT
* @const integer IMAP_WRITETIMEOUT
* @const integer IMAP_CLOSETIMEOUT
* @const integer OP_DEBUG
* @const integer OP_READONLY
* @const integer OP_ANONYMOUS
* @const integer OP_SHORTCACHE
* @const integer OP_SILENT
* @const integer OP_PROTOTYPE
* @const integer OP_HALFOPEN
* @const integer OP_EXPUNGE
* @const integer OP_SECURE
* @const integer CL_EXPUNGE
* @const integer FT_UID
* @const integer FT_PEEK
* @const integer FT_NOT
* @const integer FT_INTERNAL
* @const integer FT_PREFETCHTEXT
* @const integer ST_UID
* @const integer ST_SILENT
* @const integer ST_SET
* @const integer CP_UID
* @const integer CP_MOVE
* @const integer SE_UID
* @const integer SE_FREE
* @const integer SE_NOPREFETCH
* @const integer SO_FREE
* @const integer SO_NOSERVER
* @const integer SA_MESSAGES
* @const integer SA_RECENT
* @const integer SA_UNSEEN
* @const integer SA_UIDNEXT
* @const integer SA_UIDVALIDITY
* @const integer SA_ALL
* @const integer LATT_NOINFERIORS
* @const integer LATT_NOSELECT
* @const integer LATT_MARKED
* @const integer LATT_UNMARKED
* @const integer LATT_REFERRAL
* @const integer LATT_HASCHILDREN
* @const integer LATT_HASNOCHILDREN
* @const integer SORTDATE
* @const integer SORTARRIVAL
* @const integer SORTFROM
* @const integer SORTSUBJECT
* @const integer SORTTO
* @const integer SORTCC
* @const integer SORTSIZE
* @const integer TYPETEXT
* @const integer TYPEMULTIPART
* @const integer TYPEMESSAGE
* @const integer TYPEAPPLICATION
* @const integer TYPEAUDIO
* @const integer TYPEIMAGE
* @const integer TYPEVIDEO
* @const integer TYPEMODEL
* @const integer TYPEOTHER
* @const integer ENC7BIT
* @const integer ENC8BIT
* @const integer ENCBINARY
* @const integer ENCBASE64
* @const integer ENCQUOTEDPRINTABLE
* @const integer ENCOTHER
* @const integer IMAP_GC_ELT
* @const integer IMAP_GC_ENV
* @const integer IMAP_GC_TEXTS
*/
const NIL = 0;
const IMAP_OPENTIMEOUT = 1;
const IMAP_READTIMEOUT = 2;
const IMAP_WRITETIMEOUT = 3;
const IMAP_CLOSETIMEOUT = 4;
const OP_DEBUG = 1;
/**
* Open mailbox read-only
* @link http://php.net/manual/en/imap.constants.php
*/
const OP_READONLY = 2;
/**
* Don't use or update a .newsrc for news
* (NNTP only)
* @link http://php.net/manual/en/imap.constants.php
*/
const OP_ANONYMOUS = 4;
const OP_SHORTCACHE = 8;
const OP_SILENT = 16;
const OP_PROTOTYPE = 32;
/**
* For IMAP and NNTP
* names, open a connection but don't open a mailbox.
* @link http://php.net/manual/en/imap.constants.php
*/
const OP_HALFOPEN = 64;
const OP_EXPUNGE = 128;
const OP_SECURE = 256;
/**
* silently expunge the mailbox before closing when
* calling <b>imap_close</b>
* @link http://php.net/manual/en/imap.constants.php
*/
const CL_EXPUNGE = 32768;
/**
* The parameter is a UID
* @link http://php.net/manual/en/imap.constants.php
*/
const FT_UID = 1;
/**
* Do not set the \Seen flag if not already set
* @link http://php.net/manual/en/imap.constants.php
*/
const FT_PEEK = 2;
const FT_NOT = 4;
/**
* The return string is in internal format, will not canonicalize to CRLF.
* @link http://php.net/manual/en/imap.constants.php
*/
const FT_INTERNAL = 8;
const FT_PREFETCHTEXT = 32;
/**
* The sequence argument contains UIDs instead of sequence numbers
* @link http://php.net/manual/en/imap.constants.php
*/
const ST_UID = 1;
const ST_SILENT = 2;
const ST_MSGN = 3;
const ST_SET = 4;
/**
* the sequence numbers contain UIDS
* @link http://php.net/manual/en/imap.constants.php
*/
const CP_UID = 1;
/**
* Delete the messages from the current mailbox after copying
* with <b>imap_mail_copy</b>
* @link http://php.net/manual/en/imap.constants.php
*/
const CP_MOVE = 2;
/**
* Return UIDs instead of sequence numbers
* @link http://php.net/manual/en/imap.constants.php
*/
const SE_UID = 1;
const SE_FREE = 2;
/**
* Don't prefetch searched messages
* @link http://php.net/manual/en/imap.constants.php
*/
const SE_NOPREFETCH = 4;
const SO_FREE = 8;
const SO_NOSERVER = 16;
const SA_MESSAGES = 1;
const SA_RECENT = 2;
const SA_UNSEEN = 4;
const SA_UIDNEXT = 8;
const SA_UIDVALIDITY = 16;
const SA_ALL = 31;
/**
* This mailbox has no "children" (there are no
* mailboxes below this one).
* @link http://php.net/manual/en/imap.constants.php
*/
const LATT_NOINFERIORS = 1;
/**
* This is only a container, not a mailbox - you
* cannot open it.
* @link http://php.net/manual/en/imap.constants.php
*/
const LATT_NOSELECT = 2;
/**
* This mailbox is marked. Only used by UW-IMAPD.
* @link http://php.net/manual/en/imap.constants.php
*/
const LATT_MARKED = 4;
/**
* This mailbox is not marked. Only used by
* UW-IMAPD.
* @link http://php.net/manual/en/imap.constants.php
*/
const LATT_UNMARKED = 8;
const LATT_REFERRAL = 16;
const LATT_HASCHILDREN = 32;
const LATT_HASNOCHILDREN = 64;
/**
* Sort criteria for <b>imap_sort</b>:
* message Date
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTDATE = 0;
/**
* Sort criteria for <b>imap_sort</b>:
* arrival date
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTARRIVAL = 1;
/**
* Sort criteria for <b>imap_sort</b>:
* mailbox in first From address
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTFROM = 2;
/**
* Sort criteria for <b>imap_sort</b>:
* message subject
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTSUBJECT = 3;
/**
* Sort criteria for <b>imap_sort</b>:
* mailbox in first To address
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTTO = 4;
/**
* Sort criteria for <b>imap_sort</b>:
* mailbox in first cc address
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTCC = 5;
/**
* Sort criteria for <b>imap_sort</b>:
* size of message in octets
* @link http://php.net/manual/en/imap.constants.php
*/
const SORTSIZE = 6;
const TYPETEXT = 0;
const TYPEMULTIPART = 1;
const TYPEMESSAGE = 2;
const TYPEAPPLICATION = 3;
const TYPEAUDIO = 4;
const TYPEIMAGE = 5;
const TYPEVIDEO = 6;
const TYPEMODEL = 7;
const TYPEOTHER = 8;
const ENC7BIT = 0;
const ENC8BIT = 1;
const ENCBINARY = 2;
const ENCBASE64 = 3;
const ENCQUOTEDPRINTABLE = 4;
const ENCOTHER = 5;
/**
* Garbage collector, clear message cache elements.
* @link http://php.net/manual/en/imap.constants.php
*/
const IMAP_GC_ELT = 1;
/**
* Garbage collector, clear envelopes and bodies.
* @link http://php.net/manual/en/imap.constants.php
*/
const IMAP_GC_ENV = 2;
/**
* Garbage collector, clear texts.
* @link http://php.net/manual/en/imap.constants.php
*/
const IMAP_GC_TEXTS = 4;
}

1432
vendor/webklex/php-imap/src/Message.php vendored Executable file

File diff suppressed because it is too large Load Diff

312
vendor/webklex/php-imap/src/Part.php vendored Normal file
View File

@@ -0,0 +1,312 @@
<?php
/*
* File: Part.php
* Category: -
* Author: M.Goldenbaum
* Created: 17.09.20 20:38
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
/**
* Class Part
*
* @package Webklex\PHPIMAP
*/
class Part {
/**
* Raw part
*
* @var string $raw
*/
public $raw = "";
/**
* Part type
*
* @var int $type
*/
public $type = IMAP::MESSAGE_TYPE_TEXT;
/**
* Part content
*
* @var string $content
*/
public $content = "";
/**
* Part subtype
*
* @var string $subtype
*/
public $subtype = null;
/**
* Part charset - if available
*
* @var string $charset
*/
public $charset = "utf-8";
/**
* Part encoding method
*
* @var int $encoding
*/
public $encoding = IMAP::MESSAGE_ENC_OTHER;
/**
* Alias to check if the part is an attachment
*
* @var boolean $ifdisposition
*/
public $ifdisposition = false;
/**
* Indicates if the part is an attachment
*
* @var string $disposition
*/
public $disposition = null;
/**
* Alias to check if the part has a description
*
* @var boolean $ifdescription
*/
public $ifdescription = false;
/**
* Part description if available
*
* @var string $description
*/
public $description = null;
/**
* Part filename if available
*
* @var string $filename
*/
public $filename = null;
/**
* Part name if available
*
* @var string $name
*/
public $name = null;
/**
* Part id if available
*
* @var string $id
*/
public $id = null;
/**
* The part number of the current part
*
* @var integer $part_number
*/
public $part_number = 0;
/**
* Part length in bytes
*
* @var integer $bytes
*/
public $bytes = null;
/**
* Part content type
*
* @var string|null $content_type
*/
public $content_type = null;
/**
* @var Header $header
*/
private $header = null;
/**
* Part constructor.
* @param $raw_part
* @param Header|null $header
* @param integer $part_number
*
* @throws InvalidMessageDateException
*/
public function __construct($raw_part, Header $header = null, int $part_number = 0) {
$this->raw = $raw_part;
$this->header = $header;
$this->part_number = $part_number;
$this->parse();
}
/**
* Parse the raw parts
*
* @throws InvalidMessageDateException
*/
protected function parse(){
if ($this->header === null) {
$body = $this->findHeaders();
}else{
$body = $this->raw;
}
$this->parseDisposition();
$this->parseDescription();
$this->parseEncoding();
$this->charset = $this->header->get("charset");
$this->name = $this->header->get("name");
$this->filename = $this->header->get("filename");
if(!empty($this->header->get("id"))) {
$this->id = $this->header->get("id");
} else if(!empty($this->header->get("x_attachment_id"))){
$this->id = $this->header->get("x_attachment_id");
} else if(!empty($this->header->get("content_id"))){
$this->id = strtr($this->header->get("content_id"), [
'<' => '',
'>' => ''
]);
}
$content_types = $this->header->get("content_type");
if(!empty($content_types)){
$this->subtype = $this->parseSubtype($content_types);
$content_type = $content_types;
if (is_array($content_types)) {
$content_type = $content_types[0];
}
$parts = explode(';', $content_type);
$this->content_type = trim($parts[0]);
}
$this->content = trim(rtrim($body));
$this->bytes = strlen($this->content);
}
/**
* Find all available headers and return the leftover body segment
*
* @return string
* @throws InvalidMessageDateException
*/
private function findHeaders(): string {
$body = $this->raw;
while (($pos = strpos($body, "\r\n")) > 0) {
$body = substr($body, $pos + 2);
}
$headers = substr($this->raw, 0, strlen($body) * -1);
$body = substr($body, 0, -2);
$this->header = new Header($headers);
return $body;
}
/**
* Try to parse the subtype if any is present
* @param $content_type
*
* @return string
*/
private function parseSubtype($content_type){
if (is_array($content_type)) {
foreach ($content_type as $part){
if ((strpos($part, "/")) !== false){
return $this->parseSubtype($part);
}
}
return null;
}
if (($pos = strpos($content_type, "/")) !== false){
return substr($content_type, $pos + 1);
}
return null;
}
/**
* Try to parse the disposition if any is present
*/
private function parseDisposition(){
$content_disposition = $this->header->get("content_disposition");
if($content_disposition !== null) {
$this->ifdisposition = true;
$this->disposition = (is_array($content_disposition)) ? implode(' ', $content_disposition) : $content_disposition;
}
}
/**
* Try to parse the description if any is present
*/
private function parseDescription(){
$content_description = $this->header->get("content_description");
if($content_description !== null) {
$this->ifdescription = true;
$this->description = $content_description;
}
}
/**
* Try to parse the encoding if any is present
*/
private function parseEncoding(){
$encoding = $this->header->get("content_transfer_encoding");
if($encoding !== null) {
switch (strtolower($encoding)) {
case "quoted-printable":
$this->encoding = IMAP::MESSAGE_ENC_QUOTED_PRINTABLE;
break;
case "base64":
$this->encoding = IMAP::MESSAGE_ENC_BASE64;
break;
case "7bit":
$this->encoding = IMAP::MESSAGE_ENC_7BIT;
break;
case "8bit":
$this->encoding = IMAP::MESSAGE_ENC_8BIT;
break;
case "binary":
$this->encoding = IMAP::MESSAGE_ENC_BINARY;
break;
default:
$this->encoding = IMAP::MESSAGE_ENC_OTHER;
break;
}
}
}
/**
* Check if the current part represents an attachment
*
* @return bool
*/
public function isAttachment(): bool {
$valid_disposition = in_array(strtolower($this->disposition ?? ''), ClientManager::get('options.dispositions'));
if ($this->type == IMAP::MESSAGE_TYPE_TEXT && ($this->ifdisposition == 0 || empty($this->disposition) || !$valid_disposition)) {
if (($this->subtype == null || in_array((strtolower($this->subtype)), ["plain", "html"])) && $this->filename == null && $this->name == null) {
return false;
}
}
return true;
}
}

View File

@@ -0,0 +1,979 @@
<?php
/*
* File: Query.php
* Category: -
* Author: M. Goldenbaum
* Created: 21.07.18 18:54
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Query;
use Carbon\Carbon;
use Exception;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use ReflectionException;
use Webklex\PHPIMAP\Client;
use Webklex\PHPIMAP\ClientManager;
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
use Webklex\PHPIMAP\Exceptions\GetMessagesFailedException;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
use Webklex\PHPIMAP\Exceptions\MessageFlagException;
use Webklex\PHPIMAP\Exceptions\MessageHeaderFetchingException;
use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
use Webklex\PHPIMAP\Exceptions\RuntimeException;
use Webklex\PHPIMAP\IMAP;
use Webklex\PHPIMAP\Message;
use Webklex\PHPIMAP\Support\MessageCollection;
/**
* Class Query
*
* @package Webklex\PHPIMAP\Query
*/
class Query {
/** @var Collection $query */
protected $query;
/** @var string $raw_query */
protected $raw_query;
/** @var string[] $extensions */
protected $extensions;
/** @var Client $client */
protected $client;
/** @var int $limit */
protected $limit = null;
/** @var int $page */
protected $page = 1;
/** @var int $fetch_options */
protected $fetch_options = null;
/** @var int $fetch_body */
protected $fetch_body = true;
/** @var int $fetch_flags */
protected $fetch_flags = true;
/** @var int|string $sequence */
protected $sequence = IMAP::NIL;
/** @var string $fetch_order */
protected $fetch_order;
/** @var string $date_format */
protected $date_format;
/** @var bool $soft_fail */
protected $soft_fail = false;
/** @var array $errors */
protected $errors = [];
/**
* Query constructor.
* @param Client $client
* @param string[] $extensions
*/
public function __construct(Client $client, array $extensions = []) {
$this->setClient($client);
$this->sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
if (ClientManager::get('options.fetch') === IMAP::FT_PEEK) $this->leaveUnread();
if (ClientManager::get('options.fetch_order') === 'desc') {
$this->fetch_order = 'desc';
} else {
$this->fetch_order = 'asc';
}
$this->date_format = ClientManager::get('date_format', 'd M y');
$this->soft_fail = ClientManager::get('options.soft_fail', false);
$this->setExtensions($extensions);
$this->query = new Collection();
$this->boot();
}
/**
* Instance boot method for additional functionality
*/
protected function boot() {
}
/**
* Parse a given value
* @param mixed $value
*
* @return string
*/
protected function parse_value($value): string {
if ($value instanceof Carbon) {
$value = $value->format($this->date_format);
}
return (string)$value;
}
/**
* Check if a given date is a valid carbon object and if not try to convert it
* @param string|Carbon $date
*
* @return Carbon
* @throws MessageSearchValidationException
*/
protected function parse_date($date): Carbon {
if ($date instanceof Carbon) return $date;
try {
$date = Carbon::parse($date);
} catch (Exception $e) {
throw new MessageSearchValidationException();
}
return $date;
}
/**
* Get the raw IMAP search query
*
* @return string
*/
public function generate_query(): string {
$query = '';
$this->query->each(function($statement) use (&$query) {
if (count($statement) == 1) {
$query .= $statement[0];
} else {
if ($statement[1] === null) {
$query .= $statement[0];
} else {
if (is_numeric($statement[1])) {
$query .= $statement[0] . ' ' . $statement[1];
} else {
$query .= $statement[0] . ' "' . $statement[1] . '"';
}
}
}
$query .= ' ';
});
$this->raw_query = trim($query);
return $this->raw_query;
}
/**
* Perform an imap search request
*
* @return Collection
* @throws GetMessagesFailedException
*/
protected function search(): Collection {
$this->generate_query();
try {
$available_messages = $this->client->getConnection()->search([$this->getRawQuery()], $this->sequence);
return new Collection($available_messages);
} catch (RuntimeException $e) {
throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
} catch (ConnectionFailedException $e) {
throw new GetMessagesFailedException("failed to fetch messages", 0, $e);
}
}
/**
* Count all available messages matching the current search criteria
*
* @return int
* @throws GetMessagesFailedException
*/
public function count(): int {
return $this->search()->count();
}
/**
* Fetch a given id collection
* @param Collection $available_messages
*
* @return array
* @throws ConnectionFailedException
* @throws RuntimeException
*/
protected function fetch(Collection $available_messages): array {
if ($this->fetch_order === 'desc') {
$available_messages = $available_messages->reverse();
}
$uids = $available_messages->forPage($this->page, $this->limit)->toArray();
$extensions = $this->getExtensions();
if (empty($extensions) === false && method_exists($this->client->getConnection(), "fetch")) {
$extensions = $this->client->getConnection()->fetch($extensions, $uids, null, $this->sequence);
}
$flags = $this->client->getConnection()->flags($uids, $this->sequence);
$headers = $this->client->getConnection()->headers($uids, "RFC822", $this->sequence);
$contents = [];
if ($this->getFetchBody()) {
$contents = $this->client->getConnection()->content($uids, "RFC822", $this->sequence);
}
return [
"uids" => $uids,
"flags" => $flags,
"headers" => $headers,
"contents" => $contents,
"extensions" => $extensions,
];
}
/**
* Make a new message from given raw components
* @param integer $uid
* @param integer $msglist
* @param string $header
* @param string $content
* @param array $flags
*
* @return Message|null
* @throws ConnectionFailedException
* @throws EventNotFoundException
* @throws GetMessagesFailedException
* @throws ReflectionException
*/
protected function make(int $uid, int $msglist, string $header, string $content, array $flags) {
try {
return Message::make($uid, $msglist, $this->getClient(), $header, $content, $flags, $this->getFetchOptions(), $this->sequence);
} catch (MessageNotFoundException $e) {
$this->setError($uid, $e);
} catch (RuntimeException $e) {
$this->setError($uid, $e);
} catch (MessageFlagException $e) {
$this->setError($uid, $e);
} catch (InvalidMessageDateException $e) {
$this->setError($uid, $e);
} catch (MessageContentFetchingException $e) {
$this->setError($uid, $e);
}
$this->handleException($uid);
return null;
}
/**
* Get the message key for a given message
* @param string $message_key
* @param integer $msglist
* @param Message $message
*
* @return string
*/
protected function getMessageKey(string $message_key, int $msglist, Message $message): string {
switch ($message_key) {
case 'number':
$key = $message->getMessageNo();
break;
case 'list':
$key = $msglist;
break;
case 'uid':
$key = $message->getUid();
break;
default:
$key = $message->getMessageId();
break;
}
return (string)$key;
}
/**
* Curates a given collection aof messages
* @param Collection $available_messages
*
* @return MessageCollection
* @throws GetMessagesFailedException
*/
public function curate_messages(Collection $available_messages): MessageCollection {
try {
if ($available_messages->count() > 0) {
return $this->populate($available_messages);
}
return MessageCollection::make([]);
} catch (Exception $e) {
throw new GetMessagesFailedException($e->getMessage(), 0, $e);
}
}
/**
* Populate a given id collection and receive a fully fetched message collection
* @param Collection $available_messages
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws EventNotFoundException
* @throws GetMessagesFailedException
* @throws ReflectionException
* @throws RuntimeException
*/
protected function populate(Collection $available_messages): MessageCollection {
$messages = MessageCollection::make([]);
$messages->total($available_messages->count());
$message_key = ClientManager::get('options.message_key');
$raw_messages = $this->fetch($available_messages);
$msglist = 0;
foreach ($raw_messages["headers"] as $uid => $header) {
$content = $raw_messages["contents"][$uid] ?? "";
$flag = $raw_messages["flags"][$uid] ?? [];
$extensions = $raw_messages["extensions"][$uid] ?? [];
$message = $this->make($uid, $msglist, $header, $content, $flag);
foreach($extensions as $key => $extension) {
$message->getHeader()->set($key, $extension);
}
if ($message !== null) {
$key = $this->getMessageKey($message_key, $msglist, $message);
$messages->put("$key", $message);
}
$msglist++;
}
return $messages;
}
/**
* Fetch the current query and return all found messages
*
* @return MessageCollection
* @throws GetMessagesFailedException
*/
public function get(): MessageCollection {
return $this->curate_messages($this->search());
}
/**
* Fetch the current query as chunked requests
* @param callable $callback
* @param int $chunk_size
* @param int $start_chunk
*
* @throws ConnectionFailedException
* @throws EventNotFoundException
* @throws GetMessagesFailedException
* @throws ReflectionException
* @throws RuntimeException
*/
public function chunked(callable $callback, int $chunk_size = 10, int $start_chunk = 1) {
$available_messages = $this->search();
if (($available_messages_count = $available_messages->count()) > 0) {
$old_limit = $this->limit;
$old_page = $this->page;
$this->limit = $chunk_size;
$this->page = $start_chunk;
$handled_messages_count = 0;
do {
$messages = $this->populate($available_messages);
$handled_messages_count += $messages->count();
$callback($messages, $this->page);
$this->page++;
} while ($handled_messages_count < $available_messages_count);
$this->limit = $old_limit;
$this->page = $old_page;
}
}
/**
* Paginate the current query
* @param int $per_page Results you which to receive per page
* @param int|null $page The current page you are on (e.g. 0, 1, 2, ...) use `null` to enable auto mode
* @param string $page_name The page name / uri parameter used for the generated links and the auto mode
*
* @return LengthAwarePaginator
* @throws GetMessagesFailedException
*/
public function paginate(int $per_page = 5, $page = null, string $page_name = 'imap_page'): LengthAwarePaginator {
if (
$page === null
&& isset($_GET[$page_name])
&& $_GET[$page_name] > 0
) {
$this->page = intval($_GET[$page_name]);
} elseif ($page > 0) {
$this->page = $page;
}
$this->limit = $per_page;
return $this->get()->paginate($per_page, $this->page, $page_name, true);
}
/**
* Get a new Message instance
* @param int $uid
* @param int|null $msglist
* @param int|string|null $sequence
*
* @return Message
* @throws ConnectionFailedException
* @throws RuntimeException
* @throws InvalidMessageDateException
* @throws MessageContentFetchingException
* @throws MessageHeaderFetchingException
* @throws EventNotFoundException
* @throws MessageFlagException
* @throws MessageNotFoundException
*/
public function getMessage(int $uid, $msglist = null, $sequence = null): Message {
return new Message($uid, $msglist, $this->getClient(), $this->getFetchOptions(), $this->getFetchBody(), $this->getFetchFlags(), $sequence ? $sequence : $this->sequence);
}
/**
* Get a message by its message number
* @param $msgn
* @param int|null $msglist
*
* @return Message
* @throws ConnectionFailedException
* @throws InvalidMessageDateException
* @throws MessageContentFetchingException
* @throws MessageHeaderFetchingException
* @throws RuntimeException
* @throws EventNotFoundException
* @throws MessageFlagException
* @throws MessageNotFoundException
*/
public function getMessageByMsgn($msgn, $msglist = null): Message {
return $this->getMessage($msgn, $msglist, IMAP::ST_MSGN);
}
/**
* Get a message by its uid
* @param $uid
*
* @return Message
* @throws ConnectionFailedException
* @throws InvalidMessageDateException
* @throws MessageContentFetchingException
* @throws MessageHeaderFetchingException
* @throws RuntimeException
* @throws EventNotFoundException
* @throws MessageFlagException
* @throws MessageNotFoundException
*/
public function getMessageByUid($uid): Message {
return $this->getMessage($uid, null, IMAP::ST_UID);
}
/**
* Filter all available uids by a given closure and get a curated list of messages
* @param callable $closure
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function filter(callable $closure): MessageCollection {
$connection = $this->getClient()->getConnection();
$uids = $connection->getUid();
$available_messages = new Collection();
if (is_array($uids)) {
foreach ($uids as $id){
if ($closure($id)) {
$available_messages->push($id);
}
}
}
return $this->curate_messages($available_messages);
}
/**
* Get all messages with an uid greater or equal to a given UID
* @param int $uid
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function getByUidGreaterOrEqual(int $uid): MessageCollection {
return $this->filter(function($id) use($uid){
return $id >= $uid;
});
}
/**
* Get all messages with an uid greater than a given UID
* @param int $uid
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function getByUidGreater(int $uid): MessageCollection {
return $this->filter(function($id) use($uid){
return $id > $uid;
});
}
/**
* Get all messages with an uid lower than a given UID
* @param int $uid
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function getByUidLower(int $uid): MessageCollection {
return $this->filter(function($id) use($uid){
return $id < $uid;
});
}
/**
* Get all messages with an uid lower or equal to a given UID
* @param int $uid
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function getByUidLowerOrEqual(int $uid): MessageCollection {
return $this->filter(function($id) use($uid){
return $id <= $uid;
});
}
/**
* Get all messages with an uid greater than a given UID
* @param int $uid
*
* @return MessageCollection
* @throws ConnectionFailedException
* @throws GetMessagesFailedException
* @throws MessageNotFoundException
*/
public function getByUidLowerThan(int $uid): MessageCollection {
return $this->filter(function($id) use($uid){
return $id < $uid;
});
}
/**
* Don't mark messages as read when fetching
*
* @return $this
*/
public function leaveUnread(): Query {
$this->setFetchOptions(IMAP::FT_PEEK);
return $this;
}
/**
* Mark all messages as read when fetching
*
* @return $this
*/
public function markAsRead(): Query {
$this->setFetchOptions(IMAP::FT_UID);
return $this;
}
/**
* Set the sequence type
* @param int $sequence
*
* @return $this
*/
public function setSequence(int $sequence): Query {
$this->sequence = $sequence;
return $this;
}
/**
* Get the sequence type
*
* @return int|string
*/
public function getSequence() {
return $this->sequence;
}
/**
* @return Client
* @throws ConnectionFailedException
*/
public function getClient(): Client {
$this->client->checkConnection();
return $this->client;
}
/**
* Set the limit and page for the current query
* @param int $limit
* @param int $page
*
* @return $this
*/
public function limit(int $limit, int $page = 1): Query {
if ($page >= 1) $this->page = $page;
$this->limit = $limit;
return $this;
}
/**
* @return Collection
*/
public function getQuery(): Collection {
return $this->query;
}
/**
* @param array $query
* @return Query
*/
public function setQuery(array $query): Query {
$this->query = new Collection($query);
return $this;
}
/**
* @return string
*/
public function getRawQuery(): string {
return $this->raw_query;
}
/**
* @param string $raw_query
* @return Query
*/
public function setRawQuery(string $raw_query): Query {
$this->raw_query = $raw_query;
return $this;
}
/**
* @return string[]
*/
public function getExtensions(): array {
return $this->extensions;
}
/**
* @param string[] $extensions
* @return Query
*/
public function setExtensions(array $extensions): Query {
$this->extensions = $extensions;
if (count($this->extensions) > 0) {
if (in_array("UID", $this->extensions) === false) {
$this->extensions[] = "UID";
}
}
return $this;
}
/**
* @param Client $client
* @return Query
*/
public function setClient(Client $client): Query {
$this->client = $client;
return $this;
}
/**
* Get the set fetch limit
* @return int
*/
public function getLimit() {
return $this->limit;
}
/**
* @param int $limit
* @return Query
*/
public function setLimit(int $limit): Query {
$this->limit = $limit <= 0 ? null : $limit;
return $this;
}
/**
* @return int
*/
public function getPage(): int {
return $this->page;
}
/**
* @param int $page
* @return Query
*/
public function setPage(int $page): Query {
$this->page = $page;
return $this;
}
/**
* @param int $fetch_options
* @return Query
*/
public function setFetchOptions(int $fetch_options): Query {
$this->fetch_options = $fetch_options;
return $this;
}
/**
* @param int $fetch_options
* @return Query
*/
public function fetchOptions(int $fetch_options): Query {
return $this->setFetchOptions($fetch_options);
}
/**
* @return int
*/
public function getFetchOptions() {
return $this->fetch_options;
}
/**
* @return boolean
*/
public function getFetchBody() {
return $this->fetch_body;
}
/**
* @param boolean $fetch_body
* @return Query
*/
public function setFetchBody(bool $fetch_body): Query {
$this->fetch_body = $fetch_body;
return $this;
}
/**
* @param boolean $fetch_body
* @return Query
*/
public function fetchBody(bool $fetch_body): Query {
return $this->setFetchBody($fetch_body);
}
/**
* @return int
*/
public function getFetchFlags() {
return $this->fetch_flags;
}
/**
* @param int $fetch_flags
* @return Query
*/
public function setFetchFlags(int $fetch_flags): Query {
$this->fetch_flags = $fetch_flags;
return $this;
}
/**
* @param string $fetch_order
* @return Query
*/
public function setFetchOrder(string $fetch_order): Query {
$fetch_order = strtolower($fetch_order);
if (in_array($fetch_order, ['asc', 'desc'])) {
$this->fetch_order = $fetch_order;
}
return $this;
}
/**
* @param string $fetch_order
* @return Query
*/
public function fetchOrder(string $fetch_order): Query {
return $this->setFetchOrder($fetch_order);
}
/**
* @return string
*/
public function getFetchOrder(): string {
return $this->fetch_order;
}
/**
* @return Query
*/
public function setFetchOrderAsc(): Query {
return $this->setFetchOrder('asc');
}
/**
* @return Query
*/
public function fetchOrderAsc(): Query {
return $this->setFetchOrderAsc();
}
/**
* @return Query
*/
public function setFetchOrderDesc(): Query {
return $this->setFetchOrder('desc');
}
/**
* @return Query
*/
public function fetchOrderDesc(): Query {
return $this->setFetchOrderDesc();
}
/**
* @return Query
* @var boolean $state
*
*/
public function softFail(bool $state = true): Query {
return $this->setSoftFail($state);
}
/**
* @return Query
* @var boolean $state
*
*/
public function setSoftFail(bool $state = true): Query {
$this->soft_fail = $state;
return $this;
}
/**
* @return boolean
*/
public function getSoftFail(): bool {
return $this->soft_fail;
}
/**
* Handle the exception for a given uid
* @param integer $uid
*
* @throws GetMessagesFailedException
*/
protected function handleException(int $uid) {
if ($this->soft_fail === false && $this->hasError($uid)) {
$error = $this->getError($uid);
throw new GetMessagesFailedException($error->getMessage(), 0, $error);
}
}
/**
* Add a new error to the error holder
* @param integer $uid
* @param Exception $error
*/
protected function setError(int $uid, Exception $error) {
$this->errors[$uid] = $error;
}
/**
* Check if there are any errors / exceptions present
* @return boolean
* @var integer|null $uid
*
*/
public function hasErrors($uid = null): bool {
if ($uid !== null) {
return $this->hasError($uid);
}
return count($this->errors) > 0;
}
/**
* Check if there is an error / exception present
* @return boolean
* @var integer $uid
*
*/
public function hasError(int $uid): bool {
return isset($this->errors[$uid]);
}
/**
* Get all available errors / exceptions
*
* @return array
*/
public function errors(): array {
return $this->getErrors();
}
/**
* Get all available errors / exceptions
*
* @return array
*/
public function getErrors(): array {
return $this->errors;
}
/**
* Get a specific error / exception
* @return Exception|null
* @var integer $uid
*
*/
public function error(int $uid) {
return $this->getError($uid);
}
/**
* Get a specific error / exception
* @return Exception|null
* @var integer $uid
*
*/
public function getError(int $uid) {
if ($this->hasError($uid)) {
return $this->errors[$uid];
}
return null;
}
}

View File

@@ -0,0 +1,546 @@
<?php
/*
* File: Query.php
* Category: -
* Author: M. Goldenbaum
* Created: 21.07.18 18:54
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Query;
use Closure;
use Illuminate\Support\Str;
use Webklex\PHPIMAP\Exceptions\InvalidWhereQueryCriteriaException;
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
use Webklex\PHPIMAP\Exceptions\MessageSearchValidationException;
/**
* Class WhereQuery
*
* @package Webklex\PHPIMAP\Query
*
* @method WhereQuery all()
* @method WhereQuery answered()
* @method WhereQuery deleted()
* @method WhereQuery new()
* @method WhereQuery old()
* @method WhereQuery recent()
* @method WhereQuery seen()
* @method WhereQuery unanswered()
* @method WhereQuery undeleted()
* @method WhereQuery unflagged()
* @method WhereQuery unseen()
* @method WhereQuery not()
* @method WhereQuery unkeyword($value)
* @method WhereQuery to($value)
* @method WhereQuery text($value)
* @method WhereQuery subject($value)
* @method WhereQuery since($date)
* @method WhereQuery on($date)
* @method WhereQuery keyword($value)
* @method WhereQuery from($value)
* @method WhereQuery flagged()
* @method WhereQuery cc($value)
* @method WhereQuery body($value)
* @method WhereQuery before($date)
* @method WhereQuery bcc($value)
* @method WhereQuery inReplyTo($value)
* @method WhereQuery messageId($value)
*
* @mixin Query
*/
class WhereQuery extends Query {
/**
* @var array $available_criteria
*/
protected $available_criteria = [
'OR', 'AND',
'ALL', 'ANSWERED', 'BCC', 'BEFORE', 'BODY', 'CC', 'DELETED', 'FLAGGED', 'FROM', 'KEYWORD',
'NEW', 'NOT', 'OLD', 'ON', 'RECENT', 'SEEN', 'SINCE', 'SUBJECT', 'TEXT', 'TO',
'UNANSWERED', 'UNDELETED', 'UNFLAGGED', 'UNKEYWORD', 'UNSEEN', 'UID'
];
/**
* Magic method in order to allow alias usage of all "where" methods in an optional connection with "NOT"
* @param string $name
* @param array|null $arguments
*
* @return mixed
* @throws InvalidWhereQueryCriteriaException
* @throws MethodNotFoundException
*/
public function __call(string $name, $arguments) {
$that = $this;
$name = Str::camel($name);
if (strtolower(substr($name, 0, 3)) === 'not') {
$that = $that->whereNot();
$name = substr($name, 3);
}
if (strpos(strtolower($name), "where") === false) {
$method = 'where' . ucfirst($name);
} else {
$method = lcfirst($name);
}
if (method_exists($this, $method) === true) {
return call_user_func_array([$that, $method], $arguments);
}
throw new MethodNotFoundException("Method " . self::class . '::' . $method . '() is not supported');
}
/**
* Validate a given criteria
* @param $criteria
*
* @return string
* @throws InvalidWhereQueryCriteriaException
*/
protected function validate_criteria($criteria): string {
$command = strtoupper($criteria);
if (substr($command, 0, 7) === "CUSTOM ") {
return substr($criteria, 7);
}
if (in_array($command, $this->available_criteria) === false) {
throw new InvalidWhereQueryCriteriaException("Invalid imap search criteria: $command");
}
return $criteria;
}
/**
* Register search parameters
* @param mixed $criteria
* @param null $value
*
* @return $this
* @throws InvalidWhereQueryCriteriaException
*
* Examples:
* $query->from("someone@email.tld")->seen();
* $query->whereFrom("someone@email.tld")->whereSeen();
* $query->where([["FROM" => "someone@email.tld"], ["SEEN"]]);
* $query->where(["FROM" => "someone@email.tld"])->where(["SEEN"]);
* $query->where(["FROM" => "someone@email.tld", "SEEN"]);
* $query->where("FROM", "someone@email.tld")->where("SEEN");
*/
public function where($criteria, $value = null): WhereQuery {
if (is_array($criteria)) {
foreach ($criteria as $key => $value) {
if (is_numeric($key)) {
$this->where($value);
}else{
$this->where($key, $value);
}
}
} else {
$this->push_search_criteria($criteria, $value);
}
return $this;
}
/**
* Push a given search criteria and value pair to the search query
* @param $criteria string
* @param $value mixed
*
* @throws InvalidWhereQueryCriteriaException
*/
protected function push_search_criteria(string $criteria, $value){
$criteria = $this->validate_criteria($criteria);
$value = $this->parse_value($value);
if ($value === null || $value === '') {
$this->query->push([$criteria]);
} else {
$this->query->push([$criteria, $value]);
}
}
/**
* @param Closure $closure
*
* @return $this
*/
public function orWhere(Closure $closure = null): WhereQuery {
$this->query->push(['OR']);
if ($closure !== null) $closure($this);
return $this;
}
/**
* @param Closure $closure
*
* @return $this
*/
public function andWhere(Closure $closure = null): WhereQuery {
$this->query->push(['AND']);
if ($closure !== null) $closure($this);
return $this;
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereAll(): WhereQuery {
return $this->where('ALL');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereAnswered(): WhereQuery {
return $this->where('ANSWERED');
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereBcc(string $value): WhereQuery {
return $this->where('BCC', $value);
}
/**
* @param mixed $value
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
* @throws MessageSearchValidationException
*/
public function whereBefore($value): WhereQuery {
$date = $this->parse_date($value);
return $this->where('BEFORE', $date);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereBody(string $value): WhereQuery {
return $this->where('BODY', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereCc(string $value): WhereQuery {
return $this->where('CC', $value);
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereDeleted(): WhereQuery {
return $this->where('DELETED');
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereFlagged(string $value): WhereQuery {
return $this->where('FLAGGED', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereFrom(string $value): WhereQuery {
return $this->where('FROM', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereKeyword(string $value): WhereQuery {
return $this->where('KEYWORD', $value);
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereNew(): WhereQuery {
return $this->where('NEW');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereNot(): WhereQuery {
return $this->where('NOT');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereOld(): WhereQuery {
return $this->where('OLD');
}
/**
* @param mixed $value
*
* @return WhereQuery
* @throws MessageSearchValidationException
* @throws InvalidWhereQueryCriteriaException
*/
public function whereOn($value): WhereQuery {
$date = $this->parse_date($value);
return $this->where('ON', $date);
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereRecent(): WhereQuery {
return $this->where('RECENT');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereSeen(): WhereQuery {
return $this->where('SEEN');
}
/**
* @param mixed $value
*
* @return WhereQuery
* @throws MessageSearchValidationException
* @throws InvalidWhereQueryCriteriaException
*/
public function whereSince($value): WhereQuery {
$date = $this->parse_date($value);
return $this->where('SINCE', $date);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereSubject(string $value): WhereQuery {
return $this->where('SUBJECT', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereText(string $value): WhereQuery {
return $this->where('TEXT', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereTo(string $value): WhereQuery {
return $this->where('TO', $value);
}
/**
* @param string $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUnkeyword(string $value): WhereQuery {
return $this->where('UNKEYWORD', $value);
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUnanswered(): WhereQuery {
return $this->where('UNANSWERED');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUndeleted(): WhereQuery {
return $this->where('UNDELETED');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUnflagged(): WhereQuery {
return $this->where('UNFLAGGED');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUnseen(): WhereQuery {
return $this->where('UNSEEN');
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereNoXSpam(): WhereQuery {
return $this->where("CUSTOM X-Spam-Flag NO");
}
/**
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereIsXSpam(): WhereQuery {
return $this->where("CUSTOM X-Spam-Flag YES");
}
/**
* Search for a specific header value
* @param $header
* @param $value
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereHeader($header, $value): WhereQuery {
return $this->where("CUSTOM HEADER $header $value");
}
/**
* Search for a specific message id
* @param $messageId
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereMessageId($messageId): WhereQuery {
return $this->whereHeader("Message-ID", $messageId);
}
/**
* Search for a specific message id
* @param $messageId
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereInReplyTo($messageId): WhereQuery {
return $this->whereHeader("In-Reply-To", $messageId);
}
/**
* @param $country_code
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereLanguage($country_code): WhereQuery {
return $this->where("Content-Language $country_code");
}
/**
* Get message be it UID.
*
* @param int|string $uid
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUid($uid): WhereQuery {
return $this->where('UID', $uid);
}
/**
* Get messages by their UIDs.
*
* @param array<int, int> $uids
*
* @return WhereQuery
* @throws InvalidWhereQueryCriteriaException
*/
public function whereUidIn(array $uids): WhereQuery {
$uids = implode(',', $uids);
return $this->where('UID', $uids);
}
/**
* Apply the callback if the given "value" is truthy.
* copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
*
* @param mixed $value
* @param callable $callback
* @param callable|null $default
* @return $this|mixed
*/
public function when($value, callable $callback, $default = null) {
if ($value) {
return $callback($this, $value) ?: $this;
} elseif ($default) {
return $default($this, $value) ?: $this;
}
return $this;
}
/**
* Apply the callback if the given "value" is falsy.
* copied from @url https://github.com/laravel/framework/blob/8.x/src/Illuminate/Support/Traits/Conditionable.php
*
* @param mixed $value
* @param callable $callback
* @param callable|null $default
* @return $this|mixed
*/
public function unless($value, callable $callback, $default = null) {
if (!$value) {
return $callback($this, $value) ?: $this;
} elseif ($default) {
return $default($this, $value) ?: $this;
}
return $this;
}
}

View File

@@ -0,0 +1,174 @@
<?php
/*
* File: Structure.php
* Category: -
* Author: M.Goldenbaum
* Created: 17.09.20 20:38
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP;
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
use Webklex\PHPIMAP\Exceptions\MessageContentFetchingException;
/**
* Class Structure
*
* @package Webklex\PHPIMAP
*/
class Structure {
/**
* Raw structure
*
* @var string $raw
*/
public $raw = "";
/**
* @var Header $header
*/
private $header = null;
/**
* Message type (if multipart or not)
*
* @var int $type
*/
public $type = IMAP::MESSAGE_TYPE_TEXT;
/**
* All available parts
*
* @var Part[] $parts
*/
public $parts = [];
/**
* Config holder
*
* @var array $config
*/
protected $config = [];
/**
* Structure constructor.
* @param $raw_structure
* @param Header $header
*
* @throws MessageContentFetchingException
* @throws InvalidMessageDateException
*/
public function __construct($raw_structure, Header $header) {
$this->raw = $raw_structure;
$this->header = $header;
$this->config = ClientManager::get('options');
$this->parse();
}
/**
* Parse the given raw structure
*
* @throws MessageContentFetchingException
* @throws InvalidMessageDateException
*/
protected function parse(){
$this->findContentType();
$this->parts = $this->find_parts();
}
/**
* Determine the message content type
*/
public function findContentType(){
$content_type = $this->header->get("content_type");
$content_type = (is_array($content_type)) ? implode(' ', $content_type) : $content_type;
if($content_type && stripos($content_type, 'multipart') === 0) {
$this->type = IMAP::MESSAGE_TYPE_MULTIPART;
}else{
$this->type = IMAP::MESSAGE_TYPE_TEXT;
}
}
/**
* Find all available headers and return the left over body segment
* @var string $context
* @var integer $part_number
*
* @return Part[]
* @throws InvalidMessageDateException
*/
private function parsePart(string $context, int $part_number = 0): array {
$body = $context;
while (($pos = strpos($body, "\r\n")) > 0) {
$body = substr($body, $pos + 2);
}
$headers = substr($context, 0, strlen($body) * -1);
$body = substr($body, 0, -2);
$headers = new Header($headers);
if (($boundary = $headers->getBoundary()) !== null) {
return $this->detectParts($boundary, $body, $part_number);
}
return [new Part($body, $headers, $part_number)];
}
/**
* @param string $boundary
* @param string $context
* @param int $part_number
*
* @return array
* @throws InvalidMessageDateException
*/
private function detectParts(string $boundary, string $context, int $part_number = 0): array {
$base_parts = explode( $boundary, $context);
$final_parts = [];
foreach($base_parts as $ctx) {
$ctx = substr($ctx, 2);
if ($ctx !== "--" && $ctx != "") {
$parts = $this->parsePart($ctx, $part_number);
foreach ($parts as $part) {
$final_parts[] = $part;
$part_number = $part->part_number;
}
$part_number++;
}
}
return $final_parts;
}
/**
* Find all available parts
*
* @return array
* @throws MessageContentFetchingException
* @throws InvalidMessageDateException
*/
public function find_parts(): array {
if($this->type === IMAP::MESSAGE_TYPE_MULTIPART) {
if (($boundary = $this->header->getBoundary()) === null) {
throw new MessageContentFetchingException("no content found", 0);
}
return $this->detectParts($boundary, $this->raw);
}
return [new Part($this->raw, $this->header)];
}
/**
* Try to find a boundary if possible
*
* @return string|null
* @Depricated since version 2.4.4
*/
public function getBoundary(){
return $this->header->getBoundary();
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: AttachmentCollection.php
* Category: Collection
* Author: M. Goldenbaum
* Created: 16.03.18 03:13
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support;
/**
* Class AttachmentCollection
*
* @package Webklex\PHPIMAP\Support
*/
class AttachmentCollection extends PaginatedCollection {
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: FlagCollection.php
* Category: Collection
* Author: M. Goldenbaum
* Created: 21.07.18 23:10
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support;
/**
* Class FlagCollection
*
* @package Webklex\PHPIMAP\Support
*/
class FlagCollection extends PaginatedCollection {
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: FolderCollection.php
* Category: Collection
* Author: M. Goldenbaum
* Created: 18.03.18 02:21
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support;
/**
* Class FolderCollection
*
* @package Webklex\PHPIMAP\Support
*/
class FolderCollection extends PaginatedCollection {
}

View File

@@ -0,0 +1,44 @@
<?php
/*
* File: AttachmentMask.php
* Category: Mask
* Author: M.Goldenbaum
* Created: 14.03.19 20:49
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support\Masks;
use Webklex\PHPIMAP\Attachment;
/**
* Class AttachmentMask
*
* @package Webklex\PHPIMAP\Support\Masks
*/
class AttachmentMask extends Mask {
/** @var Attachment $parent */
protected $parent;
/**
* Get the attachment content base64 encoded
*
* @return string|null
*/
public function getContentBase64Encoded() {
return base64_encode($this->parent->content);
}
/**
* Get a base64 image src string
*
* @return string|null
*/
public function getImageSrc() {
return 'data:'.$this->parent->content_type.';base64,'.$this->getContentBase64Encoded();
}
}

View File

@@ -0,0 +1,137 @@
<?php
/*
* File: Mask.php
* Category: Mask
* Author: M.Goldenbaum
* Created: 14.03.19 20:49
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support\Masks;
use Illuminate\Support\Str;
use Webklex\PHPIMAP\Exceptions\MethodNotFoundException;
/**
* Class Mask
*
* @package Webklex\PHPIMAP\Support\Masks
*/
class Mask {
/**
* Available attributes
*
* @var array $attributes
*/
protected $attributes = [];
/**
* Parent instance
*
* @var object $parent
*/
protected $parent;
/**
* Mask constructor.
* @param $parent
*/
public function __construct($parent) {
$this->parent = $parent;
if(method_exists($this->parent, 'getAttributes')){
$this->attributes = array_merge($this->attributes, $this->parent->getAttributes());
}
$this->boot();
}
/**
* Boot method made to be used by any custom mask
*/
protected function boot(){}
/**
* Call dynamic attribute setter and getter methods and inherit the parent calls
* @param string $method
* @param array $arguments
*
* @return mixed
* @throws MethodNotFoundException
*/
public function __call(string $method, array $arguments) {
if(strtolower(substr($method, 0, 3)) === 'get') {
$name = Str::snake(substr($method, 3));
if(isset($this->attributes[$name])) {
return $this->attributes[$name];
}
}elseif (strtolower(substr($method, 0, 3)) === 'set') {
$name = Str::snake(substr($method, 3));
if(isset($this->attributes[$name])) {
$this->attributes[$name] = array_pop($arguments);
return $this->attributes[$name];
}
}
if(method_exists($this->parent, $method) === true){
return call_user_func_array([$this->parent, $method], $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 mixed|null
*/
public function __get($name) {
if(isset($this->attributes[$name])) {
return $this->attributes[$name];
}
return null;
}
/**
* Get the parent instance
*
* @return object
*/
public function getParent(){
return $this->parent;
}
/**
* Get all available attributes
*
* @return array
*/
public function getAttributes(): array {
return $this->attributes;
}
}

View File

@@ -0,0 +1,86 @@
<?php
/*
* File: MessageMask.php
* Category: Mask
* Author: M.Goldenbaum
* Created: 14.03.19 20:49
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support\Masks;
use Webklex\PHPIMAP\Attachment;
use Webklex\PHPIMAP\Message;
/**
* Class MessageMask
*
* @package Webklex\PHPIMAP\Support\Masks
*/
class MessageMask extends Mask {
/** @var Message $parent */
protected $parent;
/**
* Get the message html body
*
* @return null
*/
public function getHtmlBody(){
$bodies = $this->parent->getBodies();
if (!isset($bodies['html'])) {
return null;
}
if(is_object($bodies['html']) && property_exists($bodies['html'], 'content')) {
return $bodies['html']->content;
}
return $bodies['html'];
}
/**
* Get the Message html body filtered by an optional callback
* @param callable|bool $callback
*
* @return string|null
*/
public function getCustomHTMLBody($callback = false) {
$body = $this->getHtmlBody();
if($body === null) return null;
if ($callback !== false) {
$aAttachment = $this->parent->getAttachments();
$aAttachment->each(function($oAttachment) use(&$body, $callback) {
/** @var Attachment $oAttachment */
if(is_callable($callback)) {
$body = $callback($body, $oAttachment);
}elseif(is_string($callback)) {
call_user_func($callback, [$body, $oAttachment]);
}
});
}
return $body;
}
/**
* Get the Message html body with embedded base64 images
* the resulting $body.
*
* @return string|null
*/
public function getHTMLBodyWithEmbeddedBase64Images() {
return $this->getCustomHTMLBody(function($body, $oAttachment){
/** @var Attachment $oAttachment */
if ($oAttachment->id) {
$body = str_replace('cid:'.$oAttachment->id, 'data:'.$oAttachment->getContentType().';base64, '.base64_encode($oAttachment->getContent()), $body);
}
return $body;
});
}
}

View File

@@ -0,0 +1,22 @@
<?php
/*
* File: MessageCollection.php
* Category: Collection
* Author: M. Goldenbaum
* Created: 16.03.18 03:13
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support;
/**
* Class MessageCollection
*
* @package Webklex\PHPIMAP\Support
*/
class MessageCollection extends PaginatedCollection {
}

View File

@@ -0,0 +1,82 @@
<?php
/*
* File: PaginatedCollection.php
* Category: Collection
* Author: M. Goldenbaum
* Created: 16.03.18 03:13
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Support;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Collection;
use Illuminate\Pagination\Paginator;
/**
* Class PaginatedCollection
*
* @package Webklex\PHPIMAP\Support
*/
class PaginatedCollection extends Collection {
/**
* Number of total entries
*
* @var int $total
*/
protected $total;
/**
* Paginate the current collection.
* @param int $per_page
* @param int|null $page
* @param string $page_name
* @param boolean $prepaginated
*
* @return LengthAwarePaginator
*/
public function paginate(int $per_page = 15, $page = null, string $page_name = 'page', bool $prepaginated = false): LengthAwarePaginator {
$page = $page ?: Paginator::resolveCurrentPage($page_name);
$total = $this->total ?: $this->count();
$results = !$prepaginated && $total ? $this->forPage($page, $per_page)->toArray() : $this->all();
return $this->paginator($results, $total, $per_page, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $page_name,
]);
}
/**
* Create a new length-aware paginator instance.
* @param array $items
* @param int $total
* @param int $per_page
* @param int|null $current_page
* @param array $options
*
* @return LengthAwarePaginator
*/
protected function paginator(array $items, int $total, int $per_page, $current_page, array $options): LengthAwarePaginator {
return new LengthAwarePaginator($items, $total, $per_page, $current_page, $options);
}
/**
* Get and set the total amount
* @param null $total
*
* @return int|null
*/
public function total($total = null) {
if($total === null) {
return $this->total;
}
return $this->total = $total;
}
}

View File

@@ -0,0 +1,77 @@
<?php
/*
* File: HasEvents.php
* Category: -
* Author: M.Goldenbaum
* Created: 21.09.20 22:46
* Updated: -
*
* Description:
* -
*/
namespace Webklex\PHPIMAP\Traits;
use Webklex\PHPIMAP\Events\Event;
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
/**
* Trait HasEvents
*
* @package Webklex\PHPIMAP\Traits
*/
trait HasEvents {
/**
* Event holder
*
* @var array $events
*/
protected $events = [];
/**
* Set a specific event
* @param $section
* @param $event
* @param $class
*/
public function setEvent($section, $event, $class) {
if (isset($this->events[$section])) {
$this->events[$section][$event] = $class;
}
}
/**
* Set all events
* @param $events
*/
public function setEvents($events) {
$this->events = $events;
}
/**
* Get a specific event callback
* @param $section
* @param $event
*
* @return Event|string
* @throws EventNotFoundException
*/
public function getEvent($section, $event) {
if (isset($this->events[$section])) {
return $this->events[$section][$event];
}
throw new EventNotFoundException();
}
/**
* Get all events
*
* @return array
*/
public function getEvents(): array {
return $this->events;
}
}

View File

@@ -0,0 +1,226 @@
<?php
/*
* File: imap.php
* Category: config
* Author: M. Goldenbaum
* Created: 24.09.16 22:36
* Updated: -
*
* Description:
* -
*/
return [
/*
|--------------------------------------------------------------------------
| Default date format
|--------------------------------------------------------------------------
|
| The default date format is used to convert any given Carbon::class object into a valid date string.
| These are currently known working formats: "d-M-Y", "d-M-y", "d M y"
|
*/
'date_format' => 'd-M-Y',
/*
|--------------------------------------------------------------------------
| Default account
|--------------------------------------------------------------------------
|
| The default account identifier. It will be used as default for any missing account parameters.
| If however the default account is missing a parameter the package default will be used.
| Set to 'false' [boolean] to disable this functionality.
|
*/
'default' => 'default',
/*
|--------------------------------------------------------------------------
| Available accounts
|--------------------------------------------------------------------------
|
| Please list all IMAP accounts which you are planning to use within the
| array below.
|
*/
'accounts' => [
'default' => [// account identifier
'host' => 'localhost',
'port' => 993,
'protocol' => 'imap', //might also use imap, [pop3 or nntp (untested)]
'encryption' => 'ssl', // Supported: false, 'ssl', 'tls'
'validate_cert' => true,
'username' => 'root@example.com',
'password' => '',
'authentication' => null,
'proxy' => [
'socket' => null,
'request_fulluri' => false,
'username' => null,
'password' => null,
],
"timeout" => 30,
"extensions" => []
],
/*
'gmail' => [ // account identifier
'host' => 'imap.gmail.com',
'port' => 993,
'encryption' => 'ssl',
'validate_cert' => true,
'username' => 'example@gmail.com',
'password' => 'PASSWORD',
'authentication' => 'oauth',
],
'another' => [ // account identifier
'host' => '',
'port' => 993,
'encryption' => false,
'validate_cert' => true,
'username' => '',
'password' => '',
'authentication' => null,
]
*/
],
/*
|--------------------------------------------------------------------------
| Available IMAP options
|--------------------------------------------------------------------------
|
| Available php imap config parameters are listed below
| -Delimiter (optional):
| This option is only used when calling $oClient->
| You can use any supported char such as ".", "/", (...)
| -Fetch option:
| IMAP::FT_UID - Message marked as read by fetching the body message
| IMAP::FT_PEEK - Fetch the message without setting the "seen" flag
| -Fetch sequence id:
| IMAP::ST_UID - Fetch message components using the message uid
| IMAP::ST_MSGN - Fetch message components using the message number
| -Body download option
| Default TRUE
| -Flag download option
| Default TRUE
| -Soft fail
| Default FALSE - Set to TRUE if you want to ignore certain exception while fetching bulk messages
| -RFC822
| Default TRUE - Set to FALSE to prevent the usage of \imap_rfc822_parse_headers().
| See https://github.com/Webklex/php-imap/issues/115 for more information.
| -Debug enable to trace communication traffic
| -UID cache enable the UID cache
| -Fallback date is used if the given message date could not be parsed
| -Boundary regex used to detect message boundaries. If you are having problems with empty messages, missing
| attachments or anything like this. Be advised that it likes to break which causes new problems..
| -Message key identifier option
| You can choose between the following:
| 'id' - Use the MessageID as array key (default, might cause hickups with yahoo mail)
| 'number' - Use the message number as array key (isn't always unique and can cause some interesting behavior)
| 'list' - Use the message list number as array key (incrementing integer (does not always start at 0 or 1)
| 'uid' - Use the message uid as array key (isn't always unique and can cause some interesting behavior)
| -Fetch order
| 'asc' - Order all messages ascending (probably results in oldest first)
| 'desc' - Order all messages descending (probably results in newest first)
| -Disposition types potentially considered an attachment
| Default ['attachment', 'inline']
| -Common folders
| Default folder locations and paths assumed if none is provided
| -Open IMAP options:
| DISABLE_AUTHENTICATOR - Disable authentication properties.
| Use 'GSSAPI' if you encounter the following
| error: "Kerberos error: No credentials cache
| file found (try running kinit) (...)"
| or ['GSSAPI','PLAIN'] if you are using outlook mail
| -Decoder options (currently only the message subject and attachment name decoder can be set)
| 'utf-8' - Uses imap_utf8($string) to decode a string
| 'mimeheader' - Uses mb_decode_mimeheader($string) to decode a string
|
*/
'options' => [
'delimiter' => '/',
'fetch' => \Webklex\PHPIMAP\IMAP::FT_PEEK,
'sequence' => \Webklex\PHPIMAP\IMAP::ST_UID,
'fetch_body' => true,
'fetch_flags' => true,
'soft_fail' => false,
'rfc822' => true,
'debug' => false,
'uid_cache' => true,
// 'fallback_date' => "01.01.1970 00:00:00",
'boundary' => '/boundary=(.*?(?=;)|(.*))/i',
'message_key' => 'list',
'fetch_order' => 'asc',
'dispositions' => ['attachment', 'inline'],
'common_folders' => [
"root" => "INBOX",
"junk" => "INBOX/Junk",
"draft" => "INBOX/Drafts",
"sent" => "INBOX/Sent",
"trash" => "INBOX/Trash",
],
'decoder' => [
'message' => 'utf-8', // mimeheader
'attachment' => 'utf-8' // mimeheader
],
'open' => [
// 'DISABLE_AUTHENTICATOR' => 'GSSAPI'
]
],
/*
|--------------------------------------------------------------------------
| Available flags
|--------------------------------------------------------------------------
|
| List all available / supported flags. Set to null to accept all given flags.
*/
'flags' => ['recent', 'flagged', 'answered', 'deleted', 'seen', 'draft'],
/*
|--------------------------------------------------------------------------
| Available events
|--------------------------------------------------------------------------
|
*/
'events' => [
"message" => [
'new' => \Webklex\PHPIMAP\Events\MessageNewEvent::class,
'moved' => \Webklex\PHPIMAP\Events\MessageMovedEvent::class,
'copied' => \Webklex\PHPIMAP\Events\MessageCopiedEvent::class,
'deleted' => \Webklex\PHPIMAP\Events\MessageDeletedEvent::class,
'restored' => \Webklex\PHPIMAP\Events\MessageRestoredEvent::class,
],
"folder" => [
'new' => \Webklex\PHPIMAP\Events\FolderNewEvent::class,
'moved' => \Webklex\PHPIMAP\Events\FolderMovedEvent::class,
'deleted' => \Webklex\PHPIMAP\Events\FolderDeletedEvent::class,
],
"flag" => [
'new' => \Webklex\PHPIMAP\Events\FlagNewEvent::class,
'deleted' => \Webklex\PHPIMAP\Events\FlagDeletedEvent::class,
],
],
/*
|--------------------------------------------------------------------------
| Available masking options
|--------------------------------------------------------------------------
|
| By using your own custom masks you can implement your own methods for
| a better and faster access and less code to write.
|
| Checkout the two examples custom_attachment_mask and custom_message_mask
| for a quick start.
|
| The provided masks below are used as the default masks.
*/
'masks' => [
'message' => \Webklex\PHPIMAP\Support\Masks\MessageMask::class,
'attachment' => \Webklex\PHPIMAP\Support\Masks\AttachmentMask::class
]
];