git subrepo commit (merge) mailcow/src/mailcow-dockerized
subrepo: subdir: "mailcow/src/mailcow-dockerized"
merged: "02ae5285"
upstream: origin: "https://github.com/mailcow/mailcow-dockerized.git"
branch: "master"
commit: "649a5c01"
git-subrepo: version: "0.4.3"
origin: "???"
commit: "???"
Change-Id: I870ad468fba026cc5abf3c5699ed1e12ff28b32b
diff --git a/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php
new file mode 100644
index 0000000..8ba0ef1
--- /dev/null
+++ b/mailcow/src/mailcow-dockerized/data/web/inc/lib/vendor/directorytree/ldaprecord/src/Connection.php
@@ -0,0 +1,511 @@
+<?php
+
+namespace LdapRecord;
+
+use Carbon\Carbon;
+use Closure;
+use LdapRecord\Auth\Guard;
+use LdapRecord\Configuration\DomainConfiguration;
+use LdapRecord\Events\DispatcherInterface;
+use LdapRecord\Query\Builder;
+use LdapRecord\Query\Cache;
+use Psr\SimpleCache\CacheInterface;
+
+class Connection
+{
+ use DetectsErrors;
+
+ /**
+ * The underlying LDAP connection.
+ *
+ * @var Ldap
+ */
+ protected $ldap;
+
+ /**
+ * The cache driver.
+ *
+ * @var Cache|null
+ */
+ protected $cache;
+
+ /**
+ * The domain configuration.
+ *
+ * @var DomainConfiguration
+ */
+ protected $configuration;
+
+ /**
+ * The event dispatcher;.
+ *
+ * @var DispatcherInterface|null
+ */
+ protected $dispatcher;
+
+ /**
+ * The current host connected to.
+ *
+ * @var string
+ */
+ protected $host;
+
+ /**
+ * The configured domain hosts.
+ *
+ * @var array
+ */
+ protected $hosts = [];
+
+ /**
+ * The attempted hosts that failed connecting to.
+ *
+ * @var array
+ */
+ protected $attempted = [];
+
+ /**
+ * The callback to execute upon total connection failure.
+ *
+ * @var Closure
+ */
+ protected $failed;
+
+ /**
+ * The authentication guard resolver.
+ *
+ * @var Closure
+ */
+ protected $authGuardResolver;
+
+ /**
+ * Whether the connection is retrying the initial connection attempt.
+ *
+ * @var bool
+ */
+ protected $retryingInitialConnection = false;
+
+ /**
+ * Constructor.
+ *
+ * @param array $config
+ * @param LdapInterface|null $ldap
+ */
+ public function __construct($config = [], LdapInterface $ldap = null)
+ {
+ $this->setConfiguration($config);
+
+ $this->setLdapConnection($ldap ?? new Ldap());
+
+ $this->failed = function () {
+ $this->dispatch(new Events\ConnectionFailed($this));
+ };
+
+ $this->authGuardResolver = function () {
+ return new Guard($this->ldap, $this->configuration);
+ };
+ }
+
+ /**
+ * Set the connection configuration.
+ *
+ * @param array $config
+ *
+ * @throws Configuration\ConfigurationException
+ *
+ * @return $this
+ */
+ public function setConfiguration($config = [])
+ {
+ $this->configuration = new DomainConfiguration($config);
+
+ $this->hosts = $this->configuration->get('hosts');
+
+ $this->host = reset($this->hosts);
+
+ return $this;
+ }
+
+ /**
+ * Set the LDAP connection.
+ *
+ * @param LdapInterface $ldap
+ *
+ * @return $this
+ */
+ public function setLdapConnection(LdapInterface $ldap)
+ {
+ $this->ldap = $ldap;
+
+ return $this;
+ }
+
+ /**
+ * Set the event dispatcher.
+ *
+ * @param DispatcherInterface $dispatcher
+ *
+ * @return $this
+ */
+ public function setDispatcher(DispatcherInterface $dispatcher)
+ {
+ $this->dispatcher = $dispatcher;
+
+ return $this;
+ }
+
+ /**
+ * Initializes the LDAP connection.
+ *
+ * @return void
+ */
+ public function initialize()
+ {
+ $this->configure();
+
+ $this->ldap->connect($this->host, $this->configuration->get('port'));
+ }
+
+ /**
+ * Configure the LDAP connection.
+ *
+ * @return void
+ */
+ protected function configure()
+ {
+ if ($this->configuration->get('use_ssl')) {
+ $this->ldap->ssl();
+ } elseif ($this->configuration->get('use_tls')) {
+ $this->ldap->tls();
+ }
+
+ $this->ldap->setOptions(array_replace(
+ $this->configuration->get('options'),
+ [
+ LDAP_OPT_PROTOCOL_VERSION => $this->configuration->get('version'),
+ LDAP_OPT_NETWORK_TIMEOUT => $this->configuration->get('timeout'),
+ LDAP_OPT_REFERRALS => $this->configuration->get('follow_referrals'),
+ ]
+ ));
+ }
+
+ /**
+ * Set the cache store.
+ *
+ * @param CacheInterface $store
+ *
+ * @return $this
+ */
+ public function setCache(CacheInterface $store)
+ {
+ $this->cache = new Cache($store);
+
+ return $this;
+ }
+
+ /**
+ * Get the cache store.
+ *
+ * @return Cache|null
+ */
+ public function getCache()
+ {
+ return $this->cache;
+ }
+
+ /**
+ * Get the LDAP configuration instance.
+ *
+ * @return DomainConfiguration
+ */
+ public function getConfiguration()
+ {
+ return $this->configuration;
+ }
+
+ /**
+ * Get the LDAP connection instance.
+ *
+ * @return Ldap
+ */
+ public function getLdapConnection()
+ {
+ return $this->ldap;
+ }
+
+ /**
+ * Bind to the LDAP server.
+ *
+ * If no username or password is specified, then the configured credentials are used.
+ *
+ * @param string|null $username
+ * @param string|null $password
+ *
+ * @throws Auth\BindException
+ * @throws LdapRecordException
+ *
+ * @return Connection
+ */
+ public function connect($username = null, $password = null)
+ {
+ $attempt = function () use ($username, $password) {
+ $this->dispatch(new Events\Connecting($this));
+
+ is_null($username) && is_null($password)
+ ? $this->auth()->bindAsConfiguredUser()
+ : $this->auth()->bind($username, $password);
+
+ $this->dispatch(new Events\Connected($this));
+
+ $this->retryingInitialConnection = false;
+ };
+
+ try {
+ $this->runOperationCallback($attempt);
+ } catch (LdapRecordException $e) {
+ $this->retryingInitialConnection = true;
+
+ $this->retryOnNextHost($e, $attempt);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Reconnect to the LDAP server.
+ *
+ * @throws Auth\BindException
+ * @throws ConnectionException
+ *
+ * @return void
+ */
+ public function reconnect()
+ {
+ $this->reinitialize();
+
+ $this->connect();
+ }
+
+ /**
+ * Reinitialize the connection.
+ *
+ * @return void
+ */
+ protected function reinitialize()
+ {
+ $this->disconnect();
+
+ $this->initialize();
+ }
+
+ /**
+ * Disconnect from the LDAP server.
+ *
+ * @return void
+ */
+ public function disconnect()
+ {
+ $this->ldap->close();
+ }
+
+ /**
+ * Dispatch an event.
+ *
+ * @param object $event
+ *
+ * @return void
+ */
+ public function dispatch($event)
+ {
+ if (isset($this->dispatcher)) {
+ $this->dispatcher->dispatch($event);
+ }
+ }
+
+ /**
+ * Get the attempted hosts that failed connecting to.
+ *
+ * @return array
+ */
+ public function attempted()
+ {
+ return $this->attempted;
+ }
+
+ /**
+ * Perform the operation on the LDAP connection.
+ *
+ * @param Closure $operation
+ *
+ * @return mixed
+ */
+ public function run(Closure $operation)
+ {
+ try {
+ // Before running the operation, we will check if the current
+ // connection is bound and connect if necessary. Otherwise
+ // some LDAP operations will not be executed properly.
+ if (! $this->isConnected()) {
+ $this->connect();
+ }
+
+ return $this->runOperationCallback($operation);
+ } catch (LdapRecordException $e) {
+ if ($exception = $this->getExceptionForCauseOfFailure($e)) {
+ throw $exception;
+ }
+
+ return $this->tryAgainIfCausedByLostConnection($e, $operation);
+ }
+ }
+
+ /**
+ * Attempt to get an exception for the cause of failure.
+ *
+ * @param LdapRecordException $e
+ *
+ * @return mixed
+ */
+ protected function getExceptionForCauseOfFailure(LdapRecordException $e)
+ {
+ switch (true) {
+ case $this->errorContainsMessage($e->getMessage(), 'Already exists'):
+ return Exceptions\AlreadyExistsException::withDetailedError($e, $e->getDetailedError());
+ case $this->errorContainsMessage($e->getMessage(), 'Insufficient access'):
+ return Exceptions\InsufficientAccessException::withDetailedError($e, $e->getDetailedError());
+ case $this->errorContainsMessage($e->getMessage(), 'Constraint violation'):
+ return Exceptions\ConstraintViolationException::withDetailedError($e, $e->getDetailedError());
+ default:
+ return;
+ }
+ }
+
+ /**
+ * Run the operation callback on the current LDAP connection.
+ *
+ * @param Closure $operation
+ *
+ * @throws LdapRecordException
+ *
+ * @return mixed
+ */
+ protected function runOperationCallback(Closure $operation)
+ {
+ return $operation($this->ldap);
+ }
+
+ /**
+ * Get a new auth guard instance.
+ *
+ * @return Auth\Guard
+ */
+ public function auth()
+ {
+ if (! $this->ldap->isConnected()) {
+ $this->initialize();
+ }
+
+ $guard = call_user_func($this->authGuardResolver);
+
+ $guard->setDispatcher(
+ Container::getInstance()->getEventDispatcher()
+ );
+
+ return $guard;
+ }
+
+ /**
+ * Get a new query builder for the connection.
+ *
+ * @return Query\Builder
+ */
+ public function query()
+ {
+ return (new Builder($this))
+ ->setCache($this->cache)
+ ->setBaseDn($this->configuration->get('base_dn'));
+ }
+
+ /**
+ * Determine if the LDAP connection is bound.
+ *
+ * @return bool
+ */
+ public function isConnected()
+ {
+ return $this->ldap->isBound();
+ }
+
+ /**
+ * Attempt to retry an LDAP operation if due to a lost connection.
+ *
+ * @param LdapRecordException $e
+ * @param Closure $operation
+ *
+ * @throws LdapRecordException
+ *
+ * @return mixed
+ */
+ protected function tryAgainIfCausedByLostConnection(LdapRecordException $e, Closure $operation)
+ {
+ // If the operation failed due to a lost or failed connection,
+ // we'll attempt reconnecting and running the operation again
+ // underneath the same host, and then move onto the next.
+ if ($this->causedByLostConnection($e->getMessage())) {
+ return $this->retry($operation);
+ }
+
+ throw $e;
+ }
+
+ /**
+ * Retry the operation on the current host.
+ *
+ * @param Closure $operation
+ *
+ * @throws LdapRecordException
+ *
+ * @return mixed
+ */
+ protected function retry(Closure $operation)
+ {
+ try {
+ $this->retryingInitialConnection
+ ? $this->reinitialize()
+ : $this->reconnect();
+
+ return $this->runOperationCallback($operation);
+ } catch (LdapRecordException $e) {
+ return $this->retryOnNextHost($e, $operation);
+ }
+ }
+
+ /**
+ * Attempt the operation again on the next host.
+ *
+ * @param LdapRecordException $e
+ * @param Closure $operation
+ *
+ * @throws LdapRecordException
+ *
+ * @return mixed
+ */
+ protected function retryOnNextHost(LdapRecordException $e, Closure $operation)
+ {
+ $this->attempted[$this->host] = Carbon::now();
+
+ if (($key = array_search($this->host, $this->hosts)) !== false) {
+ unset($this->hosts[$key]);
+ }
+
+ if ($next = reset($this->hosts)) {
+ $this->host = $next;
+
+ return $this->tryAgainIfCausedByLostConnection($e, $operation);
+ }
+
+ call_user_func($this->failed, $this->ldap);
+
+ throw $e;
+ }
+}