\documentclass{article}
\usepackage{ulem}
\pagestyle{headings}
\begin{document}
\part{ Dynamically selecting an IMAP server for authentication}
\textbf{Colortext: NIThis information is valid for Horde 3 only. See ImapSelectH4\footnote{http://example.com/index.php?page=ImapSelectH4} for Horde 4 or ImapSelect\footnote{http://example.com/index.php?page=ImapSelect} for Horde 5 and later.Colortext: NI}

\section{ Introduction}
During a migration from one IMAP server to another, the need arose to run both the old and the new IMAP servers in parallel. 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. \newline
The first example shows a way using a MySQL backend to select an IMAP server for authentication, given a username.\newline
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).


\noindent\rule{\textwidth}{1pt}
\section{ 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:

\begin{verbatim}

---------------------------------------------------------
|   username        |     cyrus                         |
---------------------------------------------------------
\end{verbatim}

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 \texttt{imp/config/servers.php}.

\section{ Writing a hook}
Here's some sample code for a hook placed inside horde/config/hooks.php:

\begin{verbatim}

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;
    }
}

\end{verbatim}

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


\noindent\rule{\textwidth}{1pt}
\section{ IMAP server selection by realmed username}
\subsection{Requirements}
\begin{itemize}
\item{IMP is used to do the authentication}
\item{The usernames to authenticate against the mailserver may not have a domain part (separated with '@' from the username).}
\item{There is more than one active server entry in \texttt{imp/config/servers.php}.\newline
 The realm values are different and the default server has an empty string as realm.\newline
 \textsl{\textbf{Note:}} The default server is either the first server entry in the list or the one with the \textbf{preferred} value\newline
 set.}
\item{Realm values are only lowercase strings (e.g. 'server1.example.com').}
\end{itemize}
\subsubsection{ Examplary snippet of \texttt{imp/config/servers.php}}
\begin{verbatim}
/* 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' => '',
);
\end{verbatim}

\subsubsection{ Functionality of the hook}
Entering the username \texttt{smith} in the login screen selects the necessary values given by \texttt{\$servers['imap']}:

\begin{itemize}
\item{Authentication against \texttt{imap.example.com}}
\item{using protocol \texttt{imap/notls}}
\item{\ldots}
\end{itemize}
Entering the username \texttt{smith@imap1.example.com} instead selects the values of \texttt{\$servers['imap1']}:

\begin{itemize}
\item{Authentication against \texttt{imap1.example.com}}
\item{using protocol \texttt{imap/ssl/novalidate-cert}}
\item{\ldots}
\end{itemize}
\textsl{\textbf{Note:}}

\begin{itemize}
\item{Horde treats them as different users with their own preferences.}
\item{The selection of the server is done by the \textbf{realm} value \textbf{NOT} by the \textbf{server} value.}
\end{itemize}
\subsection{ The Preauthenticate-Hook}
The following hook has to be inserted in \texttt{config/hooks.php}:\newline
\begin{verbatim}
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;
    }
}
\end{verbatim}

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

\begin{itemize}
\item{Either use the administration user interface of Horde (recommended)}
\item{or set the following entry in \texttt{config/conf.php} with your favorite editor}
\end{itemize}
\begin{verbatim}
...
$conf['hooks']['preauthenticate'] = true;
...
\end{verbatim}

 \end{document}
