/** * Handles connecting to the server and checks the response validity. * * Defaults from the constructor are used for missing parameters. * * @param string $host Hostname of server. * @param string $port Port of server. * @param array $context List of options to pass to * stream_context_create(). * @param boolean $secure Security layer requested. @see __construct(). * * @throws \Horde\ManageSieve\Exception */ public function connect($host = null, $port = null, $context = null, $secure = null) { if (isset($host)) { $this->_params['host'] = $host; } if (isset($port)) { $this->_params['port'] = $port; } if (isset($context)) { $this->_params['context'] = array_merge_recursive($this->_params['context'], $context); } if (isset($secure)) { $this->_params['secure'] = $secure; } if (self::STATE_DISCONNECTED != $this->_state) { throw new Exception\NotDisconnected(); } try { $this->_sock = new Client($this->_params['host'], $this->_params['port'], $this->_params['timeout'], $this->_params['secure'], $this->_params['context']); } catch (Client\Exception $e) { throw new Exception\ConnectionFailed($e); } if ($this->_params['bypassauth']) { $this->_state = self::STATE_AUTHENTICATED; } else { $this->_state = self::STATE_NON_AUTHENTICATED; $this->_doCmd(); } // Explicitly ask for the capabilities in case the connection is // picked up from an existing connection. try { $this->_cmdCapability(); } catch (Exception $e) { throw new Exception\ConnectionFailed($e); } // Check if we can enable TLS via STARTTLS. if ($this->_params['secure'] === 'tls' || $this->_params['secure'] === true && !empty($this->_capability['starttls'])) { $this->_doCmd('STARTTLS'); if (!$this->_sock->startTls()) { throw new Exception('Failed to establish TLS connection'); } // The server should be sending a CAPABILITY response after // negotiating TLS. Read it, and ignore if it doesn't. // Unfortunately old Cyrus versions are broken and don't send a // CAPABILITY response, thus we would wait here forever. Parse the // Cyrus version and work around this broken behavior. if (!preg_match('/^CYRUS TIMSIEVED V([0-9.]+)/', $this->_capability['implementation'], $matches) || version_compare($matches[1], '2.3.10', '>=')) { $this->_doCmd(); } // Query the server capabilities again now that we are under // encryption. try { $this->_cmdCapability(); } catch (Exception $e) { throw new Exception\ConnectionFailed($e); } } }