From: Jan Schneider Date: Thu, 9 Sep 2010 15:50:20 +0000 (+0200) Subject: Consolidate on backends.php for backend configuration files. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=f936ec44d0a4fce5b5a5b999abeed841957d14d3;p=horde.git Consolidate on backends.php for backend configuration files. --- diff --git a/chora/config/backends.php.dist b/chora/config/backends.php.dist new file mode 100644 index 000000000..33a0113ab --- /dev/null +++ b/chora/config/backends.php.dist @@ -0,0 +1,70 @@ + 'CVS', + 'location' => '/path/to/cvs/repo', + 'title' => 'CVS Repository', + 'cvsusers' => 'CVSROOT/cvsusers', + 'type' => 'cvs', +); + +$sourceroots['subversion'] = array( + 'name' => 'Subversion', + 'location' => 'http://svn.example.com/repos/svn', + 'title' => 'A Compelling Replacement for CVS', + 'type' => 'svn' +); + +$sourceroots['localsvn'] = array( + 'name' => 'MySVNProj', + 'location' => '/path/to/svn/repo', + 'title' => 'Main Subversion Repos', + 'type' => 'svn', +); + +$sourceroots['git'] = array( + 'name' => 'Git', + 'location' => '/path/to/git/repo', + 'title' => 'Git repository', + 'type' => 'git', +); diff --git a/chora/config/conf.xml b/chora/config/conf.xml index af65a0e16..a4c6a35da 100644 --- a/chora/config/conf.xml +++ b/chora/config/conf.xml @@ -86,7 +86,7 @@ $srconfig) { $perms['tree']['chora']['sourceroots'][$sourceroot] = false; $perms['title']['chora:sourceroots:' . $sourceroot] = $srconfig['name']; diff --git a/chora/lib/Test.php b/chora/lib/Test.php index 9382e1fdd..43734d1a1 100644 --- a/chora/lib/Test.php +++ b/chora/lib/Test.php @@ -42,7 +42,7 @@ class Chora_Test extends Horde_Test 'config/conf.php' => null, 'config/mime_drivers.php' => null, 'config/prefs.php' => null, - 'config/sourceroots.php' => null + 'config/backends.php' => null ); /** diff --git a/imp/config/backends.php.dist b/imp/config/backends.php.dist new file mode 100644 index 000000000..a44d919cf --- /dev/null +++ b/imp/config/backends.php.dist @@ -0,0 +1,400 @@ +getAuth() == 'foo') ? '/tmp/imaplog' : false) + * + * quota: (array) Use this if you want to display a user's quota status on + * various IMP pages. Set to an empty value to disable quota status + * (DEFAULT). + * + * The entry is configured as follows: + * + * 'quota' => array( + * // Driver name: see below + * 'driver' => [string] (REQUIRED), + * 'params' => array( + * // True if you want to hide quota output when the server + * // reports an unlimited quota. + * 'hide_when_unlimited' => true | false, + * + * // What storage unit the quota messages should be + * // displayed in. + * 'unit' => 'GB' | 'MB' (DEFAULT) | 'KB', + * + * // Output format: see below. + * 'format' => array(), + * + * // Additional driver parameters: see below. + * ) + * ) + * + * 'driver'/'params' can be the following: + * + * DRIVER: 'command' + * Use the UNIX quota command to handle quotas. + * PARAMS: 'quota_path' - [string] Path to the quota binary + * (REQUIRED) + * 'grep_path' - [string] Path to the grep binary (REQUIRED) + * 'partition' - [string] If all user mailboxes are on a + * single partition, the partition label. By + * default, quota will determine quota + * information using the user's home directory + * value. + * + * DRIVER: 'hook' + * Use the quota hook to handle quotas (see + * imp/config/hooks.php). + * PARAMS: 'params' - [array] Parameters to pass to the quota + * hook function. + * + * DRIVER: 'imap' + * Use the IMAP QUOTA extension to handle quotas. + * You must be connecting to a IMAP server capable of the + * QUOTAROOT command to use this driver. This is the + * RECOMMENDED way of handling quotas if the IMAP server + * supports it. + * PARAMS: NONE + * + * DRIVER: 'maildir' + * Use Maildir++ quota files to handle quotas. + * PARAMS: 'msg_count' - [boolean] Display information on the + * message limit rather than the storage + * limit? The storage limit information is + * displayed by default. + * 'path' - [string] The path to the user's Maildir + * directory. You may use the two-character + * sequence "~U" to represent the user's account + * name, and the actual username will be + * substituted in that location. E.g., + * '/home/~U/Maildir/' or '/var/mail/~U/Maildir/'. + * + * DRIVER: 'mdaemon' + * Use Mdaemon servers to handle quotas. + * PARAMS: 'app_location' - [string] Location of the application. + * + * DRIVER: 'mercury32' + * Use Mercury/32 servers to handle quotas. + * PARAMS: 'mail_user_folder' - [string] The path to folder mail + * mercury. + * + * DRIVER: 'sql' + * Use arbitrary SQL queries to handle quotas. + * PARAMS: 'query_quota' - (string) SQL query which returns single + * row/column with user quota (in bytes). %u is replaced + * with current user name, %U with the user name without the + * domain part, %d with the domain. + * 'query_used' - (string) SQL query which returns single + * row/column with user used space (in bytes). Placeholders + * are the same like in query_quota. + * + * Additionally, the driver takes SQL connection parameters + * 'phptype', 'hostspec',' 'username', 'password', and + * 'database'. See horde/config/conf.php for further + * information on these parameters. If using the Horde DB, + * these parameters can be found in the + * $GLOBALS['conf']['sql'] variable and may be merged into + * the parameter configuration like this: + * + * 'params' => array_merge( + * $GLOBALS['conf']['sql'], + * array( + * 'query_quota' => [...], + * 'query_used' => [...], + * ) + * ) + * + * The optional 'format' parameter is supported by all drivers and + * specifies the formats of the quota messages in the user + * interface. The parameter must be specified as a hash with the four + * possible elements 'long', 'short', 'nolimit_long', and + * 'nolimit_short'. The strings will be passed through sprintf(). + * These are the built-in default values, though they may appear + * differently in some translations ([UNIT] will be replaced with the + * value of the 'unit' parameter): + * 'long' - Quota status: %.2f [UNIT] / %.2f [UNIT] (%.2f%%) + * 'nolimit_long' - Quota status: %.2f [UNIT] / NO LIMIT + * 'short' - %.0f%% of %.0f [UNIT] + * 'nolimit_short' - %.0f [UNIT] + * + * + * *** The following options should NOT be set unless you REALLY know what *** + * *** you are doing! FOR MOST PEOPLE, AUTO-DETECTION OF THESE PARAMETERS *** + * *** (the default if the parameters are not set) SHOULD BE USED! *** + * + * capability_ignore: (array) A list of IMAP capabilites to ignore, even if + * they are supported on the server. This capability names + * should be in all capitals. This option may be useful, + * for example, if it is known that a certain capability is + * buggy on the given server. Otherwise, all available + * and supported IMAP capabilities will be (and should be) + * used. (IMAP only) + * + * comparator: (string) The search comparator to use instead of the default + * IMAP server comparator. See RFC 4790 [3.1] - "collation-id" - + * for the format. Your IMAP server must support the I18NLEVEL + * extension for this setting to have an effect. By default, + * the server default comparator is used. (IMAP only) + * + * id: (array) Send ID information to the IMAP server. This must be an array + * with the keys being the fields to send and the values being the + * associated values. Your IMAP server must support the ID extension for + * this setting to have an effect. See RFC 2971 [3.3] for a list of + * defined field values. (IMAP only) + * + * lang: (array) A list of languages (in priority order) to be used to display + * human readable messages returned by the IMAP server. Your IMAP server + * must support the LANGUAGE extensions for this setting to have an + * effect. By default, IMAP messages are output in the IMAP server + * default language. (IMAP only) + * + * namespace: (array) The list of namespaces that exist on the server. The + * entries must be encoded in the UTF7-IMAP charset. Example: + * + * array('#shared/', '#news/', '#public/') + * + * This parameter should only be used if you want to allow access + * to namespaces that may not be publicly advertised by the IMAP + * server (see RFC 2342 [3]). These additional namespaces will be + * ADDED to the list of available namespaces returned by the + * server. (IMAP only) + * + * preferred: (string | array) Useful if you want to use the same backends.php + * file for different machines. If the hostname of the IMP machine + * is identical to one of those in the preferred list, then that + * entry will be selected by default on the login screen. Otherwise + * the first entry in the list is selected. + * + * thread: (string) Set the preferred thread sort algorithm. This algorithm + * must be supported by the remote server. By default, IMP attempts + * to use REFERENCES sorting and, if this is not available, it will + * fall back to ORDEREDSUBJECT sorting done on the local server. + * + * timeout: (integer) Set the server timeout (in seconds). + * + * + * $Id$ + */ + +/* Any entries whose key value ('foo' in $servers['foo']) begin with '_' + * (an underscore character) will be treated as prompts, and you won't be + * able to log in to them. The only property these entries need is 'name'. + * This lets you put labels in the list, like this example: */ +$servers['_prompt'] = array( + 'name' => _("Choose a mail server:") +); + +/* Example configurations: */ + +$servers['imap'] = array( + 'name' => 'IMAP Server', + 'hostspec' => 'imap.example.com', + 'hordeauth' => false, + 'protocol' => 'imap', + 'port' => 143, + 'secure' => false, + 'maildomain' => 'example.com', + 'smtphost' => 'smtp.example.com', + 'smtpport' => 25, + 'cache' => false, +); + +$servers['cyrus'] = array( + 'name' => 'Cyrus IMAP Server', + 'hostspec' => 'cyrus.example.com', + 'hordeauth' => false, + 'protocol' => 'imap', + 'port' => 143, + 'secure' => false, + 'maildomain' => 'example.com', + 'smtphost' => 'smtp.example.com', + 'smtpport' => 25, + 'admin' => array( + 'params' => array( + 'admin_user' => 'cyrus', + 'admin_password' => 'cyrus_pass', + 'userhierarchy' => 'user.' + ), + ), + 'quota' => array( + 'driver' => 'imap', + 'params' => array( + 'hide_when_unlimited' => true, + 'unit' => 'MB' + ) + ), + 'acl' => true, + 'cache' => false, +); + +$servers['pop'] = array( + 'name' => 'POP3 Server', + 'hostspec' => 'pop.example.com', + 'hordeauth' => false, + 'protocol' => 'pop3', + 'port' => 110, + 'secure' => false, + 'maildomain' => 'example.com', + 'smtphost' => 'smtp.example.com', + 'smtpport' => 25, + 'cache' => false, +); + +$servers['secure-imap'] = array( + 'name' => 'Secure IMAP Server', + 'hostspec' => 'imap.example.com', + 'hordeauth' => false, + 'protocol' => 'imap', + 'port' => 143, + 'secure' => 'tls', + 'maildomain' => 'example.com', + 'smtphost' => 'smtp.example.com', + 'smtpport' => 25, + 'acl' => false, + 'cache' => false, +); + +if ($GLOBALS['conf']['kolab']['enabled']) { + require_once 'Horde/Kolab.php'; + $servers['kolab'] = array( + 'name' => 'Kolab Cyrus IMAP Server', + 'hostspec' => Kolab::getServer('imap'), + 'hordeauth' => 'full', + 'protocol' => 'imap', + 'port' => $GLOBALS['conf']['kolab']['imap']['port'], + 'secure' => false, + 'maildomain' => $GLOBALS['conf']['kolab']['imap']['maildomain'], + 'quota' => array( + 'driver' => 'imap', + 'params' => array( + 'hide_quota_when_unlimited' => true, + 'unit' => 'MB' + ) + ), + 'acl' => true, + 'cache' => false, + ); +} diff --git a/imp/config/conf.xml b/imp/config/conf.xml index 23fd025ff..1b91e05bf 100644 --- a/imp/config/conf.xml +++ b/imp/config/conf.xml @@ -47,7 +47,7 @@ array_merge( - * $GLOBALS['conf']['sql'], - * array( - * 'query_quota' => [...], - * 'query_used' => [...], - * ) - * ) - * - * The optional 'format' parameter is supported by all drivers and - * specifies the formats of the quota messages in the user - * interface. The parameter must be specified as a hash with the four - * possible elements 'long', 'short', 'nolimit_long', and - * 'nolimit_short'. The strings will be passed through sprintf(). - * These are the built-in default values, though they may appear - * differently in some translations ([UNIT] will be replaced with the - * value of the 'unit' parameter): - * 'long' - Quota status: %.2f [UNIT] / %.2f [UNIT] (%.2f%%) - * 'nolimit_long' - Quota status: %.2f [UNIT] / NO LIMIT - * 'short' - %.0f%% of %.0f [UNIT] - * 'nolimit_short' - %.0f [UNIT] - * - * - * *** The following options should NOT be set unless you REALLY know what *** - * *** you are doing! FOR MOST PEOPLE, AUTO-DETECTION OF THESE PARAMETERS *** - * *** (the default if the parameters are not set) SHOULD BE USED! *** - * - * capability_ignore: (array) A list of IMAP capabilites to ignore, even if - * they are supported on the server. This capability names - * should be in all capitals. This option may be useful, - * for example, if it is known that a certain capability is - * buggy on the given server. Otherwise, all available - * and supported IMAP capabilities will be (and should be) - * used. (IMAP only) - * - * comparator: (string) The search comparator to use instead of the default - * IMAP server comparator. See RFC 4790 [3.1] - "collation-id" - - * for the format. Your IMAP server must support the I18NLEVEL - * extension for this setting to have an effect. By default, - * the server default comparator is used. (IMAP only) - * - * id: (array) Send ID information to the IMAP server. This must be an array - * with the keys being the fields to send and the values being the - * associated values. Your IMAP server must support the ID extension for - * this setting to have an effect. See RFC 2971 [3.3] for a list of - * defined field values. (IMAP only) - * - * lang: (array) A list of languages (in priority order) to be used to display - * human readable messages returned by the IMAP server. Your IMAP server - * must support the LANGUAGE extensions for this setting to have an - * effect. By default, IMAP messages are output in the IMAP server - * default language. (IMAP only) - * - * namespace: (array) The list of namespaces that exist on the server. The - * entries must be encoded in the UTF7-IMAP charset. Example: - * - * array('#shared/', '#news/', '#public/') - * - * This parameter should only be used if you want to allow access - * to namespaces that may not be publicly advertised by the IMAP - * server (see RFC 2342 [3]). These additional namespaces will be - * ADDED to the list of available namespaces returned by the - * server. (IMAP only) - * - * preferred: (string | array) Useful if you want to use the same servers.php - * file for different machines. If the hostname of the IMP machine - * is identical to one of those in the preferred list, then that - * entry will be selected by default on the login screen. Otherwise - * the first entry in the list is selected. - * - * thread: (string) Set the preferred thread sort algorithm. This algorithm - * must be supported by the remote server. By default, IMP attempts - * to use REFERENCES sorting and, if this is not available, it will - * fall back to ORDEREDSUBJECT sorting done on the local server. - * - * timeout: (integer) Set the server timeout (in seconds). - * - * - * $Id$ - */ - -/* Any entries whose key value ('foo' in $servers['foo']) begin with '_' - * (an underscore character) will be treated as prompts, and you won't be - * able to log in to them. The only property these entries need is 'name'. - * This lets you put labels in the list, like this example: */ -$servers['_prompt'] = array( - 'name' => _("Choose a mail server:") -); - -/* Example configurations: */ - -$servers['imap'] = array( - 'name' => 'IMAP Server', - 'hostspec' => 'imap.example.com', - 'hordeauth' => false, - 'protocol' => 'imap', - 'port' => 143, - 'secure' => false, - 'maildomain' => 'example.com', - 'smtphost' => 'smtp.example.com', - 'smtpport' => 25, - 'cache' => false, -); - -$servers['cyrus'] = array( - 'name' => 'Cyrus IMAP Server', - 'hostspec' => 'cyrus.example.com', - 'hordeauth' => false, - 'protocol' => 'imap', - 'port' => 143, - 'secure' => false, - 'maildomain' => 'example.com', - 'smtphost' => 'smtp.example.com', - 'smtpport' => 25, - 'admin' => array( - 'params' => array( - 'admin_user' => 'cyrus', - 'admin_password' => 'cyrus_pass', - 'userhierarchy' => 'user.' - ), - ), - 'quota' => array( - 'driver' => 'imap', - 'params' => array( - 'hide_when_unlimited' => true, - 'unit' => 'MB' - ) - ), - 'acl' => true, - 'cache' => false, -); - -$servers['pop'] = array( - 'name' => 'POP3 Server', - 'hostspec' => 'pop.example.com', - 'hordeauth' => false, - 'protocol' => 'pop3', - 'port' => 110, - 'secure' => false, - 'maildomain' => 'example.com', - 'smtphost' => 'smtp.example.com', - 'smtpport' => 25, - 'cache' => false, -); - -$servers['secure-imap'] = array( - 'name' => 'Secure IMAP Server', - 'hostspec' => 'imap.example.com', - 'hordeauth' => false, - 'protocol' => 'imap', - 'port' => 143, - 'secure' => 'tls', - 'maildomain' => 'example.com', - 'smtphost' => 'smtp.example.com', - 'smtpport' => 25, - 'acl' => false, - 'cache' => false, -); - -if ($GLOBALS['conf']['kolab']['enabled']) { - require_once 'Horde/Kolab.php'; - $servers['kolab'] = array( - 'name' => 'Kolab Cyrus IMAP Server', - 'hostspec' => Kolab::getServer('imap'), - 'hordeauth' => 'full', - 'protocol' => 'imap', - 'port' => $GLOBALS['conf']['kolab']['imap']['port'], - 'secure' => false, - 'maildomain' => $GLOBALS['conf']['kolab']['imap']['maildomain'], - 'quota' => array( - 'driver' => 'imap', - 'params' => array( - 'hide_quota_when_unlimited' => true, - 'unit' => 'MB' - ) - ), - 'acl' => true, - 'cache' => false, - ); -} diff --git a/imp/docs/CHANGES b/imp/docs/CHANGES index 6aec7d43c..4720b6d68 100644 --- a/imp/docs/CHANGES +++ b/imp/docs/CHANGES @@ -2,6 +2,7 @@ v5.0-git -------- +[jan] Rename servers.php to backends.php. [mms] Use alternate text part to generate reply/forward text when switching compose modes if user has not altered message text (DIMP). [mms] Improved login error reporting/logging in IMP (Request #9211). diff --git a/imp/docs/INSTALL b/imp/docs/INSTALL index 828869163..57fe3caa2 100644 --- a/imp/docs/INSTALL +++ b/imp/docs/INSTALL @@ -292,10 +292,10 @@ Configuring IMP and behavior. With the following exception, the defaults will be correct for most sites. - a. servers.php + a. backends.php You must be sure to list your IMAP/POP3 server names and configuration - information in ``servers.php``. + information in ``backends.php``. You must login to Horde as a Horde Administrator to finish the configuration of IMP. Use the Horde ``Administration`` menu item to get to @@ -372,7 +372,7 @@ Configuring IMP The test script will also allow you to test your connection to the mail server and provide some auto-detected configuration parameters that can then be inserted into the server configuration located in - ``imp/config/servers.php``. + ``imp/config/backends.php``. Next, use IMP to login to a known working IMAP or POP3 server. Test at least the following: diff --git a/imp/docs/UPGRADING b/imp/docs/UPGRADING index f28819773..62b8de7f6 100644 --- a/imp/docs/UPGRADING +++ b/imp/docs/UPGRADING @@ -98,7 +98,7 @@ All code relating to fetchmail has been removed (TODO: prefs/config?). Server configuration -------------------- -The format of servers.php has changed. +The format of servers.php has changed and is renamed to backends.php. Maintenance Tasks diff --git a/imp/lib/Auth.php b/imp/lib/Auth.php index 84c7e1558..2cb784aff 100644 --- a/imp/lib/Auth.php +++ b/imp/lib/Auth.php @@ -23,7 +23,7 @@ class IMP_Auth * attempts to login to the cached session. *
      * 'password' - (string) The user password.
-     * 'server' - (string) The server key to use (from servers.php).
+     * 'server' - (string) The server key to use (from backends.php).
      * 'userId' - (string) The username.
      * 
* @@ -182,7 +182,7 @@ class IMP_Auth * namespace, quota, thread) * 'imap_ob' - (array) The serialized Horde_Imap_Client objects. Stored * by server key. - * 'maildomain' - (string) See config/servers.php. + * 'maildomain' - (string) See config/backends.php. * 'notepadavail' - (boolean) Is listing of notepads available? * 'protocol' - (string) Either 'imap' or 'pop'. * 'rteavail' - (boolean) Is the HTML editor available? @@ -199,7 +199,7 @@ class IMP_Auth * @param array $credentials An array of login credentials. *
      * 'password' - (string) The user password.
