2007-08-21 [colin] 2.10.0cvs142
[claws.git] / src / ldapquery.c
index 6ef4bbacc32fa9cdabd86b0b57cbd3cee4bd545e..d6ce17acaf4b759fc5115274d26e1c55adcf2127 100644 (file)
@@ -1,10 +1,10 @@
 /*
  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
- * Copyright (C) 2003 Match Grun
+ * Copyright (C) 2003-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,
@@ -13,8 +13,8 @@
  * 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/>.
+ * 
  */
 
 /*
 #include <glib.h>
 #include <sys/time.h>
 #include <string.h>
-#include <ldap.h>
 #include <lber.h>
 
+#include "ldaputil.h"
 #include "ldapquery.h"
 #include "ldapctrl.h"
 #include "mgutils.h"
 
 #include "addritem.h"
 #include "addrcache.h"
+#include "common/utils.h"
 
 /*
  * Key for thread specific data.
 static pthread_key_t _queryThreadKey_;
 static gboolean _queryThreadInit_ = FALSE;
 
+static gboolean callbackend (gpointer data)
+{
+       LdapQuery *qry = (LdapQuery *)data;
+       qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
+       return FALSE;
+}
+
+
 /**
  * Create new LDAP query object.
  * \return Initialized query object.
@@ -54,12 +63,15 @@ LdapQuery *ldapqry_create( void ) {
        LdapQuery *qry;
 
        qry = g_new0( LdapQuery, 1 );
+       ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
+       ADDRQUERY_ID(qry) = 0;
+       ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
+       ADDRQUERY_NAME(qry) = NULL;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
+       ADDRQUERY_FOLDER(qry) = NULL;
+       ADDRQUERY_SEARCHVALUE(qry) = NULL;
        qry->control = NULL;
-       qry->retVal = LDAPRC_SUCCESS;
-       qry->queryType = LDAPQUERY_NONE;
-       qry->queryName = NULL;
-       qry->searchValue = NULL;
-       qry->queryID = 0;
+       qry->server = NULL;
        qry->entriesRead = 0;
        qry->elapsedTime = 0;
        qry->stopFlag = FALSE;
@@ -67,11 +79,10 @@ LdapQuery *ldapqry_create( void ) {
        qry->agedFlag = FALSE;
        qry->completed = FALSE;
        qry->thread = NULL;
-       qry->callBackStart = NULL;
        qry->callBackEntry = NULL;
        qry->callBackEnd = NULL;
-       qry->folder = NULL;
-       qry->server = NULL;
+       qry->ldap = NULL;
+       qry->data = NULL;
 
        /* Mutex to protect stop and busy flags */
        qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
@@ -103,8 +114,8 @@ void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
  * \param value Name.
  */
 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
-       qry->queryName = mgu_replace_string( qry->queryName, value );
-       g_strstrip( qry->queryName );
+       ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
+       g_strstrip( ADDRQUERY_NAME(qry) );
 }
 
 /**
@@ -113,17 +124,9 @@ void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
  * \param value 
  */
 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
-       qry->searchValue = mgu_replace_string( qry->searchValue, value );
-       g_strstrip( qry->searchValue );
-}
-
-/**
- * Specify error/status.
- * \param qry   Query object.
- * \param value Status.
- */
-void ldapqry_set_error_status( LdapQuery* qry, const gint value ) {
-       qry->retVal = value;
+       ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
+       g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
+       debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry));
 }
 
 /**
@@ -136,42 +139,29 @@ void ldapqry_set_error_status( LdapQuery* qry, const gint value ) {
  * <li><code>LDAPQUERY_DYNAMIC</code></li>
  * </ul>
  */
+/*
 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
-       qry->queryType = value;
+       ADDRQUERY_TYPE(qry) = value;
 }
+*/
 
 /**
- * Specify query ID.
- * \param qry Query object.
- * \param value ID for the query.
- */
-void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
-       qry->queryID = value;
-}
-
-/**
- * Specify maximum number of LDAP entries to retrieve.
- * \param qry Query object.
- * \param value Entries to read.
+ * Specify search type.
+ * \param qry   Query object.
+ * \param value Type.
  */
-void ldapqry_set_entries_read( LdapQuery* qry, const gint value ) {
-       if( value > 0 ) {
-               qry->entriesRead = value;
-       }
-       else {
-               qry->entriesRead = 0;
-       }
+void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
+       g_return_if_fail( qry != NULL );
+       ADDRQUERY_SEARCHTYPE(qry) = value;
 }
 
 /**
- * Register a callback function that will be executed when the search
- * starts. When called, the function will be passed this query object
- * as an argument.
+ * Specify query ID.
  * \param qry Query object.
- * \param func Function.
+ * \param value ID for the query.
  */
-void ldapqry_set_callback_start( LdapQuery *qry, void *func ) {
-       qry->callBackStart = func;
+void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
+       ADDRQUERY_ID(qry) = value;
 }
 
 /**
@@ -240,9 +230,9 @@ void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
  * \param qry Query object.
  * \return Value of stop flag.
  */
-gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
+static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
        gboolean value;
-       g_return_if_fail( qry != NULL );
+       g_return_val_if_fail( qry != NULL, TRUE );
 
        pthread_mutex_lock( qry->mutexStop );
        value = qry->stopFlag;
@@ -255,8 +245,10 @@ gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
  * \param qry Query object.
  * \param value Value of busy flag.
  */
-void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
+static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
        g_return_if_fail( qry != NULL );
