From db2df47fb37a990a82e19310f085f1087301035b Mon Sep 17 00:00:00 2001 From: Torben Dannhauer Date: Wed, 8 Jul 2026 20:24:42 +0200 Subject: [PATCH] feat(core): reconnect IMAP for ActiveSync mail via mail API ActiveSync authenticates to Horde but, on hordeauth backends, the mail app may have no established IMAP login on stateless or long-running (PING) requests. mail->imapOb() then returns an empty client and the sync layer fails. On ActiveSync authentication, and lazily in the IMAP factory when the client is empty, trigger the mail app's IMAP login via the optional mail/ensureImapConnection API method (added in horde/imp #89). Guarded with Registry::hasMethod(), so Core stays agnostic of the concrete mail app and no-ops when the method is unavailable. The factory also throws a clear exception instead of returning an empty client. --- lib/Horde/Core/ActiveSync/Driver.php | 44 ++++++++++++++++++++++ lib/Horde/Core/ActiveSync/Imap/Factory.php | 14 +++++++ 2 files changed, 58 insertions(+) diff --git a/lib/Horde/Core/ActiveSync/Driver.php b/lib/Horde/Core/ActiveSync/Driver.php index 9654b694..9ff37180 100644 --- a/lib/Horde/Core/ActiveSync/Driver.php +++ b/lib/Horde/Core/ActiveSync/Driver.php @@ -357,6 +357,50 @@ public function authenticate($username, $password, $domain = null) } } + if (!$this->_ensureMailAuthenticated($username, $password)) { + return Horde_ActiveSync::AUTH_REASON_UNAVAILABLE; + } + + return true; + } + + /** + * Ensure the mail backend session exists when email sync is enabled. + * + * ActiveSync authenticates to Horde; mail operations need a separate + * login to the mail app (including hordeauth transparent login to the + * IMAP backend). Delegated to the registered 'mail' application via the + * optional 'mail/ensureImapConnection' API method, so Core stays + * agnostic of the concrete mail app. + * + * @param string $username The authenticated Horde username. + * @param string $password The ActiveSync password. + * + * @return boolean True if mail is not required or the connection is ready. + */ + protected function _ensureMailAuthenticated($username, $password) + { + global $registry; + + if (empty($this->_imap) + || !$registry->hasMethod('mail/ensureImapConnection')) { + return true; + } + + try { + $registry->mail->ensureImapConnection([ + 'userId' => $username, + 'password' => $password, + ]); + } catch (Horde_Exception $e) { + $this->_logger->warn(sprintf( + 'ActiveSync: mail server authentication failed for user %s: %s', + $username, + $e->getMessage() + )); + return false; + } + return true; } diff --git a/lib/Horde/Core/ActiveSync/Imap/Factory.php b/lib/Horde/Core/ActiveSync/Imap/Factory.php index bc42b82c..1556f297 100644 --- a/lib/Horde/Core/ActiveSync/Imap/Factory.php +++ b/lib/Horde/Core/ActiveSync/Imap/Factory.php @@ -39,6 +39,20 @@ public function getImapOb() } catch (Horde_Exception $e) { throw new Horde_ActiveSync_Exception($e); } + + if (empty($this->_adapter) + && $GLOBALS['registry']->hasMethod('mail/ensureImapConnection')) { + try { + $GLOBALS['registry']->mail->ensureImapConnection(); + $this->_adapter = $GLOBALS['registry']->mail->imapOb(); + } catch (Horde_Exception $e) { + throw new Horde_ActiveSync_Exception($e); + } + } + } + + if (empty($this->_adapter)) { + throw new Horde_ActiveSync_Exception('IMAP client is not available.'); } return $this->_adapter;