-     * 'server' - (string) The server key to use (from servers.php).
+     * 'server' - (string) The server key to use (from backends.php).
      * 'userId' - (string) The username.
      * 
* diff --git a/imp/lib/Imap.php b/imp/lib/Imap.php index b42b93c4c..7974db9a4 100644 --- a/imp/lib/Imap.php +++ b/imp/lib/Imap.php @@ -412,7 +412,7 @@ class IMP_Imap /* Static methods. */ /** - * Loads the IMP server configuration from servers.php. + * Loads the IMP server configuration from backends.php. * * @param string $server Returns this labeled entry only. * @@ -422,7 +422,7 @@ class IMP_Imap static public function loadServerConfig($server = null) { try { - $servers = Horde::loadConfiguration('servers.php', 'servers', 'imp'); + $servers = Horde::loadConfiguration('backends.php', 'servers', 'imp'); if ($servers === null) { $servers = false; } diff --git a/imp/lib/Quota/Hook.php b/imp/lib/Quota/Hook.php index 6a0781313..64a7140ef 100644 --- a/imp/lib/Quota/Hook.php +++ b/imp/lib/Quota/Hook.php @@ -3,7 +3,7 @@ * Implementation of IMP_Quota API for a generic hook function. This * requires the quota hook to be set in config/hooks.php. * - * You must configure this driver in imp/config/servers.php. The driver + * You must configure this driver in imp/config/backends.php. The driver * supports the following parameters: *
  * 'params' - (array) Parameters to pass to the quota function.
diff --git a/imp/lib/Test.php b/imp/lib/Test.php
index ddd4f70d2..dbc1371ed 100644
--- a/imp/lib/Test.php
+++ b/imp/lib/Test.php
@@ -53,7 +53,7 @@ class IMP_Test extends Horde_Test
         'config/conf.php' => 'The file ./config/conf.php appears to be missing. You must generate this file as an administrator via Horde.  See horde/docs/INSTALL.',
         'config/mime_drivers.php' => null,
         'config/prefs.php' => null,
-        'config/servers.php' => null
+        'config/backends.php' => null
     );
 
     /**
diff --git a/kastalia/config/conf.xml b/kastalia/config/conf.xml
old mode 100755
new mode 100644
diff --git a/luxor/config/backends.php.dist b/luxor/config/backends.php.dist
new file mode 100644
index 000000000..9589c52f7
--- /dev/null
+++ b/luxor/config/backends.php.dist
@@ -0,0 +1,31 @@
+ 'Horde',
+    'driver' => 'plain',
+    'root' => dirname(__FILE__) . '/../../',
+    'restrictions' => array('(.*)config/(\w*).php$')
+);
+
+$sources['pear'] = array(
+    'name' => 'PEAR',
+    'driver' => 'plain',
+    'root' => '/usr/local/lib/php/'
+);
diff --git a/luxor/config/sources.php.dist b/luxor/config/sources.php.dist
deleted file mode 100644
index 695970f17..000000000
--- a/luxor/config/sources.php.dist
+++ /dev/null
@@ -1,31 +0,0 @@
- 'Horde',
-    'driver' => 'plain',
-    'root' => dirname(__FILE__) . '/../../',
-    'restrictions' => array('(.*)config/(\w*).php$')
-);
-
-$sources['pear'] = array(
-    'name' => 'PEAR',
-    'driver' => 'plain',
-    'root' => '/usr/local/lib/php/'
-);
diff --git a/luxor/docs/CHANGES b/luxor/docs/CHANGES
index 8ec9668d2..3b7be9ae1 100644
--- a/luxor/docs/CHANGES
+++ b/luxor/docs/CHANGES
@@ -2,6 +2,7 @@
 v0.1
 ----
 
+[jan] Rename sources.php to backends.php.
 [ben] Better support for MS-SQL.
 [cjh] Support mod_rewrite style URLs in addition to GET.
 [mas] Change any output of  and  tags to  and  for better
diff --git a/luxor/lib/Luxor.php b/luxor/lib/Luxor.php
index b9274c69c..a68def574 100644
--- a/luxor/lib/Luxor.php
+++ b/luxor/lib/Luxor.php
@@ -13,7 +13,7 @@ class Luxor
     {
         global $sources, $sourceid, $source, $files, $index, $pathname;
 
-        require LUXOR_BASE . '/config/sources.php';
+        require LUXOR_BASE . '/config/backends.php';
 
         /* Default to the first source; overridden elsewhere if necessary. */
         $sourceid = Horde_Util::getFormData('source');
