2007-09-28 [colin] 3.0.1cvs45
[claws.git] / src / addrindex.c
index 7e1e0d89c046e17513d096ccf0927ff63b6988e0..ea3aeb9583034fa501c986116bfff094e93a8913 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 2001 Match Grun
+ * Copyright (C) 2001-2007 Match Grun and the Claws Mail team
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ * 
  */
 
 /*
  * General functions for accessing address index file.
  */
 
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "defs.h"
+
 #include <glib.h>
+#include <glib/gi18n.h>
 
 #include "mgutils.h"
 #include "addritem.h"
 #include "addrcache.h"
 #include "addrbook.h"
+#include "addressbook.h"
 #include "addrindex.h"
 #include "xml.h"
+#include "addrquery.h"
+#include "addr_compl.h"
+#include "utils.h"
+#include "alertpanel.h"
 
 #ifndef DEV_STANDALONE
-#include "prefs.h"
+#include "prefs_gtk.h"
+#include "codeconv.h"
 #endif
 
 #include "vcard.h"
 #endif
 
 #ifdef USE_LDAP
-#include "syldap.h"
+#include "ldapserver.h"
+#include "ldapctrl.h"
+#include "ldapquery.h"
+#include "ldapupdate.h"
+#include "ldaputil.h"
 #endif
 
 #define TAG_ADDRESS_INDEX    "addressbook"
@@ -56,7 +73,7 @@
 #define TAG_DS_JPILOT        "jpilot"
 #define TAG_DS_LDAP          "server"
 
-// XML Attribute names
+/* XML Attribute names */
 #define ATTAG_BOOK_NAME       "name"
 #define ATTAG_BOOK_FILE       "file"
 
 #define ATTAG_LDAP_CRITERIA   "criteria"
 #define ATTAG_LDAP_MAX_ENTRY  "max-entry"
 #define ATTAG_LDAP_TIMEOUT    "timeout"
+#define ATTAG_LDAP_MAX_AGE    "max-age"
+#define ATTAG_LDAP_DYN_SEARCH "dyn-search"
+#define ATTAG_LDAP_MATCH_OPT  "match-opt"
+#define ATTAG_LDAP_ENABLE_TLS "enable-tls"
+#define ATTAG_LDAP_ENABLE_SSL "enable-ssl"
+
+#define ELTAG_LDAP_ATTR_SRCH  "attribute"
+#define ATTAG_LDAP_ATTR_NAME  "name"
 
-#define DISP_NEW_COMMON       "Common Address"
-#define DISP_NEW_PERSONAL     "Personal Address"
+/* Attribute values */
+#define ATVAL_BOOLEAN_YES         "yes"
+#define ATVAL_BOOLEAN_NO          "no"
+#define ATVAL_LDAP_MATCH_BEGIN    "begin-with"
+#define ATVAL_LDAP_MATCH_CONTAINS "contains"
 
-// Old address book
+/* New attributes */
+#define ATTAG_LDAP_DEFAULT    "default"
+
+#define DISP_NEW_COMMON       _("Common addresses")
+#define DISP_NEW_PERSONAL     _("Personal addresses")
+
+/* Old address book */
 #define TAG_IF_OLD_COMMON     "common_address"
 #define TAG_IF_OLD_PERSONAL   "personal_address"
 
-#define DISP_OLD_COMMON       "Common Address"
-#define DISP_OLD_PERSONAL     "Personal Address"
+#define DISP_OLD_COMMON       _("Common address")
+#define DISP_OLD_PERSONAL     _("Personal address")
 
+/**
+ * Singleton object.
+ */
+static AddressIndex *_addressIndex_ = NULL;
+
+/*
+ * Define attribute name-value pair.
+ */
 typedef struct _AddressIfAttr AddressIfAttrib;
 struct _AddressIfAttr {
        gchar *name;
        gchar *value;
 };
 
+static AddressDataSource *addrindex_create_datasource  ( AddressIfType ifType );
+
+static GList *addrindex_ds_get_all_persons     ( AddressDataSource *ds );
+static GList *addrindex_ds_get_all_groups      ( AddressDataSource *ds );
+static AddressDataSource *addrindex_get_datasource     ( AddressIndex *addrIndex,
+                                                 const gchar *cacheID );
+static AddressInterface *addrindex_get_interface       ( AddressIndex *addrIndex,
+                                                 AddressIfType ifType );
+static gint addrindex_write_to                 ( AddressIndex *addrIndex,
+                                         const gchar *newFile );
+
 /*
-* Build interface with default values.
+ * Define DOM fragment.
+ */
+typedef struct _AddressIfFrag AddressIfFragment;
+struct _AddressIfFrag {
+       gchar *name;
+       GList *children;
+       GList *attributes;
+};
+
+/**
+ * Build interface with default values.
+ *
+ * \param type Interface type.
+ * \param name Interface name.
+ * \param tagIf XML tag name for interface in address index file.
+ * \param tagDS XML tag name for datasource in address index file.
+ * \return Address interface object.
 */
