mirror of
https://github.com/itflow-org/itflow
synced 2026-02-28 02:44:53 +00:00
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:
1300
plugins/php-imap/Connection/Protocols/ImapProtocol.php
Normal file
1300
plugins/php-imap/Connection/Protocols/ImapProtocol.php
Normal file
File diff suppressed because it is too large
Load Diff
814
plugins/php-imap/Connection/Protocols/LegacyProtocol.php
Normal file
814
plugins/php-imap/Connection/Protocols/LegacyProtocol.php
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
366
plugins/php-imap/Connection/Protocols/Protocol.php
Normal file
366
plugins/php-imap/Connection/Protocols/Protocol.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
447
plugins/php-imap/Connection/Protocols/ProtocolInterface.php
Normal file
447
plugins/php-imap/Connection/Protocols/ProtocolInterface.php
Normal file
@@ -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);
|
||||
}
|
||||
417
plugins/php-imap/Connection/Protocols/Response.php
Normal file
417
plugins/php-imap/Connection/Protocols/Response.php
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user