diff --git a/turba/config/backends.php.dist b/turba/config/backends.php.dist
new file mode 100644
index 000000000..883c9bc7f
--- /dev/null
+++ b/turba/config/backends.php.dist
@@ -0,0 +1,1104 @@
+ 'deleted=0'.
+ *                  Don't enclose filter in brackets - this will done
+ *                  automatically. Also keep in mind that a full filter line
+ *                  will be built from 'filter' and 'objectclass' parameters.
+ *
+ *   version:       Only applies to LDAP servers. If set, specify LDAP server
+ *                  version, can be 2 or 3. Active Directory servers
+ *                  require version 3.
+ *
+ * map:         This is a list of mappings from the Turba attribute names (on
+ *              the left) to the attribute names by which they are known in
+ *              this contact source (on the right). Turba also supports
+ *              composite fields. A composite field is defined by mapping the
+ *              field name to an array containing a list of component fields
+ *              and a format string (similar to a printf() format string;
+ *              however, note that positioned parameters like %1$s will NOT
+ *              work). 'attribute' defines where the composed value is saved,
+ *              and can be left out. 'parse' defines a list of format strings
+ *              and field names that should be used for splitting up composite
+ *              fields, in the order of precedence, and can be left out. Here
+ *              is an example:
+ *              ...
+ *              'name' => array('fields' => array('firstname', 'lastname'),
+ *                              'format' => '%s %s',
+ *                              'attribute' => 'object_name'),
+ *              'firstname' => 'object_firstname',
+ *              'lastname' => 'object_lastname',
+ *              ...
+ *
+ *              Standard Turba attributes are:
+ *                __key     : A backend-specific ID for the entry (any value
+ *                            as long as it is unique inside that source;
+ *                            required)
+ *                __uid     : Globally unique ID of the entry (used for
+ *                            synchronizing and must be able to be set to any
+ *                            value)
+ *                __owner   : User name of the contact's owner
+ *                __type    : Either 'Object' or 'Group'
+ *                __members : Serialized PHP array with list of Group members.
+ *              More Turba attributes are defined in config/attributes.php.
+ *
+ * tabs:        All fields can be grouped into tabs with this optional entry.
+ *              This list is multidimensional hash, the keys are the tab
+ *              titles.
+ *              Here is an example:
+ *              'tabs' => array(
+ *                  'Names' => array('firstname', 'lastname', 'alias'),
+ *                  'Addresses' => array('homeAddress', 'workAddress')
+ *              );
+ *
+ * search:      A list of Turba attribute names that can be searched for this
+ *              source.
+ *
+ * strict:      A list of native field/attribute names that must
+ *              always be matched exactly in a search.
+ *
+ * approximate: Only applies to LDAP servers. If set, should be an
+ *              array of native field/attribute names to search
+ *              "approximately" (for example, "Sánchez", "Sanchez",
+ *              and "Sanchéz" will all match a search string of
+ *              "sanchez").
+ *
+ * export:      If set to true, this source will appear on the Export menu,
+ *              allowing users to export the contacts to a CSV (etc.) file.
+ *
+ * browse:      If set to true, this source will be browseable via the Browse
+ *              menu item, and empty searches against the source will return
+ *              all contacts.
+ *
+ * use_shares:  If this is present and true, Horde_Share functionality will
+ *              be enabled for this source - allowing users to share their
+ *              personal address books as well as to create new ones. Since
+ *              Turba only supports having one backend configured for
+ *              creating new shares, use the 'shares' configuration option to
+ *              specify which backend will be used for creating new shares.
+ *              All permission checking will be done against Horde_Share, but
+ *              note that any 'extended' permissions (such as max_contacts)
+ *              will still be enforced. Also note that the backend driver
+ *              must have support for using this. Currently SQL and IMSP.
+ *
+ * list_name_field:  If this is present and non-empty, it will be taken as the
+ *                   field to store contact list names in.
+ *                   This is required when using a composite field as the 'name'
+ *                   field.
+ *
+ * alternative_name: If this is present and non-empty, it will be taken as the
+ *                   field to use an alternative in case that the name field is
+ *                   empty.
+ *
+ * Here are some example configurations:
+ *
+ * $Id$
+ */
+
+/**
+ * A local address book in an SQL database. This implements a private
+ * per-user address book. Sharing of this source with other users may be
+ * accomplished by enabling Horde_Share for this source by setting
+ * 'use_shares' => true.
+ *
+ * Be sure to create a turba_objects table in your Horde database from the
+ * schema in turba/scripts/db/turba.sql if you use this source.
+ */
+$cfgSources['localsql'] = array(
+    'title' => _("My Address Book"),
+    'type' => 'sql',
+    // The default connection details are pulled from the Horde-wide SQL
+    // connection configuration.
+    'params' => array_merge($GLOBALS['conf']['sql'], array('table' => 'turba_objects')),
+    // Using two tables as datasource.
+    // 'params' => array_merge($GLOBALS['conf']['sql'],
+    //                         array('table' => 'leaddetails LEFT JOIN leadaddress ON leaddetails.leadid = leadaddress.leadaddressid',
+    //                               'filter' => 'leaddetails.converted = 0')),
+    'map' => array(
+        '__key' => 'object_id',
+        '__owner' => 'owner_id',
+        '__type' => 'object_type',
+        '__members' => 'object_members',
+        '__uid' => 'object_uid',
+        'firstname' => 'object_firstname',
+        'lastname' => 'object_lastname',
+        'middlenames' => 'object_middlenames',
+        'namePrefix' => 'object_nameprefix',
+        'nameSuffix' => 'object_namesuffix',
+        'name' => array('fields' => array('namePrefix', 'firstname',
+                                          'middlenames', 'lastname',
+                                          'nameSuffix'),
+                        'format' => '%s %s %s %s %s',
+                        'parse' => array(
+                            array('fields' => array('firstname', 'middlenames',
+                                                    'lastname'),
+                                  'format' => '%s %s %s'),
+                            array('fields' => array('firstname', 'lastname'),
+                                  'format' => '%s %s'))),
+        // This is a shorter version of a "name" composite field which only
+        // consists of the first name and last name.
+        // 'name' => array('fields' => array('firstname', 'lastname'),
+        //                 'format' => '%s %s'),
+        'alias' => 'object_alias',
+        'birthday' => 'object_bday',
+        // The photo field requires at least Horde 3.3 and a matching type
+        // field.
+        // 'photo' => 'object_photo',
+        // 'phototype' => 'object_phototype',
+        'homeStreet' => 'object_homestreet',
+        'homePOBox' => 'object_homepob',
+        'homeCity' => 'object_homecity',
+        'homeProvince' => 'object_homeprovince',
+        'homePostalCode' => 'object_homepostalcode',
+        'homeCountry' => 'object_homecountry',
+        // This is an example composite field for addresses, so you can display
+        // the various map links. If you use this, be sure to add 'homeAddress'
+        // to the 'tabs' parameter below.
+        // 'homeAddress' => array('fields' => array('homeStreet', 'homeCity',
+        //                                          'homeProvince',
+        //                                          'homePostalCode'),
+        //                        'format' => "%s \n %s, %s  %s"),
+        'workStreet' => 'object_workstreet',
+        'workPOBox' => 'object_workpob',
+        'workCity' => 'object_workcity',
+        'workProvince' => 'object_workprovince',
+        'workPostalCode' => 'object_workpostalcode',
+        'workCountry' => 'object_workcountry',
+        'timezone' => 'object_tz',
+        'email' => 'object_email',
+        'homePhone' => 'object_homephone',
+        'workPhone' => 'object_workphone',
+        'cellPhone' => 'object_cellphone',
+        'fax' => 'object_fax',
+        'pager' => 'object_pager',
+        'title' => 'object_title',
+        'role' => 'object_role',
+        'company' => 'object_company',
+        // The logo field requires at least Horde 3.3 and a matching type
+        // field.
+        // 'logo' => 'object_logo',
+        // 'logotype' => 'object_logotype',
+        'category' => 'object_category',
+        'notes' => 'object_notes',
+        'website' => 'object_url',
+        'freebusyUrl' => 'object_freebusyurl',
+        'pgpPublicKey' => 'object_pgppublickey',
+        'smimePublicKey' => 'object_smimepublickey',
+    ),
+    'tabs' => array(
+        _("Personal") => array('firstname', 'lastname', 'middlenames',
+                               'namePrefix', 'nameSuffix', 'name', 'alias',
+                               'birthday', 'photo'),
+        _("Location") => array('homeStreet', 'homePOBox', 'homeCity',
+                               'homeProvince', 'homePostalCode', 'homeCountry',
+                               'workStreet', 'workPOBox', 'workCity',
+                               'workProvince', 'workPostalCode', 'workCountry',
+                               'timezone'),
+        _("Communications") => array('email', 'homePhone', 'workPhone',
+                                     'cellPhone', 'fax', 'pager'),
+        _("Organization") => array('title', 'role', 'company', 'logo'),
+        _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
+                            'pgpPublicKey', 'smimePublicKey'),
+    ),
+    'search' => array(
+        'name',
+        'email'
+    ),
+    'strict' => array(
+        'object_id',
+        'owner_id',
+        'object_type',
+    ),
+    'export' => true,
+    'browse' => true,
+    'use_shares' => true,
+    'list_name_field' => 'lastname',
+    'alternative_name' => 'company',
+);
+
+/**
+ * A local address book in an LDAP directory. This implements a public
+ * (shared) address book.
+ *
+ * To store distribution lists in the LDAP directory, you'll need to include
+ * horde/scripts/ldap/horde.schema in your LDAP configuration.
+ *
+ * To store freebusy information in the LDAP directory, you'll need to include
+ * turba/scripts/ldap/rfc2739.schema in your LDAP configuration.
+ */
+// $cfgSources['localldap'] = array(
+//     'title' => _("Shared Directory"),
+//     'type' => 'ldap',
+//     'params' => array(
+//         'server' => 'ldap.example.com',
+//         'port' => 389,
+//         'tls' => false,
+//         'root' => 'dc=example,dc=com',
+//         'bind_dn' => 'cn=admin,ou=users,dc=example,dc=com',
+//         // For Active Directory:
+//         // 'bind_dn' => 'username@example.com',
+//         'bind_password' => '********',
+//         'sizelimit' => 200,
+//         // For Active Directory:
+//         // 'sizelimit' => 0,
+//         'dn' => array('cn'),
+//         'objectclass' => array('top',
+//                                'person',
+//                                'organizationalPerson',
+//                                'inetOrgPerson'),
+//                                // Add 'turbaContact' to this array if using
+//                                // 'turbaType' attribute below, and 'calEntry'
+//                                // if using 'freebusyUrl'.
+//         // For Active Directory:
+//         // 'objectclass' => array('organizationalPerson',
+//         //                        'user',
+//         //                        'group',
+//         //                        'contact'),
+//         'scope' => 'one',
+//         // For Active Directory:
+//         // 'scope' => 'sub',
+//         'charset' => 'utf-8',
+//         // Consult the LDAP schema to verify that all required attributes for
+//         // an entry are set and add them if needed.
+//         'checkrequired' => false,
+//         // Value used to fill in missing required attributes.
+//         'checkrequired_string' => ' ',
+//         // Check LDAP schema for valid syntax. If this is false an address
+//         // field is assumed to have postalAddress syntax; otherwise the schema
+//         // is consulted for the syntax to use.
+//         'checksyntax' => false,
+//         'version' => 3,
+//
+//         // For Active Directory you probably want to also set the following
+//         // parameters:
+//         // 'deref' => LDAP_DEREF_ALWAYS,
+//         // 'filter' => '&(SAMAccountName=*)(mail=*)',
+//         // 'referrals' => 0,
+//     ),
+//     'map' => array(
+//         '__key' => 'dn',
+//
+//         // Remove this mapping if using Active Directory server:
+//         '__uid' => 'uid',
+//
+//         // From horde.schema.  Make sure you have 'turbaContact' objectClass
+//         // included above:
+//         // '__type' => 'turbaType',
+//         // '__members' => 'turbaMembers',
+//
+//         'name' => 'cn',
+//         'email' => 'mail',
+//         'homePhone' => 'homephone',
+//         'workPhone' => 'telephonenumber',
+//         'cellPhone' => 'mobiletelephonenumber',
+//         'homeAddress' => 'homepostaladdress',
+//
+//         // From rfc2739.schema:
+//         // 'freebusyUrl' => 'calFBURL',
+//
+//         // For Active Directory servers:
+//         // 'name' => 'displayname',
+//         // 'title' => 'title',
+//         // 'cellPhone' => 'mobile',
+//         // 'department' => 'department',
+//         // 'company' => 'company',
+//     ),
+//     'search' => array(
+//         'name',
+//         'email',
+//         'homePhone',
+//         'workPhone',
+//         'cellPhone',
+//         'homeAddress'
+//     ),
+//     'strict' => array(
+//         'dn',
+//     ),
+//     'approximate' => array(
+//         'cn',
+//     ),
+//     // For Active Directory servers:
+//     // 'approximate' => array(
+//     //     'displayname',
+//     //     'samaccountname',
+//     // ),
+//     'export' => true,
+//     'browse' => true,
+// );
+
+/**
+ * A personal LDAP address book. This assumes that the login is
+ * @domain.com and that the users are stored on the same LDAP
+ * server. Thus it is possible to bind with the username and password from the
+ * user. For more info; please refer to the docs/LDAP file in the Turba
+ * distribution.
+ *
+ * To store distribution lists in the LDAP directory, you'll need to include
+ * horde/scripts/ldap/horde.schema in your LDAP configuration.
+ *
+ * To store freebusy information in the LDAP directory, you'll need to include
+ * turba/scripts/ldap/rfc2739.schema in your LDAP configuration.
+ */
+/* First we need to get the uid. */
+// $_ldap_uid = $GLOBALS['registry']->getAuth('bare');
+// $_ldap_basedn = 'dc=example,dc=com';
+// $cfgSources['personal_ldap'] = array(
+//     'title' => _("My Address Book"),
+//     'type' => 'ldap',
+//     'params' => array(
+//         'server' => 'localhost',
+//         'tls' => false,
+//         'root' => 'ou=' . $_ldap_uid . ',ou=personal_addressbook,' . $_ldap_basedn,
+//         'bind_dn' => 'uid=' . $_ldap_uid . ',ou=People,' . $_ldap_basedn,
+//         'bind_password' => $GLOBALS['registry']->getAuthCredential('password'),
+//         'dn' => array('uid'),
+//         'objectclass' => array('top',
+//                                'person',
+//                                // 'turbaContact',
+//                                'inetOrgPerson',
+//                                // 'calEntry',
+//                                'organizationalPerson'),
+//         'scope' => 'one',
+//         'charset' => 'utf-8',
+//         'version' => 3
+//     ),
+//     'map' => array(
+//         '__key' => 'dn',
+//         '__uid' => 'uid',
+//
+//         // From horde.schema:
+//         // '__type' => 'turbaType',
+//         // '__members' => 'turbaMembers',
+//
+//         'name' => 'cn',
+//         'email' => 'mail',
+//         'lastname' => 'sn',
+//         'title' => 'title',
+//         'company' => 'organizationname',
+//         'businessCategory' => 'businesscategory',
+//         'workAddress' => 'postaladdress',
+//         'workPostalCode' => 'postalcode',
+//         'workPhone' => 'telephonenumber',
+//         'fax' => 'facsimiletelephonenumber',
+//         'homeAddress' => 'homepostaladdress',
+//         'homePhone' => 'homephone',
+//         'cellPhone' => 'mobile',
+//         'notes' => 'description',
+//
+//         // Evolution interopt attributes: (those that do not require the
+//         // evolution.schema)
+//         'office' => 'roomNumber',
+//         'department' => 'ou',
+//         'nickname' => 'displayName',
+//         'website' => 'labeledURI',
+//
+//         // These are not stored on the LDAP server.
+//         'pgpPublicKey' => 'object_pgppublickey',
+//         'smimePublicKey' => 'object_smimepublickey',
+//
+//         // From rfc2739.schema:
+//         // 'freebusyUrl' => 'calFBURL',
+//     ),
+//     'search' => array(
+//         'name',
+//         'email',
+//         'businessCategory',
+//         'title',
+//         'homePhone',
+//         'workPhone',
+//         'cellPhone',
+//         'homeAddress'
+//     ),
+//     'strict' => array(
+//         'dn',
+//     ),
+//     'approximate' => array(
+//         'cn',
+//     ),
+//     'export' => true,
+//     'browse' => true,
+// );
+
+/**
+ * Public netcenter and verisign LDAP directories.
+ */
+// $cfgSources['netcenter'] = array(
+//     'title' => _("Netcenter Member Directory"),
+//     'type' => 'ldap',
+//     'params' => array(
+//         'server' => 'memberdir.netscape.com',
+//         'port' => 389,
+//         'tls' => false,
+//         'root' => 'ou=member_directory,o=netcenter.com',
+//         'dn' => array('cn'),
+//         'objectclass' => 'person',
+//         'filter' => '',
+//         'scope' => 'sub',
+//         'charset' => 'iso-8859-1'
+//     ),
+//     'map' => array(
+//         '__key' => 'dn',
+//         'name' => 'cn',
+//         'email' => 'mail',
+//         'alias' => 'givenname'
+//     ),
+//     'search' => array(
+//         'name',
+//         'email',
+//         'alias'
+//     ),
+//     'strict' => array(
+//         'dn'
+//     ),
+//     'approximate' => array(
+//         'cn',
+//     ),
+//     'export' => false,
+//     'browse' => false,
+// );
+//
+// $cfgSources['verisign'] = array(
+//     'title' => _("Verisign Directory"),
+//     'type' => 'ldap',
+//     'params' => array(
+//         'server' => 'directory.verisign.com',
+//         'port' => 389,
+//         'tls' => false,
+//         'root' => '',
+//         'scope' => 'sub',
+//         'charset' => 'iso-8859-1'
+//     ),
+//     'map' => array(
+//         '__key' => 'dn',
+//         'name' => 'cn',
+//         'email' => 'mail'
+//     ),
+//     'search' => array(
+//         'name',
+//         'email'
+//     ),
+//     'strict' => array(
+//         'dn'
+//     ),
+//     'approximate' => array(
+//         'cn',
+//     ),
+//     'export' => false,
+//     'browse' => false,
+// );
+
+/**
+ * A preferences-based address book. This will always be private. You can add
+ * any attributes you like to the map and it will just work; you can also
+ * create multiple prefs-based address books by changing the 'name' parameter.
+ * This is best for address books that are expected to remain small; it's not
+ * the most efficient, but it can't be beat for getting up and running
+ * quickly, especially if you already have Horde preferences working. Note
+ * that it is not searchable, though - searches will simply return the whole
+ * address book.
+ */
+// $cfgSources['prefs'] = array(
+//     'title' => _("Private Address Book"),
+//     'type' => 'prefs',
+//     'params' => array(
+//         'name' => 'prefs',
+//         'charset' => $GLOBALS['registry']->getCharset()
+//     ),
+//     'map' => array(
+//         '__key' => 'id',
+//         '__type' => '_type',
+//         '__members' => '_members',
+//         '__uid' => 'uid',
+//         'name' => 'name',
+//         'email' => 'mail',
+//         'alias' => 'alias'
+//     ),
+//     'search' => array(
+//         'name',
+//         'email',
+//         'alias'
+//     ),
+//     'strict' => array(
+//         'id',
+//         '_type',
+//     ),
+//     'export' => true,
+//     'browse' => true,
+// );
+
+/**
+ * An address book based on message recipients. This will always be private and
+ * read-only. The address book content is provided by the
+ * contacts/favouriteRecipients API method which should be implemented by a
+ * mail client that collects the most regular message recipients, like IMP
+ * 4.2.
+ */
+$cfgSources['favourites'] = array(
+    'title' => _("Favourite Recipients"),
+    'type' => 'favourites',
+    'params' => array(
+        'limit' => 10
+    ),
+    'map' => array(
+        '__key' => 'email',
+        'name' => 'email',
+        'email' => 'email'
+    ),
+    'search' => array(
+        'name',
+        'email'
+    ),
+    'strict' => array(
+        'id',
+    ),
+    'export' => true,
+    'browse' => true,
+);
+
+/**
+ * A driver to show a user's Facebook friends as a turba address book. Some
+ * data (like email) is not readily available via the API, but other data, like
+ * birthdays (which will show up via the listTimeObjects API) may still be
+ * useful to some
+ */
+//$cfgSources['facebook'] = array(
+//    'title' => _("Facebook Friends"),
+//    'type' => 'facebook',
+//    'params' => array(
+//        'limit' => 10
+//    ),
+//   'map' => array(
+//        '__key' => 'uid',
+//        'name' => 'name',
+//        'lastname' => 'last_name',
+//        'firstname' => 'first_name',
+//        'email' => 'proxied_email',
+//        'birthday' => 'birthday',
+//        'homeCity' => 'current_location.city',
+//        'homeProvince' => 'current_location.state',
+//        'homePostalCode' => 'current_location.zip',
+//        'homeCountry' => 'current_location.country',
+//   ),
+//    'search' => array(
+//        'name',
+//        'email',
+//    ),
+//    'export' => true,
+//    'browse' => true,
+//);
+
+/**
+ * This source creates an address book for each group the current user is a
+ * member in.  The address book will always be read only, and the group members
+ * must have an email address entered in their default identity.  No email
+ * address will result in that member not being included in the address book.
+ */
+//$grpSource = array(
+//    'title' => _("Group"),
+//    'type' => 'group',
+//    'params' => array(
+//       'name' => ''
+//    ),
+//    'map' => array(
+//        '__key' => 'email',
+//        'name' => 'name',
+//        'email' => 'email'
+//    ),
+//    'search' => array(
+//        'name',
+//        'email'
+//    ),
+//    'export' => true,
+//    'browse' => true,
+//);
+
+//$_group_driver = &Group::singleton();
+//$_group_list = $_group_driver->getGroupMemberships($GLOBALS['registry']->getAuth());
+//foreach ($_group_list as $_group_id => $_group_name) {
+//    $cfgSources['group_' . $_group_id] = $grpSource;
+//    $cfgSources['group_' . $_group_id]['params'] = array('gid' => $_group_id);
+//    $cfgSources['group_' . $_group_id]['title'] = $_group_name;
+//}
+
+/**
+ * IMSP based address book.
+ *
+ * Communicates with an IMSP backend server.
+ *
+ * Notes:
+ * You should configure the user's "main" address book here. The name of the
+ * address book is set in the 'name' element of the params array. It should
+ * be configured to be the same as the IMSP server username. Any other
+ * address books the user has access to will automatically be configured at
+ * runtime.
+ *
+ * In the params array, accepted values for auth_method are 'cram_md5',
+ * 'imtest', and 'plaintext' - these must match a IMSP_Auth_ driver. If you
+ * are using the imtest driver for Cyrus, please read the
+ * framework/Net_IMSP/Auth/imtest.php file for more configuration information.
+ *
+ * Groups in other IMSP-aware applications are just entries with multiple
+ * email addresses in the email field and a 'group' field set to flag the
+ * entry as a group. (The Cyrusoft applications, Mulberry and Silkymail both
+ * use a field named 'group' set to equal 'group' to signify this). A
+ * Turba_Object_Group is basically a List of existing Turba_Objects. The IMSP
+ * driver will map between these two structures when reading and writing
+ * groups.
+ * The only caveat is that IMSP groups that contain email addresses which do
+ * not have a cooresponding contact entry will be ignored. The group_id_field
+ * should be set to the IMSP field that flags the entry as a 'group' entry and
+ * the group_id_value should be set to the value given to that field.
+ *
+ * By default, the username and password that were used to login to Horde is
+ * used to login to the IMSP server. If these credentials are different,
+ * there is a user preference in Horde to allow another username / password to
+ * be entered. The alternate credentials are always used if present.
+ *
+ * In the map array, since IMSP uses the 'name' attribute as a key, this is
+ * what __key is mapped to ... and a dynamic field 'fullname' is added and
+ * mapped to the horde 'name' field. This is populated with the IMSP 'name'
+ * field when the object is read from the server.
+ *
+ * If you wish to keep track of ownership of individual contacts, set
+ * 'contact_ownership' = true. Note that entries created using other clients
+ * will not be created this way and will therefore have no 'owner'. Set
+ * 'contact_ownership' = false and the __owner field will be automatically
+ * populated with the current username.
+ *
+ * To integrate with Horde_Share, set use_shares to true and uncomment the
+ * IMSP Horde_Share hooks in horde/config/hooks.php.
+ */
+// Check that IMSP is configured in Horde but fall through if there is no
+// configuration at all.
+if (!empty($GLOBALS['conf']['imsp']['enabled']) ||
+    !isset($GLOBALS['conf']['imsp']['enabled'])) {
+    // First, get the user name to login to IMSP server with.
+    $_imsp_auth_user = $GLOBALS['prefs']->getValue('imsp_auth_user');
+    $_imsp_auth_pass = $GLOBALS['prefs']->getValue('imsp_auth_pass');
+    if (!strlen($_imsp_auth_user)) {
+        $_imsp_auth_user = $GLOBALS['registry']->getAuth('bare');
+        $_imsp_auth_pass = $GLOBALS['registry']->getAuthCredential('password');
+    }
+    $cfgSources['imsp'] = array(
+        'title' => _("IMSP"),
+        'type' => 'imsp',
+        'params' => array(
+            'server'  => $GLOBALS['conf']['imsp']['server'],
+            'port'    => $GLOBALS['conf']['imsp']['port'],
+            'auth_method' => $GLOBALS['conf']['imsp']['auth_method'],
+            // socket, command, and auth_mechanism are for imtest driver.
+            'socket'  => isset($GLOBALS['conf']['imsp']['socket']) ?
+                         $GLOBALS['conf']['imsp']['socket'] . $_imsp_auth_user . '.sck' :
+                         '',
+            'command' => isset($GLOBALS['conf']['imsp']['command']) ?
+                         $GLOBALS['conf']['imsp']['command'] : '' ,
+            'auth_mechanism' => isset($GLOBALS['conf']['imsp']['auth_mechanism']) ?
+                                $GLOBALS['conf']['imsp']['auth_mechanism'] : '',
+            'username' => $_imsp_auth_user,
+            'password' => $_imsp_auth_pass,
+            'name' => $_imsp_auth_user,
+            'group_id_field' => 'group',
+            'group_id_value' => 'group',
+            'contact_ownership' => false,
+            // Dynamically generated acl rights for current user.
+            'my_rights' => '',
+            ),
+        'map' => array(
+            '__key' => 'name',
+            '__type' => '__type',
+            '__members' => '__members',
+            '__owner' => '__owner',
+            '__uid' => '__uid',
+            'name' => 'fullname',
+            'email' => 'email',
+            'alias' => 'alias',
+            'company' => 'company',
+            'notes' => 'notes',
+            'workPhone' => 'phone-work',
+            'fax' => 'fax',
+            'homePhone' => 'phone-home',
+            'cellPhone' => 'cellphone',
+            'freebusyUrl' => 'freebusyUrl'
+            ),
+        'search' => array(
+            'name',
+            'email',
+            'alias',
+            'company',
+            'homePhone'
+            ),
+        'strict' => array(),
+        'export' => true,
+        'browse' => true,
+        'use_shares' => false,
+        );
+
+    /**
+     * Get any other address books this user might be privy to.
+     * The values for attributes such as 'export' and 'browse' for books
+     * that are added below will be the same as the values set in the default
+     * book above. Any entries defined explicitly in cfgSources[]
+     * will override any entries gathered dynamically below.
+     */
+     if (empty($cfgSources['imsp']['use_shares'])) {
+        $result = Net_IMSP_Utils::getAllBooks($cfgSources['imsp']);
+
+        if (!$result instanceof PEAR_Error) {
+            $resultCount = count($result);
+            for ($i = 0; $i < $resultCount; ++$i) {
+                // Make sure we didn't define this source explicitly,
+                // but set the acls from the server regardless.
+                $dup = false;
+                foreach ($cfgSources as $key => $thisSource) {
+                    if (($thisSource['type'] == 'imsp') &&
+                        ($thisSource['params']['name'] == $result[$i]['params']['name'])) {
+
+                        $dup = true;
+                        $acl = $result[$i]['params']['my_rights'];
+                        $cfgSources[$key]['params']['my_rights'] = $acl;
+                        break;
+                    }
+                }
+                if (!$dup) {
+                    $cfgSources[$result[$i]['params']['name']] = $result[$i];
+                }
+            }
+        } else {
+            $notification->push($result);
+        }
+     }
+}
+/* End IMSP sources. */
+
+/* Begin Kolab sources. */
+if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
+
+    /* Only use LDAP if we have that extension in PHP */
+    if (function_exists('ldap_connect')) {
+        require_once 'Horde/Kolab.php';
+
+        if (!is_callable('Kolab', 'getServer')) {
+            $_kolab_server = $GLOBALS['conf']['kolab']['ldap']['server'];
+        } else {
+            $_kolab_server = Kolab::getServer('ldap');
+        }
+
+        /* A global address book for a Kolab Server. This is typically a
+         * read-only public directory, stored in the default Kolab LDAP server.
+         * The user accessing this should have read permissions to the shared
+         * directory in LDAP. */
+        $cfgSources['kolab_global'] = array(
+            'title' => _("Global Address Book"),
+            'type' => 'ldap',
+            'params' => array(
+                'server' => $_kolab_server,
+                'port' => $GLOBALS['conf']['kolab']['ldap']['port'],
+                'tls' => false,
+                'root' => $GLOBALS['conf']['kolab']['ldap']['basedn'],
+                'sizelimit' => 200,
+                'dn' => array('cn'),
+                'objectclass' => array(
+                    'inetOrgPerson'
+                ),
+                'scope' => 'sub',
+                'charset' => 'utf-8',
+                'version' => 3,
+                'bind_dn' => '',
+                'bind_password' => '',
+            ),
+            'map' => array(
+                '__key'             => 'dn',
+                'name'              => 'cn',
+                'firstname'         => 'givenName',
+                'lastname'          => 'sn',
+                'email'             => 'mail',
+                'alias'             => 'alias',
+                'title'             => 'title',
+                'company'           => 'o',
+                'workStreet'        => 'street',
+                'workCity'          => 'l',
+                'workProvince'      => 'st',
+                'workPostalCode'    => 'postalCode',
+                'workCountry'       => 'c',
+                'homePhone'         => 'homePhone',
+                'workPhone'         => 'telephoneNumber',
+                'cellPhone'         => 'mobile',
+                'fax'               => 'fax',
+                'notes'             => 'description',
+                'kolabHomeServer'   => 'kolabHomeServer',
+                'freebusyUrl'       => array('fields' => array('kolabHomeServer', 'email'),
+                                             'format' => 'https://%s/freebusy/%s.ifb'),
+            ),
+            'search' => array(
+                'name',
+                'firstname',
+                'lastname',
+                'email',
+                'title',
+                'company',
+                'workAddress',
+                'workCity',
+                'workProvince',
+                'workPostalCode',
+                'workCountry',
+                'homePhone',
+                'workPhone',
+                'cellPhone',
+                'fax',
+                'notes',
+            ),
+            'strict' => array(
+                'dn',
+            ),
+            'export' => true,
+            'browse' => true,
+        );
+    }
+
+    /**
+     * The local address books for a Kolab user. These are stored in specially
+     * flagged contact folder within the users Cyrus IMAP mailbox.
+     *
+     * Still missing attributes are:
+     *
+     *   picture, sensitivity
+     */
+
+    $cfgSources['kolab'] = array(
+        'title' => _("Contacts"),
+        'type' => 'kolab',
+        'params' => array(
+            'charset' => 'utf-8',
+        ),
+        'list_name_field' => 'lastname',
+        'map' => array(
+            '__key' => 'uid',
+            '__uid' => 'uid',
+            '__type' => '__type',
+            '__members' => '__members',
+            /* Personal */
+            'name' => array('fields' => array('firstname', 'middlenames', 'lastname'),
+                            'format' => '%s %s %s',
+                            'parse' => array(
+                                array('fields' => array('firstname', 'middlenames',
+                                                        'lastname'),
+                                      'format' => '%s %s %s'),
+                                array('fields' => array( 'lastname', 'firstname'),
+                                      'format' => '%s, %s'),
+                                array('fields' => array('firstname', 'lastname'),
+                                      'format' => '%s %s'),
+                            )),
+            'firstname'         => 'given-name',
+            'lastname'          => 'last-name',
+            'middlenames'       => 'middle-names',
+            'namePrefix'        => 'prefix',
+            'nameSuffix'        => 'suffix',
+            'initials'          => 'initials',
+            'nickname'          => 'nick-name',
+            'photo'             => 'photo',
+            'phototype'         => 'phototype',
+            'gender'            => 'gender',
+            'birthday'          => 'birthday',
+            'spouse'            => 'spouse-name',
+            'anniversary'       => 'anniversary',
+            'children'          => 'children',
+            /* Location */
+            'workStreet'        => 'addr-business-street',
+            'workCity'          => 'addr-business-locality',
+            'workProvince'      => 'addr-business-region',
+            'workPostalCode'    => 'addr-business-postal-code',
+            'workCountry'       => 'addr-business-country',
+            'homeStreet'        => 'addr-home-street',
+            'homeCity'          => 'addr-home-locality',
+            'homeProvince'      => 'addr-home-region',
+            'homePostalCode'    => 'addr-home-postal-code',
+            'homeCountry'       => 'addr-home-country',
+            /* Communications */
+            'emails'            => 'emails',
+            'homePhone'         => 'phone-home1',
+            'workPhone'         => 'phone-business1',
+            'cellPhone'         => 'phone-mobile',
+            'fax'               => 'phone-businessfax',
+            'instantMessenger'  => 'im-address',
+            /* Organization */
+            'title'             => 'job-title',
+            'role'              => 'profession',
+            'company'           => 'organization',
+            'department'        => 'department',
+            'office'            => 'office-location',
+            'manager'           => 'manager-name',
+            'assistant'         => 'assistant',
+            /* Other */
+            'category'          => 'categories',
+            'notes'             => 'body',
+            'website'           => 'web-page',
+            'freebusyUrl'       => 'free-busy-url',
+            'language'          => 'language',
+            'latitude'          => 'latitude',
+            'longitude'         => 'longitude',
+            /* Invisible */
+            'email'             => 'email',
+            'pgpPublicKey'      => 'pgp-publickey',
+        ),
+        'tabs' => array(
+            _("Personal") => array('name', 'firstname', 'lastname', 'middlenames',
+                                   'namePrefix', 'nameSuffix', 'initials', 'nickname',
+                                   'photo', 'gender', 'birthday', 'spouse', 'anniversary',
+                                   'children'),
+            _("Location") => array('homeStreet', 'homeCity', 'homeProvince',
+                                   'homePostalCode', 'homeCountry', 'workStreet',
+                                   'workCity', 'workProvince', 'workPostalCode',
+                                   'workCountry'),
+            _("Communications") => array('emails', 'homePhone', 'workPhone',
+                                         'cellPhone', 'fax', 'instantMessenger'),
+            _("Organization") => array('title', 'role', 'company', 'department',
+                                       'office', 'manager', 'assistant'),
+            _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
+                                'language', 'latitude', 'longitude'),
+        ),
+        'search' => array(
+            /* Personal */
+            'name',
+            'firstname',
+            'lastname',
+            'middlenames',
+            'namePrefix',
+            'nameSuffix',
+            'initials',
+            'nickname',
+            'gender',
+            'birthday',
+            'spouse',
+            'anniversary',
+            'children',
+            /* Location */
+            'workStreet',
+            'workCity',
+            'workProvince',
+            'workPostalCode',
+            'workCountry',
+            'homeStreet',
+            'homeCity',
+            'homeProvince',
+            'homePostalCode',
+            'homeCountry',
+            /* Communications */
+            'emails',
+            'homePhone',
+            'workPhone',
+            'cellPhone',
+            'fax',
+            'instantMessenger',
+            /* Organization */
+            'title',
+            'role',
+            'company',
+            'department',
+            'office',
+            'manager',
+            'assistant',
+            /* Other */
+            'category',
+            'notes',
+            'website',
+            'language',
+        ),
+        'strict' => array(
+            'uid',
+        ),
+        'export' => true,
+        'browse' => true,
+        'use_shares' => true,
+        'shares_only' => true,
+    );
+}
+/* End Kolab sources. */
diff --git a/turba/config/hooks.php.dist b/turba/config/hooks.php.dist
index f304b7456..d92457aa0 100644
--- a/turba/config/hooks.php.dist
+++ b/turba/config/hooks.php.dist
@@ -29,7 +29,7 @@ class Turba_Hooks
 //             return;
 //         }
 //
