[commits] [Wiki] created: ImapSelectH3

Jan Schneider jan at horde.org
Wed Sep 4 09:28:42 UTC 2013


jan  Wed, 04 Sep 2013 09:28:42 +0000

Created page: http://wiki.horde.org/ImapSelectH3

+ Dynamically selecting an IMAP server for authentication

**##red|This information is valid for Horde 3 only. See ImapSelectH4  
for Horde 4 or ImapSelect for Horde 5 and later.##**

++ 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.
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:

<code>
---------------------------------------------------------
|   username        |     cyrus                         |
---------------------------------------------------------
</code>

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:

<code type="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;
     }
}

</code>

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

++++ 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 at 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}}:
<code type="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>

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
<code>...
$conf['hooks']['preauthenticate'] = true;
...
</code>




More information about the commits mailing list