\documentclass{article}
\usepackage{ulem}
\usepackage{graphicx}
\usepackage{hyperref}
\pagestyle{headings}
\begin{document}
\part{Dynamically selecting an IMAP server for authentication}
\textbf{This information is valid for Horde 3 only. See <a href="https://wiki.horde.org/ImapSelectH4">ImapSelectH4</a> for Horde 4 or <a href="https://wiki.horde.org/ImapSelect">ImapSelect</a> for Horde 5 and later.}

\section{Introduction}
\section{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.<br />
The first example shows a way using a MySQL backend to select an IMAP server for authentication, given a username.<br />
The second example instead uses the realmed username to select the IMAP server \textbackslash\{\}<br />
(i.e. the username used by Horde internally to prevent username clashes).}
\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:

<pre><code>
---------------------------------------------------------
|   username        |     cyrus                         |
---------------------------------------------------------
</code></pre>
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:

<pre><code class="language-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=\textbackslash\{\}'' . mysql\_real\_escape\_string(\$userID) . '\textbackslash\{\}'') 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;
    \}
\}
</code></pre>
\section{All that's left to do, is to activate the preauthenticate hook in Horde's configuration.}
\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}. \_<br />
The realm values are different and the default server has an empty string as realm.\newline
\textit{\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}}
<pre><code>/* 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' => '',
);
</code></pre>
\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 ...


\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 ...<br />
\textit{\textbf{Note:}}


\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}:

<pre><code class="language-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;
    \}
\}
</code></pre>
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}
<pre><code>...
\$conf['hooks']['preauthenticate'] = true;
...
</code></pre>
\end{document}