+       if (qry->mutexBusy == NULL)
+               return; /* exiting, mutex already freed */
 
        pthread_mutex_lock( qry->mutexBusy );
        qry->busyFlag = value;
@@ -269,9 +261,9 @@ void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
  * \param qry Query object.
  * \return Value of busy flag.
  */
-gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
+static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
        gboolean value;
-       g_return_if_fail( qry != NULL );
+       g_return_val_if_fail( qry != NULL, FALSE );
 
        pthread_mutex_lock( qry->mutexBusy );
        value = qry->busyFlag;
@@ -284,59 +276,38 @@ gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
  * \param qry Query object.
  * \param value Value of aged flag.
  */
-void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
+static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
        g_return_if_fail( qry != NULL );
        qry->agedFlag = value;
 }
 
-/**
- * Test value of aged flag.
- * \param qry Query object.
- * \return <i>TRUE</i> if query has been marked as aged (and can be retired).
- */
-gboolean ldapqry_get_aged_flag( LdapQuery *qry ) {
-       g_return_if_fail( qry != NULL );
-       return qry->agedFlag;
-}
-
-/**
- * Release the LDAP control data associated with the query.
- * \param qry Query object to process.
- */
-void ldapqry_release_control( LdapQuery *qry ) {
-       g_return_if_fail( qry != NULL );
-       if( qry->control != NULL ) {
-               ldapctl_free( qry->control );
-       }
-       qry->control = NULL;
-}
-
 /**
  * Clear LDAP query member variables.
  * \param qry Query object.
  */
-void ldapqry_clear( LdapQuery *qry ) {
+static void ldapqry_clear( LdapQuery *qry ) {
        g_return_if_fail( qry != NULL );
 
        /* Free internal stuff */
-       g_free( qry->queryName );
-       g_free( qry->searchValue );
+       g_free( ADDRQUERY_NAME(qry) );
+       g_free( ADDRQUERY_SEARCHVALUE(qry) );
 
        /* Clear pointers and value */
-       qry->queryName = NULL;
-       qry->searchValue = NULL;
-       qry->retVal = LDAPRC_SUCCESS;
-       qry->queryType = LDAPQUERY_NONE;
-       qry->queryID = 0;
+       ADDRQUERY_NAME(qry) = NULL;
+       ADDRQUERY_SEARCHVALUE(qry) = NULL;
+       ADDRQUERY_ID(qry) = 0;
+       ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
        qry->entriesRead = 0;
        qry->elapsedTime = 0;
        qry->stopFlag = FALSE;
        qry->busyFlag = FALSE;
        qry->agedFlag = FALSE;
        qry->completed = FALSE;
-       qry->callBackStart = NULL;
        qry->callBackEntry = NULL;
        qry->callBackEnd = NULL;
+       qry->ldap = NULL;
+       qry->data = NULL;
 }
 
 /**
@@ -348,6 +319,7 @@ void ldapqry_free( LdapQuery *qry ) {
        g_return_if_fail( qry != NULL );
 
        /* Clear out internal members */
+       ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
        ldapqry_clear( qry );
 
        /* Free the mutex */
@@ -362,7 +334,7 @@ void ldapqry_free( LdapQuery *qry ) {
        qry->mutexStop = NULL;
 
        /* Do not free folder - parent server object should free */     
-       qry->folder = NULL;
+       ADDRQUERY_FOLDER(qry) = NULL;
 
        /* Do not free thread - thread should be terminated before freeing */
        qry->thread = NULL;
@@ -374,29 +346,6 @@ void ldapqry_free( LdapQuery *qry ) {
        g_free( qry );
 }
 
-/**
- * Display object to specified stream.
- * \param qry    Query object to process.
- * \param stream Output stream.
- */
-void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
-       g_return_if_fail( qry != NULL );
-
-       fprintf( stream, "LdapQuery:\n" );
-       fprintf( stream, "  control?: %s\n",   qry->control ? "yes" : "no" );
-       fprintf( stream, "err/status: %d\n",   qry->retVal );
-       fprintf( stream, "query type: %d\n",   qry->queryType );
-       fprintf( stream, "query name: '%s'\n", qry->queryName );
-       fprintf( stream, "search val: '%s'\n", qry->searchValue );
-       fprintf( stream, "   queryID: %d\n",   qry->queryID );
-       fprintf( stream, "   entries: %d\n",   qry->entriesRead );
-       fprintf( stream, "   elapsed: %d\n",   qry->elapsedTime );
-       fprintf( stream, " stop flag: %s\n",   qry->stopFlag  ? "yes" : "no" );
-       fprintf( stream, " busy flag: %s\n",   qry->busyFlag  ? "yes" : "no" );
-       fprintf( stream, " aged flag: %s\n",   qry->agedFlag  ? "yes" : "no" );
-       fprintf( stream, " completed: %s\n",   qry->completed ? "yes" : "no" );
-}
-
 /**
  * Free linked lists of character strings.
  * \param listName  List of common names.
@@ -406,12 +355,17 @@ void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
  */
 static void ldapqry_free_lists(
                GSList *listName, GSList *listAddr, GSList *listFirst,
-               GSList *listLast )
+               GSList *listLast, GSList *listDisplay, GSList *other_attrs )
 {
+       GSList *cur = other_attrs;
        mgu_free_list( listName );
        mgu_free_list( listAddr );
        mgu_free_list( listFirst );
        mgu_free_list( listLast );
+       mgu_free_list( listDisplay );
+       for(;cur; cur = cur->next)
+               addritem_free_attribute((UserAttribute *)cur->data);
+       g_slist_free(other_attrs);
 }
 
 /**
@@ -426,15 +380,15 @@ static GSList *ldapqry_add_list_values(
 {
        GSList *list = NULL;
        gint i;
-       gchar **vals;
+       struct berval **vals;
 
-       if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
+       if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
                for( i = 0; vals[i] != NULL; i++ ) {
-                       /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
-                       list = g_slist_append( list, g_strdup( vals[i] ) );
+                       /*debug_print("lv\t%s: %s\n", attr, vals[i]->bv_val);*/
+                       list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
                }
        }