-//         require TURBA_BASE . '/config/sources.php';
+//         require TURBA_BASE . '/config/backends.php';
 //         $shares = Turba::listShares(true);
 //
 //         foreach ($shares as $uid => $share) {
diff --git a/turba/config/prefs.php.dist b/turba/config/prefs.php.dist
index 034560c30..aa0e11131 100644
--- a/turba/config/prefs.php.dist
+++ b/turba/config/prefs.php.dist
@@ -40,7 +40,7 @@ $_prefs['addressbookselect'] = array(
 
 // Address books to be displayed in the address book selection widget
 // and in the Browse menu item.  The address book name is stored using
-// the source key from sources.php (e.g. "localsql").
+// the source key from backends.php (e.g. "localsql").
 // You can provide default values this way:
 //   'value' => json_encode(array('source_one', 'source_two'))
 // If 'value' is empty (''), all address books that the user has permissions
diff --git a/turba/config/sources.php.dist b/turba/config/sources.php.dist
deleted file mode 100644
index 883c9bc7f..000000000
--- a/turba/config/sources.php.dist
+++ /dev/null
@@ -1,1104 +0,0 @@
- 'deleted=0'.
- *                  Don't enclose filter in brackets - this will done
- *                  automatically. Also keep in mind that a full filter line
- *                  will be built from 'filter' and 'objectclass' parameters.
- *
- *   version:       Only applies to LDAP servers. If set, specify LDAP server
- *                  version, can be 2 or 3. Active Directory servers
- *                  require version 3.
- *
- * map:         This is a list of mappings from the Turba attribute names (on
- *              the left) to the attribute names by which they are known in
- *              this contact source (on the right). Turba also supports
- *              composite fields. A composite field is defined by mapping the
- *              field name to an array containing a list of component fields
- *              and a format string (similar to a printf() format string;
- *              however, note that positioned parameters like %1$s will NOT
- *              work). 'attribute' defines where the composed value is saved,
- *              and can be left out. 'parse' defines a list of format strings
- *              and field names that should be used for splitting up composite
- *              fields, in the order of precedence, and can be left out. Here
- *              is an example:
- *              ...
- *              'name' => array('fields' => array('firstname', 'lastname'),
- *                              'format' => '%s %s',
- *                              'attribute' => 'object_name'),
- *              'firstname' => 'object_firstname',
- *              'lastname' => 'object_lastname',
- *              ...
- *
- *              Standard Turba attributes are:
- *                __key     : A backend-specific ID for the entry (any value
- *                            as long as it is unique inside that source;
- *                            required)
- *                __uid     : Globally unique ID of the entry (used for
- *                            synchronizing and must be able to be set to any
- *                            value)
- *                __owner   : User name of the contact's owner
- *                __type    : Either 'Object' or 'Group'
- *                __members : Serialized PHP array with list of Group members.
- *              More Turba attributes are defined in config/attributes.php.
- *
- * tabs:        All fields can be grouped into tabs with this optional entry.
- *              This list is multidimensional hash, the keys are the tab
- *              titles.
- *              Here is an example:
- *              'tabs' => array(
- *                  'Names' => array('firstname', 'lastname', 'alias'),
- *                  'Addresses' => array('homeAddress', 'workAddress')
- *              );
- *
- * search:      A list of Turba attribute names that can be searched for this
- *              source.
- *
- * strict:      A list of native field/attribute names that must
- *              always be matched exactly in a search.
- *
- * approximate: Only applies to LDAP servers. If set, should be an
- *              array of native field/attribute names to search
- *              "approximately" (for example, "Sánchez", "Sanchez",
- *              and "Sanchéz" will all match a search string of
- *              "sanchez").
- *
- * export:      If set to true, this source will appear on the Export menu,
- *              allowing users to export the contacts to a CSV (etc.) file.
- *
- * browse:      If set to true, this source will be browseable via the Browse
- *              menu item, and empty searches against the source will return
- *              all contacts.
- *
- * use_shares:  If this is present and true, Horde_Share functionality will
- *              be enabled for this source - allowing users to share their
- *              personal address books as well as to create new ones. Since
- *              Turba only supports having one backend configured for
- *              creating new shares, use the 'shares' configuration option to
- *              specify which backend will be used for creating new shares.
- *              All permission checking will be done against Horde_Share, but
- *              note that any 'extended' permissions (such as max_contacts)
- *              will still be enforced. Also note that the backend driver
- *              must have support for using this. Currently SQL and IMSP.
- *
- * list_name_field:  If this is present and non-empty, it will be taken as the
- *                   field to store contact list names in.
- *                   This is required when using a composite field as the 'name'
- *                   field.
- *
- * alternative_name: If this is present and non-empty, it will be taken as the
- *                   field to use an alternative in case that the name field is
- *                   empty.
- *
- * Here are some example configurations:
- *
- * $Id$
- */
-
-/**
- * A local address book in an SQL database. This implements a private
- * per-user address book. Sharing of this source with other users may be
- * accomplished by enabling Horde_Share for this source by setting
- * 'use_shares' => true.
- *
- * Be sure to create a turba_objects table in your Horde database from the
- * schema in turba/scripts/db/turba.sql if you use this source.
- */
-$cfgSources['localsql'] = array(
-    'title' => _("My Address Book"),
-    'type' => 'sql',
-    // The default connection details are pulled from the Horde-wide SQL
-    // connection configuration.
-    'params' => array_merge($GLOBALS['conf']['sql'], array('table' => 'turba_objects')),
-    // Using two tables as datasource.
-    // 'params' => array_merge($GLOBALS['conf']['sql'],
-    //                         array('table' => 'leaddetails LEFT JOIN leadaddress ON leaddetails.leadid = leadaddress.leadaddressid',
-    //                               'filter' => 'leaddetails.converted = 0')),
-    'map' => array(
-        '__key' => 'object_id',
-        '__owner' => 'owner_id',
-        '__type' => 'object_type',
-        '__members' => 'object_members',
-        '__uid' => 'object_uid',
-        'firstname' => 'object_firstname',
-        'lastname' => 'object_lastname',
-        'middlenames' => 'object_middlenames',
-        'namePrefix' => 'object_nameprefix',
-        'nameSuffix' => 'object_namesuffix',
-        'name' => array('fields' => array('namePrefix', 'firstname',
-                                          'middlenames', 'lastname',
-                                          'nameSuffix'),
-                        'format' => '%s %s %s %s %s',
-                        'parse' => array(
-                            array('fields' => array('firstname', 'middlenames',
-                                                    'lastname'),
-                                  'format' => '%s %s %s'),
-                            array('fields' => array('firstname', 'lastname'),
-                                  'format' => '%s %s'))),
-        // This is a shorter version of a "name" composite field which only
-        // consists of the first name and last name.
-        // 'name' => array('fields' => array('firstname', 'lastname'),
-        //                 'format' => '%s %s'),
-        'alias' => 'object_alias',
-        'birthday' => 'object_bday',
-        // The photo field requires at least Horde 3.3 and a matching type
-        // field.
-        // 'photo' => 'object_photo',
-        // 'phototype' => 'object_phototype',
-        'homeStreet' => 'object_homestreet',
-        'homePOBox' => 'object_homepob',
-        'homeCity' => 'object_homecity',
-        'homeProvince' => 'object_homeprovince',
-        'homePostalCode' => 'object_homepostalcode',
-        'homeCountry' => 'object_homecountry',
-        // This is an example composite field for addresses, so you can display
-        // the various map links. If you use this, be sure to add 'homeAddress'
-        // to the 'tabs' parameter below.
-        // 'homeAddress' => array('fields' => array('homeStreet', 'homeCity',
-        //                                          'homeProvince',
-        //                                          'homePostalCode'),
-        //                        'format' => "%s \n %s, %s  %s"),
-        'workStreet' => 'object_workstreet',
-        'workPOBox' => 'object_workpob',
-        'workCity' => 'object_workcity',
-        'workProvince' => 'object_workprovince',
-        'workPostalCode' => 'object_workpostalcode',
-        'workCountry' => 'object_workcountry',
-        'timezone' => 'object_tz',
-        'email' => 'object_email',
-        'homePhone' => 'object_homephone',
-        'workPhone' => 'object_workphone',
-        'cellPhone' => 'object_cellphone',
-        'fax' => 'object_fax',
-        'pager' => 'object_pager',
-        'title' => 'object_title',
-        'role' => 'object_role',
-        'company' => 'object_company',
-        // The logo field requires at least Horde 3.3 and a matching type
-        // field.
-        // 'logo' => 'object_logo',
-        // 'logotype' => 'object_logotype',
-        'category' => 'object_category',
-        'notes' => 'object_notes',
-        'website' => 'object_url',
-        'freebusyUrl' => 'object_freebusyurl',
-        'pgpPublicKey' => 'object_pgppublickey',
-        'smimePublicKey' => 'object_smimepublickey',
-    ),
-    'tabs' => array(
-        _("Personal") => array('firstname', 'lastname', 'middlenames',
-                               'namePrefix', 'nameSuffix', 'name', 'alias',
-                               'birthday', 'photo'),
-        _("Location") => array('homeStreet', 'homePOBox', 'homeCity',
-                               'homeProvince', 'homePostalCode', 'homeCountry',
-                               'workStreet', 'workPOBox', 'workCity',
-                               'workProvince', 'workPostalCode', 'workCountry',
-                               'timezone'),
-        _("Communications") => array('email', 'homePhone', 'workPhone',
-                                     'cellPhone', 'fax', 'pager'),
-        _("Organization") => array('title', 'role', 'company', 'logo'),
-        _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
-                            'pgpPublicKey', 'smimePublicKey'),
-    ),
-    'search' => array(
-        'name',
-        'email'
-    ),
-    'strict' => array(
-        'object_id',
-        'owner_id',
-        'object_type',
-    ),
-    'export' => true,
-    'browse' => true,
-    'use_shares' => true,
-    'list_name_field' => 'lastname',
-    'alternative_name' => 'company',
-);
-
-/**
- * A local address book in an LDAP directory. This implements a public
- * (shared) address book.
- *
- * To store distribution lists in the LDAP directory, you'll need to include
- * horde/scripts/ldap/horde.schema in your LDAP configuration.
- *
- * To store freebusy information in the LDAP directory, you'll need to include
- * turba/scripts/ldap/rfc2739.schema in your LDAP configuration.
- */
-// $cfgSources['localldap'] = array(
-//     'title' => _("Shared Directory"),
-//     'type' => 'ldap',
-//     'params' => array(
-//         'server' => 'ldap.example.com',
-//         'port' => 389,
-//         'tls' => false,
-//         'root' => 'dc=example,dc=com',
-//         'bind_dn' => 'cn=admin,ou=users,dc=example,dc=com',
-//         // For Active Directory:
-//         // 'bind_dn' => 'username@example.com',
-//         'bind_password' => '********',
-//         'sizelimit' => 200,
-//         // For Active Directory:
-//         // 'sizelimit' => 0,
-//         'dn' => array('cn'),
-//         'objectclass' => array('top',
-//                                'person',
-//                                'organizationalPerson',
-//                                'inetOrgPerson'),
-//                                // Add 'turbaContact' to this array if using
-//                                // 'turbaType' attribute below, and 'calEntry'
-//                                // if using 'freebusyUrl'.
-//         // For Active Directory:
-//         // 'objectclass' => array('organizationalPerson',
-//         //                        'user',
-//         //                        'group',
-//         //                        'contact'),
-//         'scope' => 'one',
-//         // For Active Directory:
-//         // 'scope' => 'sub',
-//         'charset' => 'utf-8',
-//         // Consult the LDAP schema to verify that all required attributes for
-//         // an entry are set and add them if needed.
-//         'checkrequired' => false,
-//         // Value used to fill in missing required attributes.
-//         'checkrequired_string' => ' ',
-//         // Check LDAP schema for valid syntax. If this is false an address
-//         // field is assumed to have postalAddress syntax; otherwise the schema
-//         // is consulted for the syntax to use.
-//         'checksyntax' => false,
-//         'version' => 3,
-//
-//         // For Active Directory you probably want to also set the following
-//         // parameters:
-//         // 'deref' => LDAP_DEREF_ALWAYS,
-//         // 'filter' => '&(SAMAccountName=*)(mail=*)',
-//         // 'referrals' => 0,
-//     ),
-//     'map' => array(
-//         '__key' => 'dn',
-//
-//         // Remove this mapping if using Active Directory server:
-//         '__uid' => 'uid',
-//
-//         // From horde.schema.  Make sure you have 'turbaContact' objectClass
-//         // included above:
-//         // '__type' => 'turbaType',
-//         // '__members' => 'turbaMembers',
-//
-//         'name' => 'cn',
-//         'email' => 'mail',
-//         'homePhone' => 'homephone',
-//         'workPhone' => 'telephonenumber',
-//         'cellPhone' => 'mobiletelephonenumber',
-//         'homeAddress' => 'homepostaladdress',
-//
-//         // From rfc2739.schema:
-//         // 'freebusyUrl' => 'calFBURL',
-//
-//         // For Active Directory servers:
-//         // 'name' => 'displayname',
-//         // 'title' => 'title',
-//         // 'cellPhone' => 'mobile',
-//         // 'department' => 'department',
-//         // 'company' => 'company',
-//     ),
-//     'search' => array(
-//         'name',
-//         'email',
-//         'homePhone',
-//         'workPhone',
-//         'cellPhone',
-//         'homeAddress'
-//     ),
-//     'strict' => array(
-//         'dn',
-//     ),
-//     'approximate' => array(
-//         'cn',
-//     ),
-//     // For Active Directory servers:
-//     // 'approximate' => array(
-//     //     'displayname',
-//     //     'samaccountname',
-//     // ),
-//     'export' => true,
-//     'browse' => true,
-// );
-
-/**
- * A personal LDAP address book. This assumes that the login is
- * @domain.com and that the users are stored on the same LDAP
- * server. Thus it is possible to bind with the username and password from the
- * user. For more info; please refer to the docs/LDAP file in the Turba
- * distribution.
- *
- * To store distribution lists in the LDAP directory, you'll need to include
- * horde/scripts/ldap/horde.schema in your LDAP configuration.
- *
- * To store freebusy information in the LDAP directory, you'll need to include
- * turba/scripts/ldap/rfc2739.schema in your LDAP configuration.
- */
-/* First we need to get the uid. */
-// $_ldap_uid = $GLOBALS['registry']->getAuth('bare');
-// $_ldap_basedn = 'dc=example,dc=com';
-// $cfgSources['personal_ldap'] = array(
-//     'title' => _("My Address Book"),
-//     'type' => 'ldap',
-//     'params' => array(
-//         'server' => 'localhost',
-//         'tls' => false,
-//         'root' => 'ou=' . $_ldap_uid . ',ou=personal_addressbook,' . $_ldap_basedn,
-//         'bind_dn' => 'uid=' . $_ldap_uid . ',ou=People,' . $_ldap_basedn,
-//         'bind_password' => $GLOBALS['registry']->getAuthCredential('password'),
-//         'dn' => array('uid'),
-//         'objectclass' => array('top',
-//                                'person',
-//                                // 'turbaContact',
-//                                'inetOrgPerson',
-//                                // 'calEntry',
-//                                'organizationalPerson'),
-//         'scope' => 'one',
-//         'charset' => 'utf-8',
-//         'version' => 3
-//     ),
-//     'map' => array(
-//         '__key' => 'dn',
-//         '__uid' => 'uid',
-//
-//         // From horde.schema:
-//         // '__type' => 'turbaType',
-//         // '__members' => 'turbaMembers',
-//
-//         'name' => 'cn',
-//         'email' => 'mail',
-//         'lastname' => 'sn',
-//         'title' => 'title',
-//         'company' => 'organizationname',
-//         'businessCategory' => 'businesscategory',
-//         'workAddress' => 'postaladdress',
-//         'workPostalCode' => 'postalcode',
-//         'workPhone' => 'telephonenumber',
-//         'fax' => 'facsimiletelephonenumber',
-//         'homeAddress' => 'homepostaladdress',
-//         'homePhone' => 'homephone',
-//         'cellPhone' => 'mobile',
-//         'notes' => 'description',
-//
-//         // Evolution interopt attributes: (those that do not require the
-//         // evolution.schema)
-//         'office' => 'roomNumber',
-//         'department' => 'ou',
-//         'nickname' => 'displayName',
-//         'website' => 'labeledURI',
-//
-//         // These are not stored on the LDAP server.
-//         'pgpPublicKey' => 'object_pgppublickey',
-//         'smimePublicKey' => 'object_smimepublickey',
-//
-//         // From rfc2739.schema:
-//         // 'freebusyUrl' => 'calFBURL',
-//     ),
-//     'search' => array(
-//         'name',
-//         'email',
-//         'businessCategory',
-//         'title',
-//         'homePhone',
-//         'workPhone',
-//         'cellPhone',
-//         'homeAddress'
-//     ),
-//     'strict' => array(
-//         'dn',
-//     ),
-//     'approximate' => array(
-//         'cn',
-//     ),
-//     'export' => true,
-//     'browse' => true,
-// );
-
-/**
- * Public netcenter and verisign LDAP directories.
- */
-// $cfgSources['netcenter'] = array(
-//     'title' => _("Netcenter Member Directory"),
-//     'type' => 'ldap',
-//     'params' => array(
-//         'server' => 'memberdir.netscape.com',
-//         'port' => 389,
-//         'tls' => false,
-//         'root' => 'ou=member_directory,o=netcenter.com',
-//         'dn' => array('cn'),
-//         'objectclass' => 'person',
-//         'filter' => '',
-//         'scope' => 'sub',
-//         'charset' => 'iso-8859-1'
-//     ),
-//     'map' => array(
-//         '__key' => 'dn',
-//         'name' => 'cn',
-//         'email' => 'mail',
-//         'alias' => 'givenname'
-//     ),
-//     'search' => array(
-//         'name',
-//         'email',
-//         'alias'
-//     ),
-//     'strict' => array(
-//         'dn'
-//     ),
-//     'approximate' => array(
-//         'cn',
-//     ),
-//     'export' => false,
-//     'browse' => false,
-// );
-//
-// $cfgSources['verisign'] = array(
-//     'title' => _("Verisign Directory"),
-//     'type' => 'ldap',
-//     'params' => array(
-//         'server' => 'directory.verisign.com',
-//         'port' => 389,
-//         'tls' => false,
-//         'root' => '',
-//         'scope' => 'sub',
-//         'charset' => 'iso-8859-1'
-//     ),
-//     'map' => array(
-//         '__key' => 'dn',
-//         'name' => 'cn',
-//         'email' => 'mail'
-//     ),
-//     'search' => array(
-//         'name',
-//         'email'
-//     ),
-//     'strict' => array(
-//         'dn'
-//     ),
-//     'approximate' => array(
-//         'cn',
-//     ),
-//     'export' => false,
-//     'browse' => false,
-// );
-
-/**
- * A preferences-based address book. This will always be private. You can add
- * any attributes you like to the map and it will just work; you can also
- * create multiple prefs-based address books by changing the 'name' parameter.
- * This is best for address books that are expected to remain small; it's not
- * the most efficient, but it can't be beat for getting up and running
- * quickly, especially if you already have Horde preferences working. Note
- * that it is not searchable, though - searches will simply return the whole
- * address book.
- */
-// $cfgSources['prefs'] = array(
-//     'title' => _("Private Address Book"),
-//     'type' => 'prefs',
-//     'params' => array(
-//         'name' => 'prefs',
-//         'charset' => $GLOBALS['registry']->getCharset()
-//     ),
-//     'map' => array(
-//         '__key' => 'id',
-//         '__type' => '_type',
-//         '__members' => '_members',
-//         '__uid' => 'uid',
-//         'name' => 'name',
-//         'email' => 'mail',
-//         'alias' => 'alias'
-//     ),
-//     'search' => array(
-//         'name',
-//         'email',
-//         'alias'
-//     ),
-//     'strict' => array(
-//         'id',
-//         '_type',
-//     ),
-//     'export' => true,
-//     'browse' => true,
-// );
-
-/**
- * An address book based on message recipients. This will always be private and
- * read-only. The address book content is provided by the
- * contacts/favouriteRecipients API method which should be implemented by a
- * mail client that collects the most regular message recipients, like IMP
- * 4.2.
- */
-$cfgSources['favourites'] = array(
-    'title' => _("Favourite Recipients"),
-    'type' => 'favourites',
-    'params' => array(
-        'limit' => 10
-    ),
-    'map' => array(
-        '__key' => 'email',
-        'name' => 'email',
-        'email' => 'email'
-    ),
-    'search' => array(
-        'name',
-        'email'
-    ),
-    'strict' => array(
-        'id',
-    ),
-    'export' => true,
-    'browse' => true,
-);
-
-/**
- * A driver to show a user's Facebook friends as a turba address book. Some
- * data (like email) is not readily available via the API, but other data, like
- * birthdays (which will show up via the listTimeObjects API) may still be
- * useful to some
- */
-//$cfgSources['facebook'] = array(
-//    'title' => _("Facebook Friends"),
-//    'type' => 'facebook',
-//    'params' => array(
-//        'limit' => 10
-//    ),
-//   'map' => array(
-//        '__key' => 'uid',
-//        'name' => 'name',
-//        'lastname' => 'last_name',
-//        'firstname' => 'first_name',
-//        'email' => 'proxied_email',
-//        'birthday' => 'birthday',
-//        'homeCity' => 'current_location.city',
-//        'homeProvince' => 'current_location.state',
-//        'homePostalCode' => 'current_location.zip',
-//        'homeCountry' => 'current_location.country',
-//   ),
-//    'search' => array(
-//        'name',
-//        'email',
-//    ),
-//    'export' => true,
-//    'browse' => true,
-//);
-
-/**
- * This source creates an address book for each group the current user is a
- * member in.  The address book will always be read only, and the group members
- * must have an email address entered in their default identity.  No email
- * address will result in that member not being included in the address book.
- */
-//$grpSource = array(
-//    'title' => _("Group"),
-//    'type' => 'group',
-//    'params' => array(
-//       'name' => ''
-//    ),
-//    'map' => array(
-//        '__key' => 'email',
-//        'name' => 'name',
-//        'email' => 'email'
-//    ),
-//    'search' => array(
-//        'name',
-//        'email'
-//    ),
-//    'export' => true,
-//    'browse' => true,
-//);
-
-//$_group_driver = &Group::singleton();
-//$_group_list = $_group_driver->getGroupMemberships($GLOBALS['registry']->getAuth());
-//foreach ($_group_list as $_group_id => $_group_name) {
-//    $cfgSources['group_' . $_group_id] = $grpSource;
-//    $cfgSources['group_' . $_group_id]['params'] = array('gid' => $_group_id);
-//    $cfgSources['group_' . $_group_id]['title'] = $_group_name;
-//}
-
-/**
- * IMSP based address book.
- *
- * Communicates with an IMSP backend server.
- *
- * Notes:
- * You should configure the user's "main" address book here. The name of the
- * address book is set in the 'name' element of the params array. It should
- * be configured to be the same as the IMSP server username. Any other
- * address books the user has access to will automatically be configured at
- * runtime.
- *
- * In the params array, accepted values for auth_method are 'cram_md5',
- * 'imtest', and 'plaintext' - these must match a IMSP_Auth_ driver. If you
- * are using the imtest driver for Cyrus, please read the
- * framework/Net_IMSP/Auth/imtest.php file for more configuration information.
- *
- * Groups in other IMSP-aware applications are just entries with multiple
- * email addresses in the email field and a 'group' field set to flag the
- * entry as a group. (The Cyrusoft applications, Mulberry and Silkymail both
- * use a field named 'group' set to equal 'group' to signify this). A
- * Turba_Object_Group is basically a List of existing Turba_Objects. The IMSP
- * driver will map between these two structures when reading and writing
- * groups.
- * The only caveat is that IMSP groups that contain email addresses which do
- * not have a cooresponding contact entry will be ignored. The group_id_field
- * should be set to the IMSP field that flags the entry as a 'group' entry and
- * the group_id_value should be set to the value given to that field.
- *
- * By default, the username and password that were used to login to Horde is
- * used to login to the IMSP server. If these credentials are different,
- * there is a user preference in Horde to allow another username / password to
- * be entered. The alternate credentials are always used if present.
- *
- * In the map array, since IMSP uses the 'name' attribute as a key, this is
- * what __key is mapped to ... and a dynamic field 'fullname' is added and
- * mapped to the horde 'name' field. This is populated with the IMSP 'name'
- * field when the object is read from the server.
- *
- * If you wish to keep track of ownership of individual contacts, set
- * 'contact_ownership' = true. Note that entries created using other clients
- * will not be created this way and will therefore have no 'owner'. Set
- * 'contact_ownership' = false and the __owner field will be automatically
- * populated with the current username.
- *
- * To integrate with Horde_Share, set use_shares to true and uncomment the
- * IMSP Horde_Share hooks in horde/config/hooks.php.
- */
-// Check that IMSP is configured in Horde but fall through if there is no
-// configuration at all.
-if (!empty($GLOBALS['conf']['imsp']['enabled']) ||
-    !isset($GLOBALS['conf']['imsp']['enabled'])) {
-    // First, get the user name to login to IMSP server with.
-    $_imsp_auth_user = $GLOBALS['prefs']->getValue('imsp_auth_user');
-    $_imsp_auth_pass = $GLOBALS['prefs']->getValue('imsp_auth_pass');
-    if (!strlen($_imsp_auth_user)) {
-        $_imsp_auth_user = $GLOBALS['registry']->getAuth('bare');
-        $_imsp_auth_pass = $GLOBALS['registry']->getAuthCredential('password');
-    }
-    $cfgSources['imsp'] = array(
-        'title' => _("IMSP"),
-        'type' => 'imsp',
-        'params' => array(
-            'server'  => $GLOBALS['conf']['imsp']['server'],
-            'port'    => $GLOBALS['conf']['imsp']['port'],
-            'auth_method' => $GLOBALS['conf']['imsp']['auth_method'],
-            // socket, command, and auth_mechanism are for imtest driver.
-            'socket'  => isset($GLOBALS['conf']['imsp']['socket']) ?
-                         $GLOBALS['conf']['imsp']['socket'] . $_imsp_auth_user . '.sck' :
-                         '',
-            'command' => isset($GLOBALS['conf']['imsp']['command']) ?
-                         $GLOBALS['conf']['imsp']['command'] : '' ,
-            'auth_mechanism' => isset($GLOBALS['conf']['imsp']['auth_mechanism']) ?
-                                $GLOBALS['conf']['imsp']['auth_mechanism'] : '',
-            'username' => $_imsp_auth_user,
-            'password' => $_imsp_auth_pass,
-            'name' => $_imsp_auth_user,
-            'group_id_field' => 'group',
-            'group_id_value' => 'group',
-            'contact_ownership' => false,
-            // Dynamically generated acl rights for current user.
-            'my_rights' => '',
-            ),
-        'map' => array(
-            '__key' => 'name',
-            '__type' => '__type',
-            '__members' => '__members',
-            '__owner' => '__owner',
-            '__uid' => '__uid',
-            'name' => 'fullname',
-            'email' => 'email',
-            'alias' => 'alias',
-            'company' => 'company',
-            'notes' => 'notes',
-            'workPhone' => 'phone-work',
-            'fax' => 'fax',
-            'homePhone' => 'phone-home',
-            'cellPhone' => 'cellphone',
-            'freebusyUrl' => 'freebusyUrl'
-            ),
-        'search' => array(
-            'name',
-            'email',
-            'alias',
-            'company',
-            'homePhone'
-            ),
-        'strict' => array(),
-        'export' => true,
-        'browse' => true,
-        'use_shares' => false,
-        );
-
-    /**
-     * Get any other address books this user might be privy to.
-     * The values for attributes such as 'export' and 'browse' for books
-     * that are added below will be the same as the values set in the default
-     * book above. Any entries defined explicitly in cfgSources[]
-     * will override any entries gathered dynamically below.
-     */
-     if (empty($cfgSources['imsp']['use_shares'])) {
-        $result = Net_IMSP_Utils::getAllBooks($cfgSources['imsp']);
-
-        if (!$result instanceof PEAR_Error) {
-            $resultCount = count($result);
-            for ($i = 0; $i < $resultCount; ++$i) {
-                // Make sure we didn't define this source explicitly,
-                // but set the acls from the server regardless.
-                $dup = false;
-                foreach ($cfgSources as $key => $thisSource) {
-                    if (($thisSource['type'] == 'imsp') &&
-                        ($thisSource['params']['name'] == $result[$i]['params']['name'])) {
-
-                        $dup = true;
-                        $acl = $result[$i]['params']['my_rights'];
-                        $cfgSources[$key]['params']['my_rights'] = $acl;
-                        break;
-                    }
-                }
-                if (!$dup) {
-                    $cfgSources[$result[$i]['params']['name']] = $result[$i];
-                }
-            }
-        } else {
-            $notification->push($result);
-        }
-     }
-}
-/* End IMSP sources. */
-
-/* Begin Kolab sources. */
-if (!empty($GLOBALS['conf']['kolab']['enabled'])) {
-
-    /* Only use LDAP if we have that extension in PHP */
-    if (function_exists('ldap_connect')) {
-        require_once 'Horde/Kolab.php';
-
-        if (!is_callable('Kolab', 'getServer')) {
-            $_kolab_server = $GLOBALS['conf']['kolab']['ldap']['server'];
-        } else {
-            $_kolab_server = Kolab::getServer('ldap');
-        }
-
-        /* A global address book for a Kolab Server. This is typically a
-         * read-only public directory, stored in the default Kolab LDAP server.
-         * The user accessing this should have read permissions to the shared
-         * directory in LDAP. */
-        $cfgSources['kolab_global'] = array(
-            'title' => _("Global Address Book"),
-            'type' => 'ldap',
-            'params' => array(
-                'server' => $_kolab_server,
-                'port' => $GLOBALS['conf']['kolab']['ldap']['port'],
-                'tls' => false,
-                'root' => $GLOBALS['conf']['kolab']['ldap']['basedn'],
-                'sizelimit' => 200,
-                'dn' => array('cn'),
-                'objectclass' => array(
-                    'inetOrgPerson'
-                ),
-                'scope' => 'sub',
-                'charset' => 'utf-8',
-                'version' => 3,
-                'bind_dn' => '',
-                'bind_password' => '',
-            ),
-            'map' => array(
-                '__key'             => 'dn',
-                'name'              => 'cn',
-                'firstname'         => 'givenName',
-                'lastname'          => 'sn',
-                'email'             => 'mail',
-                'alias'             => 'alias',
-                'title'             => 'title',
-                'company'           => 'o',
-                'workStreet'        => 'street',
-                'workCity'          => 'l',
-                'workProvince'      => 'st',
-                'workPostalCode'    => 'postalCode',
-                'workCountry'       => 'c',
-                'homePhone'         => 'homePhone',
-                'workPhone'         => 'telephoneNumber',
-                'cellPhone'         => 'mobile',
-                'fax'               => 'fax',
-                'notes'             => 'description',
-                'kolabHomeServer'   => 'kolabHomeServer',
-                'freebusyUrl'       => array('fields' => array('kolabHomeServer', 'email'),
-                                             'format' => 'https://%s/freebusy/%s.ifb'),
-            ),
-            'search' => array(
-                'name',
-                'firstname',
-                'lastname',
-                'email',
-                'title',
-                'company',
-                'workAddress',
-                'workCity',
-                'workProvince',
-                'workPostalCode',
-                'workCountry',
-                'homePhone',
-                'workPhone',
-                'cellPhone',
-                'fax',
-                'notes',
-            ),
-            'strict' => array(
-                'dn',
-            ),
-            'export' => true,
-            'browse' => true,
-        );
-    }
-
-    /**
-     * The local address books for a Kolab user. These are stored in specially
-     * flagged contact folder within the users Cyrus IMAP mailbox.
-     *
-     * Still missing attributes are:
-     *
-     *   picture, sensitivity
-     */
-
-    $cfgSources['kolab'] = array(
-        'title' => _("Contacts"),
-        'type' => 'kolab',
-        'params' => array(
-            'charset' => 'utf-8',
-        ),
-        'list_name_field' => 'lastname',
-        'map' => array(
-            '__key' => 'uid',
-            '__uid' => 'uid',
-            '__type' => '__type',
-            '__members' => '__members',
-            /* Personal */
-            'name' => array('fields' => array('firstname', 'middlenames', 'lastname'),
-                            'format' => '%s %s %s',
-                            'parse' => array(
-                                array('fields' => array('firstname', 'middlenames',
-                                                        'lastname'),
-                                      'format' => '%s %s %s'),
-                                array('fields' => array( 'lastname', 'firstname'),
-                                      'format' => '%s, %s'),
-                                array('fields' => array('firstname', 'lastname'),
-                                      'format' => '%s %s'),
-                            )),
-            'firstname'         => 'given-name',
-            'lastname'          => 'last-name',
-            'middlenames'       => 'middle-names',
-            'namePrefix'        => 'prefix',
-            'nameSuffix'        => 'suffix',
-            'initials'          => 'initials',
-            'nickname'          => 'nick-name',
-            'photo'             => 'photo',
-            'phototype'         => 'phototype',
-            'gender'            => 'gender',
-            'birthday'          => 'birthday',
-            'spouse'            => 'spouse-name',
-            'anniversary'       => 'anniversary',
-            'children'          => 'children',
-            /* Location */
-            'workStreet'        => 'addr-business-street',
-            'workCity'          => 'addr-business-locality',
-            'workProvince'      => 'addr-business-region',
-            'workPostalCode'    => 'addr-business-postal-code',
-            'workCountry'       => 'addr-business-country',
-            'homeStreet'        => 'addr-home-street',
-            'homeCity'          => 'addr-home-locality',
-            'homeProvince'      => 'addr-home-region',
-            'homePostalCode'    => 'addr-home-postal-code',
-            'homeCountry'       => 'addr-home-country',
-            /* Communications */
-            'emails'            => 'emails',
-            'homePhone'         => 'phone-home1',
-            'workPhone'         => 'phone-business1',
-            'cellPhone'         => 'phone-mobile',
-            'fax'               => 'phone-businessfax',
-            'instantMessenger'  => 'im-address',
-            /* Organization */
-            'title'             => 'job-title',
-            'role'              => 'profession',
-            'company'           => 'organization',
-            'department'        => 'department',
-            'office'            => 'office-location',
-            'manager'           => 'manager-name',
-            'assistant'         => 'assistant',
-            /* Other */
-            'category'          => 'categories',
-            'notes'             => 'body',
-            'website'           => 'web-page',
-            'freebusyUrl'       => 'free-busy-url',
-            'language'          => 'language',
-            'latitude'          => 'latitude',
-            'longitude'         => 'longitude',
-            /* Invisible */
-            'email'             => 'email',
-            'pgpPublicKey'      => 'pgp-publickey',
-        ),
-        'tabs' => array(
-            _("Personal") => array('name', 'firstname', 'lastname', 'middlenames',
-                                   'namePrefix', 'nameSuffix', 'initials', 'nickname',
-                                   'photo', 'gender', 'birthday', 'spouse', 'anniversary',
-                                   'children'),
-            _("Location") => array('homeStreet', 'homeCity', 'homeProvince',
-                                   'homePostalCode', 'homeCountry', 'workStreet',
-                                   'workCity', 'workProvince', 'workPostalCode',
-                                   'workCountry'),
-            _("Communications") => array('emails', 'homePhone', 'workPhone',
-                                         'cellPhone', 'fax', 'instantMessenger'),
-            _("Organization") => array('title', 'role', 'company', 'department',
-                                       'office', 'manager', 'assistant'),
-            _("Other") => array('category', 'notes', 'website', 'freebusyUrl',
-                                'language', 'latitude', 'longitude'),
-        ),
-        'search' => array(
-            /* Personal */
-            'name',
-            'firstname',
-            'lastname',
-            'middlenames',
-            'namePrefix',
-            'nameSuffix',
-            'initials',
-            'nickname',
-            'gender',
-            'birthday',
-            'spouse',
-            'anniversary',
-            'children',
-            /* Location */
-            'workStreet',
-            'workCity',
-            'workProvince',
-            'workPostalCode',
-            'workCountry',
-            'homeStreet',
-            'homeCity',
-            'homeProvince',
-            'homePostalCode',
-            'homeCountry',
-            /* Communications */
-            'emails',
-            'homePhone',
-            'workPhone',
-            'cellPhone',
-            'fax',
-            'instantMessenger',
-            /* Organization */
-            'title',
-            'role',
-            'company',
-            'department',
-            'office',
-            'manager',
-            'assistant',
-            /* Other */
-            'category',
-            'notes',
-            'website',
-            'language',
-        ),
-        'strict' => array(
-            'uid',
-        ),
-        'export' => true,
-        'browse' => true,
-        'use_shares' => true,
-        'shares_only' => true,
-    );
-}
-/* End Kolab sources. */
diff --git a/turba/docs/CHANGES b/turba/docs/CHANGES
index 365ca2cc6..7606ca104 100644
--- a/turba/docs/CHANGES
+++ b/turba/docs/CHANGES
@@ -2,6 +2,7 @@
 v3.0-git
 --------
 