-static AddressInterface *addrindex_create_interface( gint type, gchar *name, gchar *tagIf, gchar *tagDS ) {
+static AddressInterface *addrindex_create_interface(
+               gint type, gchar *name, gchar *tagIf, gchar *tagDS )
+{
        AddressInterface *iface = g_new0( AddressInterface, 1 );
+
        ADDRITEM_TYPE(iface) = ITEMTYPE_INTERFACE;
        ADDRITEM_ID(iface) = NULL;
        ADDRITEM_NAME(iface) = g_strdup( name );
@@ -115,6 +187,8 @@ static AddressInterface *addrindex_create_interface( gint type, gchar *name, gch
        iface->haveLibrary = TRUE;
        iface->useInterface = TRUE;
        iface->readOnly      = TRUE;
+
+       /* Set callbacks to NULL values - override for each interface */
        iface->getAccessFlag = NULL;
        iface->getModifyFlag = NULL;
        iface->getReadFlag   = NULL;
@@ -124,19 +198,30 @@ static AddressInterface *addrindex_create_interface( gint type, gchar *name, gch
        iface->getListFolder = NULL;
        iface->getListPerson = NULL;
        iface->getAllPersons = NULL;
+       iface->getAllGroups  = NULL;
        iface->getName       = NULL;
        iface->listSource = NULL;
+
+       /* Search stuff */
+       iface->externalQuery = FALSE;
+       iface->searchOrder = 0;         /* Ignored */
+       iface->startSearch = NULL;
+       iface->stopSearch = NULL;
+
        return iface;
 }
 
-/*
-* Build table of interfaces.
-*/
+/**
+ * Build table of of all address book interfaces.
+ * \param addrIndex Address index object.
+ */
 static void addrindex_build_if_list( AddressIndex *addrIndex ) {
        AddressInterface *iface;
-       gint seq = 0;
 
-       iface = addrindex_create_interface( ADDR_IF_BOOK, "Address Book", TAG_IF_ADDRESS_BOOK, TAG_DS_ADDRESS_BOOK );
+       /* Create intrinsic XML address book interface */
+       iface = addrindex_create_interface(
+                       ADDR_IF_BOOK, "Address Book", TAG_IF_ADDRESS_BOOK,
+                       TAG_DS_ADDRESS_BOOK );
        iface->readOnly      = FALSE;
        iface->getModifyFlag = ( void * ) addrbook_get_modified;
        iface->getAccessFlag = ( void * ) addrbook_get_accessed;
@@ -147,12 +232,19 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) {
        iface->getListFolder = ( void * ) addrbook_get_list_folder;
        iface->getListPerson = ( void * ) addrbook_get_list_person;
        iface->getAllPersons = ( void * ) addrbook_get_all_persons;
+       iface->getAllGroups  = ( void * ) addrbook_get_all_groups;
        iface->getName       = ( void * ) addrbook_get_name;
        iface->setAccessFlag = ( void * ) addrbook_set_accessed;
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       iface->searchOrder   = 0;
+
+       /* Add to list of interfaces in address book */ 
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
-       iface = addrindex_create_interface( ADDR_IF_VCARD, "V-Card", TAG_IF_VCARD, TAG_DS_VCARD );
+       /* Create vCard interface */
+       iface = addrindex_create_interface(
+                       ADDR_IF_VCARD, "vCard", TAG_IF_VCARD, TAG_DS_VCARD );
        iface->getModifyFlag = ( void * ) vcard_get_modified;
        iface->getAccessFlag = ( void * ) vcard_get_accessed;
        iface->getReadFlag   = ( void * ) vcard_get_read_flag;
@@ -164,13 +256,18 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) {
        iface->getAllPersons = ( void * ) vcard_get_all_persons;
        iface->getName       = ( void * ) vcard_get_name;
        iface->setAccessFlag = ( void * ) vcard_set_accessed;
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       iface->searchOrder   = 0;
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
-       iface = addrindex_create_interface( ADDR_IF_JPILOT, "J-Pilot", TAG_IF_JPILOT, TAG_DS_JPILOT );
+       /* Create JPilot interface */
+       iface = addrindex_create_interface(
+                       ADDR_IF_JPILOT, "J-Pilot", TAG_IF_JPILOT,
+                       TAG_DS_JPILOT );
 #ifdef USE_JPILOT
-       iface->useInterface = TRUE;
        iface->haveLibrary = jpilot_test_pilot_lib();
+       iface->useInterface = iface->haveLibrary;
        iface->getModifyFlag = ( void * ) jpilot_get_modified;
        iface->getAccessFlag = ( void * ) jpilot_get_accessed;
        iface->getReadFlag   = ( void * ) jpilot_get_read_flag;
@@ -182,185 +279,204 @@ static void addrindex_build_if_list( AddressIndex *addrIndex ) {
        iface->getAllPersons = ( void * ) jpilot_get_all_persons;
        iface->getName       = ( void * ) jpilot_get_name;
        iface->setAccessFlag = ( void * ) jpilot_set_accessed;
+       iface->searchOrder   = 0;
 #else
        iface->useInterface = FALSE;
        iface->haveLibrary = FALSE;
 #endif
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
-       iface = addrindex_create_interface( ADDR_IF_LDAP, "LDAP", TAG_IF_LDAP, TAG_DS_LDAP );
+       /* Create LDAP interface */
+       iface = addrindex_create_interface(
+                       ADDR_IF_LDAP, "LDAP", TAG_IF_LDAP, TAG_DS_LDAP );
 #ifdef USE_LDAP
-       iface->haveLibrary = syldap_test_ldap_lib();
-       iface->getAccessFlag = ( void * ) syldap_get_accessed;
-       // iface->getModifyFlag = ( void * ) syldap_get_modified;
-       // iface->getReadFlag   = ( void * ) syldap_get_read_flag;
-       iface->getStatusCode = ( void * ) syldap_get_status;
-       iface->getReadData   = ( void * ) syldap_read_data;
-       iface->getRootFolder = ( void * ) syldap_get_root_folder;
-       iface->getListFolder = ( void * ) syldap_get_list_folder;
-       iface->getListPerson = ( void * ) syldap_get_list_person;
-       iface->getName       = ( void * ) syldap_get_name;
-       iface->setAccessFlag = ( void * ) syldap_set_accessed;
+       iface->readOnly = FALSE;
+       /* iface->haveLibrary = ldapsvr_test_ldap_lib(); */
+       iface->haveLibrary = ldaputil_test_ldap_lib();
+       iface->useInterface = iface->haveLibrary;
+       iface->getModifyFlag = ( void * ) ldapsvr_get_modified;
+       iface->getAccessFlag = ( void * ) ldapsvr_get_accessed;
+       iface->getReadFlag   = ( void * ) ldapsvr_get_read_flag;
+       iface->getStatusCode = ( void * ) ldapsvr_get_status;
+       iface->getReadData   = ( void * ) ldapsvr_read_data;
+       iface->getRootFolder = ( void * ) ldapsvr_get_root_folder;
+       iface->getListFolder = ( void * ) ldapsvr_get_list_folder;
+       iface->getListPerson = ( void * ) ldapsvr_get_list_person;
+       iface->getName       = ( void * ) ldapsvr_get_name;
+       iface->setAccessFlag = ( void * ) ldapsvr_set_accessed;
+       iface->externalQuery = TRUE;
+       iface->searchOrder   = 1;
 #else
        iface->useInterface = FALSE;
        iface->haveLibrary = FALSE;
 #endif
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
-       // Two old legacy data sources
-       iface = addrindex_create_interface( ADDR_IF_COMMON, "Old Address - common", TAG_IF_OLD_COMMON, NULL );
+       /* Two old legacy data sources (pre 0.7.0) */
+       iface = addrindex_create_interface(
+                       ADDR_IF_COMMON, "Old Address - common",
+                       TAG_IF_OLD_COMMON, NULL );
        iface->legacyFlag = TRUE;
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
-       iface = addrindex_create_interface( ADDR_IF_COMMON, "Old Address - personal", TAG_IF_OLD_PERSONAL, NULL );
+       iface = addrindex_create_interface(
+                       ADDR_IF_COMMON, "Old Address - personal",
+                       TAG_IF_OLD_PERSONAL, NULL );
        iface->legacyFlag = TRUE;
-       addrIndex->interfaceList = g_list_append( addrIndex->interfaceList, iface );
+       addrIndex->interfaceList =
+               g_list_append( addrIndex->interfaceList, iface );
        ADDRITEM_PARENT(iface) = ADDRITEM_OBJECT(addrIndex);
 
 }
 
-/*
-* Free name-value pairs.
-*/
-static void addrindex_free_attributes( GList *list ) {
-       GList *node = list;
+/**
+ * Free DOM fragment.
+ * \param fragment Fragment to free.
+ */
+static void addrindex_free_fragment( AddressIfFragment *fragment ) {
+       GList *node;
+
+       /* Free children */
+       node = fragment->children;
+       while( node ) {
+               AddressIfFragment *child = node->data;
+               addrindex_free_fragment( child );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+       g_list_free( fragment->children );
+
+       /* Free attributes */
+       node = fragment->attributes;
        while( node ) {
                AddressIfAttrib *nv = node->data;
-               g_free( nv->name ); nv->name = NULL;
-               g_free( nv->value ); nv->value = NULL;
+               g_free( nv->name );
+               g_free( nv->value );
                g_free( nv );
                node->data = NULL;
                node = g_list_next( node );
        }
-       g_list_free( list );
-}
+       g_list_free( fragment->attributes );
 
-/*
-* Free up data source.
-*/
-void addrindex_free_datasource( AddressIndex *addrIndex, AddressDataSource *ds ) {
-       AddressInterface *iface = NULL;
-       g_return_if_fail( addrIndex != NULL );
-       g_return_if_fail( ds != NULL );
-
-       if( ds->interface == NULL ) {
-               iface = addrindex_get_interface( addrIndex, ds->type );
-       }
-       if( iface == NULL ) return;
+       g_free( fragment->name );
+       fragment->name = NULL;
+       fragment->attributes = NULL;
+       fragment->children = NULL;
 
-       if( iface->useInterface ) {
-               if( iface->type == ADDR_IF_BOOK ) {
-                       AddressBookFile *abf = ds->rawDataSource;
-                       if( abf ) {
-                               addrbook_free_book( abf );
-                       }
-               }
-               else if( iface->type == ADDR_IF_VCARD ) {
-                       VCardFile *vcf = ds->rawDataSource;
-                       if( vcf ) {
-                               vcard_free( vcf );
-                       }
-               }
-#ifdef USE_JPILOT
-               else if( iface->type == ADDR_IF_JPILOT ) {
-                       JPilotFile *jpf = ds->rawDataSource;
-                       if( jpf ) {
-                               jpilot_free( jpf );
-                       }
-               }
-#endif
-#ifdef USE_LDAP
-               else if( iface->type == ADDR_IF_LDAP ) {
-                       SyldapServer *server = ds->rawDataSource;
-                       if( server ) {
-                               syldap_free( server );
-                       }
-               }
-#endif
-       }
-       else {
-               GList *list = ds->rawDataSource;
-               addrindex_free_attributes( list );
-       }
-
-       g_free( ADDRITEM_ID(addrIndex) );
-       g_free( ADDRITEM_NAME(addrIndex) );
+       g_free( fragment );
+}
 
-       ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE;
-       ADDRITEM_ID(addrIndex) = NULL;
-       ADDRITEM_NAME(addrIndex) = NULL;
-       ADDRITEM_PARENT(addrIndex) = NULL;
-       ADDRITEM_SUBTYPE(addrIndex) = 0;
-       ds->type = ADDR_IF_NONE;
-       ds->rawDataSource = NULL;
-       ds->interface = NULL;
+/**
+ * Create a new data source.
+ * \param ifType Interface type to create.
+ * \return Initialized data source.
+ */
+AddressDataSource *addrindex_create_datasource( AddressIfType ifType ) {
+       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
 
-       ds->type = ADDR_IF_NONE;
+       ADDRITEM_TYPE(ds) = ITEMTYPE_DATASOURCE;
+       ADDRITEM_ID(ds) = NULL;
+       ADDRITEM_NAME(ds) = NULL;
+       ADDRITEM_PARENT(ds) = NULL;
+       ADDRITEM_SUBTYPE(ds) = 0;
+       ds->type = ifType;
        ds->rawDataSource = NULL;
        ds->interface = NULL;
-       g_free( ds );
+       return ds;
 }
 
-static void addrindex_free_all_datasources( AddressInterface *iface ) {
-       GList *node = iface->listSource;
-       while( node ) {
-               AddressDataSource *ds = node->data;
-               if( iface->useInterface ) {
-                       if( iface->type == ADDR_IF_BOOK ) {
-                               AddressBookFile *abf = ds->rawDataSource;
-                               if( abf ) {
+/**
+ * Free up data source.
+ * \param ds Data source to free.
+ */
+void addrindex_free_datasource( AddressDataSource *ds ) {
+       AddressInterface *iface;
+
+       g_return_if_fail( ds != NULL );
+
+       iface = ds->interface;
+       if( ds->rawDataSource != NULL ) {
+               if( iface != NULL ) {
+                       if( iface->useInterface ) {
+                               if( iface->type == ADDR_IF_BOOK ) {
+                                       AddressBookFile *abf = ds->rawDataSource;
                                        addrbook_free_book( abf );
                                }
-                       }
-                       else if( iface->type == ADDR_IF_VCARD ) {
-                               VCardFile *vcf = ds->rawDataSource;
-                               if( vcf ) {
+                               else if( iface->type == ADDR_IF_VCARD ) {
+                                       VCardFile *vcf = ds->rawDataSource;
                                        vcard_free( vcf );
                                }
-                       }
 #ifdef USE_JPILOT
-                       else if( iface->type == ADDR_IF_JPILOT ) {
-                               JPilotFile *jpf = ds->rawDataSource;
-                               if( jpf ) {
+                               else if( iface->type == ADDR_IF_JPILOT ) {
+                                       JPilotFile *jpf = ds->rawDataSource;
                                        jpilot_free( jpf );
                                }
-                       }
 #endif
 #ifdef USE_LDAP
-                       else if( iface->type == ADDR_IF_LDAP ) {
-                               SyldapServer *server = ds->rawDataSource;
-                               if( server ) {
-                                       syldap_free( server );
+                               else if( iface->type == ADDR_IF_LDAP ) {
+                                       LdapServer *server = ds->rawDataSource;
+                                       ldapsvr_free( server );
                                }
-                       }
 #endif
+                               else {
+                               }
+                       }
+                       else {
+                               AddressIfFragment *fragment = ds->rawDataSource;
+                               addrindex_free_fragment( fragment );
+                       }
                }
-               else {
-                       GList *list = ds->rawDataSource;
-                       addrindex_free_attributes( list );
-               }
+       }
+
+       ADDRITEM_TYPE(ds) = ITEMTYPE_NONE;
+       ADDRITEM_ID(ds) = NULL;
+       ADDRITEM_NAME(ds) = NULL;
+       ADDRITEM_PARENT(ds) = NULL;
+       ADDRITEM_SUBTYPE(ds) = 0;
+       ds->type = ADDR_IF_NONE;
+       ds->interface = NULL;
+       ds->rawDataSource = NULL;
+
+       g_free( ds );
+}
 
-               ds->type = ADDR_IF_NONE;
-               ds->rawDataSource = NULL;
-               ds->interface = NULL;
-               g_free( ds );
+/**
+ * Free up all data sources for specified interface.
+ * \param iface Address interface to process.
+ */
+static void addrindex_free_all_datasources( AddressInterface *iface ) {
+       GList *node = iface->listSource;
+       while( node ) {
+               AddressDataSource *ds = node->data;
+               addrindex_free_datasource( ds );
                node->data = NULL;
                node = g_list_next( node );
        }
 }
 
-static addrindex_free_interface( AddressInterface *iface ) {
+/**
+ * Free up specified interface.
+ * \param iface Interface to process.
+ */
+static void addrindex_free_interface( AddressInterface *iface ) {
+       /* Free up data sources */
        addrindex_free_all_datasources( iface );
+       g_list_free( iface->listSource );
 
+       /* Free internal storage */
        g_free( ADDRITEM_ID(iface) );
        g_free( ADDRITEM_NAME(iface) );
        g_free( iface->name );
        g_free( iface->listTag );
        g_free( iface->itemTag );
 
+       /* Clear all pointers */
        ADDRITEM_TYPE(iface) = ITEMTYPE_NONE;
        ADDRITEM_ID(iface) = NULL;
        ADDRITEM_NAME(iface) = NULL;
@@ -373,69 +489,230 @@ static addrindex_free_interface( AddressInterface *iface ) {
        iface->legacyFlag = FALSE;
        iface->useInterface = FALSE;
        iface->haveLibrary = FALSE;
-
-       g_list_free( iface->listSource );
        iface->listSource = NULL;
+
+       /* Search stuff */
+       iface->searchOrder = 0;
+       iface->startSearch = NULL;
+       iface->stopSearch = NULL;
+
+       g_free( iface );
+}
+
+/**
+ * Return cache ID for specified data source.
+ *
+ * \param  addrIndex Address index.
+ * \param  ds        Data source.
+ * \return ID or NULL if not found. This should be <code>g_free()</code>
+ *         when done.
+ */
+gchar *addrindex_get_cache_id( AddressIndex *addrIndex, AddressDataSource *ds ) {
+       gchar *cacheID = NULL;
+       AddrBookBase *adbase;
+       AddressCache *cache;
+
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+       g_return_val_if_fail( ds != NULL, NULL );
+
+       adbase = ( AddrBookBase * ) ds->rawDataSource;
+       if( adbase ) {
+               cache = adbase->addressCache;
+               if( cache ) {
+                       cacheID = g_strdup( cache->cacheID );
+               }
+       }
+
+       return cacheID;
+}
+
+/**
+ * Return reference to data source for specified cacheID.
+ * \param addrIndex Address index.
+ * \param cacheID   ID.
+ * \return Data source, or NULL if not found.
+ */
+static AddressDataSource *addrindex_get_datasource(
+               AddressIndex *addrIndex, const gchar *cacheID )
+{
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+       g_return_val_if_fail( cacheID != NULL, NULL );
+       return ( AddressDataSource * ) g_hash_table_lookup( addrIndex->hashCache, cacheID );
+}
+
+/**
+ * Return reference to address cache for specified cacheID.
+ * \param addrIndex Address index.
+ * \param cacheID   ID.
+ * \return Address cache, or NULL if not found.
+ */
+AddressCache *addrindex_get_cache( AddressIndex *addrIndex, const gchar *cacheID ) {
+       AddressDataSource *ds;
+       AddrBookBase *adbase;
+       AddressCache *cache;
+
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+       g_return_val_if_fail( cacheID != NULL, NULL );
+
+       cache = NULL;
+       ds = addrindex_get_datasource( addrIndex, cacheID );
+       if( ds ) {
+               adbase = ( AddrBookBase * ) ds->rawDataSource;
+               cache = adbase->addressCache;
+       }
+       return cache;
+}
+
+/**
+ * Add data source into hash table.
+ * \param addrIndex Address index.
+ * \param ds        Data source.
+ */
+static void addrindex_hash_add_cache(
+               AddressIndex *addrIndex, AddressDataSource *ds )
+{
+       gchar *cacheID;
+
+       cacheID = addrindex_get_cache_id( addrIndex, ds );
+       if( cacheID ) {
+               g_hash_table_insert( addrIndex->hashCache, cacheID, ds );
+       }
 }
 
 /*
-* Create new object.
-*/
-AddressIndex *addrindex_create_index() {
-       AddressIndex *addrIndex = g_new0( AddressIndex, 1 );
-       ADDRITEM_TYPE(addrIndex) = ITEMTYPE_INDEX;
-       ADDRITEM_ID(addrIndex) = NULL;
-       ADDRITEM_NAME(addrIndex) = g_strdup( "Address Index" );
-       ADDRITEM_PARENT(addrIndex) = NULL;
-       ADDRITEM_SUBTYPE(addrIndex) = 0;
-       addrIndex->filePath = NULL;
-       addrIndex->fileName = NULL;
-       addrIndex->retVal = MGU_SUCCESS;
-       addrIndex->needsConversion = FALSE;
-       addrIndex->wasConverted = FALSE;
-       addrIndex->conversionError = FALSE;
-       addrIndex->interfaceList = NULL;
-       addrIndex->lastType = ADDR_IF_NONE;
-       addrIndex->dirtyFlag = FALSE;
-       addrindex_build_if_list( addrIndex );
-       return addrIndex;
+ * Free hash table callback function.
+ */
+static gboolean addrindex_free_cache_cb( gpointer key, gpointer value, gpointer data ) {
+       g_free( key );
+       key = NULL;
+       value = NULL;
+       return TRUE;
 }
 
 /*
-* Specify file to be used.
-*/
+ * Free hash table of address cache items.
+ */
+static void addrindex_free_cache_hash( GHashTable *table ) {
+       g_hash_table_foreach_remove( table, addrindex_free_cache_cb, NULL );
+       g_hash_table_destroy( table );
+}
+
+/**
+ * Remove data source from internal hashtable.
+ * \param addrIndex Address index.
+ * \param ds        Data source to remove.
+ */
+static void addrindex_hash_remove_cache(
+               AddressIndex *addrIndex, AddressDataSource *ds )
+{
+       gchar *cacheID;
+
+       cacheID = addrindex_get_cache_id( addrIndex, ds );
+       if( cacheID ) {
+               g_hash_table_remove( addrIndex->hashCache, cacheID );
+               g_free( cacheID );
+               cacheID = NULL;
+       }
+}
+
+/**
+ * Create a new address index. This is created as a singleton object.
+ * \return Initialized address index object.
+ */
+AddressIndex *addrindex_create_index( void ) {
+       AddressIndex *index;
+
+       if( _addressIndex_ == NULL ) {
+               index = g_new0( AddressIndex, 1 );
+               ADDRITEM_TYPE(index) = ITEMTYPE_INDEX;
+               ADDRITEM_ID(index) = NULL;
+               ADDRITEM_NAME(index) = g_strdup( "Address Index" );
+               ADDRITEM_PARENT(index) = NULL;
+               ADDRITEM_SUBTYPE(index) = 0;
+               index->filePath = NULL;
+               index->fileName = NULL;
+               index->retVal = MGU_SUCCESS;
+               index->needsConversion = FALSE;
+               index->wasConverted = FALSE;
+               index->conversionError = FALSE;
+               index->interfaceList = NULL;
+               index->lastType = ADDR_IF_NONE;
+               index->dirtyFlag = FALSE;
+               index->hashCache = g_hash_table_new( g_str_hash, g_str_equal );
+               index->loadedFlag = FALSE;
+               index->searchOrder = NULL;
+               addrindex_build_if_list( index );
+               _addressIndex_ = index;
+       }
+       return _addressIndex_;
+}
+
+/**
+ * Property - Specify file path to address index file.
+ * \param addrIndex Address index.
+ * \param value Path to index file.
+ */
 void addrindex_set_file_path( AddressIndex *addrIndex, const gchar *value ) {
        g_return_if_fail( addrIndex != NULL );
        addrIndex->filePath = mgu_replace_string( addrIndex->filePath, value );
 }
+
+/**
+ * Property - Specify file name to address index file.
+ * \param addrIndex Address index.
+ * \param value File name.
+ */
 void addrindex_set_file_name( AddressIndex *addrIndex, const gchar *value ) {
        g_return_if_fail( addrIndex != NULL );
        addrIndex->fileName = mgu_replace_string( addrIndex->fileName, value );
 }
-void addrindex_set_dirty( AddressIndex *addrIndex, const gboolean value ) {
-       g_return_if_fail( addrIndex != NULL );
-       addrIndex->dirtyFlag = value;
-}
 
-/*
-* Return list of interfaces.
-*/
+/**
+ * Return list of address interfaces.
+ * \param addrIndex Address index.
+ * \return List of address interfaces.
+ */
 GList *addrindex_get_interface_list( AddressIndex *addrIndex ) {
-       g_return_if_fail( addrIndex != NULL );
+       g_return_val_if_fail( addrIndex != NULL, NULL );
        return addrIndex->interfaceList;
 }
 
-/*
-* Free up object.
-*/
+/**
+ * Perform any other initialization of address index.
+ */
+void addrindex_initialize( void ) {
+       qrymgr_initialize();
+       addrcompl_initialize();
+}
+
+/**
+ * Perform any other teardown of address index.
+ */
+void addrindex_teardown( void ) {
+       addrcompl_teardown();
+       qrymgr_teardown();
+}
+
+/**
+ * Free up address index.
+ * \param addrIndex Address index.
+ */
 void addrindex_free_index( AddressIndex *addrIndex ) {
        GList *node;
+
        g_return_if_fail( addrIndex != NULL );
 
+       /* Search stuff */
+       g_list_free( addrIndex->searchOrder );
+       addrIndex->searchOrder = NULL;
+
+       /* Free internal storage */
        g_free( ADDRITEM_ID(addrIndex) );
        g_free( ADDRITEM_NAME(addrIndex) );
        g_free( addrIndex->filePath );
        g_free( addrIndex->fileName );
+
+       /* Clear pointers */    
        ADDRITEM_TYPE(addrIndex) = ITEMTYPE_NONE;
        ADDRITEM_ID(addrIndex) = NULL;
        ADDRITEM_NAME(addrIndex) = NULL;
@@ -449,6 +726,8 @@ void addrindex_free_index( AddressIndex *addrIndex ) {
        addrIndex->conversionError = FALSE;
        addrIndex->lastType = ADDR_IF_NONE;
        addrIndex->dirtyFlag = FALSE;
+
+       /* Free up interfaces */        
        node = addrIndex->interfaceList;
        while( node ) {
                AddressInterface *iface = node->data;
@@ -457,30 +736,50 @@ void addrindex_free_index( AddressIndex *addrIndex ) {
        }
        g_list_free( addrIndex->interfaceList );
        addrIndex->interfaceList = NULL;
+
+       /* Free up hash cache */
+       addrindex_free_cache_hash( addrIndex->hashCache );
+       addrIndex->hashCache = NULL;
+
+       addrIndex->loadedFlag = FALSE;
+
        g_free( addrIndex );
+       addrIndex = NULL;
+       _addressIndex_ = NULL;
 }
 
-/*
-* Print address index.
+/**
+ * Print address index.
+ * \param addrIndex Address index.
+ * \parem stream    Stream to print.
 */
 void addrindex_print_index( AddressIndex *addrIndex, FILE *stream ) {
        g_return_if_fail( addrIndex != NULL );
        fprintf( stream, "AddressIndex:\n" );
        fprintf( stream, "\tfile path: '%s'\n", addrIndex->filePath );
        fprintf( stream, "\tfile name: '%s'\n", addrIndex->fileName );
-       fprintf( stream, "\t   status: %d : '%s'\n", addrIndex->retVal, mgu_error2string( addrIndex->retVal ) );
-       fprintf( stream, "\tconverted: '%s'\n", addrIndex->wasConverted ? "yes" : "no" );
-       fprintf( stream, "\tcvt error: '%s'\n", addrIndex->conversionError ? "yes" : "no" );
+       fprintf( stream, "\t   status: %d\n", addrIndex->retVal );
+       fprintf( stream, "\tconverted: '%s'\n",
+                       addrIndex->wasConverted ? "yes" : "no" );
+       fprintf( stream, "\tcvt error: '%s'\n",
+                       addrIndex->conversionError ? "yes" : "no" );
        fprintf( stream, "\t---\n" );
 }
 
-/*
-* Retrieve specified interface from index.
-*/
-AddressInterface *addrindex_get_interface( AddressIndex *addrIndex, AddressIfType ifType ) {
+/**
+ * Retrieve reference to address interface for specified interface type.
+ * \param  addrIndex Address index.
+ * \param  ifType Interface type.
+ * \return Address interface, or NULL if not found.
+ */
+static AddressInterface *addrindex_get_interface(
+       AddressIndex *addrIndex, AddressIfType ifType )
+{
        AddressInterface *retVal = NULL;
        GList *node;
-       g_return_if_fail( addrIndex != NULL );
+
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+
        node = addrIndex->interfaceList;
        while( node ) {
                AddressInterface *iface = node->data;
@@ -493,74 +792,84 @@ AddressInterface *addrindex_get_interface( AddressIndex *addrIndex, AddressIfTyp
        return retVal;
 }
 
-AddressDataSource *addrindex_create_datasource() {
-       AddressDataSource *ds = NULL;
-       ds = g_new0( AddressDataSource, 1 );
-       ADDRITEM_TYPE(ds) = ITEMTYPE_DATASOURCE;
-       ADDRITEM_ID(ds) = NULL;
-       ADDRITEM_NAME(ds) = NULL;
-       ADDRITEM_PARENT(ds) = NULL;
-       ADDRITEM_SUBTYPE(ds) = 0;
-       ds->type = ADDR_IF_NONE;
-       ds->rawDataSource = NULL;
-       ds->interface = NULL;
-       return ds;
-}
-
-/*
-* Add data source to index.
-* Enter: addrIndex  Address index object.
-*        ifType     Interface type to add.
-*        dataSource Actual data source to add.
-* Return: TRUE if data source was added.
-* Note: The raw data object (for example, AddressBookFile or VCardFile object) should be
-* supplied as the dataSource argument.
-*/
-AddressDataSource *addrindex_index_add_datasource( AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource ) {
+/**
+ * Add raw data source to index. The raw data object (an AddressBookFile or
+ * VCardFile object, for example) should be supplied as the raw dataSource
+ * argument.
+ *
+ * \param  addrIndex Address index.
+ * \param ifType     Interface type to add.
+ * \param dataSource Actual raw data source to add. 
+ * \return Data source added, or NULL if invalid interface type.
+ */
+AddressDataSource *addrindex_index_add_datasource(
+       AddressIndex *addrIndex, AddressIfType ifType, gpointer dataSource )
+{
        AddressInterface *iface;
        AddressDataSource *ds = NULL;
-       g_return_if_fail( addrIndex != NULL );
-       g_return_if_fail( dataSource != NULL );
+
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+       g_return_val_if_fail( dataSource != NULL, NULL );
 
        iface = addrindex_get_interface( addrIndex, ifType );
        if( iface ) {
-               ds = addrindex_create_datasource();
+               ds = addrindex_create_datasource( ifType );
                ADDRITEM_PARENT(ds) = ADDRITEM_OBJECT(iface);
                ds->type = ifType;
                ds->rawDataSource = dataSource;
                ds->interface = iface;
                iface->listSource = g_list_append( iface->listSource, ds );
                addrIndex->dirtyFlag = TRUE;
+
+               addrindex_hash_add_cache( addrIndex, ds );
        }
        return ds;
 }
 
-/*
-* Remove data source from index.
-* Enter: addrIndex  Address index object.
-*        dataSource Data source to remove.
-* Return: Data source if removed, or NULL if data source was not found in
-* index. Note the this object must still be freed.
-*/
-AddressDataSource *addrindex_index_remove_datasource( AddressIndex *addrIndex, AddressDataSource *dataSource ) {
+/**
+ * Remove specified data source from index.
+ * \param  addrIndex Address index.
+ * \param  dataSource Data source to add. 
+ * \return Reference to data source if removed, or NULL if data source was not
+ *         found in index. Note the this object must still be freed.
+ */
+AddressDataSource *addrindex_index_remove_datasource(
+       AddressIndex *addrIndex, AddressDataSource *dataSource )
+{
        AddressDataSource *retVal = FALSE;
        AddressInterface *iface;
-       g_return_if_fail( addrIndex != NULL );
-       g_return_if_fail( dataSource != NULL );
+
+       g_return_val_if_fail( addrIndex != NULL, NULL );
+       g_return_val_if_fail( dataSource != NULL, NULL );
 
        iface = addrindex_get_interface( addrIndex, dataSource->type );
        if( iface ) {
                iface->listSource = g_list_remove( iface->listSource, dataSource );
                addrIndex->dirtyFlag = TRUE;
                dataSource->interface = NULL;
+
+               /* Remove cache from hash table */
+               addrindex_hash_remove_cache( addrIndex, dataSource );
+
                retVal = dataSource;
        }
        return retVal;
 }
 
-static AddressInterface *addrindex_tag_get_interface( AddressIndex *addrIndex, gchar *tag, AddressIfType ifType ) {
+/**
+ * Retrieve a reference to address interface for specified interface type and
+ * XML interface tag name.
+ * \param  addrIndex Address index.
+ * \param  tag       XML interface tag name to match.
+ * \param  ifType    Interface type to match.
+ * \return Reference to address index, or NULL if not found in index.
+ */
+static AddressInterface *addrindex_tag_get_interface(
+       AddressIndex *addrIndex, gchar *tag, AddressIfType ifType )
+{
        AddressInterface *retVal = NULL;
        GList *node = addrIndex->interfaceList;
+
        while( node ) {
                AddressInterface *iface = node->data;
                node = g_list_next( node );
@@ -580,9 +889,20 @@ static AddressInterface *addrindex_tag_get_interface( AddressIndex *addrIndex, g
        return retVal;
 }
 
-static AddressInterface *addrindex_tag_get_datasource( AddressIndex *addrIndex, AddressIfType ifType, gchar *tag ) {
+/**
+ * Retrieve a reference to address interface for specified interface type and
+ * XML datasource tag name.
+ * \param  addrIndex Address index.
+ * \param  ifType    Interface type to match.
+ * \param  tag       XML datasource tag name to match.
+ * \return Reference to address index, or NULL if not found in index.
+ */
+static AddressInterface *addrindex_tag_get_datasource(
+       AddressIndex *addrIndex, AddressIfType ifType, gchar *tag )
+{
        AddressInterface *retVal = NULL;
        GList *node = addrIndex->interfaceList;
+
        while( node ) {
                AddressInterface *iface = node->data;
                node = g_list_next( node );
@@ -600,24 +920,27 @@ static AddressInterface *addrindex_tag_get_datasource( AddressIndex *addrIndex,
 * Interface XML parsing functions.
 * ***********************************************************************
 */
-static void show_attribs( GList *attr ) {
-       while( attr ) {
-               gchar *name = ((XMLAttr *)attr->data)->name;
-               gchar *value = ((XMLAttr *)attr->data)->value;
-               printf( "\tattr value : %s :%s:\n", name, value );
-               attr = g_list_next( attr );
-       }
-       printf( "\t---\n" );
-}
 
-static void addrindex_write_elem_s( FILE *fp, gint lvl, gchar *name ) {
+/**
+ * Write start of XML element to file.
+ * \param fp   File.
+ * \param lvl  Indentation level.
+ * \param name Element name.
+ */
+static void addrindex_write_elem_s( FILE *fp, const gint lvl, const gchar *name ) {
        gint i;
        for( i = 0; i < lvl; i++ ) fputs( "  ", fp );
        fputs( "<", fp );
        fputs( name, fp );
 }
 
-static void addrindex_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
+/**
+ * Write end of XML element to file.
+ * \param fp   File.
+ * \param lvl  Indentation level.
+ * \param name Element name.
+ */
+static void addrindex_write_elem_e( FILE *fp, const gint lvl, const gchar *name ) {
        gint i;
        for( i = 0; i < lvl; i++ ) fputs( "  ", fp );
        fputs( "</", fp );
@@ -625,7 +948,13 @@ static void addrindex_write_elem_e( FILE *fp, gint lvl, gchar *name ) {
        fputs( ">\n", fp );
 }
 
-static void addrindex_write_attr( FILE *fp, gchar *name, gchar *value ) {
+/**
+ * Write XML attribute to file.
+ * \param fp    File.
+ * \param name  Attribute name.
+ * \param value Attribute value.
+ */
+static void addrindex_write_attr( FILE *fp, const gchar *name, const gchar *value ) {
        fputs( " ", fp );
        fputs( name, fp );
        fputs( "=\"", fp );
@@ -633,16 +962,38 @@ static void addrindex_write_attr( FILE *fp, gchar *name, gchar *value ) {
        fputs( "\"", fp );
 }
 
-/*
-* Return list of name-value pairs.
-*/
-static GList *addrindex_read_attributes( XMLFile *file ) {
-       GList *list = NULL;
+/**
+ * Return DOM fragment for current XML tag from file.
+ * \param  file XML file being processed.
+ * \return Fragment representing DOM fragment for configuration element.
+ */
+static AddressIfFragment *addrindex_read_fragment( XMLFile *file ) {
+       AddressIfFragment *fragment;
+       AddressIfFragment *child;
        AddressIfAttrib *nv;
+       XMLTag *xtag;
+       GList *list;
        GList *attr;
        gchar *name;
        gchar *value;
+       guint prevLevel;
+       gint rc;
+
+       /* g_print( "addrindex_read_fragment\n" ); */
+
+       prevLevel = file->level;
+
+       /* Get current tag name */
+       xtag = xml_get_current_tag( file );
 
+       /* Create new fragment */
+       fragment = g_new0( AddressIfFragment, 1 );
+       fragment->name = g_strdup( xtag->tag );
+       fragment->children = NULL;
+       fragment->attributes = NULL;
+
+       /* Read attributes */
+       list = NULL;
        attr = xml_get_current_tag_attr( file );
        while( attr ) {
                name = ((XMLAttr *)attr->data)->name;
@@ -653,41 +1004,77 @@ static GList *addrindex_read_attributes( XMLFile *file ) {
                list = g_list_append( list, nv );
                attr = g_list_next( attr );
        }
-       return list;
+       fragment->attributes = list;
+
+       /* Now read the children */
+       while( TRUE ) {
+               rc = xml_parse_next_tag( file );
+               if( rc != 0 ) {
+                       /* End of file? */
+                       break;
+               }
+               if( file->level < prevLevel ) {
+                       /* We must be above level we start at */
+                       break;
+               }
+               child = addrindex_read_fragment( file );
+               fragment->children = g_list_append( fragment->children, child );
+       }
+
+       return fragment;
 }
 
-/*
-* Output name-value pairs.
-*/
-static void addrindex_write_attributes( FILE *fp, gchar *tag, GList *list, gint lvl ) {
+/**
+ * Write DOM fragment to file.
+ * \param fp       File to write.
+ * \param fragment DOM fragment for configuration element.
+ * \param lvl      Indent level.
+ */
+static void addrindex_write_fragment(
+               FILE *fp, const AddressIfFragment *fragment, const gint lvl )
+{
        GList *node;
-       AddressIfAttrib *nv;
-       if( list ) {
-               addrindex_write_elem_s( fp, lvl, tag );
-               node = list;
+
+       if( fragment ) {
+               addrindex_write_elem_s( fp, lvl, fragment->name );
+               node = fragment->attributes;
                while( node ) {
-                       nv = node->data;
+                       AddressIfAttrib *nv = node->data;
                        addrindex_write_attr( fp, nv->name, nv->value );
                        node = g_list_next( node );
                }
-               fputs(" />\n", fp);
-       }
-}
+               if( fragment->children ) {
+                       fputs(" >\n", fp);
+
+                       /* Output children */
+                       node = fragment->children;
+                       while( node ) {
+                               AddressIfFragment *child = node->data;
+                               addrindex_write_fragment( fp, child, 1+lvl );
+                               node = g_list_next( node );
+                       }
 
-static void addrindex_print_attributes( GList *list, FILE *stream ) {
-       GList *node = list;
-       while( node ) {
-               AddressIfAttrib *nv = node->data;
-               fprintf( stream, "%s : %s\n", nv->name, nv->value );
-               node = g_list_next( node );
+                       /* Output closing tag */
+                       addrindex_write_elem_e( fp, lvl, fragment->name );
+               }
+               else {
+                       fputs(" />\n", fp);
+               }
        }
 }
 
+/**
+ * Read/parse address index file, creating a data source for a regular
+ * intrinsic XML addressbook.
+ * \param  file Address index file.
+ * \return Data source.
+ */
 static AddressDataSource *addrindex_parse_book( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
+       AddressDataSource *ds;
        AddressBookFile *abf;
        GList *attr;
 
+       ds = addrindex_create_datasource( ADDR_IF_BOOK );
        abf = addrbook_create_book();
        attr = xml_get_current_tag_attr( file );
        while( attr ) {
@@ -709,17 +1096,18 @@ static void addrindex_write_book( FILE *fp, AddressDataSource *ds, gint lvl ) {
        AddressBookFile *abf = ds->rawDataSource;
        if( abf ) {
                addrindex_write_elem_s( fp, lvl, TAG_DS_ADDRESS_BOOK );
-               addrindex_write_attr( fp, ATTAG_BOOK_NAME, abf->name );
+               addrindex_write_attr( fp, ATTAG_BOOK_NAME, addrbook_get_name( abf ) );
                addrindex_write_attr( fp, ATTAG_BOOK_FILE, abf->fileName );
                fputs( " />\n", fp );
        }
 }
 
 static AddressDataSource *addrindex_parse_vcard( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
+       AddressDataSource *ds;
        VCardFile *vcf;
        GList *attr;
 
+       ds = addrindex_create_datasource( ADDR_IF_VCARD );
        vcf = vcard_create();
        attr = xml_get_current_tag_attr( file );
        while( attr ) {
@@ -741,7 +1129,7 @@ static void addrindex_write_vcard( FILE *fp, AddressDataSource *ds, gint lvl ) {
        VCardFile *vcf = ds->rawDataSource;
        if( vcf ) {
                addrindex_write_elem_s( fp, lvl, TAG_DS_VCARD );
-               addrindex_write_attr( fp, ATTAG_VCARD_NAME, vcf->name );
+               addrindex_write_attr( fp, ATTAG_VCARD_NAME, vcard_get_name( vcf ) );
                addrindex_write_attr( fp, ATTAG_VCARD_FILE, vcf->path );
                fputs( " />\n", fp );
        }
@@ -749,10 +1137,11 @@ static void addrindex_write_vcard( FILE *fp, AddressDataSource *ds, gint lvl ) {
 
 #ifdef USE_JPILOT
 static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
+       AddressDataSource *ds;
        JPilotFile *jpf;
        GList *attr;
 
+       ds = addrindex_create_datasource( ADDR_IF_JPILOT );
        jpf = jpilot_create();
        attr = xml_get_current_tag_attr( file );
        while( attr ) {
@@ -789,13 +1178,14 @@ static void addrindex_write_jpilot( FILE *fp,AddressDataSource *ds, gint lvl ) {
                GList *node;
                GList *customLbl = jpilot_get_custom_labels( jpf );
                addrindex_write_elem_s( fp, lvl, TAG_DS_JPILOT );
-               addrindex_write_attr( fp, ATTAG_JPILOT_NAME, jpf->name );
+               addrindex_write_attr( fp, ATTAG_JPILOT_NAME, jpilot_get_name( jpf ) );
                addrindex_write_attr( fp, ATTAG_JPILOT_FILE, jpf->path );
                node = customLbl;
                ind = 1;
                while( node ) {
                        gchar name[256];
-                       sprintf( name, "%s%d", ATTAG_JPILOT_CUSTOM, ind );
+                       g_snprintf( name, sizeof(name), "%s%d",
+                                   ATTAG_JPILOT_CUSTOM, ind );
                        addrindex_write_attr( fp, name, node->data );
                        ind++;
                        node = g_list_next( node );
@@ -803,107 +1193,299 @@ static void addrindex_write_jpilot( FILE *fp,AddressDataSource *ds, gint lvl ) {
                fputs( " />\n", fp );
        }
 }
+
 #else
-// Just read/write name-value pairs
+/*
+ * Just read/write DOM fragments (preserve data found in file).
+ */
 static AddressDataSource *addrindex_parse_jpilot( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
-       GList *list = addrindex_read_attributes( file );
-       ds->rawDataSource = list;
+       AddressDataSource *ds;
+
+       ds = addrindex_create_datasource( ADDR_IF_JPILOT );
+       ds->rawDataSource = addrindex_read_fragment( file );
        return ds;
 }
 
 static void addrindex_write_jpilot( FILE *fp, AddressDataSource *ds, gint lvl ) {
-       GList *list = ds->rawDataSource;
-       if( list ) {
-               addrindex_write_attributes( fp, TAG_DS_JPILOT, list, lvl );
+       AddressIfFragment *fragment = ds->rawDataSource;
+       if( fragment ) {
+               addrindex_write_fragment( fp, fragment, lvl );
        }
 }
 #endif
 
 #ifdef USE_LDAP
-static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
-       SyldapServer *server;
+/**
+ * Parse LDAP criteria attribute data from XML file.
+ * \param file Index file.
+ * \param ctl  LDAP control object to populate.
+ */
+static void addrindex_parse_ldap_attrlist( XMLFile *file, LdapControl *ctl ) {
+       guint prevLevel;
+       XMLTag *xtag;
+       XMLTag *xtagPrev;
+       gint rc;
        GList *attr;
+       GList *list;
+       GList *node;
+
+       if( file == NULL ) {
+               return;
+       }
+
+       list = NULL;
+       prevLevel = file->level;
+       xtagPrev = xml_get_current_tag( file );
+       while( TRUE ) {
+               rc = xml_parse_next_tag( file );
+               if( rc != 0 ) {
+                       /* Terminate prematurely */
+                       mgu_free_dlist( list );
+                       list = NULL;
+                       return;
+               }
+               if( file->level < prevLevel ) {
+                       /* We must be above level we start at */
+                       break;
+               }
+
+               /* Get a tag (element) */
+               xtag = xml_get_current_tag( file );
+               if( strcmp( xtag->tag, ELTAG_LDAP_ATTR_SRCH ) == 0 ) {
+                       /* LDAP criteria attribute */
+                       attr = xml_get_current_tag_attr( file );
+                       while( attr ) {
+                               gchar *name = ((XMLAttr *)attr->data)->name;
+                               gchar *value = ((XMLAttr *)attr->data)->value;
+                               if( strcmp( name, ATTAG_LDAP_ATTR_NAME ) == 0 ) {
+                                       if( value && strlen( value ) > 0 ) {
+                                               list = g_list_append(
+                                                       list, g_strdup( value ) );
+                                       }
+                               }
+                               attr = g_list_next( attr );
+                       }
+               }
+               else {
+                       if( xtag != xtagPrev ) {
+                               /* Found a new tag */
+                               break;
+                       }
+               }
+               xtag = xtagPrev;
+       }
+
+       /* Build list of search attributes */
+       ldapctl_criteria_list_clear( ctl );
+       node = list;
+       while( node ) {
+               ldapctl_criteria_list_add( ctl, node->data );
+               g_free( node->data );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+       g_list_free( list );
+       list = NULL;
 
-       server = syldap_create();
+}
+
+void ldapsvr_set_control( LdapServer *server, LdapControl *ctl );
+/**
+ * Parse LDAP control data from XML file.
+ * \param  file Index file.
+ * \return Initialized data soruce object.
+ */
+static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) {
+       AddressDataSource *ds;
+       LdapServer *server;
+       LdapControl *ctl;
+       GList *attr;
+       gchar *serverName = NULL;
+       gchar *criteria = NULL;
+       gboolean bDynSearch;
+       gboolean bTLS, bSSL;
+       gint iMatch;
+
+       /* g_print( "addrindex_parse_ldap\n" ); */
+       /* Set up some defaults */
+       bDynSearch = FALSE;
+       bTLS = FALSE;
+       bSSL = FALSE;
+       iMatch = LDAPCTL_MATCH_BEGINWITH;
+
+       ds = addrindex_create_datasource( ADDR_IF_LDAP );
+       ctl = ldapctl_create();
        attr = xml_get_current_tag_attr( file );
        while( attr ) {
                gchar *name = ((XMLAttr *)attr->data)->name;
                gchar *value = ((XMLAttr *)attr->data)->value;
                gint ivalue = atoi( value );
+
                if( strcmp( name, ATTAG_LDAP_NAME ) == 0 ) {
-                       syldap_set_name( server, value );
+                       g_free( serverName );
+                       serverName = g_strdup( value );
                }
                else if( strcmp( name, ATTAG_LDAP_HOST ) == 0 ) {
-                       syldap_set_host( server, value );
+                       ldapctl_set_host( ctl, value );
                }
                else if( strcmp( name, ATTAG_LDAP_PORT ) == 0 ) {
-                       syldap_set_port( server, ivalue );
+                       ldapctl_set_port( ctl, ivalue );
                }
                else if( strcmp( name, ATTAG_LDAP_BASE_DN ) == 0 ) {
-                       syldap_set_base_dn( server, value );
+                       ldapctl_set_base_dn( ctl, value );
                }
                else if( strcmp( name, ATTAG_LDAP_BIND_DN ) == 0 ) {
-                       syldap_set_bind_dn( server, value );
+                       ldapctl_set_bind_dn( ctl, value );
                }
                else if( strcmp( name, ATTAG_LDAP_BIND_PASS ) == 0 ) {
-                       syldap_set_bind_password( server, value );
+                       ldapctl_set_bind_password( ctl, value );
                }
                else if( strcmp( name, ATTAG_LDAP_CRITERIA ) == 0 ) {
-                       syldap_set_search_criteria( server, value );
+                       g_free( criteria );
+                       criteria = g_strdup( value );
+                       g_print("criteria %s\n", criteria);
                }
                else if( strcmp( name, ATTAG_LDAP_MAX_ENTRY ) == 0 ) {
-                       syldap_set_max_entries( server, ivalue );
+                       ldapctl_set_max_entries( ctl, ivalue );
                }
                else if( strcmp( name, ATTAG_LDAP_TIMEOUT ) == 0 ) {
-                       syldap_set_timeout( server, ivalue );
+                       ldapctl_set_timeout( ctl, ivalue );
+               }
+               else if( strcmp( name, ATTAG_LDAP_MAX_AGE ) == 0 ) {
+                       ldapctl_set_max_query_age( ctl, ivalue );
+               }
+               else if( strcmp( name, ATTAG_LDAP_DYN_SEARCH ) == 0 ) {
+                       bDynSearch = FALSE;
+                       if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
+                               bDynSearch = TRUE;
+                       }
+               }
+               else if( strcmp( name, ATTAG_LDAP_MATCH_OPT ) == 0 ) {
+                       iMatch = LDAPCTL_MATCH_BEGINWITH;
+                       if( strcmp( value, ATVAL_LDAP_MATCH_CONTAINS ) == 0 ) {
+                               iMatch = LDAPCTL_MATCH_CONTAINS;
+                       }
+               }
+               else if( strcmp( name, ATTAG_LDAP_ENABLE_TLS ) == 0 ) {
+                       bTLS = FALSE;
+                       if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
+                               bTLS = TRUE;
+                       }
+               }
+               else if( strcmp( name, ATTAG_LDAP_ENABLE_SSL ) == 0 ) {
+                       bSSL = FALSE;
+                       if( strcmp( value, ATVAL_BOOLEAN_YES ) == 0 ) {
+                               bSSL = TRUE;
+                       }
                }
                attr = g_list_next( attr );
        }
 
+       server = ldapsvr_create_noctl();
+       ldapsvr_set_name( server, serverName );
+       ldapsvr_set_search_flag( server, bDynSearch );
+       ldapctl_set_matching_option( ctl, iMatch );
+#ifdef USE_LDAP_TLS
+       ldapctl_set_tls( ctl, bTLS );
+       ldapctl_set_ssl( ctl, bSSL );
+#endif
+       g_free( serverName );
+       ldapsvr_set_control( server, ctl );
        ds->rawDataSource = server;
+
+       addrindex_parse_ldap_attrlist( file, ctl );
+       /*
+        * If criteria have been specified and no attributes were listed, then
+        * convert old style criteria into an attribute list. Any criteria will
+        * be dropped when saving data.
+        */
+       if( criteria ) {
+               if( ! ldapctl_get_criteria_list( ctl ) ) {
+                       ldapctl_parse_ldap_search( ctl, criteria );
+               }
+               g_free( criteria );
+       }
+       /* ldapsvr_print_data( server, stdout ); */
+
        return ds;
 }
 
 static void addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) {
-       SyldapServer *server = ds->rawDataSource;
+       LdapServer *server = ds->rawDataSource;
+       LdapControl *ctl = NULL;
+       GList *node;
+       gchar value[256];
+
        if( server ) {
-               gchar value[256];
+               ctl = server->control;
+       }
+       if( ctl == NULL ) return;
+
+       /* Output start element with attributes */
+       addrindex_write_elem_s( fp, lvl, TAG_DS_LDAP );
+       addrindex_write_attr( fp, ATTAG_LDAP_NAME, ldapsvr_get_name( server ) );
+       addrindex_write_attr( fp, ATTAG_LDAP_HOST, ctl->hostName );
+
+       sprintf( value, "%d", ctl->port );      
+       addrindex_write_attr( fp, ATTAG_LDAP_PORT, value );
 
-               addrindex_write_elem_s( fp, lvl, TAG_DS_LDAP );
-               addrindex_write_attr( fp, ATTAG_LDAP_NAME, server->name );
-               addrindex_write_attr( fp, ATTAG_LDAP_HOST, server->hostName );
+       addrindex_write_attr( fp, ATTAG_LDAP_BASE_DN, ctl->baseDN );
+       addrindex_write_attr( fp, ATTAG_LDAP_BIND_DN, ctl->bindDN );
+       addrindex_write_attr( fp, ATTAG_LDAP_BIND_PASS, ctl->bindPass );
 
-               sprintf( value, "%d", server->port );   
-               addrindex_write_attr( fp, ATTAG_LDAP_PORT, value );
+       sprintf( value, "%d", ctl->maxEntries );
+       addrindex_write_attr( fp, ATTAG_LDAP_MAX_ENTRY, value );
+       sprintf( value, "%d", ctl->timeOut );
+       addrindex_write_attr( fp, ATTAG_LDAP_TIMEOUT, value );
+       sprintf( value, "%d", ctl->maxQueryAge );
+       addrindex_write_attr( fp, ATTAG_LDAP_MAX_AGE, value );
 
-               addrindex_write_attr( fp, ATTAG_LDAP_BASE_DN, server->baseDN );
-               addrindex_write_attr( fp, ATTAG_LDAP_BIND_DN, server->bindDN );
-               addrindex_write_attr( fp, ATTAG_LDAP_BIND_PASS, server->bindPass );
-               addrindex_write_attr( fp, ATTAG_LDAP_CRITERIA, server->searchCriteria );
+       addrindex_write_attr( fp, ATTAG_LDAP_DYN_SEARCH,
+                       server->searchFlag ?
+                       ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO );
 
-               sprintf( value, "%d", server->maxEntries );
-               addrindex_write_attr( fp, ATTAG_LDAP_MAX_ENTRY, value );
-               sprintf( value, "%d", server->timeOut );
-               addrindex_write_attr( fp, ATTAG_LDAP_TIMEOUT, value );
+       addrindex_write_attr( fp, ATTAG_LDAP_MATCH_OPT,
+               ( ctl->matchingOption == LDAPCTL_MATCH_CONTAINS ) ?
+               ATVAL_LDAP_MATCH_CONTAINS : ATVAL_LDAP_MATCH_BEGIN );
 
+       addrindex_write_attr( fp, ATTAG_LDAP_ENABLE_TLS,
+                       ctl->enableTLS ?
+                       ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO );
+       addrindex_write_attr( fp, ATTAG_LDAP_ENABLE_SSL,
+                       ctl->enableSSL ?
+                       ATVAL_BOOLEAN_YES : ATVAL_BOOLEAN_NO );
+
+       fputs(" >\n", fp);
+
+       /* Output attributes */
+       node = ldapctl_get_criteria_list( ctl );
+       while( node ) {
+               addrindex_write_elem_s( fp, 1+lvl, ELTAG_LDAP_ATTR_SRCH );
+               addrindex_write_attr( fp, ATTAG_LDAP_ATTR_NAME, node->data );
                fputs(" />\n", fp);
+               node = g_list_next( node );
        }
+
+       /* End of element */    
+       addrindex_write_elem_e( fp, lvl, TAG_DS_LDAP );
 }
+
 #else
-// Just read/write name-value pairs
+/*
+ * Just read/write DOM fragments (preserve data found in file).
+ */
 static AddressDataSource *addrindex_parse_ldap( XMLFile *file ) {
-       AddressDataSource *ds = g_new0( AddressDataSource, 1 );
-       GList *list = addrindex_read_attributes( file );
-       ds->data = list;
+       AddressDataSource *ds;
+
+       ds = addrindex_create_datasource( ADDR_IF_LDAP );
+       ds->rawDataSource = addrindex_read_fragment( file );
        return ds;
 }
 
 static void addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) {
-       GList *list = ds->rawDataSource;
-       if( list ) {
-               addrindex_write_attributes( fp, TAG_DS_LDAP, list, lvl );
+       AddressIfFragment *fragment = ds->rawDataSource;
+       if( fragment ) {
+               addrindex_write_fragment( fp, fragment, lvl );
        }
 }
 #endif
@@ -912,89 +1494,108 @@ static void addrindex_write_ldap( FILE *fp, AddressDataSource *ds, gint lvl ) {
 * Address index I/O functions.
 * ***********************************************************************
 */
+/**
+ * Read address index file, creating appropriate data sources for each address
+ * index file entry.
+ *
+ * \param  addrIndex Address index.
+ * \param  file Address index file.
+ */
 static void addrindex_read_index( AddressIndex *addrIndex, XMLFile *file ) {
        guint prev_level;
-       gchar *element;
-       GList *attr;
        XMLTag *xtag;
        AddressInterface *iface = NULL, *dsIFace = NULL;
        AddressDataSource *ds;
+       gint rc;
 
+       addrIndex->loadedFlag = FALSE;
        for (;;) {
                prev_level = file->level;
-               xml_parse_next_tag( file );
-               if( file->level < prev_level ) return;
+               rc = xml_parse_next_tag( file );
+               if( file->level == 0 ) return;
 
                xtag = xml_get_current_tag( file );
-               // printf( "tag : %s\n", xtag->tag );
 
                iface = addrindex_tag_get_interface( addrIndex, xtag->tag, ADDR_IF_NONE );
                if( iface ) {
                        addrIndex->lastType = iface->type;
                        if( iface->legacyFlag ) addrIndex->needsConversion = TRUE;
-                       // printf( "found : %s\n", iface->name );
                }
                else {
-                       dsIFace = addrindex_tag_get_datasource( addrIndex, addrIndex->lastType, xtag->tag );
+                       dsIFace = addrindex_tag_get_datasource(
+                                       addrIndex, addrIndex->lastType, xtag->tag );
                        if( dsIFace ) {
-                               // Add data source to list
-                               // printf( "\tdata source: %s\n", dsIFace->name );
+                               /* Add data source to list */
                                ds = NULL;
                                if( addrIndex->lastType == ADDR_IF_BOOK ) {
                                        ds = addrindex_parse_book( file );
                                        if( ds->rawDataSource ) {
-                                               addrbook_set_path( ds->rawDataSource, addrIndex->filePath );
-                                               // addrbook_print_book( ds->rawDataSource, stdout );
+                                               addrbook_set_path( ds->rawDataSource,
+                                                       addrIndex->filePath );
                                        }
                                }
                                else if( addrIndex->lastType == ADDR_IF_VCARD ) {
                                        ds = addrindex_parse_vcard( file );
-                                       // if( ds->rawDataSource ) {
-                                       //      vcard_print_file( ds->rawDataSource, stdout );
-                                       // }
                                }
                                else if( addrIndex->lastType == ADDR_IF_JPILOT ) {
                                        ds = addrindex_parse_jpilot( file );
-                                       /*
-                                       if( ds->rawDataSource ) {
-                                               jpilot_print_file( ds->rawDataSource, stdout );
-                                               // addrindex_print_attributes( ds->rawDataSource, stdout );
-                                       }
-                                       */
                                }
                                else if( addrIndex->lastType == ADDR_IF_LDAP ) {
                                        ds = addrindex_parse_ldap( file );
-                                       /*
-                                       if( ds->rawDataSource ) {
-                                               syldap_print_data( ds->rawDataSource, stdout );
-                                               // addrindex_print_attributes( ds->rawDataSource, stdout );
-                                       }
-                                       */
                                }
                                if( ds ) {
-                                       ds->type = addrIndex->lastType;
                                        ds->interface = dsIFace;
-                                       dsIFace->listSource = g_list_append( dsIFace->listSource, ds );
+                                       addrindex_hash_add_cache( addrIndex, ds );
+                                       dsIFace->listSource =
+                                               g_list_append( dsIFace->listSource, ds );
                                }
-                               // printf( "=============================\n\n" );
                        }
                }
-               /*
-               element = xml_get_element( file );
-               attr = xml_get_current_tag_attr( file );
-               if( _interfaceLast_ && ! _interfaceLast_->legacyFlag ) {
-                       show_attribs( attr );
-                       printf( "\ttag  value : %s :\n", element );
+       }
+}
+
+/*
+ * Search order sorting comparison function for building search order list.
+ */
+static gint addrindex_search_order_compare( gconstpointer ptrA, gconstpointer ptrB ) {
+       AddressInterface *ifaceA = ( AddressInterface * ) ptrA;
+       AddressInterface *ifaceB = ( AddressInterface * ) ptrB;
+
+       return ifaceA->searchOrder - ifaceB->searchOrder;
+}
+
+/**
+ * Build list of data sources to process.
+ * \param addrIndex Address index object.
+ */
+static void addrindex_build_search_order( AddressIndex *addrIndex ) {
+       GList *nodeIf;
+
+       /* Clear existing list */
+       g_list_free( addrIndex->searchOrder );
+       addrIndex->searchOrder = NULL;
+
+       /* Build new list */
+       nodeIf = addrIndex->interfaceList;
+       while( nodeIf ) {
+               AddressInterface *iface = nodeIf->data;
+               if( iface->useInterface ) {
+                       if( iface->searchOrder > 0 ) {
+                               /* Add to search order list */
+                               addrIndex->searchOrder = g_list_insert_sorted(
+                                       addrIndex->searchOrder, iface,
+                                       addrindex_search_order_compare );
+                       }
                }
-               */
-               addrindex_read_index( addrIndex, file );
+               nodeIf = g_list_next( nodeIf );
        }
 }
 
 static gint addrindex_read_file( AddressIndex *addrIndex ) {
        XMLFile *file = NULL;
        gchar *fileSpec = NULL;
-       g_return_if_fail( addrIndex != NULL );
+
+       g_return_val_if_fail( addrIndex != NULL, -1 );
 
        fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, addrIndex->fileName, NULL );
        addrIndex->retVal = MGU_NO_FILE;
@@ -1002,7 +1603,9 @@ static gint addrindex_read_file( AddressIndex *addrIndex ) {
        g_free( fileSpec );
 
        if( file == NULL ) {
-               // fprintf( stdout, " file '%s' does not exist.\n", addrIndex->fileName );
+               /*
+               g_print( " file '%s' does not exist.\n", addrIndex->fileName );
+               */
                return addrIndex->retVal;
        }
 
@@ -1017,6 +1620,8 @@ static gint addrindex_read_file( AddressIndex *addrIndex ) {
        }
        xml_close_file( file );
 
+       addrindex_build_search_order( addrIndex );
+
        return addrIndex->retVal;
 }
 
@@ -1063,19 +1668,19 @@ static void addrindex_write_index( AddressIndex *addrIndex, FILE *fp ) {
 * return: Status code, from addrIndex->retVal.
 * Note: File will be created in directory specified by addrIndex.
 */
-gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
+static gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
        FILE *fp;
        gchar *fileSpec;
 #ifndef DEV_STANDALONE
        PrefFile *pfile;
 #endif
 
-       g_return_if_fail( addrIndex != NULL );
+       g_return_val_if_fail( addrIndex != NULL, -1 );
 
        fileSpec = g_strconcat( addrIndex->filePath, G_DIR_SEPARATOR_S, newFile, NULL );
        addrIndex->retVal = MGU_OPEN_FILE;
 #ifdef DEV_STANDALONE
-       fp = fopen( fileSpec, "w" );
+       fp = g_fopen( fileSpec, "wb" );
        g_free( fileSpec );
        if( fp ) {
                fputs( "<?xml version=\"1.0\" ?>\n", fp );
@@ -1084,8 +1689,7 @@ gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
        g_free( fileSpec );
        if( pfile ) {
                fp = pfile->fp;
-               fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n",
-                               conv_get_current_charset_str() );
+               fprintf( fp, "<?xml version=\"1.0\" encoding=\"%s\" ?>\n", CS_INTERNAL );
 #endif
                addrindex_write_elem_s( fp, 0, TAG_ADDRESS_INDEX );
                fputs( ">\n", fp );
@@ -1097,7 +1701,7 @@ gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
 #ifdef DEV_STANDALONE
                fclose( fp );
 #else
-               if( prefs_write_close( pfile ) < 0 ) {
+               if( prefs_file_close( pfile ) < 0 ) {
                        addrIndex->retVal = MGU_ERROR_WRITE;
                }
 #endif
@@ -1112,8 +1716,44 @@ gint addrindex_write_to( AddressIndex *addrIndex, const gchar *newFile ) {
 * return: Status code, from addrIndex->retVal.
 */
 gint addrindex_save_data( AddressIndex *addrIndex ) {
-       g_return_if_fail( addrIndex != NULL );
+#ifdef USE_LDAP
+       GList *nodeIf;
+       GList *nodeDS;
+#endif
+       
+       g_return_val_if_fail( addrIndex != NULL, -1 );
 
+#ifdef USE_LDAP
+       nodeIf = addrIndex->interfaceList;
+       /* save LDAP interfaces */
+       while ( nodeIf ) {
+               AddressInterface *iface = nodeIf->data;
+               if( iface->type == ADDR_IF_LDAP ) {
+                       nodeDS = iface->listSource;
+                       while( nodeDS ) {
+                               AddressDataSource *ds = nodeDS->data;
+                               LdapServer *abf = ds->rawDataSource;
+                               if( ldapsvr_get_read_flag( abf ) ) {
+                                       if( ldapsvr_get_modified( abf ) ) {
+                                               ldapsvr_update_book( abf, NULL );
+                                               if( abf->retVal != LDAPRC_SUCCESS ) {
+                                                       alertpanel( _("Address(es) update"),
+                                                               _("Update failed. Changes not written to Directory."),
+                                                               GTK_STOCK_CLOSE, NULL, NULL );
+                                               }
+                                               else {
+                                                       abf->retVal = MGU_SUCCESS;
+                                                       ldapsvr_set_modified( abf, FALSE );
+                                               }
+                                       }
+                               }
+                               nodeDS = g_list_next( nodeDS );
+                       }
+                       break;
+               }
+               nodeIf = g_list_next( nodeIf );
+       }
+#endif
        addrIndex->retVal = MGU_NO_FILE;
        if( addrIndex->fileName == NULL || *addrIndex->fileName == '\0' ) return addrIndex->retVal;
        if( addrIndex->filePath == NULL || *addrIndex->filePath == '\0' ) return addrIndex->retVal;
@@ -1132,6 +1772,7 @@ gint addrindex_save_data( AddressIndex *addrIndex ) {
 gint addrindex_save_all_books( AddressIndex *addrIndex ) {
        gint retVal = MGU_SUCCESS;
        GList *nodeIf, *nodeDS;
+
        nodeIf = addrIndex->interfaceList;
        while( nodeIf ) {
                AddressInterface *iface = nodeIf->data;
@@ -1140,8 +1781,8 @@ gint addrindex_save_all_books( AddressIndex *addrIndex ) {
                        while( nodeDS ) {
                                AddressDataSource *ds = nodeDS->data;
                                AddressBookFile *abf = ds->rawDataSource;
-                               if( abf->dirtyFlag ) {
-                                       if( abf->readFlag ) {
+                               if( addrbook_get_dirty( abf ) ) {
+                                       if( addrbook_get_read_flag( abf ) ) {
                                                addrbook_save_data( abf );
                                                if( abf->retVal != MGU_SUCCESS ) {
                                                        retVal = abf->retVal;
@@ -1213,6 +1854,7 @@ static AddressCvtNode *addrindex_parse_item( XMLFile *file ) {
                if( xml_compare_tag( file, ELTAG_IF_OLD_REMARKS ) ) {
                        nn->remarks = g_strdup( element );
                }
+               g_free(element);
                xml_parse_next_tag(file);
        }
 }
@@ -1275,7 +1917,7 @@ static void addrindex_add_obj( XMLFile *file, AddressCvtNode *node ) {
                        node->list = g_list_append( node->list, newNode );
                }
                else {
-                       // printf( "invalid: !!! \n" );
+                       /* g_print( "invalid: !!! \n" ); */
                        attr = xml_get_current_tag_attr( file );
                }
        }
@@ -1296,11 +1938,11 @@ static void addrindex_consume_tree( XMLFile *file ) {
                if (file->level < prev_level) return;
 
                xtag = xml_get_current_tag( file );
-               // printf( "tag : %s\n", xtag->tag );
+               /* g_print( "tag : %s\n", xtag->tag ); */
                element = xml_get_element( file );
                attr = xml_get_current_tag_attr( file );
-               // show_attribs( attr );
-               // printf( "\ttag  value : %s :\n", element );
+               /* show_attribs( attr ); */
+               /* g_print( "\ttag  value : %s :\n", element ); */
                addrindex_consume_tree( file );
        }
 }
@@ -1310,6 +1952,7 @@ static void addrindex_consume_tree( XMLFile *file ) {
 */
 static void addrindex_print_node( AddressCvtNode *node, FILE *stream  ) {
        GList *list;
+
        fprintf( stream, "Node:\ttype :%d:\n", node->type );
        fprintf( stream, "\tname :%s:\n", node->name );
        fprintf( stream, "\taddr :%s:\n", node->address );
@@ -1331,6 +1974,7 @@ static void addrindex_print_node( AddressCvtNode *node, FILE *stream  ) {
 */
 static void addrindex_free_node( AddressCvtNode *node ) {
        GList *list = node->list;
+
        while( list ) {
                AddressCvtNode *lNode = list->data;
                list = g_list_next( list );
@@ -1352,7 +1996,7 @@ static void addrindex_process_node(
                ItemGroup *parentGrp, ItemFolder *folderGrp )
 {
        GList *list;
-       ItemFolder *itemFolder;
+       ItemFolder *itemFolder = NULL;
        ItemGroup *itemGParent = parentGrp;
        ItemFolder *itemGFolder = folderGrp;
        AddressCache *cache = abf->addressCache;
@@ -1371,7 +2015,7 @@ static void addrindex_process_node(
                ItemGroup *itemGroup;
                gchar *fName;
 
-               // Create a folder for group
+               /* Create a folder for group */
                fName = g_strdup_printf( "Cvt - %s", node->name );
                itemGFolder = addritem_create_item_folder();
                addritem_folder_set_name( itemGFolder, fName );
@@ -1379,7 +2023,7 @@ static void addrindex_process_node(
                addrcache_folder_add_folder( cache, parent, itemGFolder );
                g_free( fName );
 
-               // Add group into folder
+               /* Add group into folder */
                itemGroup = addritem_create_item_group();
                addritem_group_set_name( itemGroup, node->name );
                addrcache_id_group( cache, itemGroup );
@@ -1390,7 +2034,7 @@ static void addrindex_process_node(
                ItemPerson *itemPerson;
                ItemEMail *itemEMail;
 
-               // Create person and email objects
+               /* Create person and email objects */
                itemPerson = addritem_create_item_person();
                addritem_person_set_common_name( itemPerson, node->name );
                addrcache_id_person( cache, itemPerson );
@@ -1400,7 +2044,7 @@ static void addrindex_process_node(
                addrcache_id_email( cache, itemEMail );
                addrcache_person_add_email( cache, itemPerson, itemEMail );
 
-               // Add person into appropriate folder
+               /* Add person into appropriate folder */
                if( itemGFolder ) {
                        addrcache_folder_add_person( cache, itemGFolder, itemPerson );
                }
@@ -1408,7 +2052,7 @@ static void addrindex_process_node(
                        addrcache_folder_add_person( cache, parent, itemPerson );
                }
 
-               // Add email address only into group
+               /* Add email address only into group */
                if( parentGrp ) {
                        addrcache_group_add_email( cache, parentGrp, itemEMail );
                }
@@ -1433,20 +2077,20 @@ static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file,
        GList *fileList = NULL;
        gint fileNum  = 0;
 
-       // Setup root node
+       /* Setup root node */
        rootNode = g_new0( AddressCvtNode, 1 );
        rootNode->type = TEMPNODE_ROOT;
        rootNode->name = g_strdup( "root" );
        rootNode->list = NULL;
        addrindex_add_obj( file, rootNode );
-       // addrindex_print_node( rootNode, stdout );
+       /* addrindex_print_node( rootNode, stdout ); */
 
-       // Create new address book
+       /* Create new address book */
        abf = addrbook_create_book();
        addrbook_set_name( abf, displayName );
        addrbook_set_path( abf, addrIndex->filePath );
 
-       // Determine next available file number
+       /* Determine next available file number */
        fileList = addrbook_get_bookfile_list( abf );
        if( fileList ) {
                fileNum = 1 + abf->maxValue;
@@ -1461,7 +2105,7 @@ static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file,
 
        addrindex_process_node( abf, rootNode, abf->addressCache->rootFolder, NULL, NULL );
 
-       // addrbook_dump_book( abf, stdout );
+       /* addrbook_dump_book( abf, stdout ); */
        addrbook_save_data( abf );
        addrIndex->retVal = abf->retVal;
        if( abf->retVal == MGU_SUCCESS ) retVal = TRUE;
@@ -1471,7 +2115,7 @@ static gboolean addrindex_process_book( AddressIndex *addrIndex, XMLFile *file,
        addrindex_free_node( rootNode );
        rootNode = NULL;
 
-       // Create entries in address index
+       /* Create entries in address index */
        if( retVal ) {
                abf = addrbook_create_book();
                addrbook_set_name( abf, displayName );
@@ -1492,14 +2136,14 @@ static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) {
        GList *attr;
        XMLTag *xtag;
 
-       // Process file
+       /* Process file */
        for (;;) {
                prev_level = file->level;
                xml_parse_next_tag( file );
                if (file->level < prev_level) return;
 
                xtag = xml_get_current_tag( file );
-               // printf( "tag : %d : %s\n", prev_level, xtag->tag );
+               /* g_print( "tag : %d : %s\n", prev_level, xtag->tag ); */
                if( strcmp( xtag->tag, TAG_IF_OLD_COMMON ) == 0 ) {
                        if( addrindex_process_book( addrIndex, file, DISP_OLD_COMMON ) ) {
                                addrIndex->needsConversion = FALSE;
@@ -1518,8 +2162,8 @@ static void addrindex_convert_tree( AddressIndex *addrIndex, XMLFile *file ) {
                }
                element = xml_get_element( file );
                attr = xml_get_current_tag_attr( file );
-               // show_attribs( attr );
-               // printf( "\ttag  value : %s :\n", element );
+               /* show_attribs( attr ); */
+               /* g_print( "\ttag  value : %s :\n", element ); */
                addrindex_consume_tree( file );
        }
 }
@@ -1534,7 +2178,7 @@ static gint addrindex_convert_data( AddressIndex *addrIndex ) {
        g_free( fileSpec );
 
        if( file == NULL ) {
-               // fprintf( stdout, " file '%s' does not exist.\n", addrIndex->fileName );
+               /* g_print( " file '%s' does not exist.\n", addrIndex->fileName ); */
                return addrIndex->retVal;
        }
 
@@ -1560,12 +2204,12 @@ static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displ
        GList *fileList = NULL;
        gint fileNum = 0;
 
-       // Create new address book
+       /* Create new address book */
        abf = addrbook_create_book();
        addrbook_set_name( abf, displayName );
        addrbook_set_path( abf, addrIndex->filePath );
 
-       // Determine next available file number
+       /* Determine next available file number */
        fileList = addrbook_get_bookfile_list( abf );
        if( fileList ) {
                fileNum = 1 + abf->maxValue;
@@ -1584,7 +2228,7 @@ static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displ
        addrbook_free_book( abf );
        abf = NULL;
 
-       // Create entries in address index
+       /* Create entries in address index */
        if( retVal ) {
                abf = addrbook_create_book();
                addrbook_set_name( abf, displayName );
@@ -1607,7 +2251,7 @@ static gboolean addrindex_create_new_book( AddressIndex *addrIndex, gchar *displ
 *      "Gathered addresses" - a new address book.
 */
 gint addrindex_read_data( AddressIndex *addrIndex ) {
-       g_return_if_fail( addrIndex != NULL );
+       g_return_val_if_fail( addrIndex != NULL, -1 );
 
        addrIndex->conversionError = FALSE;
        addrindex_read_file( addrIndex );
@@ -1638,7 +2282,7 @@ gint addrindex_read_data( AddressIndex *addrIndex ) {
 gint addrindex_create_new_books( AddressIndex *addrIndex ) {
        gboolean flg;
 
-       g_return_if_fail( addrIndex != NULL );
+       g_return_val_if_fail( addrIndex != NULL, -1 );
 
        flg = addrindex_create_new_book( addrIndex, DISP_NEW_COMMON );
        if( flg ) {
@@ -1728,6 +2372,10 @@ gint addrindex_ds_read_data( AddressDataSource *ds ) {
        iface = ds->interface;
        if( iface == NULL ) return retVal;
        if( iface->getReadData ) {
+               /*
+               gchar *name = ( iface->getName ) ( ds->rawDataSource );
+               g_print( "addrindex_ds_read_data...reading:::%s:::\n", name );
+               */
                retVal = ( iface->getReadData ) ( ds->rawDataSource );
        }
        return retVal;
@@ -1749,38 +2397,6 @@ ItemFolder *addrindex_ds_get_root_folder( AddressDataSource *ds ) {
        return retVal;
 }
 
-/*
- * Return list of folders for specified data source.
- */
-GList *addrindex_ds_get_list_folder( AddressDataSource *ds ) {
-       GList *retVal = FALSE;
-       AddressInterface *iface;
-
-       if( ds == NULL ) return retVal;
-       iface = ds->interface;
-       if( iface == NULL ) return retVal;
-       if( iface->getListFolder ) {
-               retVal = ( iface->getListFolder ) ( ds->rawDataSource );
-       }
-       return retVal;
-}
-
-/*
- * Return list of persons in root folder for specified data source.
- */
-GList *addrindex_ds_get_list_person( AddressDataSource *ds ) {
-       GList *retVal = FALSE;
-       AddressInterface *iface;
-
-       if( ds == NULL ) return retVal;
-       iface = ds->interface;
-       if( iface == NULL ) return retVal;
-       if( iface->getListPerson ) {
-               retVal = ( iface->getListPerson ) ( ds->rawDataSource );
-       }
-       return retVal;
-}
-
 /*
  * Return name for specified data source.
  */
@@ -1825,7 +2441,7 @@ gboolean addrindex_ds_get_readonly( AddressDataSource *ds ) {
 /*
  * Return list of all persons for specified data source.
  */
-GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
+static GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
        GList *retVal = NULL;
        AddressInterface *iface;
 
@@ -1839,7 +2455,582 @@ GList *addrindex_ds_get_all_persons( AddressDataSource *ds ) {
 }
 
 /*
-* End of Source.
+ * Return list of all groups for specified data source.
+ */
+static GList *addrindex_ds_get_all_groups( AddressDataSource *ds ) {
+       GList *retVal = NULL;
+       AddressInterface *iface;
+
+       if( ds == NULL ) return retVal;
+       iface = ds->interface;
+       if( iface == NULL ) return retVal;
+       if( iface->getAllGroups ) {
+               retVal = ( iface->getAllGroups ) ( ds->rawDataSource );
+       }
+       return retVal;
+}
+
+/* **********************************************************************
+* Address search stuff.
+* ***********************************************************************
+*/
+
+/**
+ * Setup or register the dynamic search that will be performed. The search
+ * is registered with the query manager.
+ *
+ * \param searchTerm    Search term. A private copy will be made.
+ * \param callBackEntry Callback function that should be called when
+ *                     each entry is received.
+ * \param callBackEnd   Callback function that should be called when
+ *                     search has finished running.
+ * \return ID allocated to query that will be executed.
+ */
+gint addrindex_setup_search(
+       const gchar *searchTerm, void *callBackEnd, void *callBackEntry )
+{
+       QueryRequest *req;
+       gint queryID;
+
+       /* Set up a dynamic address query */
+       req = qrymgr_add_request( searchTerm, callBackEnd, callBackEntry );
+       queryID = req->queryID;
+       qryreq_set_search_type( req, ADDRSEARCH_DYNAMIC );
+
+       /* g_print( "***> query ID ::%d::\n", queryID ); */
+       return queryID;
+}
+
+#ifdef USE_LDAP
+
+/*
+ * Function prototypes (not in header file or circular reference errors are
+ * encountered!)
+ */
+LdapQuery *ldapsvr_new_dynamic_search( 
+               LdapServer *server, QueryRequest *req );
+LdapQuery *ldapsvr_new_explicit_search(
+               LdapServer *server, QueryRequest *req, ItemFolder *folder );
+void ldapsvr_execute_query( LdapServer *server, LdapQuery *qry );
+
+#endif
+
+/**
+ * Execute the previously registered dynamic search.
+ *
+ * \param  req Address query object to execute.
+ * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
+ *         failed.
+ */
+static gboolean addrindex_start_dynamic( QueryRequest *req ) {
+       AddressInterface *iface;
+       AddressDataSource *ds;
+       GList *nodeIf;
+       GList *nodeDS;
+       gint type;
+
+       /* g_print( "addrindex_start_dynamic::%d::\n", req->queryID ); */
+       nodeIf = _addressIndex_->searchOrder;
+       while( nodeIf ) {
+               iface = nodeIf->data;
+               nodeIf = g_list_next( nodeIf );
+
+               if( ! iface->useInterface ) {
+                       continue;
+               }
+               if( ! iface->externalQuery ) {
+                       continue;
+               }
+
+               type = iface->type;
+               nodeDS = iface->listSource;
+               while( nodeDS ) {
+                       ds = nodeDS->data;
+                       nodeDS = g_list_next( nodeDS );
+#ifdef USE_LDAP
+                       if( type == ADDR_IF_LDAP ) {
+                               LdapServer *server;
+                               LdapQuery *qry;
+
+                               server = ds->rawDataSource;
+                               if( ! server->searchFlag ) {
+                                       continue;
+                               }
+                               if( ldapsvr_reuse_previous( server, req ) ) {
+                                       continue;
+                               }
+
+                               /* Start a new dynamic search */
+                               qry = ldapsvr_new_dynamic_search( server, req );
+                               if( qry ) {
+                                       ldapsvr_execute_query( server, qry );
+                               }
+                       }
+#endif
+               }
+       }
+       return TRUE;
+}
+
+/**
+ * Stop the previously registered search.
+ *
+ * \param queryID ID of search query to stop.
+ */
+void addrindex_stop_search( const gint queryID ){
+       QueryRequest *req;
+       AddrQueryObject *aqo;
+       GList *node;
+
+       /* g_print( "addrindex_stop_search/queryID=%d\n", queryID ); */
+       /* If query ID does not match, search has not been setup */
+       req = qrymgr_find_request( queryID );
+       if( req == NULL ) {
+               return;
+       }
+
+       /* Stop all queries that were associated with request */
+       node = req->queryList;
+       while( node ) {
+               aqo = node->data;
+#ifdef USE_LDAP
+               if( aqo->queryType == ADDRQUERY_LDAP ) {
+                       LdapQuery *qry = ( LdapQuery * ) aqo;
+                       ldapqry_set_stop_flag( qry, TRUE );
+               }
+#endif
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+
+       /* Delete query request */
+       qrymgr_delete_request( queryID );
+}
+
+/**
+ * Setup or register the explicit search that will be performed. The search is
+ * registered with the query manager.
+ *
+ * \param  ds            Data source to search.
+ * \param  searchTerm    Search term to locate.
+ * \param  folder        Folder to receive search results; may be NULL.
+ * \param  callbackEnd   Function to call when search has terminated.
+ * \param  callbackEntry Function to called for each entry processed.
+ * \return ID allocated to query that will be executed.
+ */
+gint addrindex_setup_explicit_search(
+       AddressDataSource *ds, const gchar *searchTerm, ItemFolder *folder,
+       void *callBackEnd, void *callBackEntry )
+{
+       QueryRequest *req;
+       gint queryID;
+       gchar *name;
+       gchar *mySearch;
+
+       /* Name the query */
+       name = g_strdup_printf( "Search '%s'", searchTerm );
+
+       /* Set up query request */
+       if (!strcmp(searchTerm, "*"))
+               mySearch = g_strdup("*@");
+       else
+               mySearch = g_strdup(searchTerm);
+       
+       req = qrymgr_add_request( mySearch, callBackEnd, callBackEntry );
+
+       g_free(mySearch);
+
+       qryreq_set_search_type( req, ADDRSEARCH_EXPLICIT );
+       queryID = req->queryID;
+
+       if( ds->type == ADDR_IF_LDAP ) {
+#ifdef USE_LDAP
+               LdapServer *server;
+
+               server = ds->rawDataSource;
+               ldapsvr_new_explicit_search( server, req, folder );
+#endif
+       }
+       else {
+               qrymgr_delete_request( queryID );
+               queryID = 0;
+       }
+       g_free( name );
+
+       return queryID;
+}
+
+/**
+ * Execute the previously registered explicit search.
+ *
+ * \param  req Address query request object to execute.
+ * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
+ *         failed.
+ */
+static gboolean addrindex_start_explicit( QueryRequest *req ) {
+       gboolean retVal;
+       AddrQueryObject *aqo;
+
+       retVal = FALSE;
+
+       /* Note: there should only be one query in the list. */
+       aqo = req->queryList->data;
+#ifdef USE_LDAP
+       if( aqo->queryType == ADDRQUERY_LDAP ) {
+               LdapServer *server;
+               LdapQuery *qry;
+
+               qry = ( LdapQuery * ) aqo;
+               server = qry->server;
+
+               /* Start the search */
+               retVal = TRUE;
+               ldapsvr_execute_query( server, qry );
+       }
+#endif
+       return retVal;
+}
+
+/**
+ * Start the previously registered search.
+ *
+ * \param  queryID    ID of search query to be executed.
+ * \return <i>TRUE</i> if search started successfully, or <i>FALSE</i> if
+ *         failed.
+ */
+gboolean addrindex_start_search( const gint queryID ) {
+       gboolean retVal;
+       QueryRequest *req;
+       AddrSearchType searchType;
+
+       retVal = FALSE;
+       /* g_print( "addrindex_start_search/queryID=%d\n", queryID ); */
+       req = qrymgr_find_request( queryID );
+       if( req == NULL ) {
+               return retVal;
+       }
+
+       searchType = req->searchType;
+       if( searchType == ADDRSEARCH_DYNAMIC ) {
+               retVal = addrindex_start_dynamic( req );
+       }
+       else if( searchType == ADDRSEARCH_EXPLICIT ) {
+               retVal = addrindex_start_explicit( req );
+       }
+
+       return retVal;
+}
+
+/**
+ * Remove results (folder and data) for specified data source and folder.
+ * \param ds     Data source to process.
+ * \param folder Results folder to remove.
+ */
+void addrindex_remove_results( AddressDataSource *ds, ItemFolder *folder ) {
+       AddrBookBase *adbase;
+       AddressCache *cache;
+       gint queryID = 0;
+
+       /* g_print( "addrindex_remove_results/start\n" ); */
+
+       /* Test for folder */
+       if( folder->folderType != ADDRFOLDER_QUERY_RESULTS ) return;
+       /* g_print( "folder name ::%s::\n", ADDRITEM_NAME(folder) ); */
+       adbase = ( AddrBookBase * ) ds->rawDataSource;
+       if( adbase == NULL ) return;
+       cache = adbase->addressCache;
+
+       /* Hide folder to prevent re-display */
+       addritem_folder_set_hidden( folder, TRUE );
+
+       if( ds->type == ADDR_IF_LDAP ) {
+#ifdef USE_LDAP
+               LdapQuery *qry;
+               gboolean  delFlag;
+
+               qry = ( LdapQuery * ) folder->folderData;
+               queryID = ADDRQUERY_ID(qry);
+               /* g_print( "calling ldapquery_remove_results...queryID=%d\n", queryID ); */
+               delFlag = ldapquery_remove_results( qry );
+               if (delFlag) {
+                       ldapqry_free( qry );
+               }
+               /* g_print( "calling ldapquery_remove_results...done\n" ); */
+               /*
+               if( delFlag ) {
+                       g_print( "delFlag IS-TRUE\n" );
+               }
+               else {
+                       g_print( "delFlag IS-FALSE\n" );
+               }
+               */
+#endif
+       }
+       /* g_print( "addrindex_remove_results/end\n" ); */
+
+       /* Delete query request */
+       if( queryID > 0 ) {
+               qrymgr_delete_request( queryID );
+       }
+}
+
+/* **********************************************************************
+* Address completion stuff.
+* ***********************************************************************
 */
 
+static void addrindex_load_completion_load_persons(
+               gint (*callBackFunc) ( const gchar *, const gchar *, 
+                                      const gchar *, const gchar *, GList * ),
+               AddressDataSource *ds)
+{
+       GList *listP, *nodeP;
+       GList *nodeM;
+       gchar *sName;
+
+       /* Read address book */
+       if( addrindex_ds_get_modify_flag( ds ) ) {
+               addrindex_ds_read_data( ds );
+       }
+
+       if( ! addrindex_ds_get_read_flag( ds ) ) {
+               addrindex_ds_read_data( ds );
+       }
+
+       /* Get all groups */
+       listP = addrindex_ds_get_all_groups( ds );
+       nodeP = listP;
+       while( nodeP ) {
+               ItemGroup *group = nodeP->data;
+               GList *emails = NULL;
+               for (nodeM = group->listEMail; nodeM; nodeM = g_list_next(nodeM)) {
+                       ItemEMail *email = nodeM->data;
+                       if (email->address)
+                               emails = g_list_append(emails, email);
+               }
+               callBackFunc( ((AddrItemObject *)group)->name, NULL,
+                             NULL, NULL, emails );
+               nodeP = g_list_next( nodeP );
+       }
+
+       /* Free up the list */
+       g_list_free( listP );
+       /* Get all persons */
+       listP = addrindex_ds_get_all_persons( ds );
+       nodeP = listP;
+       while( nodeP ) {
+               ItemPerson *person = nodeP->data;
+               nodeM = person->listEMail;
+
+               /* Figure out name to use */
+               sName = ADDRITEM_NAME(person);
+               if( sName == NULL || *sName == '\0' ) {
+                       sName = person->nickName;
+               }
+
+               /* Process each E-Mail address */
+               while( nodeM ) {
+                       ItemEMail *email = nodeM->data;
+
+                       callBackFunc( sName, email->address, person->nickName, 
+                                     ADDRITEM_NAME(email), NULL );
+
+                       nodeM = g_list_next( nodeM );
+               }
+               nodeP = g_list_next( nodeP );
+       }
+
+       /* Free up the list */
+       g_list_free( listP );
+}              
+
+/**
+ * This function is used by the address completion function to load
+ * addresses for all non-external address book interfaces.
+ *
+ * \param callBackFunc Function to be called when an address is
+ *                     to be loaded.
+ * \param folderpath Addressbook's Book/folder path to restrict to (if NULL or ""
+ *                     or "Any", assume the whole addressbook
+ * \return <i>TRUE</i> if data loaded, <i>FALSE</i> if address index not loaded.
+ */
+
+gboolean addrindex_load_completion(
+               gint (*callBackFunc) ( const gchar *, const gchar *, 
+                                      const gchar *, const gchar *, GList * ),
+               gchar *folderpath )
+{
+       GList *nodeIf, *nodeDS;
+
+       if( folderpath != NULL ) {
+               AddressDataSource *book;
+               ItemFolder* folder;
+
+               /* split the folder path we've received, we'll try to match this path, subpath by
+                  subpath against the book/folder structure in order and restrict loading of
+                  addresses to that subpart (if matches). book/folder path must exist and
+                  folderpath must not be empty or NULL */
+               
+               if( ! addressbook_peek_folder_exists( folderpath, &book, &folder ) ) {
+                       g_warning("addrindex_load_completion: folder path '%s' doesn't exist\n", folderpath);
+                       return FALSE;
+               }
+
+               if( folder != NULL ) {
+
+                       GList *items;
+                       GList *nodeM;
+                       gchar *sName;
+                       ItemPerson *person;
 
+                       debug_print("addrindex_load_completion: folder %p '%s'\n", folder, folder->obj.name);
+
+                       /* Load email addresses */
+                       items = addritem_folder_get_person_list( folder );
+                       for( ; items != NULL; items = g_list_next( items ) ) {
+                               person = items->data;
+                               nodeM = person->listEMail;
+
+                               /* Figure out name to use */
+                               sName = ADDRITEM_NAME(person);
+                               if( sName == NULL || *sName == '\0' ) {
+                                       sName = person->nickName;
+                               }
+
+                               /* Process each E-Mail address */
+                               while( nodeM ) {
+                                       ItemEMail *email = nodeM->data;
+
+                                       callBackFunc( sName, email->address, person->nickName, 
+                                                         ADDRITEM_NAME(email), NULL );
+
+                                       nodeM = g_list_next( nodeM );
+                               }
+                       }
+                       /* Free up the list */
+                       mgu_clear_list( items );
+                       g_list_free( items );
+
+                       return TRUE;
+
+               } else {
+
+                       if( book != NULL ) {
+
+                               AddressBookFile *abf = book->rawDataSource;
+
+                               debug_print("addrindex_load_completion: book %p '%s'\n", book, abf?abf->fileName:"(null)");
+
+                               addrindex_load_completion_load_persons( callBackFunc, book );
+
+                               return TRUE;
+
+                       } else {
+                               g_warning("addrindex_load_completion: book/folder path is valid but got no pointer\n");
+                       }
+               }
+               return FALSE;
+
+       } else {
+
+               nodeIf = addrindex_get_interface_list( _addressIndex_ );
+               while( nodeIf ) {
+                       AddressInterface *iface = nodeIf->data;
+
+                       nodeIf = g_list_next( nodeIf );
+
+                       if( ! iface->useInterface || iface->externalQuery )
+                               continue;
+
+                       nodeDS = iface->listSource;
+                       while( nodeDS ) {
+                               addrindex_load_completion_load_persons( callBackFunc, nodeDS->data );
+                       nodeDS = g_list_next( nodeDS );
+               }
+       }
+       }
+
+       return TRUE;
+}
+
+/**
+ * This function can be used to collect information about
+ * addressbook entries that contain a specific attribute.
+ *
+ * \param attr         Name of attribute to look for
+ * \param callBackFunc Function to be called when a matching attribute was found
+ * \return <i>TRUE</i>
+ */
+gboolean addrindex_load_person_attribute(
+               const gchar *attr,
+               gint (*callBackFunc) ( ItemPerson *, const gchar * ) )
+{
+       AddressDataSource *ds;
+       GList *nodeIf, *nodeDS;
+       GList *listP, *nodeP;
+       GList *nodeA;
+
+       nodeIf = addrindex_get_interface_list( _addressIndex_ );
+       while( nodeIf ) {
+               gchar *cur_bname;
+               AddressInterface *iface = nodeIf->data;
+
+               nodeIf = g_list_next( nodeIf );
+
+               if( ! iface->useInterface || iface->externalQuery )
+                       continue;
+
+               nodeDS = iface->listSource;
+               while( nodeDS ) {
+                       ds = nodeDS->data;
+
+                       /* Read address book */
+                       if( addrindex_ds_get_modify_flag( ds ) ) {
+                               addrindex_ds_read_data( ds );
+                       }
+
+                       if( ! addrindex_ds_get_read_flag( ds ) ) {
+                               addrindex_ds_read_data( ds );
+                       }
+
+                       /* Check addressbook name */
+                       cur_bname = addrindex_ds_get_name( ds );
+
+                       /* Get all persons */
+                       listP = addrindex_ds_get_all_persons( ds );
+                       nodeP = listP;
+                       while( nodeP ) {
+                               ItemPerson *person = nodeP->data;
+
+                               /* Return all ItemPerson's if attr is NULL */
+                               if( attr == NULL ) {
+                                       callBackFunc(person, cur_bname);
+                               }
+
+                               /* Return ItemPerson's with specific attribute */
+                               else {
+                                       nodeA = person->listAttrib;
+                                       /* Process each User Attribute */
+                                       while( nodeA ) {
+                                               UserAttribute *attrib = nodeA->data;
+                                               if( attrib->name && 
+                                                   !strcmp( attrib->name,attr ) ) {
+                                                       callBackFunc(person, cur_bname);
+                                               }
+                                               nodeA = g_list_next( nodeA );
+                                       }
+                               }
+                               nodeP = g_list_next( nodeP );
+                       }
+                       /* Free up the list */
+                       g_list_free( listP );
+
+                       nodeDS = g_list_next( nodeDS );
+               }
+       }
+       return TRUE;
+}
+
+/*
+ * End of Source.
+ */