Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions lib/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,27 @@ public function imapOb()
return $GLOBALS['injector']->getInstance('IMP_Factory_Imap')->create()->client_ob;
}

/**
* Ensure a live IMAP connection exists for the current user.
*
* Stateless entry points (ActiveSync, RPC) authenticate to Horde but
* still require an explicit IMAP login for hordeauth backends. Exposed
* as a 'mail' API method so Horde_Core can trigger the login without
* depending on IMP directly.
*
* @param array $credentials Optional fallback credentials (userId,
* password, server). See
* IMP_Auth::ensureImapConnection().
*
* @throws Horde_Auth_Exception
*
* @author Torben Dannhauer <torben@dannhauer.de>
*/
public function ensureImapConnection(array $credentials = [])
{
IMP_Auth::ensureImapConnection($credentials);
}

/**
* Return the list of user-settable IMAP flags.
*
Expand Down
92 changes: 92 additions & 0 deletions lib/Auth.php
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,98 @@ public static function authenticate($credentials = [])
}
}

/**
* Ensure a live IMAP connection exists.
*
* Unlike authenticate(), this always opens the mail server connection when
* needed. Stateless entry points (ActiveSync, RPC) authenticate to Horde
* first but still require an explicit IMAP login for hordeauth backends.
*
* @param array $credentials Optional fallback credentials:
* - userId: (string) Username.
* - password: (string) Password.
* - server: (string) Server key from backends.php.
*
* @throws Horde_Auth_Exception
*
* @author Torben Dannhauer <torben@dannhauer.de>
*/
public static function ensureImapConnection(array $credentials = [])
{
global $injector, $registry;

$imp_imap = $injector->getInstance('IMP_Factory_Imap')->create();
if ($imp_imap->init) {
try {
$imp_imap->login();
} catch (IMP_Imap_Exception $e) {
self::_log(false, $imp_imap);
throw $e->authException();
}
return;
}
Comment thread
Copilot marked this conversation as resolved.

if (!isset($credentials['server'])) {
$credentials['server'] = self::getAutoLoginServer();
}
if (empty($credentials['server'])) {
throw new Horde_Auth_Exception(
_('No IMAP backend is available for auto-login.'),
Horde_Auth::REASON_MESSAGE
);
}

$servers = IMP_Imap::loadServerConfig();
if ($servers === false) {
throw new Horde_Auth_Exception(
_('Could not load IMAP backend configuration.'),
Horde_Auth::REASON_MESSAGE
);
}
$serverConfig = $servers[$credentials['server']] ?? null;
if ((empty($credentials['userId']) || !isset($credentials['password']))
&& $registry->getAuth()
&& $serverConfig
&& !empty($serverConfig->hordeauth)) {
$hordeauth = $serverConfig->hordeauth;
$credentials['userId'] = $registry->getAuth(
strcasecmp($hordeauth, 'full') === 0 ? null : 'bare'
);
$stored = $registry->getAuthCredential('password');
if ($stored !== false) {
$credentials['password'] = $stored;
}
}

if (empty($credentials['userId'])
|| !isset($credentials['password'])
|| (!is_string($credentials['password']) && !($credentials['password'] instanceof Horde_Imap_Client_Password_Xoauth2))
|| (is_string($credentials['password']) && $credentials['password'] === '')) {
throw new Horde_Auth_Exception('', Horde_Auth::REASON_BADLOGIN);
}
Comment thread
Copilot marked this conversation as resolved.

try {
$credentials = $injector->getInstance('Horde_Core_Hooks')->callHook(
'imap_preauthenticate',
'imp',
[$credentials]
);
} catch (Horde_Exception_HookNotSet $e) {
}

try {
$imp_imap->createBaseImapObject(
$credentials['userId'],
$credentials['password'],
$credentials['server']
);
$imp_imap->login();
} catch (IMP_Imap_Exception $e) {
self::_log(false, $imp_imap);
throw $e->authException();
}
}

/**
* Perform transparent authentication.
*
Expand Down
Loading