6.0.0-beta13
4/20/26
  • Dynamically selecting an IMAP server for authentication

++ Introduction

During a migration from one IMAP server to another, the need arose to run both the old and the new IMAP servers in paralell. By default, Horde only allows a single primary server to be enabled in servers.php. Of course, we could have allowed our users to simply select their server by enabling IMAP server selection, but there is a better way.

The first example shows a way using a !MySQL backend to select an IMAP server for authentication, given a username.

The second example instead uses the realmed username to select the IMAP server \

(i.e. the username used by Horde internally to prevent username clashes).


++ The SQL table

There are many ways to do this of course. In this example, we'll just be using a table with two rows:


| username | cyrus |


Your table can be constructed however you like. It can even be a part of your existing Horde DB. The important point is that you need the table to be constructed in such a way as to be able to query a username and have the lookup return a server name from {{imp/config/servers.php}}.

++ Writing a hook

Here's some sample code for a hook placed inside horde/config/hooks.php:

if (!function_exists('_horde_hook_preauthenticate')) {

function _horde_hook_preauthenticate($userID, $credential, $realm)

{

    require dirname(__FILE__) . '/../imp/config/servers.php';



    // Strip domain part from user.

    $userID = substr($userID, 0, strpos($userID, '@'));

    // Connect to database server.

    $db = mysql_connect('hostname', 'dbuser', 'dbpasswd') or die('Can not connect to database.');

    // Select database.

    mysql_select_db('dbname') or die('Can not select database.');

    // Execute the query

    $sth = mysql_query('SELECT server_name FROM users WHERE user=\'' . mysql_real_escape_string($userID) . '\'') or die ('Can not query database.');

    // Fetch the server name.

    $server = mysql_fetch_row($sth);

    if ($server === false) {

        die('Can not read user from database.');

    }



    // Set IMAP server values.

    foreach (array('server', 'folders', 'namespace') as $key) {

        $_SESSION['imp'][$key] = $servers[$server[0]][$key];

    }



    return true;

}

}

All that's left to do, is to activate the preauthenticate hook in Horde's configuration.


++ IMAP server selection by realmed username

+++ Requirements

  • IMP is used to do the authentication

  • The usernames to authenticate against the mailserver may not have a domain part (separated with '@' from the username).

  • There is more than one active server entry in {{imp/config/servers.php}}. _

The realm values are different and the default server has an empty string as realm. _

//Note:// The default server is either the first server entry in the list or the one with the preferred value _

set.

  • Realm values are only lowercase strings (e.g. 'server1.example.com').

++++ Examplary snippet of {{imp/config/servers.php}}

/* Example configuration: */

$servers['imap'] = array(

'name' => 'IMAP Server',

'server' => 'imap.example.com',

'hordeauth' => true,

'protocol' => 'imap/notls',

'port' => 143,

'maildomain' => 'example.com',

'smtphost' => 'smtp.example.com',

'smtpport' => 25,

'realm' => '',

'preferred' => '',

);

$servers['imap1'] = array(

'name' => 'IMAP Server 1',

'server' => 'imap1.example.com',

'hordeauth' => true,

'protocol' => 'imap/ssl/novalidate-cert',

'port' => 993,

'maildomain' => 'example.com',

'smtphost' => 'smtp.example.com',

'smtpport' => 25,

'realm' => 'imap1.example.com',

'preferred' => '',

);

++++ Functionality of the hook

Entering the username {{smith}} in the login screen selects the necessary values given by {{$servers['imap']}}:

  • Authentication against {{imap.example.com}}

  • using protocol {{imap/notls}}

  • ...

Entering the username {{smith@imap1.example.com}} instead selects the values of {{$servers['imap1']}}:

  • Authentication against {{imap1.example.com}}

  • using protocol {{imap/ssl/novalidate-cert}}

  • ...

//Note://

  • Horde treats them as different users with their own preferences.

  • The selection of the server is done by the realm value NOT by the server value.

+++ The Preauthenticate-Hook

The following hook has to be inserted in {{config/hooks.php}}:

if (!function_exists('_horde_hook_preauthenticate')) {

function _horde_hook_preauthenticate($userID, $credential, $realm)

{

    require dirname(__FILE__) . '/../imp/config/servers.php';



    // Convert all to lower chars (even the possible domain part)

    $userID=strtolower($userID);

    // Strip domain part from user, if it exists.

    if (($domainpart = strpos($userID, '@'))) {

      $server=substr($userID, $domainpart+1);

      $userID=substr($userID, 0, $domainpart);

      // Change the values only if a domain part was found ...

      if (!empty($server)) {

        foreach ($servers as $serverkey => $curServer) {

          if (!empty($curServer['realm']) && $server == $curServer['realm']) {

            // We found an entry, now set IMAP server values.

            foreach (array('server', 'folders', 'namespace',

              'protocol', 'port', 'smtphost', 'smtpport', 'maildomain') as $key) {

              if (isset($servers[$serverkey][$key])) {

                $_SESSION['imp'][$key] = $servers[$serverkey][$key];

              }

            }

            // Now use only the stripped version of the userID to logon to the server

            $_SESSION['imp']['user'] = $userID;

            // Setup the correct base_protocol

            $_SESSION['imp']['base_protocol'] = $_SESSION['imp']['protocol'];

            if (($pos = strpos($_SESSION['imp']['protocol'], '/'))) {

              $_SESSION['imp']['base_protocol'] = substr($_SESSION['imp']['protocol'], 0, $pos);

            }

          }

        }

      }

    }

    return true;

}

}

Like in the first example, the hook has to be activated inside {{config/conf.php}}.

  • Either use the administration user interface of Horde (recommended)

  • or set the following entry in {{config/conf.php}} with your favorite editor

...

$conf['hooks']['preauthenticate'] = true;

...