mirror of https://github.com/itflow-org/itflow
Included WebKlex PHP-IMAP Library in plugins folder to allow for future use when we convert IMAP to allow OAUTH2
This commit is contained in:
parent
9520148d4d
commit
625a6cac6c
|
|
@ -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 string $personal = "";
|
||||
public string $mailbox = "";
|
||||
public string $host = "";
|
||||
public string $mail = "";
|
||||
public string $full = "";
|
||||
|
||||
/**
|
||||
* Address constructor.
|
||||
* @param object $object
|
||||
*/
|
||||
public function __construct(object $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();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,408 @@
|
|||
<?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 hash
|
||||
* @property string name
|
||||
* @property string description
|
||||
* @property string filename
|
||||
* @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 getHash()
|
||||
* @method string setHash(string $hash)
|
||||
* @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 Message $oMessage;
|
||||
|
||||
/**
|
||||
* Used config
|
||||
*
|
||||
* @var array $config
|
||||
*/
|
||||
protected array $config = [];
|
||||
|
||||
/** @var Part $part */
|
||||
protected Part $part;
|
||||
|
||||
/**
|
||||
* Attribute holder
|
||||
*
|
||||
* @var array $attributes
|
||||
*/
|
||||
protected array $attributes = [
|
||||
'content' => null,
|
||||
'hash' => null,
|
||||
'type' => null,
|
||||
'part_number' => 0,
|
||||
'content_type' => null,
|
||||
'id' => null,
|
||||
'name' => null,
|
||||
'filename' => null,
|
||||
'description' => null,
|
||||
'disposition' => null,
|
||||
'img_src' => null,
|
||||
'size' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Default mask
|
||||
*
|
||||
* @var string $mask
|
||||
*/
|
||||
protected string $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;
|
||||
|
||||
if ($this->oMessage->getClient()) {
|
||||
$default_mask = $this->oMessage->getClient()?->getDefaultAttachmentMask();
|
||||
if ($default_mask != null) {
|
||||
$this->mask = $default_mask;
|
||||
}
|
||||
} else {
|
||||
$default_mask = ClientManager::getMask("attachment");
|
||||
if ($default_mask != "") {
|
||||
$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(): void {
|
||||
$this->type = match ($this->part->type) {
|
||||
IMAP::ATTACHMENT_TYPE_MESSAGE => 'message',
|
||||
IMAP::ATTACHMENT_TYPE_APPLICATION => 'application',
|
||||
IMAP::ATTACHMENT_TYPE_AUDIO => 'audio',
|
||||
IMAP::ATTACHMENT_TYPE_IMAGE => 'image',
|
||||
IMAP::ATTACHMENT_TYPE_VIDEO => 'video',
|
||||
IMAP::ATTACHMENT_TYPE_MODEL => 'model',
|
||||
IMAP::ATTACHMENT_TYPE_TEXT => 'text',
|
||||
IMAP::ATTACHMENT_TYPE_MULTIPART => 'multipart',
|
||||
default => 'other',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch the given attachment
|
||||
*/
|
||||
protected function fetch(): void {
|
||||
$content = $this->part->content;
|
||||
|
||||
$this->content_type = $this->part->content_type;
|
||||
$this->content = $this->oMessage->decodeString($content, $this->part->encoding);
|
||||
|
||||
// Create a hash of the raw part - this can be used to identify the attachment in the message context. However,
|
||||
// it is not guaranteed to be unique and collisions are possible.
|
||||
// Some additional online resources:
|
||||
// - https://en.wikipedia.org/wiki/Hash_collision
|
||||
// - https://www.php.net/manual/en/function.hash.php
|
||||
// - https://php.watch/articles/php-hash-benchmark
|
||||
// Benchmark speeds:
|
||||
// -xxh3 ~15.19(GB/s) (requires php-xxhash extension or >= php8.1)
|
||||
// -crc32c ~14.12(GB/s)
|
||||
// -sha256 ~0.25(GB/s)
|
||||
// xxh3 would be nice to use, because of its extra speed and 32 instead of 8 bytes, but it is not compatible with
|
||||
// php < 8.1. crc32c is the next fastest and is compatible with php >= 5.1. sha256 is the slowest, but is compatible
|
||||
// with php >= 5.1 and is the most likely to be unique. crc32c is the best compromise between speed and uniqueness.
|
||||
// Unique enough for our purposes, but not so slow that it could be a bottleneck.
|
||||
$this->hash = hash("crc32c", $this->part->getHeader()->raw."\r\n\r\n".$this->part->content);
|
||||
|
||||
if (($id = $this->part->id) !== null) {
|
||||
$this->id = str_replace(['<', '>'], '', $id);
|
||||
}else {
|
||||
$this->id = $this->hash;
|
||||
}
|
||||
|
||||
$this->size = $this->part->bytes;
|
||||
$this->disposition = $this->part->disposition;
|
||||
|
||||
if (($filename = $this->part->filename) !== null) {
|
||||
$this->filename = $this->decodeName($filename);
|
||||
}
|
||||
|
||||
if (($description = $this->part->description) !== null) {
|
||||
$this->description = $this->part->getHeader()->decode($description);
|
||||
}
|
||||
|
||||
if (($name = $this->part->name) !== null) {
|
||||
$this->name = $this->decodeName($name);
|
||||
}
|
||||
|
||||
if (IMAP::ATTACHMENT_TYPE_MESSAGE == $this->part->type) {
|
||||
if ($this->part->ifdescription) {
|
||||
if (!$this->name) {
|
||||
$this->name = $this->part->description;
|
||||
}
|
||||
} else if (!$this->name) {
|
||||
$this->name = $this->part->subtype;
|
||||
}
|
||||
}
|
||||
$this->attributes = array_merge($this->part->getHeader()->getAttributes(), $this->attributes);
|
||||
|
||||
if (!$this->filename) {
|
||||
$this->filename = $this->hash;
|
||||
}
|
||||
|
||||
if (!$this->name && $this->filename != "") {
|
||||
$this->name = $this->filename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the attachment content to your filesystem
|
||||
* @param string $path
|
||||
* @param string|null $filename
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function save(string $path, ?string $filename = null): bool {
|
||||
$filename = $filename ? $this->decodeName($filename) : $this->filename;
|
||||
|
||||
return file_put_contents($path . DIRECTORY_SEPARATOR . $filename, $this->getContent()) !== false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode a given name
|
||||
* @param string|null $name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function decodeName(?string $name): string {
|
||||
if ($name !== null) {
|
||||
if (str_contains($name, "''")) {
|
||||
$parts = explode("''", $name);
|
||||
if (EncodingAliases::has($parts[0])) {
|
||||
$name = implode("''", array_slice($parts, 1));
|
||||
}
|
||||
}
|
||||
|
||||
$decoder = $this->config['decoder']['message'];
|
||||
if (preg_match('/=\?([^?]+)\?(Q|B)\?(.+)\?=/i', $name, $matches)) {
|
||||
$name = $this->part->getHeader()->decode($name);
|
||||
} elseif ($decoder === 'utf-8' && extension_loaded('imap')) {
|
||||
$name = \imap_utf8($name);
|
||||
}
|
||||
|
||||
// check if $name is url encoded
|
||||
if (preg_match('/%[0-9A-F]{2}/i', $name)) {
|
||||
$name = urldecode($name);
|
||||
}
|
||||
|
||||
// sanitize $name
|
||||
// order of '..' is important
|
||||
return str_replace(['\\', '/', chr(0), ':', '..'], '', $name);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attachment mime type
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getMimeType(): ?string {
|
||||
return (new \finfo())->buffer($this->getContent(), FILEINFO_MIME_TYPE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to guess the attachment file extension
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getExtension(): ?string {
|
||||
$extension = null;
|
||||
$guesser = "\Symfony\Component\Mime\MimeTypes";
|
||||
if (class_exists($guesser) !== false) {
|
||||
/** @var Symfony\Component\Mime\MimeTypes $guesser */
|
||||
$extensions = $guesser::getDefault()->getExtensions($this->getMimeType());
|
||||
$extension = $extensions[0] ?? null;
|
||||
}
|
||||
if ($extension === null) {
|
||||
$deprecated_guesser = "\Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser";
|
||||
if (class_exists($deprecated_guesser) !== false) {
|
||||
/** @var \Symfony\Component\HttpFoundation\File\MimeType\ExtensionGuesser $deprecated_guesser */
|
||||
$extension = $deprecated_guesser::getInstance()->guess($this->getMimeType());
|
||||
}
|
||||
}
|
||||
if ($extension === null) {
|
||||
$parts = explode(".", $this->filename);
|
||||
$extension = count($parts) > 1 ? end($parts) : null;
|
||||
}
|
||||
if ($extension === null) {
|
||||
$parts = explode(".", $this->name);
|
||||
$extension = count($parts) > 1 ? end($parts) : null;
|
||||
}
|
||||
return $extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(string $mask = null): mixed {
|
||||
$mask = $mask !== null ? $mask : $this->mask;
|
||||
if (class_exists($mask)) {
|
||||
return new $mask($this);
|
||||
}
|
||||
|
||||
throw new MaskNotFoundException("Unknown mask provided: " . $mask);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,325 @@
|
|||
<?php
|
||||
/*
|
||||
* File: Attribute.php
|
||||
* Category: -
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 01.01.21 20:17
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP;
|
||||
|
||||
use ArrayAccess;
|
||||
use Carbon\Carbon;
|
||||
|
||||
/**
|
||||
* Class Attribute
|
||||
*
|
||||
* @package Webklex\PHPIMAP
|
||||
*/
|
||||
class Attribute implements ArrayAccess {
|
||||
|
||||
/** @var string $name */
|
||||
protected string $name;
|
||||
|
||||
/**
|
||||
* Value holder
|
||||
*
|
||||
* @var array $values
|
||||
*/
|
||||
protected array $values = [];
|
||||
|
||||
/**
|
||||
* Attribute constructor.
|
||||
* @param string $name
|
||||
* @param mixed|null $value
|
||||
*/
|
||||
public function __construct(string $name, mixed $value = null) {
|
||||
$this->setName($name);
|
||||
$this->add($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle class invocation calls
|
||||
*
|
||||
* @return array|string
|
||||
*/
|
||||
public function __invoke(): array|string {
|
||||
if ($this->count() > 1) {
|
||||
return $this->toArray();
|
||||
}
|
||||
return $this->toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the serialized address
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function __serialize(){
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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->__serialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 a given key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function has(mixed $key = 0): bool {
|
||||
return array_key_exists($key, $this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a value exists at a given key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @return bool
|
||||
*/
|
||||
public function exist(mixed $key = 0): bool {
|
||||
return $this->has($key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the attribute contains the given value
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function contains(mixed $value): bool {
|
||||
return in_array($value, $this->values, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value by a given key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get(int|string $key = 0): mixed {
|
||||
return $this->values[$key] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value by a given key.
|
||||
*
|
||||
* @param mixed $key
|
||||
* @param mixed $value
|
||||
* @return Attribute
|
||||
*/
|
||||
public function set(mixed $value, mixed $key = 0): Attribute {
|
||||
if (is_null($key)) {
|
||||
$this->values[] = $value;
|
||||
} else {
|
||||
$this->values[$key] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unset a value by a given key.
|
||||
*
|
||||
* @param int|string $key
|
||||
* @return Attribute
|
||||
*/
|
||||
public function remove(int|string $key = 0): Attribute {
|
||||
if (isset($this->values[$key])) {
|
||||
unset($this->values[$key]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add one or more values to the attribute
|
||||
* @param array|mixed $value
|
||||
* @param boolean $strict
|
||||
*
|
||||
* @return Attribute
|
||||
*/
|
||||
public function add(mixed $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach a given value to the current value array
|
||||
* @param $value
|
||||
* @param bool $strict
|
||||
* @return Attribute
|
||||
*/
|
||||
public function attach($value, bool $strict = false): Attribute {
|
||||
if ($strict === true) {
|
||||
if ($this->contains($value) === false) {
|
||||
$this->values[] = $value;
|
||||
}
|
||||
}else{
|
||||
$this->values[] = $value;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 all(): array {
|
||||
reset($this->values);
|
||||
return $this->values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first value if possible
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function first(): mixed {
|
||||
return reset($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last value if possible
|
||||
*
|
||||
* @return mixed|null
|
||||
*/
|
||||
public function last(): mixed {
|
||||
return end($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of values
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function count(): int {
|
||||
return count($this->values);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ArrayAccess::offsetExists
|
||||
* @param mixed $offset
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists(mixed $offset): bool {
|
||||
return $this->has($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ArrayAccess::offsetGet
|
||||
* @param mixed $offset
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet(mixed $offset): mixed {
|
||||
return $this->get($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ArrayAccess::offsetSet
|
||||
* @param mixed $offset
|
||||
* @param mixed $value
|
||||
* @return void
|
||||
*/
|
||||
public function offsetSet(mixed $offset, mixed $value): void {
|
||||
$this->set($value, $offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see ArrayAccess::offsetUnset
|
||||
* @param mixed $offset
|
||||
* @return void
|
||||
*/
|
||||
public function offsetUnset(mixed $offset): void {
|
||||
$this->remove($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable $callback
|
||||
* @return array
|
||||
*/
|
||||
public function map(callable $callback): array {
|
||||
return array_map($callback, $this->values);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,950 @@
|
|||
<?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\ProtocolInterface;
|
||||
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
|
||||
use Webklex\PHPIMAP\Exceptions\FolderFetchingException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
|
||||
use Webklex\PHPIMAP\Exceptions\MaskNotFoundException;
|
||||
use Webklex\PHPIMAP\Exceptions\ProtocolNotSupportedException;
|
||||
use Webklex\PHPIMAP\Exceptions\ResponseException;
|
||||
use Webklex\PHPIMAP\Exceptions\RuntimeException;
|
||||
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 ?ProtocolInterface
|
||||
*/
|
||||
public ?ProtocolInterface $connection = null;
|
||||
|
||||
/**
|
||||
* Server hostname.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $host;
|
||||
|
||||
/**
|
||||
* Server port.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
public int $port;
|
||||
|
||||
/**
|
||||
* Service protocol.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $protocol;
|
||||
|
||||
/**
|
||||
* Server encryption.
|
||||
* Supported: none, ssl, tls, starttls or notls.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $encryption;
|
||||
|
||||
/**
|
||||
* If server has to validate cert.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
public bool $validate_cert = true;
|
||||
|
||||
/**
|
||||
* Proxy settings
|
||||
* @var array
|
||||
*/
|
||||
protected array $proxy = [
|
||||
'socket' => null,
|
||||
'request_fulluri' => false,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Connection timeout
|
||||
* @var int $timeout
|
||||
*/
|
||||
public int $timeout;
|
||||
|
||||
/**
|
||||
* Account username
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $username;
|
||||
|
||||
/**
|
||||
* Account password.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $password;
|
||||
|
||||
/**
|
||||
* Additional data fetched from the server.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public array $extensions;
|
||||
|
||||
/**
|
||||
* Account authentication method.
|
||||
*
|
||||
* @var ?string
|
||||
*/
|
||||
public ?string $authentication;
|
||||
|
||||
/**
|
||||
* Active folder path.
|
||||
*
|
||||
* @var ?string
|
||||
*/
|
||||
protected ?string $active_folder = null;
|
||||
|
||||
/**
|
||||
* Default message mask
|
||||
*
|
||||
* @var string $default_message_mask
|
||||
*/
|
||||
protected string $default_message_mask = MessageMask::class;
|
||||
|
||||
/**
|
||||
* Default attachment mask
|
||||
*
|
||||
* @var string $default_attachment_mask
|
||||
*/
|
||||
protected string $default_attachment_mask = AttachmentMask::class;
|
||||
|
||||
/**
|
||||
* Used default account values
|
||||
*
|
||||
* @var array $default_account_config
|
||||
*/
|
||||
protected array $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
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function __destruct() {
|
||||
$this->disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clone the current Client instance
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function clone(): Client {
|
||||
$client = new self();
|
||||
$client->events = $this->events;
|
||||
$client->timeout = $this->timeout;
|
||||
$client->active_folder = $this->active_folder;
|
||||
$client->default_account_config = $this->default_account_config;
|
||||
$config = $this->getAccountConfig();
|
||||
foreach($config as $key => $value) {
|
||||
$client->setAccountConfig($key, $config, $this->default_account_config);
|
||||
}
|
||||
$client->default_message_mask = $this->default_message_mask;
|
||||
$client->default_attachment_mask = $this->default_message_mask;
|
||||
return $client;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getConfig(): array {
|
||||
$config = [];
|
||||
foreach($this->default_account_config as $key => $value) {
|
||||
$config[$key] = $this->$key;
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void {
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current account config
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAccountConfig(): array {
|
||||
$config = [];
|
||||
foreach($this->default_account_config as $key => $value) {
|
||||
if(property_exists($this, $key)) {
|
||||
$config[$key] = $this->$key;
|
||||
}
|
||||
}
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look for a possible events in any available config
|
||||
* @param $config
|
||||
*/
|
||||
protected function setEventsFromConfig($config): void {
|
||||
$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): void {
|
||||
|
||||
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{
|
||||
$default_mask = ClientManager::getMask("message");
|
||||
if($default_mask != ""){
|
||||
$this->default_message_mask = $default_mask;
|
||||
}else{
|
||||
throw new MaskNotFoundException("Unknown message mask provided");
|
||||
}
|
||||
}
|
||||
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{
|
||||
$default_mask = ClientManager::getMask("attachment");
|
||||
if($default_mask != ""){
|
||||
$this->default_attachment_mask = $default_mask;
|
||||
}else{
|
||||
throw new MaskNotFoundException("Unknown attachment mask provided");
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$default_mask = ClientManager::getMask("message");
|
||||
if($default_mask != ""){
|
||||
$this->default_message_mask = $default_mask;
|
||||
}else{
|
||||
throw new MaskNotFoundException("Unknown message mask provided");
|
||||
}
|
||||
|
||||
$default_mask = ClientManager::getMask("attachment");
|
||||
if($default_mask != ""){
|
||||
$this->default_attachment_mask = $default_mask;
|
||||
}else{
|
||||
throw new MaskNotFoundException("Unknown attachment mask provided");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current imap resource
|
||||
*
|
||||
* @return ProtocolInterface
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getConnection(): ProtocolInterface {
|
||||
$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.
|
||||
* Returns true if the connection was closed and has been reopened.
|
||||
*
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function checkConnection(): bool {
|
||||
try {
|
||||
if (!$this->isConnected()) {
|
||||
$this->connect();
|
||||
return true;
|
||||
}
|
||||
} catch (\Throwable) {
|
||||
$this->connect();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force the connection to reconnect
|
||||
*
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function reconnect(): void {
|
||||
if ($this->isConnected()) {
|
||||
$this->disconnect();
|
||||
}
|
||||
$this->connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to server.
|
||||
*
|
||||
* @return $this
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
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 (str_starts_with($protocol, "legacy-")) {
|
||||
$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|RuntimeException $e) {
|
||||
throw new ConnectionFailedException("connection setup failed", 0, $e);
|
||||
}
|
||||
$this->authenticate();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the current session
|
||||
*
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
protected function authenticate(): void {
|
||||
if ($this->authentication == "oauth") {
|
||||
if (!$this->connection->authenticate($this->username, $this->password)->validatedData()) {
|
||||
throw new AuthFailedException();
|
||||
}
|
||||
} elseif (!$this->connection->login($this->username, $this->password)->validatedData()) {
|
||||
throw new AuthFailedException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect from server.
|
||||
*
|
||||
* @return $this
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function disconnect(): Client {
|
||||
if ($this->isConnected()) {
|
||||
$this->connection->logout();
|
||||
}
|
||||
$this->active_folder = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a folder instance by a folder name
|
||||
* @param string $folder_name
|
||||
* @param string|null $delimiter
|
||||
* @param bool $utf7
|
||||
* @return Folder|null
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getFolder(string $folder_name, ?string $delimiter = null, bool $utf7 = false): ?Folder {
|
||||
// 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 (str_contains($folder_name, (string)$delimiter)) {
|
||||
return $this->getFolderByPath($folder_name, $utf7);
|
||||
}
|
||||
|
||||
return $this->getFolderByName($folder_name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a folder instance by a folder name
|
||||
* @param $folder_name
|
||||
* @param bool $soft_fail If true, it will return null instead of throwing an exception
|
||||
*
|
||||
* @return Folder|null
|
||||
* @throws FolderFetchingException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getFolderByName($folder_name, bool $soft_fail = false): ?Folder {
|
||||
return $this->getFolders(false, null, $soft_fail)->where("name", $folder_name)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a folder instance by a folder path
|
||||
* @param $folder_path
|
||||
* @param bool $utf7
|
||||
* @param bool $soft_fail If true, it will return null instead of throwing an exception
|
||||
*
|
||||
* @return Folder|null
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getFolderByPath($folder_path, bool $utf7 = false, bool $soft_fail = false): ?Folder {
|
||||
if (!$utf7) $folder_path = EncodingAliases::convert($folder_path, "utf-8", "utf7-imap");
|
||||
return $this->getFolders(false, null, $soft_fail)->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
|
||||
* @param bool $soft_fail If true, it will return an empty collection instead of throwing an exception
|
||||
*
|
||||
* @return FolderCollection
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getFolders(bool $hierarchical = true, string $parent_folder = null, bool $soft_fail = false): FolderCollection {
|
||||
$this->checkConnection();
|
||||
$folders = FolderCollection::make([]);
|
||||
|
||||
$pattern = $parent_folder.($hierarchical ? '%' : '*');
|
||||
$items = $this->connection->folders('', $pattern)->validatedData();
|
||||
|
||||
if(!empty($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, $soft_fail);
|
||||
$folder->setChildren($children);
|
||||
}
|
||||
|
||||
$folders->push($folder);
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}else if (!$soft_fail){
|
||||
throw new FolderFetchingException("failed to fetch any folders");
|
||||
}
|
||||
|
||||
return $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
|
||||
* @param bool $soft_fail If true, it will return an empty collection instead of throwing an exception
|
||||
*
|
||||
* @return FolderCollection
|
||||
* @throws FolderFetchingException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getFoldersWithStatus(bool $hierarchical = true, string $parent_folder = null, bool $soft_fail = false): FolderCollection {
|
||||
$this->checkConnection();
|
||||
$folders = FolderCollection::make([]);
|
||||
|
||||
$pattern = $parent_folder.($hierarchical ? '%' : '*');
|
||||
$items = $this->connection->folders('', $pattern)->validatedData();
|
||||
|
||||
if(!empty($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, $soft_fail);
|
||||
$folder->setChildren($children);
|
||||
}
|
||||
|
||||
$folder->loadStatus();
|
||||
$folders->push($folder);
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}else if (!$soft_fail){
|
||||
throw new FolderFetchingException("failed to fetch any folders");
|
||||
}
|
||||
|
||||
return $folders;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a given folder.
|
||||
* @param string $folder_path
|
||||
* @param boolean $force_select
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function openFolder(string $folder_path, bool $force_select = false): array {
|
||||
if ($this->active_folder == $folder_path && $this->isConnected() && $force_select === false) {
|
||||
return [];
|
||||
}
|
||||
$this->checkConnection();
|
||||
$this->active_folder = $folder_path;
|
||||
return $this->connection->selectFolder($folder_path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set active folder
|
||||
* @param string|null $folder_path
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setActiveFolder(?string $folder_path = null): void {
|
||||
$this->active_folder = $folder_path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get active folder
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getActiveFolder(): ?string {
|
||||
return $this->active_folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Folder
|
||||
* @param string $folder_path
|
||||
* @param boolean $expunge
|
||||
* @param bool $utf7
|
||||
* @return Folder
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws EventNotFoundException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function createFolder(string $folder_path, bool $expunge = true, bool $utf7 = false): Folder {
|
||||
$this->checkConnection();
|
||||
|
||||
if (!$utf7) $folder_path = EncodingAliases::convert($folder_path, "utf-8", "UTF7-IMAP");
|
||||
|
||||
$status = $this->connection->createFolder($folder_path)->validatedData();
|
||||
|
||||
if($expunge) $this->expunge();
|
||||
|
||||
$folder = $this->getFolderByPath($folder_path, true);
|
||||
if($status && $folder) {
|
||||
$event = $this->getEvent("folder", "new");
|
||||
$event::dispatch($folder);
|
||||
}
|
||||
|
||||
return $folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a given folder
|
||||
* @param string $folder_path
|
||||
* @param boolean $expunge
|
||||
*
|
||||
* @return array
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws EventNotFoundException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function deleteFolder(string $folder_path, bool $expunge = true): array {
|
||||
$this->checkConnection();
|
||||
|
||||
$folder = $this->getFolderByPath($folder_path);
|
||||
if ($this->active_folder == $folder->path){
|
||||
$this->active_folder = null;
|
||||
}
|
||||
$status = $this->getConnection()->deleteFolder($folder->path)->validatedData();
|
||||
if ($expunge) $this->expunge();
|
||||
|
||||
$event = $this->getEvent("folder", "deleted");
|
||||
$event::dispatch($folder);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a given folder
|
||||
* @param string $folder_path
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function checkFolder(string $folder_path): array {
|
||||
$this->checkConnection();
|
||||
return $this->connection->examineFolder($folder_path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current active folder
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFolderPath(): string {
|
||||
return $this->active_folder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange identification information
|
||||
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
|
||||
*
|
||||
* @param array|null $ids
|
||||
* @return array
|
||||
*
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function Id(array $ids = null): array {
|
||||
$this->checkConnection();
|
||||
return $this->connection->ID($ids)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the quota level settings, and usage statics per mailbox
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getQuota(): array {
|
||||
$this->checkConnection();
|
||||
return $this->connection->getQuota($this->username)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the quota settings per user
|
||||
* @param string $quota_root
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getQuotaRoot(string $quota_root = 'INBOX'): array {
|
||||
$this->checkConnection();
|
||||
return $this->connection->getQuotaRoot($quota_root)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all messages marked for deletion
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function expunge(): array {
|
||||
$this->checkConnection();
|
||||
return $this->connection->expunge()->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection timeout
|
||||
* @param integer $timeout
|
||||
*
|
||||
* @return ProtocolInterface
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function setTimeout(int $timeout): ProtocolInterface {
|
||||
$this->timeout = $timeout;
|
||||
if ($this->isConnected()) {
|
||||
$this->connection->setConnectionTimeout($timeout);
|
||||
$this->reconnect();
|
||||
}
|
||||
return $this->connection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connection timeout
|
||||
*
|
||||
* @return int
|
||||
* @throws ConnectionFailedException
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,293 @@
|
|||
<?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 array $config = [];
|
||||
|
||||
/**
|
||||
* @var array $accounts
|
||||
*/
|
||||
protected array $accounts = [];
|
||||
|
||||
/**
|
||||
* ClientManager constructor.
|
||||
* @param array|string $config
|
||||
*/
|
||||
public function __construct(array|string $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): mixed {
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the mask for a given section
|
||||
* @param string $section section name such as "message" or "attachment"
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public static function getMask(string $section): ?string {
|
||||
$default_masks = ClientManager::get("masks");
|
||||
if (isset($default_masks[$section])) {
|
||||
if (class_exists($default_masks[$section])) {
|
||||
return $default_masks[$section];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(?string $name): array {
|
||||
if ($name === null || $name === 'null' || $name === "") {
|
||||
return ['driver' => 'null'];
|
||||
}
|
||||
$account = self::$config["accounts"][$name] ?? [];
|
||||
|
||||
return is_array($account) ? $account : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 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): void {
|
||||
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(array|string $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(): mixed {
|
||||
|
||||
$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
|
|
@ -0,0 +1,814 @@
|
|||
<?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\ImapBadRequestException;
|
||||
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 string $protocol = "imap";
|
||||
protected string $host = "localhost";
|
||||
protected int $port = 993;
|
||||
|
||||
/**
|
||||
* 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, mixed $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 int|null $port
|
||||
*/
|
||||
public function connect(string $host, int $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 Response
|
||||
*/
|
||||
public function login(string $user, string $password): Response {
|
||||
return $this->response()->wrap(function($response) use ($user, $password) {
|
||||
/** @var Response $response */
|
||||
try {
|
||||
$this->stream = \imap_open(
|
||||
$this->getAddress(),
|
||||
$user,
|
||||
$password,
|
||||
0,
|
||||
$attempts = 3,
|
||||
ClientManager::get('options.open')
|
||||
);
|
||||
$response->addCommand("imap_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();
|
||||
$response->addCommand("imap_errors");
|
||||
if (is_array($errors)) {
|
||||
$status = $this->examineFolder();
|
||||
$response->stack($status);
|
||||
if ($status->data()['exists'] !== 0) {
|
||||
$message = implode("; ", $errors);
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->stream !== false) {
|
||||
return ["TAG" . $response->Noun() . " OK [] Logged in\r\n"];
|
||||
}
|
||||
|
||||
$response->addError("failed to login");
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate your current session.
|
||||
* @param string $user username
|
||||
* @param string $token access token
|
||||
*
|
||||
* @return Response
|
||||
* @throws AuthFailedException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function authenticate(string $user, string $token): Response {
|
||||
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 Response
|
||||
*/
|
||||
public function logout(): Response {
|
||||
return $this->response()->wrap(function($response) {
|
||||
/** @var Response $response */
|
||||
if ($this->stream) {
|
||||
$this->uid_cache = [];
|
||||
$response->addCommand("imap_close");
|
||||
if (\imap_close($this->stream, IMAP::CL_EXPUNGE)) {
|
||||
$this->stream = false;
|
||||
return [
|
||||
0 => "BYE Logging out\r\n",
|
||||
1 => "TAG" . $response->Noun() . " OK Logout completed (0.001 + 0.000 secs).\r\n",
|
||||
];
|
||||
}
|
||||
$this->stream = false;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array of available capabilities
|
||||
*
|
||||
* @throws MethodNotSupportedException
|
||||
*/
|
||||
public function getCapabilities(): Response {
|
||||
throw new MethodNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the current folder
|
||||
* @param string $folder change to this folder
|
||||
*
|
||||
* @return Response see examineOrselect()
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function selectFolder(string $folder = 'INBOX'): Response {
|
||||
$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.");
|
||||
}
|
||||
|
||||
return $this->response("imap_reopen")->wrap(function($response) use ($folder, $flags) {
|
||||
/** @var Response $response */
|
||||
\imap_reopen($this->stream, $this->getAddress() . $folder, $flags, 3);
|
||||
$this->uid_cache = [];
|
||||
|
||||
$status = $this->examineFolder($folder);
|
||||
$response->stack($status);
|
||||
|
||||
return $status->data();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine a given folder
|
||||
* @param string $folder examine this folder
|
||||
*
|
||||
* @return Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function examineFolder(string $folder = 'INBOX'): Response {
|
||||
if (str_starts_with($folder, ".")) {
|
||||
throw new RuntimeException("Segmentation fault prevented. Folders starts with an illegal char '.'.");
|
||||
}
|
||||
return $this->response("imap_status")->wrap(function($response) use ($folder) {
|
||||
/** @var Response $response */
|
||||
$status = \imap_status($this->stream, $this->getAddress() . $folder, IMAP::SA_ALL);
|
||||
|
||||
return $status ? [
|
||||
"flags" => [],
|
||||
"exists" => $status->messages,
|
||||
"recent" => $status->recent,
|
||||
"unseen" => $status->unseen,
|
||||
"uidnext" => $status->uidnext,
|
||||
] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch message content
|
||||
* @param int|array $uids
|
||||
* @param string $rfc
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($uids, $uid) {
|
||||
/** @var Response $response */
|
||||
|
||||
$result = [];
|
||||
$uids = is_array($uids) ? $uids : [$uids];
|
||||
foreach ($uids as $id) {
|
||||
$response->addCommand("imap_fetchbody");
|
||||
$result[$id] = \imap_fetchbody($this->stream, $id, "", $uid === IMAP::ST_UID ? IMAP::ST_UID : IMAP::NIL);
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch message headers
|
||||
* @param int|array $uids
|
||||
* @param string $rfc
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function headers(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($uids, $uid) {
|
||||
/** @var Response $response */
|
||||
|
||||
$result = [];
|
||||
$uids = is_array($uids) ? $uids : [$uids];
|
||||
foreach ($uids as $id) {
|
||||
$response->addCommand("imap_fetchheader");
|
||||
$result[$id] = \imap_fetchheader($this->stream, $id, $uid ? IMAP::ST_UID : IMAP::NIL);
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch message flags
|
||||
* @param int|array $uids
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function flags(int|array $uids, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($uids, $uid) {
|
||||
/** @var Response $response */
|
||||
|
||||
$result = [];
|
||||
$uids = is_array($uids) ? $uids : [$uids];
|
||||
foreach ($uids as $id) {
|
||||
$response->addCommand("imap_fetch_overview");
|
||||
$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[$id] = $flags;
|
||||
}
|
||||
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch message sizes
|
||||
* @param int|array $uids
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function sizes(int|array $uids, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($uids, $uid) {
|
||||
/** @var Response $response */
|
||||
$result = [];
|
||||
$uids = is_array($uids) ? $uids : [$uids];
|
||||
$uid_text = implode("','", $uids);
|
||||
$response->addCommand("imap_fetch_overview");
|
||||
if ($uid == IMAP::ST_UID) {
|
||||
$raw_overview = \imap_fetch_overview($this->stream, $uid_text, IMAP::FT_UID);
|
||||
} else {
|
||||
$raw_overview = \imap_fetch_overview($this->stream, $uid_text);
|
||||
}
|
||||
if ($raw_overview !== false) {
|
||||
foreach ($raw_overview as $overview_element) {
|
||||
$overview_element = (array)$overview_element;
|
||||
$result[$overview_element[$uid == IMAP::ST_UID ? 'uid' : 'msgno']] = $overview_element['size'];
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get uid for a given id
|
||||
* @param int|null $id message number
|
||||
*
|
||||
* @return Response message number for given message or all messages as array
|
||||
*/
|
||||
public function getUid(int $id = null): Response {
|
||||
return $this->response()->wrap(function($response) use ($id) {
|
||||
/** @var Response $response */
|
||||
if ($id === null) {
|
||||
if ($this->enable_uid_cache && $this->uid_cache) {
|
||||
return $this->uid_cache;
|
||||
}
|
||||
|
||||
$overview = $this->overview("1:*");
|
||||
$response->stack($overview);
|
||||
$uids = [];
|
||||
foreach ($overview->data() as $set) {
|
||||
$uids[$set->msgno] = $set->uid;
|
||||
}
|
||||
|
||||
$this->setUidCache($uids);
|
||||
return $uids;
|
||||
}
|
||||
|
||||
$response->addCommand("imap_uid");
|
||||
$uid = \imap_uid($this->stream, $id);
|
||||
if ($uid) {
|
||||
return $uid;
|
||||
}
|
||||
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message number for a uid
|
||||
* @param string $id uid
|
||||
*
|
||||
* @return Response message number
|
||||
*/
|
||||
public function getMessageNumber(string $id): Response {
|
||||
return $this->response("imap_msgno")->wrap(function($response) use ($id) {
|
||||
/** @var Response $response */
|
||||
return \imap_msgno($this->stream, $id);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a message overview
|
||||
* @param string $sequence uid sequence
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function overview(string $sequence, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response("imap_fetch_overview")->wrap(function($response) use ($sequence, $uid) {
|
||||
/** @var Response $response */
|
||||
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 Response folders that matched $folder as array(name => array('delimiter' => .., 'flags' => ..))
|
||||
*/
|
||||
public function folders(string $reference = '', string $folder = '*'): Response {
|
||||
return $this->response("imap_getmailboxes")->wrap(function($response) use ($reference, $folder) {
|
||||
/** @var Response $response */
|
||||
$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|string $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|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
* @param string|null $item unused attribute
|
||||
*
|
||||
* @return Response new flags if $silent is false, else true or false depending on success
|
||||
*/
|
||||
public function store(array|string $flags, int $from, int $to = null, string $mode = null, bool $silent = true, int|string $uid = IMAP::ST_UID, string $item = null): Response {
|
||||
$flag = trim(is_array($flags) ? implode(" ", $flags) : $flags);
|
||||
|
||||
return $this->response()->wrap(function($response) use ($mode, $from, $flag, $uid, $silent) {
|
||||
/** @var Response $response */
|
||||
|
||||
if ($mode == "+") {
|
||||
$response->addCommand("imap_setflag_full");
|
||||
$status = \imap_setflag_full($this->stream, $from, $flag, $uid ? IMAP::ST_UID : IMAP::NIL);
|
||||
} else {
|
||||
$response->addCommand("imap_clearflag_full");
|
||||
$status = \imap_clearflag_full($this->stream, $from, $flag, $uid ? IMAP::ST_UID : IMAP::NIL);
|
||||
}
|
||||
|
||||
if ($silent === true) {
|
||||
if ($status) {
|
||||
return [
|
||||
"TAG" . $response->Noun() . " OK Store completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
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 mixed $date date for new message
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function appendMessage(string $folder, string $message, array $flags = null, mixed $date = null): Response {
|
||||
return $this->response("imap_append")->wrap(function($response) use ($folder, $message, $flags, $date) {
|
||||
/** @var Response $response */
|
||||
if ($date != null) {
|
||||
if ($date instanceof \Carbon\Carbon) {
|
||||
$date = $date->format('d-M-Y H:i:s O');
|
||||
}
|
||||
if (\imap_append($this->stream, $this->getAddress() . $folder, $message, $flags, $date)) {
|
||||
return [
|
||||
"OK Append completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
}
|
||||
} else if (\imap_append($this->stream, $this->getAddress() . $folder, $message, $flags)) {
|
||||
return [
|
||||
"OK Append completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function copyMessage(string $folder, $from, int $to = null, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response("imap_mail_copy")->wrap(function($response) use ($from, $folder, $uid) {
|
||||
/** @var Response $response */
|
||||
|
||||
if (\imap_mail_copy($this->stream, $from, $this->getAddress() . $folder, $uid ? IMAP::ST_UID : IMAP::NIL)) {
|
||||
return [
|
||||
"TAG" . $response->Noun() . " OK Copy completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
}
|
||||
throw new ImapBadRequestException("Invalid ID $from");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy multiple messages to the target folder
|
||||
* @param array $messages List of message identifiers
|
||||
* @param string $folder Destination folder
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response Tokens if operation successful, false if an error occurred
|
||||
*/
|
||||
public function copyManyMessages(array $messages, string $folder, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($messages, $folder, $uid) {
|
||||
/** @var Response $response */
|
||||
foreach ($messages as $msg) {
|
||||
$copy_response = $this->copyMessage($folder, $msg, null, $uid);
|
||||
$response->stack($copy_response);
|
||||
if (empty($copy_response->data())) {
|
||||
return [
|
||||
"TAG" . $response->Noun() . " BAD Copy failed (0.001 + 0.000 secs).\r\n",
|
||||
"Invalid ID $msg\r\n"
|
||||
];
|
||||
}
|
||||
}
|
||||
return [
|
||||
"TAG" . $response->Noun() . " OK Copy completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response success
|
||||
*/
|
||||
public function moveMessage(string $folder, $from, int $to = null, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response("imap_mail_move")->wrap(function($response) use ($from, $folder, $uid) {
|
||||
if (\imap_mail_move($this->stream, $from, $this->getAddress() . $folder, $uid ? IMAP::ST_UID : IMAP::NIL)) {
|
||||
return [
|
||||
"TAG" . $response->Noun() . " OK Move completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
}
|
||||
throw new ImapBadRequestException("Invalid ID $from");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Move multiple messages to the target folder
|
||||
* @param array $messages List of message identifiers
|
||||
* @param string $folder Destination folder
|
||||
* @param int|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response Tokens if operation successful, false if an error occurred
|
||||
* @throws ImapBadRequestException
|
||||
*/
|
||||
public function moveManyMessages(array $messages, string $folder, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response()->wrap(function($response) use ($messages, $folder, $uid) {
|
||||
foreach ($messages as $msg) {
|
||||
$move_response = $this->moveMessage($folder, $msg, null, $uid);
|
||||
$response = $response->include($response);
|
||||
if (empty($move_response->data())) {
|
||||
return [
|
||||
"TAG" . $response->Noun() . " BAD Move failed (0.001 + 0.000 secs).\r\n",
|
||||
"Invalid ID $msg\r\n"
|
||||
];
|
||||
}
|
||||
}
|
||||
return [
|
||||
"TAG" . $response->Noun() . " OK Move completed (0.001 + 0.000 secs).\r\n"
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Exchange identification information
|
||||
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
|
||||
*
|
||||
* @param null $ids
|
||||
* @return Response
|
||||
*
|
||||
* @throws MethodNotSupportedException
|
||||
*/
|
||||
public function ID($ids = null): Response {
|
||||
throw new MethodNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new folder (and parent folders if needed)
|
||||
* @param string $folder folder name
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function createFolder(string $folder): Response {
|
||||
return $this->response("imap_createmailbox")->wrap(function($response) use ($folder) {
|
||||
return \imap_createmailbox($this->stream, $this->getAddress() . $folder) ? [
|
||||
0 => "TAG" . $response->Noun() . " OK Create completed (0.004 + 0.000 + 0.003 secs).\r\n",
|
||||
] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename an existing folder
|
||||
* @param string $old old name
|
||||
* @param string $new new name
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function renameFolder(string $old, string $new): Response {
|
||||
return $this->response("imap_renamemailbox")->wrap(function($response) use ($old, $new) {
|
||||
return \imap_renamemailbox($this->stream, $this->getAddress() . $old, $this->getAddress() . $new) ? [
|
||||
0 => "TAG" . $response->Noun() . " OK Move completed (0.004 + 0.000 + 0.003 secs).\r\n",
|
||||
] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a folder
|
||||
* @param string $folder folder name
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function deleteFolder(string $folder): Response {
|
||||
return $this->response("imap_deletemailbox")->wrap(function($response) use ($folder) {
|
||||
return \imap_deletemailbox($this->stream, $this->getAddress() . $folder) ? [
|
||||
0 => "OK Delete completed (0.004 + 0.000 + 0.003 secs).\r\n",
|
||||
] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a folder
|
||||
* @param string $folder folder name
|
||||
*
|
||||
* @throws MethodNotSupportedException
|
||||
*/
|
||||
public function subscribeFolder(string $folder): Response {
|
||||
throw new MethodNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe from a folder
|
||||
* @param string $folder folder name
|
||||
*
|
||||
* @throws MethodNotSupportedException
|
||||
*/
|
||||
public function unsubscribeFolder(string $folder): Response {
|
||||
throw new MethodNotSupportedException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply session saved changes to the server
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function expunge(): Response {
|
||||
return $this->response("imap_expunge")->wrap(function($response) {
|
||||
return \imap_expunge($this->stream) ? [
|
||||
0 => "TAG" . $response->Noun() . " OK Expunge completed (0.001 + 0.000 secs).\r\n",
|
||||
] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Send noop command
|
||||
*
|
||||
* @throws MethodNotSupportedException
|
||||
*/
|
||||
public function noop(): Response {
|
||||
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|string $uid set to IMAP::ST_UID if you pass message unique identifiers instead of numbers.
|
||||
*
|
||||
* @return Response message ids
|
||||
*/
|
||||
public function search(array $params, int|string $uid = IMAP::ST_UID): Response {
|
||||
return $this->response("imap_search")->wrap(function($response) use ($params, $uid) {
|
||||
$response->setCanBeEmpty(true);
|
||||
$result = \imap_search($this->stream, $params[0], $uid ? IMAP::ST_UID : IMAP::NIL);
|
||||
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): array|bool|string|null {
|
||||
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 Response
|
||||
*/
|
||||
public function getQuota($username): Response {
|
||||
return $this->response("imap_get_quota")->wrap(function($response) use ($username) {
|
||||
$result = \imap_get_quota($this->stream, 'user.' . $username);
|
||||
return $result ?: [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the quota settings per user
|
||||
* @param string $quota_root
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function getQuotaRoot(string $quota_root = 'INBOX'): Response {
|
||||
return $this->response("imap_get_quotaroot")->wrap(function($response) use ($quota_root) {
|
||||
$result = \imap_get_quotaroot($this->stream, $this->getAddress() . $quota_root);
|
||||
return $result ?: [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new Response instance
|
||||
* @param string|null $command
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
protected function response(?string $command = ""): Response {
|
||||
return Response::make(0, $command == "" ? [] : [$command], [], $this->debug);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,366 @@
|
|||
<?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 int $connection_timeout = 30;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected bool $debug = false;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected bool $enable_uid_cache = true;
|
||||
|
||||
/**
|
||||
* @var resource
|
||||
*/
|
||||
public $stream = false;
|
||||
|
||||
/**
|
||||
* Connection encryption method
|
||||
* @var string $encryption
|
||||
*/
|
||||
protected string $encryption = "";
|
||||
|
||||
/**
|
||||
* Set to false to ignore SSL certificate validation
|
||||
* @var bool
|
||||
*/
|
||||
protected bool $cert_validation = true;
|
||||
|
||||
/**
|
||||
* Proxy settings
|
||||
* @var array
|
||||
*/
|
||||
protected array $proxy = [
|
||||
'socket' => null,
|
||||
'request_fulluri' => false,
|
||||
'username' => null,
|
||||
'password' => null,
|
||||
];
|
||||
|
||||
/**
|
||||
* Cache for uid of active folder.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected array $uid_cache = [];
|
||||
|
||||
/**
|
||||
* Get an available cryptographic method
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getCryptoMethod(): int {
|
||||
// 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 Protocol
|
||||
*/
|
||||
public function enableCertValidation(): Protocol {
|
||||
$this->cert_validation = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SSL certificate validation
|
||||
* @return Protocol
|
||||
*/
|
||||
public function disableCertValidation(): Protocol {
|
||||
$this->cert_validation = false;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set SSL certificate validation
|
||||
* @var int $cert_validation
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function setCertValidation(int $cert_validation): Protocol {
|
||||
$this->cert_validation = $cert_validation;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we validate SSL certificate?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getCertValidation(): bool {
|
||||
return $this->cert_validation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set connection proxy settings
|
||||
* @var array $options
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function setProxy(array $options): Protocol {
|
||||
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(): array {
|
||||
return $this->proxy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare socket options
|
||||
* @return array
|
||||
*@var string $transport
|
||||
*
|
||||
*/
|
||||
private function defaultSocketOptions(string $transport): array {
|
||||
$options = [];
|
||||
if ($this->encryption) {
|
||||
$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 The socket created.
|
||||
* @throws ConnectionFailedException
|
||||
*/
|
||||
public function createStream($transport, string $host, int $port, int $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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current connection timeout
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getConnectionTimeout(): int {
|
||||
return $this->connection_timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the connection timeout
|
||||
* @param int $connection_timeout
|
||||
*
|
||||
* @return Protocol
|
||||
*/
|
||||
public function setConnectionTimeout(int $connection_timeout): Protocol {
|
||||
$this->connection_timeout = $connection_timeout;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the UID key string
|
||||
* @param int|string $uid
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getUIDKey(int|string $uid): string {
|
||||
if ($uid == IMAP::ST_UID || $uid == IMAP::FT_UID) {
|
||||
return "UID";
|
||||
}
|
||||
if (strlen($uid) > 0 && !is_numeric($uid)) {
|
||||
return (string)$uid;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a UID / MSGN command
|
||||
* @param string $command
|
||||
* @param int|string $uid
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function buildUIDCommand(string $command, int|string $uid): string {
|
||||
return trim($this->getUIDKey($uid)." ".$command);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the uid cache of current active folder
|
||||
*
|
||||
* @param array|null $uids
|
||||
*/
|
||||
public function setUidCache(?array $uids) {
|
||||
if (is_null($uids)) {
|
||||
$this->uid_cache = [];
|
||||
return;
|
||||
}
|
||||
|
||||
$messageNumber = 1;
|
||||
|
||||
$uid_cache = [];
|
||||
foreach ($uids as $uid) {
|
||||
$uid_cache[$messageNumber++] = (int)$uid;
|
||||
}
|
||||
|
||||
$this->uid_cache = $uid_cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable the uid cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableUidCache(): void {
|
||||
$this->enable_uid_cache = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable the uid cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disableUidCache(): void {
|
||||
$this->enable_uid_cache = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the encryption method
|
||||
* @param string $encryption
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setEncryption(string $encryption): void {
|
||||
$this->encryption = $encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encryption method
|
||||
* @return string
|
||||
*/
|
||||
public function getEncryption(): string {
|
||||
return $this->encryption;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current session is connected
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function connected(): bool {
|
||||
return (bool)$this->stream;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves header/meta data from the resource stream
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function meta(): array {
|
||||
if (!$this->stream) {
|
||||
return [
|
||||
"crypto" => [
|
||||
"protocol" => "",
|
||||
"cipher_name" => "",
|
||||
"cipher_bits" => 0,
|
||||
"cipher_version" => "",
|
||||
],
|
||||
"timed_out" => true,
|
||||
"blocked" => true,
|
||||
"eof" => true,
|
||||
"stream_type" => "tcp_socket/unknown",
|
||||
"mode" => "c",
|
||||
"unread_bytes" => 0,
|
||||
"seekable" => false,
|
||||
];
|
||||
}
|
||||
return stream_get_meta_data($this->stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the resource stream
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function getStream(): mixed {
|
||||
return $this->stream;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,447 @@
|
|||
<?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\Exceptions\AuthFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
|
||||
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
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
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, ?int $port = null);
|
||||
|
||||
/**
|
||||
* Login to a new session.
|
||||
*
|
||||
* @param string $user username
|
||||
* @param string $password password
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws AuthFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
*/
|
||||
public function login(string $user, string $password): Response;
|
||||
|
||||
/**
|
||||
* Authenticate your current session.
|
||||
* @param string $user username
|
||||
* @param string $token access token
|
||||
*
|
||||
* @return Response
|
||||
* @throws AuthFailedException
|
||||
*/
|
||||
public function authenticate(string $user, string $token): Response;
|
||||
|
||||
/**
|
||||
* Logout of the current server session
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function logout(): Response;
|
||||
|
||||
/**
|
||||
* Check if the current session is connected
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function connected(): bool;
|
||||
|
||||
/**
|
||||
* Get an array of available capabilities
|
||||
*
|
||||
* @return Response containing a list of capabilities
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getCapabilities(): Response;
|
||||
|
||||
/**
|
||||
* Change the current folder
|
||||
* @param string $folder change to this folder
|
||||
*
|
||||
* @return Response see examineOrSelect()
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function selectFolder(string $folder = 'INBOX'): Response;
|
||||
|
||||
/**
|
||||
* Examine a given folder
|
||||
* @param string $folder
|
||||
*
|
||||
* @return Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function examineFolder(string $folder = 'INBOX'): Response;
|
||||
|
||||
/**
|
||||
* Fetch message headers
|
||||
* @param int|array $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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function content(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* Fetch message headers
|
||||
* @param int|array $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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function headers(int|array $uids, string $rfc = "RFC822", int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* Fetch message flags
|
||||
* @param int|array $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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function flags(int|array $uids, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* Fetch message sizes
|
||||
* @param int|array $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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function sizes(int|array $uids, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* Get uid for a given id
|
||||
* @param int|null $id message number
|
||||
*
|
||||
* @return Response containing a message number for given message or all messages as array
|
||||
* @throws MessageNotFoundException
|
||||
*/
|
||||
public function getUid(?int $id = null): Response;
|
||||
|
||||
/**
|
||||
* Get a message number for a uid
|
||||
* @param string $id uid
|
||||
*
|
||||
* @return Response containing the message number
|
||||
* @throws MessageNotFoundException
|
||||
*/
|
||||
public function getMessageNumber(string $id): Response;
|
||||
|
||||
/**
|
||||
* Get a list of available folders
|
||||
* @param string $reference mailbox reference for list
|
||||
* @param string $folder mailbox / folder name match with wildcards
|
||||
*
|
||||
* @return Response containing mailboxes that matched $folder as array(globalName => array('delim' => .., 'flags' => ..))
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function folders(string $reference = '', string $folder = '*'): Response;
|
||||
|
||||
/**
|
||||
* Set message flags
|
||||
* @param array|string $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 string|null $item command used to store a flag
|
||||
*
|
||||
* @return Response containing the new flags if $silent is false, else true or false depending on success
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function store(array|string $flags, int $from, ?int $to = null, ?string $mode = null, bool $silent = true, int|string $uid = IMAP::ST_UID, ?string $item = null): Response;
|
||||
|
||||
/**
|
||||
* 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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function appendMessage(string $folder, string $message, ?array $flags = null, ?string $date = null): Response;
|
||||
|
||||
/**
|
||||
* 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 Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function copyMessage(string $folder, $from, ?int $to = null, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* 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 Response Tokens if operation successful, false if an error occurred
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function copyManyMessages(array $messages, string $folder, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* 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 Response
|
||||
*/
|
||||
public function moveMessage(string $folder, $from, ?int $to = null, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* 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 Response Tokens if operation successful, false if an error occurred
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function moveManyMessages(array $messages, string $folder, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* Exchange identification information
|
||||
* Ref.: https://datatracker.ietf.org/doc/html/rfc2971
|
||||
*
|
||||
* @param null $ids
|
||||
* @return Response
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function ID($ids = null): Response;
|
||||
|
||||
/**
|
||||
* Create a new folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function createFolder(string $folder): Response;
|
||||
|
||||
/**
|
||||
* Rename an existing folder
|
||||
*
|
||||
* @param string $old old name
|
||||
* @param string $new new name
|
||||
* @return Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function renameFolder(string $old, string $new): Response;
|
||||
|
||||
/**
|
||||
* Delete a folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function deleteFolder(string $folder): Response;
|
||||
|
||||
/**
|
||||
* Subscribe to a folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function subscribeFolder(string $folder): Response;
|
||||
|
||||
/**
|
||||
* Unsubscribe from a folder
|
||||
*
|
||||
* @param string $folder folder name
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function unsubscribeFolder(string $folder): Response;
|
||||
|
||||
/**
|
||||
* Send idle command
|
||||
*
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function idle();
|
||||
|
||||
/**
|
||||
* Send done command
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function done();
|
||||
|
||||
/**
|
||||
* Apply session saved changes to the server
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function expunge(): Response;
|
||||
|
||||
/**
|
||||
* Retrieve the quota level settings, and usage statics per mailbox
|
||||
* @param $username
|
||||
*
|
||||
* @return Response
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function getQuota($username): Response;
|
||||
|
||||
/**
|
||||
* Retrieve the quota settings per user
|
||||
*
|
||||
* @param string $quota_root
|
||||
*
|
||||
* @return Response
|
||||
* @throws ConnectionFailedException
|
||||
*/
|
||||
public function getQuotaRoot(string $quota_root = 'INBOX'): Response;
|
||||
|
||||
/**
|
||||
* Send noop command
|
||||
*
|
||||
* @return Response
|
||||
*
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function noop(): Response;
|
||||
|
||||
/**
|
||||
* 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 Response containing the message ids
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function search(array $params, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* 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 Response
|
||||
* @throws RuntimeException
|
||||
* @throws MessageNotFoundException
|
||||
* @throws InvalidMessageDateException
|
||||
*/
|
||||
public function overview(string $sequence, int|string $uid = IMAP::ST_UID): Response;
|
||||
|
||||
/**
|
||||
* 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(?array $uids);
|
||||
}
|
||||
|
|
@ -0,0 +1,417 @@
|
|||
<?php
|
||||
/*
|
||||
* File: Response.php
|
||||
* Category: -
|
||||
* Author: M.Goldenbaum
|
||||
* Created: 30.12.22 19:46
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
|
||||
namespace Webklex\PHPIMAP\Connection\Protocols;
|
||||
|
||||
use Webklex\PHPIMAP\Exceptions\ResponseException;
|
||||
|
||||
/**
|
||||
* Class Response
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Connection\Protocols
|
||||
*/
|
||||
class Response {
|
||||
|
||||
/**
|
||||
* The commands used to fetch or manipulate data
|
||||
* @var array $command
|
||||
*/
|
||||
protected array $commands = [];
|
||||
|
||||
/**
|
||||
* The original response received
|
||||
* @var array $response
|
||||
*/
|
||||
protected array $response = [];
|
||||
|
||||
/**
|
||||
* Errors that have occurred while fetching or parsing the response
|
||||
* @var array $errors
|
||||
*/
|
||||
protected array $errors = [];
|
||||
|
||||
/**
|
||||
* Result to be returned
|
||||
* @var mixed|null $result
|
||||
*/
|
||||
protected mixed $result = null;
|
||||
|
||||
/**
|
||||
* Noun to identify the request / response
|
||||
* @var int $noun
|
||||
*/
|
||||
protected int $noun = 0;
|
||||
|
||||
/**
|
||||
* Other related responses
|
||||
* @var array $response_stack
|
||||
*/
|
||||
protected array $response_stack = [];
|
||||
|
||||
/**
|
||||
* Debug flag
|
||||
* @var bool $debug
|
||||
*/
|
||||
protected bool $debug = false;
|
||||
|
||||
/**
|
||||
* Can the response be empty?
|
||||
* @var bool $can_be_empty
|
||||
*/
|
||||
protected bool $can_be_empty = false;
|
||||
|
||||
/**
|
||||
* Create a new Response instance
|
||||
*/
|
||||
public function __construct(int $noun, bool $debug = false) {
|
||||
$this->debug = $debug;
|
||||
$this->noun = $noun > 0 ? $noun : (int)str_replace(".", "", (string)microtime(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* Make a new response instance
|
||||
* @param int $noun
|
||||
* @param array $commands
|
||||
* @param array $responses
|
||||
* @param bool $debug
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public static function make(int $noun, array $commands = [], array $responses = [], bool $debug = false): Response {
|
||||
return (new self($noun, $debug))->setCommands($commands)->setResponse($responses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new empty response
|
||||
* @param bool $debug
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public static function empty(bool $debug = false): Response {
|
||||
return (new self(0, $debug));
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack another response
|
||||
* @param Response $response
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function stack(Response $response): void {
|
||||
$this->response_stack[] = $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the associated response stack
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getStack(): array {
|
||||
return $this->response_stack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all assigned commands
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getCommands(): array {
|
||||
return $this->commands;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new command
|
||||
* @param string $command
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function addCommand(string $command): Response {
|
||||
$this->commands[] = $command;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and overwrite all commands
|
||||
* @param array $commands
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function setCommands(array $commands): Response {
|
||||
$this->commands = $commands;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all set errors
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getErrors(): array {
|
||||
$errors = $this->errors;
|
||||
foreach($this->getStack() as $response) {
|
||||
$errors = array_merge($errors, $response->getErrors());
|
||||
}
|
||||
return $errors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set and overwrite all existing errors
|
||||
* @param array $errors
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function setErrors(array $errors): Response {
|
||||
$this->errors = $errors;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
* @param string $error
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function addError(string $error): Response {
|
||||
$this->errors[] = $error;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
* @param array $response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function addResponse(mixed $response): Response {
|
||||
$this->response[] = $response;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the response
|
||||
* @param array $response
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function setResponse(array $response): Response {
|
||||
$this->response = $response;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the assigned response
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getResponse(): array {
|
||||
return $this->response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the result data
|
||||
* @param mixed $result
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function setResult(mixed $result): Response {
|
||||
$this->result = $result;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap a result bearing action
|
||||
* @param callable $callback
|
||||
*
|
||||
* @return Response
|
||||
*/
|
||||
public function wrap(callable $callback): Response {
|
||||
$this->result = call_user_func($callback, $this);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response data
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function data(): mixed {
|
||||
if ($this->result !== null) {
|
||||
return $this->result;
|
||||
}
|
||||
return $this->getResponse();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response data as array
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function array(): array {
|
||||
$data = $this->data();
|
||||
if(is_array($data)){
|
||||
return $data;
|
||||
}
|
||||
return [$data];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response data as string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function string(): string {
|
||||
$data = $this->data();
|
||||
if(is_array($data)){
|
||||
return implode(" ", $data);
|
||||
}
|
||||
return (string)$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response data as integer
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function integer(): int {
|
||||
$data = $this->data();
|
||||
if(is_array($data) && isset($data[0])){
|
||||
return (int)$data[0];
|
||||
}
|
||||
return (int)$data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the response data as boolean
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function boolean(): bool {
|
||||
return (bool)$this->data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and retrieve the response data
|
||||
*
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function validatedData(): mixed {
|
||||
return $this->validate()->data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the response date
|
||||
*
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function validate(): Response {
|
||||
if ($this->failed()) {
|
||||
throw ResponseException::make($this, $this->debug);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Response can be considered successful
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function successful(): bool {
|
||||
foreach(array_merge($this->getResponse(), $this->array()) as $data) {
|
||||
if (!$this->verify_data($data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foreach($this->getStack() as $response) {
|
||||
if (!$response->successful()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return ($this->boolean() || $this->canBeEmpty()) && !$this->getErrors();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the Response can be considered failed
|
||||
* @param mixed $data
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verify_data(mixed $data): bool {
|
||||
if (is_array($data)) {
|
||||
foreach ($data as $line) {
|
||||
if (is_array($line)) {
|
||||
if(!$this->verify_data($line)){
|
||||
return false;
|
||||
}
|
||||
}else{
|
||||
if (!$this->verify_line((string)$line)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
if (!$this->verify_line((string)$data)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a single line
|
||||
* @param string $line
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function verify_line(string $line): bool {
|
||||
return !str_starts_with($line, "TAG".$this->noun." BAD ") && !str_starts_with($line, "TAG".$this->noun." NO ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Response can be considered failed
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function failed(): bool {
|
||||
return !$this->successful();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Response noun
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function Noun(): int {
|
||||
return $this->noun;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Response to be allowed to be empty
|
||||
* @param bool $can_be_empty
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setCanBeEmpty(bool $can_be_empty): Response {
|
||||
$this->can_be_empty = $can_be_empty;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the Response can be empty
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function canBeEmpty(): bool {
|
||||
return $this->can_be_empty;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,591 @@
|
|||
<?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 array $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",
|
||||
"utf-7-imap" => "UTF7-IMAP",
|
||||
"utf7-imap" => "UTF7-IMAP",
|
||||
"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 exists. If it doesn't, return unchanged $encoding
|
||||
* @param string|null $encoding
|
||||
* @param string|null $fallback
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get(?string $encoding, string $fallback = null): string {
|
||||
if (isset(self::$aliases[strtolower($encoding ?? '')])) {
|
||||
return self::$aliases[strtolower($encoding ?? '')];
|
||||
}
|
||||
return $fallback ?: $encoding;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert the encoding of a string
|
||||
* @param $str
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function convert($str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed {
|
||||
$from = self::get($from, self::detectEncoding($str));
|
||||
$to = self::get($to, self::detectEncoding($str));
|
||||
|
||||
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') && !self::isUtf7($from) && !self::isUtf7($to)) {
|
||||
return iconv($from, $to, $str);
|
||||
}
|
||||
if (!$from) {
|
||||
return mb_convert_encoding($str, $to);
|
||||
}
|
||||
return mb_convert_encoding($str, $to, $from);
|
||||
} catch (\Exception $e) {
|
||||
if (str_contains($from, '-')) {
|
||||
$from = str_replace('-', '', $from);
|
||||
return self::convert($str, $from, $to);
|
||||
}
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to detect the encoding of a string
|
||||
* @param string $string
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function detectEncoding(string $string): string {
|
||||
$encoding = mb_detect_encoding($string, array_filter(self::getEncodings(), function($value){
|
||||
return !in_array($value, [
|
||||
'ISO-8859-6-I', 'ISO-8859-6-E', 'ISO-8859-8-I', 'ISO-8859-8-E',
|
||||
'ISO-8859-11', 'ISO-8859-13', 'ISO-8859-14', 'ISO-8859-15', 'ISO-8859-16', 'ISO-IR-111',"ISO-2022-CN",
|
||||
"windows-1250", "windows-1253", "windows-1255", "windows-1256", "windows-1257", "windows-1258",
|
||||
"IBM852", "IBM855", "IBM857", "IBM866", "IBM864", "IBM862", "KOI8-R", "KOI8-U",
|
||||
"TIS-620", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4",
|
||||
"VISCII", "T.61-8bit", "Big5-HKSCS", "windows-874", "macintosh", "ISO-8859-12", "ISO-8859-7",
|
||||
"IMAP-UTF-7"
|
||||
]);
|
||||
}), true);
|
||||
if ($encoding === false) {
|
||||
$encoding = 'UTF-8';
|
||||
}
|
||||
return $encoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available encodings
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function getEncodings(): array {
|
||||
$encodings = [];
|
||||
foreach (self::$aliases as $encoding) {
|
||||
if (!in_array($encoding, $encodings)) {
|
||||
$encodings[] = $encoding;
|
||||
}
|
||||
}
|
||||
return $encodings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the encoding is UTF-7 like
|
||||
* @param string $encoding
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function isUtf7(string $encoding): bool {
|
||||
return str_contains(str_replace("-", "", strtolower($encoding)), "utf7");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an encoding is supported
|
||||
* @param string $encoding
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public static function has(string $encoding): bool {
|
||||
return isset(self::$aliases[strtolower($encoding)]);
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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 $message;
|
||||
|
||||
/** @var string $flag */
|
||||
public string $flag;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* @var array $arguments
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $arguments) {
|
||||
$this->message = $arguments[0];
|
||||
$this->flag = $arguments[1];
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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 Folder $old_folder;
|
||||
|
||||
/** @var Folder $new_folder */
|
||||
public Folder $new_folder;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* @var Folder[] $folders
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $folders) {
|
||||
$this->old_folder = $folders[0];
|
||||
$this->new_folder = $folders[1];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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 $folder;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* @var Folder[] $folders
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $folders) {
|
||||
$this->folder = $folders[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
<?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 Message $old_message;
|
||||
|
||||
/** @var Message $new_message */
|
||||
public Message $new_message;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* @var Message[] $messages
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $messages) {
|
||||
$this->old_message = $messages[0];
|
||||
$this->new_message = $messages[1];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?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 $message;
|
||||
|
||||
/**
|
||||
* Create a new event instance.
|
||||
* @var Message[] $messages
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(array $messages) {
|
||||
$this->message = $messages[0];
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* File: ImapBadRequestException.php
|
||||
* Category: Exception
|
||||
* Author: S. Janaczek
|
||||
* Created: 08.09.22 08:39
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Exceptions;
|
||||
|
||||
use \Exception;
|
||||
|
||||
/**
|
||||
* Class GetMessagesFailedException
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Exceptions
|
||||
*/
|
||||
class ImapBadRequestException extends Exception {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* File: ImapServerErrorException.php
|
||||
* Category: Exception
|
||||
* Author: S. Janaczek
|
||||
* Created: 08.09.22 08:39
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Exceptions;
|
||||
|
||||
use \Exception;
|
||||
|
||||
/**
|
||||
* Class GetMessagesFailedException
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Exceptions
|
||||
*/
|
||||
class ImapServerErrorException extends Exception {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
/*
|
||||
* File: MessageSizeFetchingException.php
|
||||
* Category: Exception
|
||||
* Author: D. Malli
|
||||
* Created: 24.02.23 17:55
|
||||
* Updated: -
|
||||
*
|
||||
* Description: Exception thrown if fetching size for a message failed.
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Exceptions;
|
||||
|
||||
use \Exception;
|
||||
|
||||
/**
|
||||
* Class MessageSizeFetchingException
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Exceptions
|
||||
*/
|
||||
class MessageSizeFetchingException extends Exception {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
/*
|
||||
* File: ResponseException.php
|
||||
* Category: Exception
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 19.01.17 22:21
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Exceptions;
|
||||
|
||||
use \Exception;
|
||||
use Webklex\PHPIMAP\Connection\Protocols\Response;
|
||||
|
||||
/**
|
||||
* Class ResponseException
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Exceptions
|
||||
*/
|
||||
class ResponseException extends Exception {
|
||||
|
||||
/**
|
||||
* Make a new ResponseException instance
|
||||
* @param Response $response
|
||||
* @param false|boolean $debug
|
||||
* @param Exception|null $exception
|
||||
*
|
||||
* @return ResponseException
|
||||
*/
|
||||
public static function make(Response $response, bool $debug = false, ?Exception $exception = null): ResponseException {
|
||||
$message = "Command failed to process:\n";
|
||||
$message .= "Causes:\n";
|
||||
|
||||
foreach($response->getErrors() as $error) {
|
||||
$message .= "\t- $error\n";
|
||||
}
|
||||
|
||||
if(!$response->data()) {
|
||||
$message .= "\t- Empty response\n";
|
||||
}
|
||||
|
||||
if ($debug) {
|
||||
$message .= self::debug_message($response);
|
||||
}
|
||||
|
||||
foreach($response->getStack() as $_response) {
|
||||
$exception = self::make($_response, $debug, $exception);
|
||||
}
|
||||
|
||||
return new self($message."Error occurred", 0, $exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a debug message containing all commands send and responses received
|
||||
* @param Response $response
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected static function debug_message(Response $response): string {
|
||||
$commands = $response->getCommands();
|
||||
$message = "Commands send:\n";
|
||||
if ($commands) {
|
||||
foreach($commands as $command) {
|
||||
$message .= "\t".str_replace("\r\n", "\\r\\n", $command)."\n";
|
||||
}
|
||||
}else{
|
||||
$message .= "\tNo command send!\n";
|
||||
}
|
||||
|
||||
$responses = $response->getResponse();
|
||||
$message .= "Responses received:\n";
|
||||
if ($responses) {
|
||||
foreach($responses as $_response) {
|
||||
if (is_array($_response)) {
|
||||
foreach($_response as $value) {
|
||||
$message .= "\t".str_replace("\r\n", "\\r\\n", "$value")."\n";
|
||||
}
|
||||
}else{
|
||||
$message .= "\t".str_replace("\r\n", "\\r\\n", "$_response")."\n";
|
||||
}
|
||||
}
|
||||
}else{
|
||||
$message .= "\tNo responses received!\n";
|
||||
}
|
||||
|
||||
return $message;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,572 @@
|
|||
<?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\Connection\Protocols\Response;
|
||||
use Webklex\PHPIMAP\Exceptions\AuthFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\ConnectionFailedException;
|
||||
use Webklex\PHPIMAP\Exceptions\EventNotFoundException;
|
||||
use Webklex\PHPIMAP\Exceptions\FolderFetchingException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapBadRequestException;
|
||||
use Webklex\PHPIMAP\Exceptions\ImapServerErrorException;
|
||||
use Webklex\PHPIMAP\Exceptions\InvalidMessageDateException;
|
||||
use Webklex\PHPIMAP\Exceptions\MessageNotFoundException;
|
||||
use Webklex\PHPIMAP\Exceptions\NotSupportedCapabilityException;
|
||||
use Webklex\PHPIMAP\Exceptions\ResponseException;
|
||||
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 $client;
|
||||
|
||||
/**
|
||||
* Folder full path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $path;
|
||||
|
||||
/**
|
||||
* Folder name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $name;
|
||||
|
||||
/**
|
||||
* Folder full name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $full_name;
|
||||
|
||||
/**
|
||||
* Children folders
|
||||
*
|
||||
* @var FolderCollection
|
||||
*/
|
||||
public FolderCollection $children;
|
||||
|
||||
/**
|
||||
* Delimiter for folder
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $delimiter;
|
||||
|
||||
/**
|
||||
* Indicates if folder can't contain any "children".
|
||||
* CreateFolder won't work on this folder.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public bool $no_inferiors;
|
||||
|
||||
/**
|
||||
* Indicates if folder is only container, not a mailbox - you can't open it.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public bool $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 bool $marked;
|
||||
|
||||
/**
|
||||
* Indicates if folder contains any "children".
|
||||
* Not provided by all IMAP servers.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public bool $has_children;
|
||||
|
||||
/**
|
||||
* Indicates if folder refers to others.
|
||||
* Not provided by all IMAP servers.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public bool $referral;
|
||||
|
||||
/** @var array */
|
||||
public array $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->children = new FolderCollection();
|
||||
$this->has_children = false;
|
||||
|
||||
$this->parseAttributes($attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new search query instance
|
||||
* @param string[] $extensions
|
||||
*
|
||||
* @return WhereQuery
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
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 ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function search(array $extensions = []): WhereQuery {
|
||||
return $this->query($extensions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a new search query instance
|
||||
* @param string[] $extensions
|
||||
*
|
||||
* @return WhereQuery
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
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 $children
|
||||
*
|
||||
* @return Folder
|
||||
*/
|
||||
public function setChildren(FolderCollection $children): Folder {
|
||||
$this->children = $children;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get children.
|
||||
*
|
||||
* @return FolderCollection
|
||||
*/
|
||||
public function getChildren(): FolderCollection {
|
||||
return $this->children;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode name.
|
||||
* It converts UTF7-IMAP encoding to UTF-8.
|
||||
* @param $name
|
||||
*
|
||||
* @return string|array|bool|string[]|null
|
||||
*/
|
||||
protected function decodeName($name): string|array|bool|null {
|
||||
$parts = [];
|
||||
foreach (explode($this->delimiter, $name) as $item) {
|
||||
$parts[] = EncodingAliases::convert($item, "UTF7-IMAP", "UTF-8");
|
||||
}
|
||||
|
||||
return implode($this->delimiter, $parts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get simple name (without parent folders).
|
||||
* @param $delimiter
|
||||
* @param $full_name
|
||||
*
|
||||
* @return string|bool
|
||||
*/
|
||||
protected function getSimpleName($delimiter, $full_name): string|bool {
|
||||
$arr = explode($delimiter, $full_name);
|
||||
return end($arr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse attributes and set it to object properties.
|
||||
* @param $attributes
|
||||
*/
|
||||
protected function parseAttributes($attributes): void {
|
||||
$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 array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws EventNotFoundException
|
||||
* @throws FolderFetchingException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function move(string $new_name, bool $expunge = true): array {
|
||||
$this->client->checkConnection();
|
||||
$status = $this->client->getConnection()->renameFolder($this->full_name, $new_name)->validatedData();
|
||||
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 ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws InvalidMessageDateException
|
||||
* @throws MessageNotFoundException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
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);
|
||||
$response = $this->client->getConnection()->overview($sequence, $uid);
|
||||
return $response->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append a string message to the current mailbox
|
||||
* @param string $message
|
||||
* @param array|null $options
|
||||
* @param string|Carbon|null $internal_date
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function appendMessage(string $message, array $options = null, Carbon|string $internal_date = null): array {
|
||||
/**
|
||||
* 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)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Rename the current folder
|
||||
* @param string $new_name
|
||||
* @param boolean $expunge
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws EventNotFoundException
|
||||
* @throws FolderFetchingException
|
||||
* @throws RuntimeException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function rename(string $new_name, bool $expunge = true): array {
|
||||
return $this->move($new_name, $expunge);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the current folder
|
||||
* @param boolean $expunge
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws EventNotFoundException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function delete(bool $expunge = true): array {
|
||||
$status = $this->client->getConnection()->deleteFolder($this->path)->validatedData();
|
||||
if ($this->client->getActiveFolder() == $this->path){
|
||||
$this->client->setActiveFolder(null);
|
||||
}
|
||||
|
||||
if ($expunge) $this->client->expunge();
|
||||
|
||||
$event = $this->getEvent("folder", "deleted");
|
||||
$event::dispatch($this);
|
||||
|
||||
return $status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe the current folder
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function subscribe(): array {
|
||||
$this->client->openFolder($this->path);
|
||||
return $this->client->getConnection()->subscribeFolder($this->path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsubscribe the current folder
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function unsubscribe(): array {
|
||||
$this->client->openFolder($this->path);
|
||||
return $this->client->getConnection()->unsubscribeFolder($this->path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Idle the current connection
|
||||
* @param callable $callback function(Message $message) gets called if a new message is received
|
||||
* @param integer $timeout max 1740 seconds - recommended by rfc2177 §3. Should not be lower than the servers "* OK Still here" message interval
|
||||
*
|
||||
* @throws ConnectionFailedException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws NotSupportedCapabilityException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function idle(callable $callback, int $timeout = 300): void {
|
||||
$this->client->setTimeout($timeout);
|
||||
|
||||
if (!in_array("IDLE", $this->client->getConnection()->getCapabilities()->validatedData())) {
|
||||
throw new Exceptions\NotSupportedCapabilityException("IMAP server does not support IDLE");
|
||||
}
|
||||
|
||||
$idle_client = $this->client->clone();
|
||||
$idle_client->connect();
|
||||
$idle_client->openFolder($this->path, true);
|
||||
$idle_client->getConnection()->idle();
|
||||
|
||||
$last_action = Carbon::now()->addSeconds($timeout);
|
||||
|
||||
$sequence = ClientManager::get('options.sequence', IMAP::ST_MSGN);
|
||||
|
||||
while (true) {
|
||||
// This polymorphic call is fine - Protocol::idle() will throw an exception beforehand
|
||||
$line = $idle_client->getConnection()->nextLine(Response::empty());
|
||||
|
||||
if (($pos = strpos($line, "EXISTS")) !== false) {
|
||||
$msgn = (int)substr($line, 2, $pos - 2);
|
||||
|
||||
// Check if the stream is still alive or should be considered stale
|
||||
if (!$this->client->isConnected() || $last_action->isBefore(Carbon::now())) {
|
||||
// Reset the connection before interacting with it. Otherwise, the resource might be stale which
|
||||
// would result in a stuck interaction. If you know of a way of detecting a stale resource, please
|
||||
// feel free to improve this logic. I tried a lot but nothing seem to work reliably...
|
||||
// Things that didn't work:
|
||||
// - Closing the resource with fclose()
|
||||
// - Verifying the resource with stream_get_meta_data()
|
||||
// - Bool validating the resource stream (e.g.: (bool)$stream)
|
||||
// - Sending a NOOP command
|
||||
// - Sending a null package
|
||||
// - Reading a null package
|
||||
// - Catching the fs warning
|
||||
|
||||
// This polymorphic call is fine - Protocol::idle() will throw an exception beforehand
|
||||
$this->client->getConnection()->reset();
|
||||
// Establish a new connection
|
||||
$this->client->connect();
|
||||
}
|
||||
$last_action = Carbon::now()->addSeconds($timeout);
|
||||
|
||||
// Always reopen the folder - otherwise the new message number isn't known to the current remote session
|
||||
$this->client->openFolder($this->path, true);
|
||||
|
||||
$message = $this->query()->getMessageByMsgn($msgn);
|
||||
$message->setSequence($sequence);
|
||||
$callback($message);
|
||||
|
||||
$event = $this->getEvent("message", "new");
|
||||
$event::dispatch($message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get folder status information
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function getStatus(): array {
|
||||
return $this->examine();
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function loadStatus(): Folder {
|
||||
$this->status = $this->getStatus();
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Examine the current folder
|
||||
*
|
||||
* @return array
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws RuntimeException
|
||||
* @throws AuthFailedException
|
||||
* @throws ResponseException
|
||||
*/
|
||||
public function examine(): array {
|
||||
return $this->client->getConnection()->examineFolder($this->path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the current folder
|
||||
*
|
||||
* @return array
|
||||
* @throws AuthFailedException
|
||||
* @throws ConnectionFailedException
|
||||
* @throws ImapBadRequestException
|
||||
* @throws ImapServerErrorException
|
||||
* @throws ResponseException
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function select(): array {
|
||||
return $this->client->getConnection()->selectFolder($this->path)->validatedData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current Client instance
|
||||
*
|
||||
* @return Client
|
||||
*/
|
||||
public function getClient(): Client {
|
||||
return $this->client;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the delimiter
|
||||
* @param $delimiter
|
||||
*/
|
||||
public function setDelimiter($delimiter): void {
|
||||
if (in_array($delimiter, [null, '', ' ', false]) === true) {
|
||||
$delimiter = ClientManager::get('options.delimiter', '/');
|
||||
}
|
||||
|
||||
$this->delimiter = $delimiter;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,808 @@
|
|||
<?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 string $raw = "";
|
||||
|
||||
/**
|
||||
* Attribute holder
|
||||
*
|
||||
* @var Attribute[]|array $attributes
|
||||
*/
|
||||
protected array $attributes = [];
|
||||
|
||||
/**
|
||||
* Config holder
|
||||
*
|
||||
* @var array $config
|
||||
*/
|
||||
protected array $config = [];
|
||||
|
||||
/**
|
||||
* Fallback Encoding
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public string $fallback_encoding = 'UTF-8';
|
||||
|
||||
/**
|
||||
* Header constructor.
|
||||
* @param string $raw_header
|
||||
*
|
||||
* @throws InvalidMessageDateException
|
||||
*/
|
||||
public function __construct(string $raw_header) {
|
||||
$this->raw = $raw_header;
|
||||
$this->config = ClientManager::get('options');
|
||||
$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
|
||||
*/
|
||||
public function get($name): Attribute {
|
||||
$name = str_replace(["-", " "], "_", strtolower($name));
|
||||
if (isset($this->attributes[$name])) {
|
||||
return $this->attributes[$name];
|
||||
}
|
||||
|
||||
return new Attribute($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific attribute exists
|
||||
* @param string $name
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function has(string $name): bool {
|
||||
$name = str_replace(["-", " "], "_", strtolower($name));
|
||||
return isset($this->attributes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific attribute
|
||||
* @param string $name
|
||||
* @param array|mixed $value
|
||||
* @param boolean $strict
|
||||
*
|
||||
* @return Attribute|array
|
||||
*/
|
||||
public function set(string $name, mixed $value, bool $strict = false): Attribute|array {
|
||||
if (isset($this->attributes[$name]) && $strict === false) {
|
||||
$this->attributes[$name]->add($value, true);
|
||||
} 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): mixed {
|
||||
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(): ?string {
|
||||
$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(): void {
|
||||
$header = $this->rfc822_parse_headers($this->raw);
|
||||
|
||||
$this->extractAddresses($header);
|
||||
|
||||
if (property_exists($header, 'subject')) {
|
||||
$this->set("subject", $this->decode($header->subject));
|
||||
}
|
||||
if (property_exists($header, 'references')) {
|
||||
$this->set("references", array_map(function ($item) {
|
||||
return str_replace(['<', '>'], '', $item);
|
||||
}, explode(" ", $header->references)));
|
||||
}
|
||||
if (property_exists($header, 'message_id')) {
|
||||
$this->set("message_id", str_replace(['<', '>'], '', $header->message_id));
|
||||
}
|
||||
if (property_exists($header, 'in_reply_to')) {
|
||||
$this->set("in_reply_to", str_replace(['<', '>'], '', $header->in_reply_to));
|
||||
}
|
||||
|
||||
$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): object {
|
||||
$headers = [];
|
||||
$imap_headers = [];
|
||||
if (extension_loaded('imap') && $this->config["rfc822"]) {
|
||||
$raw_imap_headers = (array)\imap_rfc822_parse_headers($raw_headers);
|
||||
foreach ($raw_imap_headers as $key => $values) {
|
||||
$key = strtolower(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 (str_starts_with($line, "\n")) {
|
||||
$line = substr($line, 1);
|
||||
}
|
||||
|
||||
if (str_starts_with($line, "\t")) {
|
||||
$line = substr($line, 1);
|
||||
$line = trim(rtrim($line));
|
||||
if ($prev_header !== null) {
|
||||
$headers[$prev_header][] = $line;
|
||||
}
|
||||
} elseif (str_starts_with($line, " ")) {
|
||||
$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 = strtolower(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 ((string)$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 str_starts_with($decoded, '=?')
|
||||
&& strlen($decoded) - 2 === strpos($decoded, '?=')
|
||||
&& str_contains($encoded, $decoded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the encoding
|
||||
* @param $str
|
||||
* @param string $from
|
||||
* @param string $to
|
||||
*
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function convertEncoding($str, string $from = "ISO-8859-2", string $to = "UTF-8"): mixed {
|
||||
$from = EncodingAliases::get($from, $this->fallback_encoding);
|
||||
$to = EncodingAliases::get($to, $this->fallback_encoding);
|
||||
|
||||
if ($from === $to) {
|
||||
return $str;
|
||||
}
|
||||
|
||||
return EncodingAliases::convert($str, $from, $to);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the encoding of a given abject
|
||||
* @param object|string $structure
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getEncoding(object|string $structure): string {
|
||||
if (property_exists($structure, 'parameters')) {
|
||||
foreach ($structure->parameters as $parameter) {
|
||||
if (strtolower($parameter->attribute) == "charset") {
|
||||
return EncodingAliases::get($parameter->value, $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 str_starts_with(strtolower($value), '=?utf-8?');
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decode a specific header
|
||||
* @param mixed $value
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function decode(mixed $value): mixed {
|
||||
if (is_array($value)) {
|
||||
return $this->decodeArray($value);
|
||||
}
|
||||
$original_value = $value;
|
||||
$decoder = $this->config['decoder']['message'];
|
||||
|
||||
if ($value !== null) {
|
||||
if ($decoder === 'utf-8') {
|
||||
$decoded_values = $this->mime_header_decode($value);
|
||||
$tempValue = "";
|
||||
foreach ($decoded_values as $decoded_value) {
|
||||
$tempValue .= $this->convertEncoding($decoded_value->text, $decoded_value->charset);
|
||||
}
|
||||
if ($tempValue) {
|
||||
$value = $tempValue;
|
||||
} else if (extension_loaded('imap')) {
|
||||
$value = \imap_utf8($value);
|
||||
}else if (function_exists('iconv_mime_decode')){
|
||||
$value = iconv_mime_decode($value, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8");
|
||||
}else{
|
||||
$value = mb_decode_mimeheader($value);
|
||||
}
|
||||
}elseif ($decoder === 'iconv') {
|
||||
$value = iconv_mime_decode($value, ICONV_MIME_DECODE_CONTINUE_ON_ERROR, "UTF-8");
|
||||
}else if ($this->is_uft8($value)) {
|
||||
$value = mb_decode_mimeheader($value);
|
||||
}
|
||||
|
||||
if ($this->notDecoded($original_value, $value)) {
|
||||
$value = $this->convertEncoding($original_value, $this->getEncoding($original_value));
|
||||
}
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(): void {
|
||||
$priority = $this->get("x_priority");
|
||||
|
||||
$priority = match ((int)"$priority") {
|
||||
IMAP::MESSAGE_PRIORITY_HIGHEST => IMAP::MESSAGE_PRIORITY_HIGHEST,
|
||||
IMAP::MESSAGE_PRIORITY_HIGH => IMAP::MESSAGE_PRIORITY_HIGH,
|
||||
IMAP::MESSAGE_PRIORITY_NORMAL => IMAP::MESSAGE_PRIORITY_NORMAL,
|
||||
IMAP::MESSAGE_PRIORITY_LOW => IMAP::MESSAGE_PRIORITY_LOW,
|
||||
IMAP::MESSAGE_PRIORITY_LOWEST => IMAP::MESSAGE_PRIORITY_LOWEST,
|
||||
default => IMAP::MESSAGE_PRIORITY_UNKNOWN,
|
||||
};
|
||||
|
||||
$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(object $header): void {
|
||||
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);
|
||||
|
||||
$address->personal = '';
|
||||
foreach ($personalParts as $p) {
|
||||
$address->personal .= $this->convertEncoding($p->text, $this->getEncoding($p));
|
||||
}
|
||||
|
||||
if (str_starts_with($address->personal, "'")) {
|
||||
$address->personal = str_replace("'", "", $address->personal);
|
||||
}
|
||||
}
|
||||
|
||||
if ($address->host == ".SYNTAX-ERROR.") {
|
||||
$address->host = "";
|
||||
}
|
||||
if ($address->mailbox == "UNEXPECTED_DATA_AFTER_ADDRESS") {
|
||||
$address->mailbox = "";
|
||||
}
|
||||
|
||||
$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(): void {
|
||||
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 (!in_array($key, ["user-agent", "subject"])) {
|
||||
if (($pos = strpos($value, ";")) !== false) {
|
||||
$original = substr($value, 0, $pos);
|
||||
$this->set($key, trim(rtrim($original)));
|
||||
|
||||
// Get all potential extensions
|
||||
$extensions = explode(";", substr($value, $pos + 1));
|
||||
$previousKey = null;
|
||||
$previousValue = '';
|
||||
|
||||
foreach ($extensions as $extension) {
|
||||
if (($pos = strpos($extension, "=")) !== false) {
|
||||
$key = substr($extension, 0, $pos);
|
||||
$key = trim(rtrim(strtolower($key)));
|
||||
|
||||
$matches = [];
|
||||
|
||||
if (preg_match('/^(?P<key_name>\w+)\*/', $key, $matches) !== 0) {
|
||||
$key = $matches['key_name'];
|
||||
$previousKey = $key;
|
||||
|
||||
$value = substr($extension, $pos + 1);
|
||||
$value = str_replace('"', "", $value);
|
||||
$previousValue .= trim(rtrim($value));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
$previousKey !== null
|
||||
&& $previousKey !== $key
|
||||
&& isset($this->attributes[$previousKey]) === false
|
||||
) {
|
||||
$this->set($previousKey, $previousValue);
|
||||
|
||||
$previousValue = '';
|
||||
}
|
||||
|
||||
if (isset($this->attributes[$key]) === false) {
|
||||
$value = substr($extension, $pos + 1);
|
||||
$value = str_replace('"', "", $value);
|
||||
$value = trim(rtrim($value));
|
||||
|
||||
$this->set($key, $value);
|
||||
}
|
||||
|
||||
$previousKey = $key;
|
||||
}
|
||||
}
|
||||
if ($previousValue !== '') {
|
||||
$this->set($previousKey, $previousValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception handling for invalid dates
|
||||
*
|
||||
* Known bad and "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(object $header): void {
|
||||
|
||||
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 (str_contains($date, ' ')) {
|
||||
$date = str_replace(' ', ' ', $date);
|
||||
}
|
||||
if (str_contains($date, ' UT ')) {
|
||||
$date = str_replace(' UT ', ' UTC ', $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]{2} [A-Z]{3} [0-9]{4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2} [+-][0-9]{1,4} [0-9]{1,2}:[0-9]{1,2}:[0-9]{1,2} [+-][0-9]{1,4})+$/i', $date) > 0:
|
||||
$parts = explode(' ', $date);
|
||||
array_splice($parts, -2);
|
||||
$date = implode(' ', $parts);
|
||||
break;
|
||||
case preg_match('/([A-Z]{2,4}\,\ [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:
|
||||
$array = explode(',', $date);
|
||||
array_shift($array);
|
||||
$date = Carbon::createFromFormat("d M Y H:i:s O", trim(implode(',', $array)));
|
||||
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);
|
||||
break;
|
||||
// match case for: Di., 15 Feb. 2022 06:52:44 +0100 (MEZ)/Di., 15 Feb. 2022 06:52:44 +0100 (MEZ)
|
||||
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}\ \([A-Z]{3,4}\))\/([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}\ \([A-Z]{3,4}\))+$/i', $date) > 0:
|
||||
$dates = explode('/', $date);
|
||||
$date = array_shift($dates);
|
||||
$array = explode(',', $date);
|
||||
array_shift($array);
|
||||
$date = trim(implode(',', $array));
|
||||
$array = explode(' ', $date);
|
||||
array_pop($array);
|
||||
$date = trim(implode(' ', $array));
|
||||
$date = Carbon::createFromFormat("d M. Y H:i:s O", $date);
|
||||
break;
|
||||
// match case for: fr., 25 nov. 2022 06:27:14 +0100/fr., 25 nov. 2022 06:27:14 +0100
|
||||
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})\/([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:
|
||||
$dates = explode('/', $date);
|
||||
$date = array_shift($dates);
|
||||
$array = explode(',', $date);
|
||||
array_shift($array);
|
||||
$date = trim(implode(',', $array));
|
||||
$date = Carbon::createFromFormat("d M. Y H:i:s O", $date);
|
||||
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]{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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all header attributes
|
||||
* @param array $attributes
|
||||
*
|
||||
* @return Header
|
||||
*/
|
||||
public function setAttributes(array $attributes): Header {
|
||||
$this->attributes = $attributes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the configuration used for parsing a raw header
|
||||
* @param array $config
|
||||
*
|
||||
* @return Header
|
||||
*/
|
||||
public function setConfig(array $config): Header {
|
||||
$this->config = $config;
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,308 @@
|
|||
<?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 string $raw = "";
|
||||
|
||||
/**
|
||||
* Part type
|
||||
*
|
||||
* @var int $type
|
||||
*/
|
||||
public int $type = IMAP::MESSAGE_TYPE_TEXT;
|
||||
|
||||
/**
|
||||
* Part content
|
||||
*
|
||||
* @var string $content
|
||||
*/
|
||||
public string $content = "";
|
||||
|
||||
/**
|
||||
* Part subtype
|
||||
*
|
||||
* @var ?string $subtype
|
||||
*/
|
||||
public ?string $subtype = null;
|
||||
|
||||
/**
|
||||
* Part charset - if available
|
||||
*
|
||||
* @var string $charset
|
||||
*/
|
||||
public string $charset = "utf-8";
|
||||
|
||||
/**
|
||||
* Part encoding method
|
||||
*
|
||||
* @var int $encoding
|
||||
*/
|
||||
public int $encoding = IMAP::MESSAGE_ENC_OTHER;
|
||||
|
||||
/**
|
||||
* Alias to check if the part is an attachment
|
||||
*
|
||||
* @var boolean $ifdisposition
|
||||
*/
|
||||
public bool $ifdisposition = false;
|
||||
|
||||
/**
|
||||
* Indicates if the part is an attachment
|
||||
*
|
||||
* @var ?string $disposition
|
||||
*/
|
||||
public ?string $disposition = null;
|
||||
|
||||
/**
|
||||
* Alias to check if the part has a description
|
||||
*
|
||||
* @var boolean $ifdescription
|
||||
*/
|
||||
public bool $ifdescription = false;
|
||||
|
||||
/**
|
||||
* Part description if available
|
||||
*
|
||||
* @var ?string $description
|
||||
*/
|
||||
public ?string $description = null;
|
||||
|
||||
/**
|
||||
* Part filename if available
|
||||
*
|
||||
* @var ?string $filename
|
||||
*/
|
||||
public ?string $filename = null;
|
||||
|
||||
/**
|
||||
* Part name if available
|
||||
*
|
||||
* @var ?string $name
|
||||
*/
|
||||
public ?string $name = null;
|
||||
|
||||
/**
|
||||
* Part id if available
|
||||
*
|
||||
* @var ?string $id
|
||||
*/
|
||||
public ?string $id = null;
|
||||
|
||||
/**
|
||||
* The part number of the current part
|
||||
*
|
||||
* @var integer $part_number
|
||||
*/
|
||||
public int $part_number = 0;
|
||||
|
||||
/**
|
||||
* Part length in bytes
|
||||
*
|
||||
* @var integer $bytes
|
||||
*/
|
||||
public int $bytes;
|
||||
|
||||
/**
|
||||
* Part content type
|
||||
*
|
||||
* @var string|null $content_type
|
||||
*/
|
||||
public ?string $content_type = null;
|
||||
|
||||
/**
|
||||
* @var ?Header $header
|
||||
*/
|
||||
private ?Header $header;
|
||||
|
||||
/**
|
||||
* 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(): void {
|
||||
if ($this->header === null) {
|
||||
$body = $this->findHeaders();
|
||||
}else{
|
||||
$body = $this->raw;
|
||||
}
|
||||
|
||||
$this->parseDisposition();
|
||||
$this->parseDescription();
|
||||
$this->parseEncoding();
|
||||
|
||||
$this->charset = $this->header->get("charset")->first();
|
||||
$this->name = $this->header->get("name");
|
||||
$this->filename = $this->header->get("filename");
|
||||
|
||||
if($this->header->get("id")->exist()) {
|
||||
$this->id = $this->header->get("id");
|
||||
} else if($this->header->get("x_attachment_id")->exist()){
|
||||
$this->id = $this->header->get("x_attachment_id");
|
||||
} else if($this->header->get("content_id")->exist()){
|
||||
$this->id = strtr($this->header->get("content_id"), [
|
||||
'<' => '',
|
||||
'>' => ''
|
||||
]);
|
||||
}
|
||||
|
||||
$content_types = $this->header->get("content_type")->all();
|
||||
if(!empty($content_types)){
|
||||
$this->subtype = $this->parseSubtype($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): ?string {
|
||||
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(explode(";", $content_type)[0], $pos + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the disposition if any is present
|
||||
*/
|
||||
private function parseDisposition(): void {
|
||||
$content_disposition = $this->header->get("content_disposition")->first();
|
||||
if($content_disposition) {
|
||||
$this->ifdisposition = true;
|
||||
$this->disposition = (is_array($content_disposition)) ? implode(' ', $content_disposition) : explode(";", $content_disposition)[0];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the description if any is present
|
||||
*/
|
||||
private function parseDescription(): void {
|
||||
$content_description = $this->header->get("content_description")->first();
|
||||
if($content_description) {
|
||||
$this->ifdescription = true;
|
||||
$this->description = $content_description;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to parse the encoding if any is present
|
||||
*/
|
||||
private function parseEncoding(): void {
|
||||
$encoding = $this->header->get("content_transfer_encoding")->first();
|
||||
if($encoding) {
|
||||
$this->encoding = match (strtolower($encoding)) {
|
||||
"quoted-printable" => IMAP::MESSAGE_ENC_QUOTED_PRINTABLE,
|
||||
"base64" => IMAP::MESSAGE_ENC_BASE64,
|
||||
"7bit" => IMAP::MESSAGE_ENC_7BIT,
|
||||
"8bit" => IMAP::MESSAGE_ENC_8BIT,
|
||||
"binary" => IMAP::MESSAGE_ENC_BINARY,
|
||||
default => IMAP::MESSAGE_ENC_OTHER,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->disposition === "inline" && $this->filename == null && $this->name == null && !$this->header->has("content_id")) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the part header
|
||||
*
|
||||
* @return Header|null
|
||||
*/
|
||||
public function getHeader(): ?Header {
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,555 @@
|
|||
<?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 array $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, ?array $arguments) {
|
||||
$that = $this;
|
||||
|
||||
$name = Str::camel($name);
|
||||
|
||||
if (strtolower(substr($name, 0, 3)) === 'not') {
|
||||
$that = $that->whereNot();
|
||||
$name = substr($name, 3);
|
||||
}
|
||||
|
||||
if (!str_contains(strtolower($name), "where")) {
|
||||
$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 (str_starts_with($command, "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 mixed $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(mixed $criteria, mixed $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, mixed $value){
|
||||
$criteria = $this->validate_criteria($criteria);
|
||||
$value = $this->parse_value($value);
|
||||
|
||||
if ($value === '') {
|
||||
$this->query->push([$criteria]);
|
||||
} else {
|
||||
$this->query->push([$criteria, $value]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure|null $closure
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function orWhere(Closure $closure = null): WhereQuery {
|
||||
$this->query->push(['OR']);
|
||||
if ($closure !== null) $closure($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Closure|null $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(mixed $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(mixed $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(mixed $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(int|string $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|null
|
||||
*/
|
||||
public function when(mixed $value, callable $callback, ?callable $default = null): mixed {
|
||||
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(mixed $value, callable $callback, ?callable $default = null): mixed {
|
||||
if (!$value) {
|
||||
return $callback($this, $value) ?: $this;
|
||||
} elseif ($default) {
|
||||
return $default($this, $value) ?: $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available search criteria
|
||||
*
|
||||
* @return array|string[]
|
||||
*/
|
||||
public function getAvailableCriteria(): array {
|
||||
return $this->available_criteria;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,164 @@
|
|||
<?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 string $raw = "";
|
||||
|
||||
/**
|
||||
* @var Header $header
|
||||
*/
|
||||
private Header $header;
|
||||
|
||||
/**
|
||||
* Message type (if multipart or not)
|
||||
*
|
||||
* @var int $type
|
||||
*/
|
||||
public int $type = IMAP::MESSAGE_TYPE_TEXT;
|
||||
|
||||
/**
|
||||
* All available parts
|
||||
*
|
||||
* @var Part[] $parts
|
||||
*/
|
||||
public array $parts = [];
|
||||
|
||||
/**
|
||||
* Config holder
|
||||
*
|
||||
* @var array $config
|
||||
*/
|
||||
protected array $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(): void {
|
||||
$this->findContentType();
|
||||
$this->parts = $this->find_parts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the message content type
|
||||
*/
|
||||
public function findContentType(): void {
|
||||
$content_type = $this->header->get("content_type")->first();
|
||||
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 leftover 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 != "" && $ctx != "\r\n") {
|
||||
$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)];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/*
|
||||
* File: AttachmentCollection.php
|
||||
* Category: Collection
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 16.03.18 03:13
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Webklex\PHPIMAP\Attachment;
|
||||
|
||||
/**
|
||||
* Class AttachmentCollection
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Support
|
||||
* @implements Collection<int, Attachment>
|
||||
*/
|
||||
class AttachmentCollection extends PaginatedCollection {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
<?php
|
||||
/*
|
||||
* File: FlagCollection.php
|
||||
* Category: Collection
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 21.07.18 23:10
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
/**
|
||||
* Class FlagCollection
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Support
|
||||
* @implements Collection<string, string>
|
||||
*/
|
||||
class FlagCollection extends PaginatedCollection {
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/*
|
||||
* File: FolderCollection.php
|
||||
* Category: Collection
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 18.03.18 02:21
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Webklex\PHPIMAP\Folder;
|
||||
|
||||
/**
|
||||
* Class FolderCollection
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Support
|
||||
* @implements Collection<int, Folder>
|
||||
*/
|
||||
class FolderCollection extends PaginatedCollection {
|
||||
|
||||
}
|
||||
|
|
@ -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 mixed $parent;
|
||||
|
||||
/**
|
||||
* Get the attachment content base64 encoded
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getContentBase64Encoded(): ?string {
|
||||
return base64_encode($this->parent->content);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a base64 image src string
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getImageSrc(): ?string {
|
||||
return 'data:'.$this->parent->content_type.';base64,'.$this->getContentBase64Encoded();
|
||||
}
|
||||
}
|
||||
|
|
@ -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 array $attributes = [];
|
||||
|
||||
/**
|
||||
* Parent instance
|
||||
*
|
||||
* @var mixed $parent
|
||||
*/
|
||||
protected mixed $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(): void {}
|
||||
|
||||
/**
|
||||
* 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 mixed
|
||||
*/
|
||||
public function getParent(): mixed {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available attributes
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getAttributes(): array {
|
||||
return $this->attributes;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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 mixed $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|null $callback
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getCustomHTMLBody(?callable $callback = null): ?string {
|
||||
$body = $this->getHtmlBody();
|
||||
if($body === null) return null;
|
||||
|
||||
if ($callback !== null) {
|
||||
$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(): ?string {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
/*
|
||||
* File: MessageCollection.php
|
||||
* Category: Collection
|
||||
* Author: M. Goldenbaum
|
||||
* Created: 16.03.18 03:13
|
||||
* Updated: -
|
||||
*
|
||||
* Description:
|
||||
* -
|
||||
*/
|
||||
|
||||
namespace Webklex\PHPIMAP\Support;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Webklex\PHPIMAP\Message;
|
||||
|
||||
/**
|
||||
* Class MessageCollection
|
||||
*
|
||||
* @package Webklex\PHPIMAP\Support
|
||||
* @implements Collection<int, Message>
|
||||
*/
|
||||
class MessageCollection extends PaginatedCollection {
|
||||
|
||||
}
|
||||
|
|
@ -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 int $total = 0;
|
||||
|
||||
/**
|
||||
* 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, ?int $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, ?int $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): ?int {
|
||||
if($total === null) {
|
||||
return $this->total;
|
||||
}
|
||||
|
||||
return $this->total = $total;
|
||||
}
|
||||
}
|
||||
|
|
@ -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 array $events = [];
|
||||
|
||||
/**
|
||||
* Set a specific event
|
||||
* @param string $section
|
||||
* @param string $event
|
||||
* @param mixed $class
|
||||
*/
|
||||
public function setEvent(string $section, string $event, mixed $class): void {
|
||||
if (isset($this->events[$section])) {
|
||||
$this->events[$section][$event] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set all events
|
||||
* @param array $events
|
||||
*/
|
||||
public function setEvents(array $events): void {
|
||||
$this->events = $events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific event callback
|
||||
* @param string $section
|
||||
* @param string $event
|
||||
*
|
||||
* @return Event|string
|
||||
* @throws EventNotFoundException
|
||||
*/
|
||||
public function getEvent(string $section, string $event): Event|string {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
5.5.0
|
||||
|
|
@ -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
|
||||
]
|
||||
];
|
||||
Loading…
Reference in New Issue