-       ldap_value_free( vals );
+       ldap_value_free_len( vals );
        return list;
 }
 
@@ -447,15 +401,15 @@ static GSList *ldapqry_add_list_values(
  */
 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
        GSList *list = NULL;
-       gchar **vals;
+       struct berval **vals;
 
-       if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
+       if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
                if( vals[0] != NULL ) {
-                       /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
-                       list = g_slist_append( list, g_strdup( vals[0] ) );
+                       debug_print("sv\t%s: %s\n", attr, vals[0]->bv_val);
+                       list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
                }
        }
-       ldap_value_free( vals );
+       ldap_value_free_len( vals );
        return list;
 }
 
@@ -483,19 +437,25 @@ static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *att
 static GList *ldapqry_build_items_fl(
                AddressCache *cache, LdapQuery *qry, gchar *dn,
                GSList *listName, GSList *listAddr, GSList *listFirst,
-               GSList *listLast )
+               GSList *listLast, GSList *listDisplay, GSList *attributes )
 {
-       GSList *nodeAddress;
+       GSList *nodeAddress, *cur;
        gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
-       gboolean allocated;
+       gboolean allocated = FALSE;
        ItemPerson *person;
        ItemEMail *email;
        ItemFolder *folder;
-       GList *listReturn;
+       GList *listReturn = NULL;
 
-       listReturn = NULL;
+       folder = ADDRQUERY_FOLDER(qry);
+       if( folder == NULL ) return listReturn;
        if( listAddr == NULL ) return listReturn;
 
+       if ( listDisplay ) {
+               allocated = FALSE;
+               fullName = listDisplay->data;
+       }
+
        /* Find longest first name in list */
        firstName = mgu_slist_longest_entry( listFirst );
 
@@ -504,43 +464,30 @@ static GList *ldapqry_build_items_fl(
                lastName = listLast->data;
        }
 
-       /* Find longest common name */
-       allocated = FALSE;
-       fullName = mgu_slist_longest_entry( listName );
-       if( fullName == NULL ) {
-               /* Format a full name from first and last names */
-               if( firstName ) {
-                       if( lastName ) {
-                               fullName = g_strdup_printf( "%s %s", firstName, lastName );
+       if ( fullName == NULL ) {
+               /* Find longest common name */
+               allocated = FALSE;
+               fullName = mgu_slist_longest_entry( listName );
+               if( fullName == NULL ) {
+                       /* Format a full name from first and last names */
+                       if( firstName ) {
+                               if( lastName ) {
+                                       fullName = g_strdup_printf( "%s %s", firstName, lastName );
+                               }
+                               else {
+                                       fullName = g_strdup_printf( "%s", firstName );
+                               }
                        }
                        else {
-                               fullName = g_strdup_printf( "%s", firstName );
+                               if( lastName ) {
+                                       fullName = g_strdup_printf( "%s", lastName );
+                               }
                        }
-               }
-               else {
-                       if( lastName ) {
-                               fullName = g_strdup_printf( "%s", lastName );
+                       if( fullName ) {
+                               g_strchug( fullName ); g_strchomp( fullName );
+                               allocated = TRUE;
                        }
                }
-               if( fullName ) {
-                       g_strchug( fullName ); g_strchomp( fullName );
-                       allocated = TRUE;
-               }
-       }
-
-       /* Create new folder for results */
-       if( qry->folder == NULL ) {
-               folder = addritem_create_item_folder();
-               addritem_folder_set_name( folder, qry->queryName );
-               addritem_folder_set_remarks( folder, "" );
-               addrcache_id_folder( cache, folder );
-               addrcache_add_folder( cache, folder );
-               qry->folder = folder;
-
-               /* Specify folder type and back reference */                    
-               folder->folderType = ADDRFOLDER_LDAP_QUERY;
-               folder->folderData = ( gpointer ) qry;
-               folder->isHidden = TRUE;
        }
 
        /* Add person into folder */            
@@ -548,9 +495,16 @@ static GList *ldapqry_build_items_fl(
        addritem_person_set_common_name( person, fullName );
        addritem_person_set_first_name( person, firstName );
        addritem_person_set_last_name( person, lastName );
+       addritem_person_set_nick_name( person, fullName );
        addrcache_id_person( cache, person );
        addritem_person_set_external_id( person, dn );
-       addrcache_folder_add_person( cache, qry->folder, person );
+       
+       for (cur = attributes; cur; cur = cur->next) {
+               UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
+               addritem_person_add_attribute( person, attrib );
+       }
+       
+       addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
 
        qry->entriesRead++;
 
@@ -562,6 +516,9 @@ static GList *ldapqry_build_items_fl(
                addrcache_id_email( cache, email );
                addrcache_person_add_email( cache, person, email );
                addritem_person_add_email( person, email );
+               /*if (debug_get_mode()) {
+                       addritem_print_item_email(email, stdout);
+               }*/
                listReturn = g_list_append( listReturn, email );
                nodeAddress = g_slist_next( nodeAddress );
        }
@@ -592,17 +549,18 @@ static GList *ldapqry_process_single_entry(
        BerElement *ber;
        GSList *listName = NULL, *listAddress = NULL;
        GSList *listFirst = NULL, *listLast = NULL;
+       GSList *listDisplay = NULL;
+       GSList *other_attrs = NULL;
        GList *listReturn;
 
        listReturn = NULL;
        ctl = qry->control;
        dnEntry = ldap_get_dn( ld, e );
-       /* printf( "DN: %s\n", dnEntry ); */
+       debug_print( "DN: %s\n", dnEntry );
 
        /* Process all attributes */
        for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
                attribute = ldap_next_attribute( ld, e, ber ) ) {
-
                if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
                        listAddress = ldapqry_add_list_values( ld, e, attribute );
                }
@@ -614,19 +572,30 @@ static GList *ldapqry_process_single_entry(
                }
                else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
                        listLast = ldapqry_add_single_value( ld, e, attribute );
+               } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
+                       listDisplay = ldapqry_add_single_value( ld, e, attribute );
+               } else {
+                       GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
+                       UserAttribute *attrib = addritem_create_attribute();
+                       const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
+                       if (attvalue) {
+                               addritem_attrib_set_name( attrib, attribute );
+                               addritem_attrib_set_value( attrib, attvalue );
+                               other_attrs = g_slist_prepend(other_attrs, attrib);
+                       }
+                       mgu_free_list(attlist);
                }
-
                /* Free memory used to store attribute */
                ldap_memfree( attribute );
        }
 
        /* Format and add items to cache */
        listReturn = ldapqry_build_items_fl(
-               cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
+               cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
 
        /* Free up */
-       ldapqry_free_lists( listName, listAddress, listFirst, listLast );
-       listName = listAddress = listFirst = listLast = NULL;
+       ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
+       listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
 
        if( ber != NULL ) {
                ber_free( ber, 0 );
@@ -644,7 +613,7 @@ static GList *ldapqry_process_single_entry(
  */
 gboolean ldapqry_check_search( LdapQuery *qry ) {
        LdapControl *ctl;
-       qry->retVal = LDAPRC_CRITERIA;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
 
        /* Test for control data */
        ctl = qry->control;
@@ -653,14 +622,13 @@ gboolean ldapqry_check_search( LdapQuery *qry ) {
        }
 
        /* Test for search value */
-       if( qry->searchValue == NULL ) {
+       if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
                return FALSE;
        }
-       if( strlen( qry->searchValue ) < 1 ) {
+       if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
                return FALSE;
        }
-
-       qry->retVal = LDAPRC_SUCCESS;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
        return TRUE;
 }
 
@@ -673,6 +641,114 @@ void ldapqry_touch( LdapQuery *qry ) {
        qry->agedFlag = FALSE;
 }
 
+/**
+ * Connect to LDAP server.
+ * \param  qry Query object to process.
+ * \return Error/status code.
+ */
+static gint ldapqry_connect( LdapQuery *qry ) {
+       LdapControl *ctl;
+       LDAP *ld = NULL;
+       gint rc;
+       gint version;
+       gchar *uri = NULL;
+
+       /* Initialize connection */
+       if (debug_get_mode()) {
+               debug_print("===ldapqry_connect===\n");
+               /*ldapqry_print(qry, stdout);*/
+       }
+       ctl = qry->control;
+       /*if (debug_get_mode()) {
+               ldapctl_print(ctl, stdout);
+               debug_print("======\n");
+       }*/
+       ldapqry_touch( qry );
+       qry->startTime = qry->touchTime;
+       qry->elapsedTime = -1;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
+
+       uri = g_strdup_printf("ldap%s://%s:%d",
+                               ctl->enableSSL?"s":"",
+                               ctl->hostName, ctl->port);
+       ldap_initialize(&ld, uri);
+       g_free(uri);
+
+       if (ld == NULL)
+               return ADDRQUERY_RETVAL(qry);
+
+       qry->ldap = ld;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
+       if( ldapqry_get_stop_flag( qry ) ) {
+               return ADDRQUERY_RETVAL(qry);
+       }
+       ldapqry_touch( qry );
+
+       debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
+
+#ifdef USE_LDAP_TLS
+       /* Handle TLS */
+       version = LDAP_VERSION3;
+       rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
+       if( rc == LDAP_OPT_SUCCESS ) {
+               ctl->version = LDAP_VERSION3;
+       }
+
+       if( ctl->version == LDAP_VERSION3 ) {
+               if( ctl->enableTLS && !ctl->enableSSL ) {
+                       ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
+                       rc = ldap_start_tls_s( ld, NULL, NULL );
+                       
+                       debug_print("rc=%d\n", rc);
+                       debug_print("LDAP Status: set_option: %s\n", ldap_err2string( rc ) );
+                       
+                       if( rc != LDAP_SUCCESS ) {
+                               return ADDRQUERY_RETVAL(qry);
+                       }
+               }
+       }
+#endif
+
+       /* Bind to the server, if required */
+       ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
+       if( ctl->bindDN ) {
+               if( * ctl->bindDN != '\0' ) {
+                       debug_print("binding...\n");
+                       rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
+                       debug_print("rc=%d\n", rc);
+                       if( rc != LDAP_SUCCESS ) {
+                               debug_print("LDAP Error: ldap_simple_bind_s: %s\n",     ldap_err2string(rc));
+                               return ADDRQUERY_RETVAL(qry);
+                       }
+               }
+       }
+       ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
+       if( ldapqry_get_stop_flag( qry ) ) {
+               return ADDRQUERY_RETVAL(qry);
+       }
+       ldapqry_touch( qry );
+
+       ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
+
+       return ADDRQUERY_RETVAL(qry);
+}
+
+/**
+ * Connect to LDAP server.
+ * \param  qry Query object to process.
+ * \return Error/status code.
+ */
+static gint ldapqry_disconnect( LdapQuery *qry ) {
+       /* Disconnect */
+       if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
+       qry->ldap = NULL;
+
+       ldapqry_touch( qry );
+       qry->elapsedTime = qry->touchTime - qry->startTime;
+
+       return ADDRQUERY_RETVAL(qry);
+}
+
 /**
  * Perform the LDAP search, reading LDAP entries into cache.
  * Note that one LDAP entry can have multiple values for many of its
@@ -683,123 +759,76 @@ void ldapqry_touch( LdapQuery *qry ) {
  * \param  qry Query object to process.
  * \return Error/status code.
  */
-static gint ldapqry_perform_search( LdapQuery *qry ) {
+static gint ldapqry_search_retrieve( LdapQuery *qry ) {
        LdapControl *ctl;
        LDAP *ld;
-       LDAPMessage *result, *e;
+       LDAPMessage *result, *e = NULL;
        char **attribs;
        gchar *criteria;
+       gboolean searchFlag;
        gboolean entriesFound;
        gboolean first;
        struct timeval timeout;
        gint rc;
-       time_t tstart, tend;
        AddressCache *cache;
        GList *listEMail;
 
        /* Initialize some variables */
+       ld = qry->ldap;
        ctl = qry->control;
        cache = qry->server->addressCache;
        timeout.tv_sec = ctl->timeOut;
        timeout.tv_usec = 0L;
        entriesFound = FALSE;
-       qry->elapsedTime = -1;
-       qry->retVal = LDAPRC_SUCCESS;
-
-       /* Check search criteria */     
-       if( ! ldapqry_check_search( qry ) ) {
-               return qry->retVal;
-       }
-
-       /* Initialize connection */
-       ldapqry_touch( qry );
-       tstart = qry->touchTime;
-       tend = tstart - 1;
-       if( ( ld = ldap_init( ctl->hostName, ctl->port ) ) == NULL ) {
-               qry->retVal = LDAPRC_INIT;
-               return qry->retVal;
-       }
-       if( ldapqry_get_stop_flag( qry ) ) {
-               qry->retVal = LDAPRC_SUCCESS;
-               return qry->retVal;
-       }
-       ldapqry_touch( qry );
-
-       /*
-       printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
-       */
-
-       /* Bind to the server, if required */
-       if( ctl->bindDN ) {
-               if( * ctl->bindDN != '\0' ) {
-                       /* printf( "binding...\n" ); */
-                       rc = ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
-                       /* printf( "rc=%d\n", rc ); */
-                       if( rc != LDAP_SUCCESS ) {
-                               /*
-                               printf( "LDAP Error: ldap_simple_bind_s: %s\n",
-                                       ldap_err2string( rc ) );
-                               */
-                               ldap_unbind( ld );
-                               qry->retVal = LDAPRC_BIND;
-                               return qry->retVal;
-                       }
-               }
-       }
-       if( ldapqry_get_stop_flag( qry ) ) {
-               ldap_unbind( ld );
-               qry->retVal = LDAPRC_SUCCESS;
-               return qry->retVal;
-       }
-       ldapqry_touch( qry );
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
 
        /* Define all attributes we are interested in. */
-       attribs = ldapctl_attribute_array( ctl );
+       attribs = ldapctl_full_attribute_array( ctl );
 
        /* Create LDAP search string */
-       criteria = ldapctl_format_criteria( ctl, qry->searchValue );
-       /* printf( "Search criteria ::%s::\n", criteria ); */
+       criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
+       debug_print("Search criteria ::%s::\n", criteria);
 
        /*
         * Execute the search - this step may take some time to complete
         * depending on network traffic and server response time.
         */
+       ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
        rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
                attribs, 0, NULL, NULL, &timeout, 0, &result );
+       debug_print("LDAP Error: ldap_search_st: %d\n", rc);
+       debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
        ldapctl_free_attribute_array( attribs );
        g_free( criteria );
        criteria = NULL;
        if( rc == LDAP_TIMEOUT ) {
-               ldap_unbind( ld );
-               qry->retVal = LDAPRC_TIMEOUT;
-               return qry->retVal;
+               return ADDRQUERY_RETVAL(qry);
        }
-       if( rc != LDAP_SUCCESS ) {
-               /*
-               printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
-               */
-               ldap_unbind( ld );
-               qry->retVal = LDAPRC_SEARCH;
-               return qry->retVal;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
+
+       /* Test valid returns */
+       searchFlag = FALSE;
+       if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+               searchFlag = TRUE;
        }
-       if( ldapqry_get_stop_flag( qry ) ) {
-               qry->retVal = LDAPRC_SUCCESS;
-               return qry->retVal;
+       else if( rc == LDAP_SUCCESS ) {
+               searchFlag = TRUE;
        }
-       ldapqry_touch( qry );
-
-       /*
-       printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
-       */
-
-       if( ldapqry_get_stop_flag( qry ) ) {
-               qry->retVal = LDAPRC_SUCCESS;
-               return qry->retVal;
+       else if( rc == LDAP_PARTIAL_RESULTS ) {
+               searchFlag = TRUE;
        }
+       else {
+               debug_print("LDAP Error: ldap_search_st: %d\n", rc);
+               debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
+               return ADDRQUERY_RETVAL(qry);
+       }
+       ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
+
+       debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
 
        /* Process results */
        first = TRUE;
-       while( TRUE ) {
+       while( searchFlag ) {
                ldapqry_touch( qry );
                if( qry->entriesRead >= ctl->maxEntries ) break;                
 
@@ -817,7 +846,6 @@ static gint ldapqry_perform_search( LdapQuery *qry ) {
                        e = ldap_next_entry( ld, e );
                }
                if( e == NULL ) break;
-
                entriesFound = TRUE;
 
                /* Setup a critical section here */
@@ -828,38 +856,69 @@ static gint ldapqry_perform_search( LdapQuery *qry ) {
 
                /* Process callback */
                if( qry->callBackEntry ) {
-                       qry->callBackEntry( qry, listEMail );
+                       qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
                }
                else {
+                       /*if (debug_get_mode()) {
+                               GList *node = listEMail;
+                               while (node) {
+                                       addritem_print_item_email(node->data, stdout);
+                                       node = g_list_next(node);
+                               }
+                       }*/
                        g_list_free( listEMail );
                }
-
                pthread_mutex_unlock( qry->mutexEntry );
        }
 
        /* Free up and disconnect */
        ldap_msgfree( result );
-       ldap_unbind( ld );
-       ldapqry_touch( qry );
-       tend = qry->touchTime;
-       qry->elapsedTime = tend - tstart;
 
-       if( entriesFound ) {
-               qry->retVal = LDAPRC_SUCCESS;
+       if( searchFlag ) {
+               if( entriesFound ) {
+                       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
+               }
+               else {
+                       ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
+               }
        }
-       else {
-               qry->retVal = LDAPRC_NOENTRIES;
+
+       return ADDRQUERY_RETVAL(qry);
+}
+
+/**
+ * Connection, perform search and disconnect.
+ * \param  qry Query object to process.
+ * \return Error/status code.
+ */
+static gint ldapqry_perform_search( LdapQuery *qry ) {
+       /* Check search criteria */     
+       if( ! ldapqry_check_search( qry ) ) {
+               return ADDRQUERY_RETVAL(qry);
+       }
+
+       /* Connect */
+       qry->ldap = NULL;
+       ldapqry_connect( qry );
+       if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
+               /* Perform search */
+               ldapqry_search_retrieve( qry );
        }
+       /* Disconnect */
+       ldapqry_disconnect( qry );
+       qry->ldap = NULL;
 
-       return qry->retVal;
+       return ADDRQUERY_RETVAL(qry);
 }
 
+static gint ldapqry_perform_locate( LdapQuery *qry );
+
 /**
  * Wrapper around search.
  * \param  qry Query object to process.
  * \return Error/status code.
  */
-gint ldapqry_search( LdapQuery *qry ) {
+static gint ldapqry_search( LdapQuery *qry ) {
        gint retVal;
 
        g_return_val_if_fail( qry != NULL, -1 );
@@ -868,11 +927,6 @@ gint ldapqry_search( LdapQuery *qry ) {
        ldapqry_touch( qry );
        qry->completed = FALSE;
 
-       /* Process callback */  
-       if( qry->callBackStart ) {
-               qry->callBackStart( qry );
-       }
-
        /* Setup pointer to thread specific area */
        pthread_setspecific( _queryThreadKey_, qry );
 
@@ -880,24 +934,25 @@ gint ldapqry_search( LdapQuery *qry ) {
        
        /* Now perform the search */
        qry->entriesRead = 0;
-       qry->retVal = LDAPRC_SUCCESS;
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
        ldapqry_set_busy_flag( qry, TRUE );
        ldapqry_set_stop_flag( qry, FALSE );
-       retVal = ldapqry_perform_search( qry );
+       if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
+               retVal = ldapqry_perform_locate( qry );
+       }
+       else {
+               retVal = ldapqry_perform_search( qry );
+       }
        if( retVal == LDAPRC_SUCCESS ) {
                qry->server->addressCache->dataRead = TRUE;
                qry->server->addressCache->accessFlag = FALSE;
                if( ldapqry_get_stop_flag( qry ) ) {
-                       /*
-                       printf( "Search was terminated prematurely\n" );
-                       */
+                       debug_print("Search was terminated prematurely\n");
                }
                else {
                        ldapqry_touch( qry );
                        qry->completed = TRUE;
-                       /*
-                       printf( "Search ran to completion\n" );
-                       */
+                       debug_print("Search ran to completion\n");
                }
        }
        ldapqry_set_stop_flag( qry, TRUE );
@@ -905,10 +960,10 @@ gint ldapqry_search( LdapQuery *qry ) {
 
        /* Process callback */  
        if( qry->callBackEnd ) {
-               qry->callBackEnd( qry );
+               g_timeout_add(0, callbackend, qry);
        }
 
-       return qry->retVal;
+       return ADDRQUERY_RETVAL(qry);
 }
 
 /**
@@ -924,10 +979,8 @@ gint ldapqry_read_data_th( LdapQuery *qry ) {
        ldapqry_set_stop_flag( qry, FALSE );
        ldapqry_touch( qry );
        if( ldapqry_check_search( qry ) ) {
-               if( qry->retVal == LDAPRC_SUCCESS ) {
-                       /*
-                       printf( "Starting LDAP search thread\n");
-                       */
+               if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
+                       debug_print("Starting LDAP search thread\n");
                        ldapqry_set_busy_flag( qry, TRUE );
                        qry->thread = g_malloc0( sizeof( pthread_t ) );
 
@@ -936,21 +989,7 @@ gint ldapqry_read_data_th( LdapQuery *qry ) {
                                (void *) ldapqry_search, (void *) qry );
                }
        }
-       return qry->retVal;
-}
-
-/**
- * Join the thread associated with the query. This should probably be removed
- * to prevent joining threads.
- * \param qry Query object to process.
- */
-void ldapqry_join_threadX( LdapQuery *qry ) {
-       g_return_if_fail( qry != NULL );
-
-       /* Wait for thread */
-       /* printf( "ldapqry_join_thread::Joining thread...\n" ); */
-       pthread_join( * qry->thread, NULL );
-       /* printf( "ldapqry_join_thread::Thread terminated\n" ); */
+       return ADDRQUERY_RETVAL(qry);
 }
 
 /**
@@ -962,9 +1001,7 @@ static void ldapqry_destroyer( void * ptr ) {
        LdapQuery *qry;
 
        qry = ( LdapQuery * ) ptr;
-       /*
-       printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), qry->queryName );
-       */
+       debug_print("ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry));
 
        /* Perform any destruction here */
        if( qry->control != NULL ) {
@@ -973,9 +1010,6 @@ static void ldapqry_destroyer( void * ptr ) {
        qry->control = NULL;
        qry->thread = NULL;
        ldapqry_set_busy_flag( qry, FALSE );
-       /*
-       printf( "...destroy exiting\n" );
-       */
 }
 
 /**
@@ -985,11 +1019,10 @@ static void ldapqry_destroyer( void * ptr ) {
 void ldapqry_cancel( LdapQuery *qry ) {
        g_return_if_fail( qry != NULL );
 
-       /*
-       printf( "cancelling::%d::%s\n", (int) pthread_self(), qry->queryName );
-       */
+       debug_print("cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry));
        if( ldapqry_get_busy_flag( qry ) ) {
                if( qry->thread ) {
+                       debug_print("calling pthread_cancel\n");
                        pthread_cancel( * qry->thread );
                }
        }
@@ -1000,15 +1033,13 @@ void ldapqry_cancel( LdapQuery *qry ) {
  * any LDAP queries to initialize thread specific data.
  */
 void ldapqry_initialize( void ) {
-       /* printf( "ldapqry_initialize...\n" ); */
+       debug_print("ldapqry_initialize...\n");
        if( ! _queryThreadInit_ ) {
-               /*
-               printf( "ldapqry_initialize::creating thread specific area\n" );
-               */
+               debug_print("ldapqry_initialize::creating thread specific area\n");
                pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
                _queryThreadInit_ = TRUE;
        }
-       /* printf( "ldapqry_initialize... done!\n" ); */
+       debug_print("ldapqry_initialize... done!\n");
 }
 
 /**
@@ -1041,15 +1072,276 @@ void ldapqry_delete_folder( LdapQuery *qry ) {
 
        g_return_if_fail( qry != NULL );
 
-       folder = qry->folder;
+       folder = ADDRQUERY_FOLDER(qry);
        if( folder ) {
                cache = qry->server->addressCache;
                folder = addrcache_remove_folder_delete( cache, folder );
                if( folder ) {
                        addritem_free_item_folder( folder );
                }
-               qry->folder = NULL;
+               ADDRQUERY_FOLDER(qry) = NULL;
+       }
+}
+
+/**
+ * Create a name/value pair object.
+ * \param n Name.
+ * \param v Value.
+ * \return Initialized object.
+ */
+static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
+       NameValuePair *nvp = g_new0( NameValuePair, 1 );
+
+       nvp->name = g_strdup( n );
+       nvp->value = g_strdup( v );
+       return nvp;
+}
+
+/**
+ * Free up name/value pair object.
+ * \param nvp Name/value object.
+ */
+void ldapqry_free_name_value( NameValuePair *nvp ) {
+       if( nvp ) {
+               g_free( nvp->name );
+               g_free( nvp->value );
+               nvp->name = nvp->value = NULL;
+               g_free( nvp );
+       }
+}
+
+/**
+ * Free up a list name/value pair objects.
+ * \param list List of name/value objects.
+ */
+void ldapqry_free_list_name_value( GList *list ) {
+       GList *node;
+
+       node = list;
+       while( node ) {
+               NameValuePair *nvp = ( NameValuePair * ) node->data;
+               ldapqry_free_name_value( nvp );
+               node->data = NULL;
+               node = g_list_next( node );
+       }
+       g_list_free( list );
+}
+
+/**
+ * Load a list of name/value pairs from LDAP attributes.
+ * \param  ld          LDAP handle.
+ * \param  e          LDAP message.
+ * \param  attr       Attribute name.
+ * \param  listValues List to populate.
+ * \return List of attribute name/value pairs.
+ */
+static GList *ldapqry_load_attrib_values(
+               LDAP *ld, LDAPMessage *entry, char *attr,
+               GList *listValues )
+{
+       GList *list = NULL;
+       gint i;
+       struct berval **vals;
+       NameValuePair *nvp;
+
+       list = listValues;
+       if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
+               for( i = 0; vals[i] != NULL; i++ ) {
+                       gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
+                       nvp = ldapqry_create_name_value( attr, tmp );
+                       g_free(tmp);
+                       list = g_list_append( list, nvp );
+               }
+       }
+       ldap_value_free_len( vals );
+       return list;
+}
+
+/**
+ * Fetch a list of all attributes.
+ * \param  ld    LDAP handle.
+ * \param  e     LDAP message.
+ * \return List of attribute name/value pairs.
+ */
+static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
+{
+       char *attribute;
+       BerElement *ber;
+       GList *listValues = NULL;
+
+       /* Process all attributes */
+       for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
+               attribute = ldap_next_attribute( ld, e, ber ) ) {
+               listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
+               ldap_memfree( attribute );
+       }
+
+       /* Free up */
+       if( ber != NULL ) {
+               ber_free( ber, 0 );
+       }
+       return listValues;
+}
+
+#define CRITERIA_SINGLE "(objectclass=*)"
+
+/**
+ * Perform the data retrieval for a specific LDAP record.
+ * 
+ * \param  qry Query object to process.
+ * \return Error/status code.
+ */
+static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
+       LdapControl *ctl;
+       LDAP *ld;
+       LDAPMessage *result, *e = NULL;
+       gboolean entriesFound;
+       gboolean first;
+       struct timeval timeout;
+       gint rc;
+       gchar *dn;
+       GList *listValues;
+
+       /* Initialize some variables */
+       ld = qry->ldap;
+       ctl = qry->control;
+       dn = ADDRQUERY_SEARCHVALUE(qry);
+       timeout.tv_sec = ctl->timeOut;
+       timeout.tv_usec = 0L;
+       entriesFound = FALSE;
+
+       /*
+        * Execute the search - this step may take some time to complete
+        * depending on network traffic and server response time.
+        */
+       ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
+       rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
+               NULL, 0, NULL, NULL, &timeout, 0, &result );
+       if( rc == LDAP_TIMEOUT ) {
+               return ADDRQUERY_RETVAL(qry);
+       }
+       ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
+       if( rc != LDAP_SUCCESS ) {
+               debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
+               return ADDRQUERY_RETVAL(qry);
+       }
+
+       debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
+
+       /* Process results */
+       ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
+       first = TRUE;
+       while( TRUE ) {
+               ldapqry_touch( qry );
+               if( qry->entriesRead >= ctl->maxEntries ) break;                
+
+               /* Test for stop */
+               if( ldapqry_get_stop_flag( qry ) ) {
+                       break;
+               }
+
+               /* Retrieve entry */            
+               if( first ) {
+                       first = FALSE;
+                       e = ldap_first_entry( ld, result );
+               }
+               else {
+                       e = ldap_next_entry( ld, e );
+               }
+               if( e == NULL ) break;
+
+               entriesFound = TRUE;
+
+               /* Setup a critical section here */
+               pthread_mutex_lock( qry->mutexEntry );
+
+               /* Process entry */
+               listValues = ldapqry_fetch_attribs( ld, e );
+
+               /* Process callback */
+               if( qry->callBackEntry ) {
+                       qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
+               }
+               ldapqry_free_list_name_value( listValues );
+               listValues = NULL;
+
+               pthread_mutex_unlock( qry->mutexEntry );
+       }
+
+       /* Free up and disconnect */
+       ldap_msgfree( result );
+
+       if( entriesFound ) {
+               ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
+       }
+       else {
+               ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
        }
+
+       return ADDRQUERY_RETVAL(qry);
+}
+
+/**
+ * Perform the search to locate a specific LDAP record identified by
+ * distinguished name (dn).
+ * 
+ * \param  qry Query object to process.
+ * \return Error/status code.
+ */
+static gint ldapqry_perform_locate( LdapQuery *qry ) {
+       /* Connect */
+       qry->ldap = NULL;
+       ldapqry_connect( qry );
+       if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
+               /* Perform search */
+               ldapqry_locate_retrieve( qry );
+       }
+       /* Disconnect */
+       ldapqry_disconnect( qry );
+       qry->ldap = NULL;
+
+       /* Process callback */  
+       if( qry->callBackEnd ) {
+               g_timeout_add(0, callbackend, qry);
+       }
+
+       return ADDRQUERY_RETVAL(qry);
+}
+
+/**
+ * Remove results (folder and data) for specified LDAP query.
+ * \param  qry Query object to process.
+ * \return TRUE if folder deleted successfully.
+ */
+gboolean ldapquery_remove_results( LdapQuery *qry ) {
+       gboolean retVal = FALSE;
+
+       ldapqry_set_aged_flag( qry, TRUE );
+
+       if( ldapqry_get_busy_flag( qry ) ) {
+               ldapqry_set_stop_flag( qry, TRUE );
+       }
+       else {
+               LdapServer *server = qry->server;
+               server->listQuery = g_list_remove(server->listQuery, qry);
+
+               retVal = TRUE;
+       }
+       return retVal;
+}
+
+void ldapqry_print(LdapQuery *qry, FILE *stream) {
+       ldapsvr_print_data(qry->server, stream);
+       ldapctl_print(qry->control, stream);
+       fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
+       fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
+       fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
+       fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
+       fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
+       fprintf(stream, "completed: %d\n", qry->completed);
+       fprintf(stream, "startTime: %d\n", (int) qry->startTime);
+       fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
+       fprintf(stream, "data: %s\n", (gchar *) qry->data);
 }
 
 #endif /* USE_LDAP */