+[jan] Rename sources.php to backends.php.
 [mjr] If default addressbook is not browsable, try one from the addressbook pref
       (Request: 6536).
 [jan] Add duplicate search and merging.
diff --git a/turba/docs/INSTALL b/turba/docs/INSTALL
index da4927308..05109db55 100644
--- a/turba/docs/INSTALL
+++ b/turba/docs/INSTALL
@@ -150,10 +150,10 @@ Configuring Turba
 
    Documentation on the format and purpose of those files can be found in each
    file.  You may edit these files if you wish to customize Turba's appearance
-   and behaviour.  With the exception of the ``sources.php`` file the defaults
+   and behaviour.  With the exception of the ``backends.php`` file the defaults
    will be correct for most sites.
 
-   You must configure ``sources.php`` to list your data sources (both SQL and
+   You must configure ``backends.php`` to list your data sources (both SQL and
    LDAP if necessary).  This configuration file contains a large number of
    **examples**.  Please remove or comment out those examples that **you don't
    need**.
diff --git a/turba/docs/LDAP b/turba/docs/LDAP
index 928727693..5eeaf1160 100644
--- a/turba/docs/LDAP
+++ b/turba/docs/LDAP
@@ -10,14 +10,14 @@ stored on the same LDAP server.
 This is not comprehensive, but it notes how some users have implemented
 personal LDAP address books.
 
-1. Configure ``turba/config/sources.php``.
+1. Configure ``turba/config/backends.php``.
 
    Note that OpenLDAP and some other LDAP servers use UTF8 to encode its data,
    so you should have ``encoding = 'utf8'`` set in the parameters for your
    LDAP address book source.
 
    There is an example of personal address books in
-   ``turba/config/sources.php.dist``.  You will have to modify it to to match
+   ``turba/config/backends.php.dist``.  You will have to modify it to to match
    the fields, settings, and schema of your LDAP server, but it's a solid
    place to start.
 
diff --git a/turba/docs/UPGRADING b/turba/docs/UPGRADING
index 4cb2a7501..ac8fc7bf3 100644
--- a/turba/docs/UPGRADING
+++ b/turba/docs/UPGRADING
@@ -13,6 +13,10 @@ use the updated data with your old Turba version anymore.
 Upgrading Turba from 2.x to 3.x
 ===============================
 
+
+The ``config/sources.php`` configuration file has been renamed to
+``config/backends.php``.
+
 The ``_turba_hook_encode_{attribute}`` hook has been moved to the
 'encode_attribute' hook.
 
diff --git a/turba/lib/Api.php b/turba/lib/Api.php
index ba47d654d..22b63cea1 100644
--- a/turba/lib/Api.php
+++ b/turba/lib/Api.php
@@ -123,7 +123,7 @@ class Turba_Api extends Horde_Registry_Api
         global $prefs;
 
         // Bring in a clean copy of sources.
-        require TURBA_BASE . '/config/sources.php';
+        require TURBA_BASE . '/config/backends.php';
 
         if (!empty($_SESSION['turba']['has_share'])) {
             $shares = Turba::listShares(true);
diff --git a/turba/lib/Application.php b/turba/lib/Application.php
index ca5e0ca8e..0b1201b53 100644
--- a/turba/lib/Application.php
+++ b/turba/lib/Application.php
@@ -70,7 +70,7 @@ class Turba_Application extends Horde_Registry_Application
 
         // Turba source and attribute configuration.
         $attributes = Horde::loadConfiguration('attributes.php', 'attributes', 'turba');
-        include TURBA_BASE . '/config/sources.php';
+        include TURBA_BASE . '/config/backends.php';
 
         /* UGLY UGLY UGLY - we should NOT be using this as a global
          * variable all over the place. */
@@ -147,7 +147,7 @@ class Turba_Application extends Horde_Registry_Application
      */
     public function perms()
     {
-        require TURBA_BASE . '/config/sources.php';
+        require TURBA_BASE . '/config/backends.php';
 
         $perms['tree']['turba']['sources'] = false;
         $perms['title']['turba:sources'] = _("Sources");
@@ -387,7 +387,7 @@ class Turba_Application extends Horde_Registry_Application
     public function removeUserData($user)
     {
         /* We need a clean copy of the $cfgSources array here.*/
-        require TURBA_BASE . '/config/sources.php';
+        require TURBA_BASE . '/config/backends.php';
 
         foreach ($cfgSources as $source) {
             if (empty($source['use_shares'])) {
diff --git a/turba/lib/Driver.php b/turba/lib/Driver.php
index 0fa8880eb..f10489ff3 100644
--- a/turba/lib/Driver.php
+++ b/turba/lib/Driver.php
@@ -56,7 +56,7 @@ class Turba_Driver implements Countable
 
     /**
      * Array of fields to search "approximately" (@see
-     * config/sources.php.dist).
+     * config/backends.php.dist).
      *
      * @var array
      */
diff --git a/turba/lib/Form/CreateAddressBook.php b/turba/lib/Form/CreateAddressBook.php
index 3ffc4976a..d3c403907 100644
--- a/turba/lib/Form/CreateAddressBook.php
+++ b/turba/lib/Form/CreateAddressBook.php
@@ -33,7 +33,7 @@ class Turba_Form_CreateAddressBook extends Horde_Form
     function execute()
     {
         // Need a clean cfgSources array
-        include TURBA_BASE . '/config/sources.php';
+        include TURBA_BASE . '/config/backends.php';
 
         $driver = $GLOBALS['injector']->getInstance('Turba_Driver')->getDriver($cfgSources[$GLOBALS['conf']['shares']['source']]);
 
diff --git a/turba/lib/Test.php b/turba/lib/Test.php
index 0fe96ed2b..ed713b4d6 100644
--- a/turba/lib/Test.php
+++ b/turba/lib/Test.php
@@ -54,7 +54,7 @@ class Turba_Test extends Horde_Test
         'config/conf.php' => null,
         'config/mime_drivers.php' => null,
         'config/prefs.php' => null,
-        'config/sources.php' => null
+        'config/backends.php' => null
     );
 
     /**
diff --git a/turba/lib/Turba.php b/turba/lib/Turba.php
index 9a46681af..bcf531020 100644
--- a/turba/lib/Turba.php
+++ b/turba/lib/Turba.php
@@ -366,7 +366,7 @@ class Turba {
      *
      * This will only sync shares that are unique to Horde (basically, a SQL
      * driver source for now).  Any backend that supports ACLs or similar
-     * mechanism should be configured from within sources.php or
+     * mechanism should be configured from within backends.php or
      * _horde_hook_share_* calls.
      *
      * @param array $sources  The default $cfgSources array.
@@ -502,7 +502,7 @@ class Turba {
     function getSourceFromShare($share)
     {
         // Require a fresh config file.
-        require TURBA_BASE . '/config/sources.php';
+        require TURBA_BASE . '/config/backends.php';
 
         $params = @unserialize($share->get('params'));
         $newConfig = $cfgSources[$params['source']];
diff --git a/turba/lib/tests/KolabTestBase.php b/turba/lib/tests/KolabTestBase.php
index 226131551..f829764ac 100644
--- a/turba/lib/tests/KolabTestBase.php
+++ b/turba/lib/tests/KolabTestBase.php
@@ -149,7 +149,7 @@ class Turba_KolabTestBase extends Horde_Kolab_Test_Storage
 
         // Turba source and attribute configuration.
         include TURBA_BASE . '/config/attributes.php';
-        include TURBA_BASE . '/config/sources.php';
+        include TURBA_BASE . '/config/backends.php';
         unset($cfgSources['kolab_global']);
 
         $this->prepareNewFolder($world['storage'], 'Contacts', 'contact', true);
diff --git a/turba/scripts/import_squirrelmail_file_abook.php b/turba/scripts/import_squirrelmail_file_abook.php
index 7291ef50f..0f60edc56 100755
--- a/turba/scripts/import_squirrelmail_file_abook.php
+++ b/turba/scripts/import_squirrelmail_file_abook.php
@@ -64,7 +64,7 @@ foreach($files as $file) {
 
     // Reset $cfgSources for current user.
     unset($cfgSources);
-    include TURBA_BASE . '/config/sources.php';
+    include TURBA_BASE . '/config/backends.php';
     $cfgSources = Turba::getConfigFromShares($cfgSources);
     $cfgSources = Turba::permissionsFilter($cfgSources);
 
diff --git a/turba/scripts/import_squirrelmail_sql_abook.php b/turba/scripts/import_squirrelmail_sql_abook.php
index 8f8db057c..87f52c719 100755
--- a/turba/scripts/import_squirrelmail_sql_abook.php
+++ b/turba/scripts/import_squirrelmail_sql_abook.php
@@ -61,7 +61,7 @@ while ($row = $handle->fetchRow(DB_FETCHMODE_ASSOC)) {
         // Reset $cfgSources for current user.
         unset($cfgSources);
         $hasShares = false;
-        include TURBA_BASE . '/config/sources.php';
+        include TURBA_BASE . '/config/backends.php';
         foreach ($cfgSources as $key => $cfg) {
             if (!empty($cfg['use_shares'])) {
                 $has_share = true;
diff --git a/turba/scripts/upgrades/2007-06-17_flatten_shares.php b/turba/scripts/upgrades/2007-06-17_flatten_shares.php
index 607c1c8d6..5496aefd7 100755
--- a/turba/scripts/upgrades/2007-06-17_flatten_shares.php
+++ b/turba/scripts/upgrades/2007-06-17_flatten_shares.php
@@ -10,7 +10,7 @@ require_once dirname(__FILE__) . '/../lib/Application.php';
 Horde_Registry::appInit('turba', array('authentication' => 'none', 'cli' => true));
 
 // Re-load source config.
-require TURBA_BASE . '/config/sources.php';
+require TURBA_BASE . '/config/backends.php';
 
 // See if any of our sources are configured to use Horde_Share.
 if (empty($_SESSION['turba']['has_share'])) {