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