protected function _login()
{
$secure = $this->getParam('secure');
if (!empty($this->_temp['preauth'])) {
unset($this->_temp['preauth']);
/* Don't allow PREAUTH if we are requring secure access, since
* PREAUTH cannot provide secure access. */
if (!$this->isSecureConnection() && $secure !== false) {
$this->logout();
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Could not open secure TLS connection to the IMAP server."), Horde_Imap_Client_Exception::LOGIN_TLSFAILURE);
}
return $this->_loginTasks();
}
/* Blank passwords are not allowed, so no need to even try
* authentication to determine this. */
if (!strlen($this->getParam('password'))) {
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("No password provided."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED);
}
$this->_connect();
$first_login = empty($this->_init['authmethod']);
// Switch to secure channel if using TLS.
if (!$this->isSecureConnection() && ($secure === 'tls' || $secure === true && $this->_capability('LOGINDISABLED'))) {
if ($first_login && !$this->_capability('STARTTLS')) {
/* We should never hit this - STARTTLS is required pursuant to
* RFC 3501 [6.2.1]. */
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Server does not support TLS connections."), Horde_Imap_Client_Exception::LOGIN_TLSFAILURE);
}
// Switch over to a TLS connection.
// STARTTLS returns no untagged response.
$this->_sendCmd($this->_command('STARTTLS'));
if (!$this->_connection->startTls()) {
$this->logout();
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Could not open secure TLS connection to the IMAP server."), Horde_Imap_Client_Exception::LOGIN_TLSFAILURE);
}
$this->_debug->info('Successfully completed TLS negotiation.');
$this->setParam('secure', 'tls');
$secure = 'tls';
if ($first_login) {
// Expire cached CAPABILITY information (RFC 3501 [6.2.1])
$this->_setInit('capability');
// Reset language (RFC 5255 [3.1])
$this->_setInit('lang');
}
// Set language if using imapproxy
if (!empty($this->_init['imapproxy'])) {
$this->setLanguage();
}
}
/* If we reached this point and don't have a secure connection, then
* a secure connections is not available. */
if ($secure === true && !$this->isSecureConnection()) {
$this->setParam('secure', false);
$secure = false;
}
if ($first_login) {
// Add authentication methods.
$auth_mech = array();
$auth = array_flip($this->_capability()->getParams('AUTH'));
// XOAUTH2
if (isset($auth['XOAUTH2']) && $this->getParam('xoauth2_token')) {
$auth_mech[] = 'XOAUTH2';
}
unset($auth['XOAUTH2']);
/* 'AUTH=PLAIN' authentication always exists if under TLS (RFC 3501
* [7.2.1]; RFC 2595), even though we might get here with a
* non-TLS secure connection too. Use it over all other
* authentication methods, although we need to do sanity checking
* since broken IMAP servers may not support as required -
* fallback to LOGIN instead, if not explicitly disabled. */
if ($secure) {
if (isset($auth['PLAIN'])) {
$auth_mech[] = 'PLAIN';
unset($auth['PLAIN']);
} elseif (!$this->_capability('LOGINDISABLED')) {
$auth_mech[] = 'LOGIN';
}
}
// Check for supported SCRAM AUTH mechanisms. Preferred because it
// provides verificaion of server authenticity.
foreach (array_keys($auth) as $key) {
switch ($key) {
case 'SCRAM-SHA-1':
$auth_mech[] = $key;
unset($auth[$key]);
break;
}
}
// Check for supported CRAM AUTH mechanisms.
foreach (array_keys($auth) as $key) {
switch ($key) {
case 'CRAM-SHA1':
case 'CRAM-SHA256':
$auth_mech[] = $key;
unset($auth[$key]);
break;
}
}
// Prefer CRAM-MD5 over DIGEST-MD5, as the latter has been
// obsoleted (RFC 6331).
if (isset($auth['CRAM-MD5'])) {
$auth_mech[] = 'CRAM-MD5';
} elseif (isset($auth['DIGEST-MD5'])) {
$auth_mech[] = 'DIGEST-MD5';
}
unset($auth['CRAM-MD5'], $auth['DIGEST-MD5']);
// Add other auth mechanisms.
$auth_mech = array_merge($auth_mech, array_keys($auth));
// Fall back to 'LOGIN' if available.
if (!$secure && !$this->_capability('LOGINDISABLED')) {
$auth_mech[] = 'LOGIN';
}
if (empty($auth_mech)) {
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("No supported IMAP authentication method could be found."), Horde_Imap_Client_Exception::LOGIN_NOAUTHMETHOD);
}
$auth_mech = array_unique($auth_mech);
} else {
$auth_mech = array($this->_init['authmethod']);
}
$login_err = null;
foreach ($auth_mech as $method) {
try {
$resp = $this->_tryLogin($method);
$data = $resp->data;
$this->_setInit('authmethod', $method);
unset($this->_temp['referralcount']);
} catch (Horde_Imap_Client_Exception_ServerResponse $e) {
$data = $e->resp_data;
if (isset($data['loginerr'])) {
$login_err = $data['loginerr'];
}
$resp = false;
} catch (Horde_Imap_Client_Exception $e) {
$resp = false;
}
// Check for login referral (RFC 2221) response - can happen for
// an OK, NO, or BYE response.
if (isset($data['referral'])) {
foreach (array('host', 'port', 'username') as $val) {
if (!is_null($data['referral']->{$val})) {
$this->setParam($val, $data['referral']->{$val});
}
}
if (!is_null($data['referral']->auth)) {
$this->_setInit('authmethod', $data['referral']->auth);
}
if (!isset($this->_temp['referralcount'])) {
$this->_temp['referralcount'] = 0;
}
// RFC 2221 [3] - Don't follow more than 10 levels of referral
// without consulting the user.
if (++$this->_temp['referralcount'] < 10) {
$this->logout();
$this->_setInit('capability');
$this->_setInit('namespace');
return $this->login();
}
unset($this->_temp['referralcount']);
}
if ($resp) {
return $this->_loginTasks($first_login, $resp->data);
}
}
/* Try again from scratch if authentication failed in an established,
* previously-authenticated object. */
if (!empty($this->_init['authmethod'])) {
$this->_setInit();
unset($this->_temp['no_cap']);
try {
return $this->_login();
} catch (Horde_Imap_Client_Exception $e) {
}
}
/* Default to AUTHENTICATIONFAILED error (see RFC 5530[3]). */
if (is_null($login_err)) {
throw new Horde_Imap_Client_Exception(Horde_Imap_Client_Translation::r("Mail server denied authentication."), Horde_Imap_Client_Exception::LOGIN_AUTHENTICATIONFAILED);
}
throw $login_err;
}