2006-07-12 [paul] 2.3.1cvs80
[claws.git] / src / ldapquery.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2006 Match Grun and the Sylpheed-Claws team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 /*
21  * Functions necessary to define and perform LDAP queries.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #ifdef USE_LDAP
29
30 #include <glib.h>
31 #include <sys/time.h>
32 #include <string.h>
33 #include <lber.h>
34
35 #include "ldapquery.h"
36 #include "ldapctrl.h"
37 #include "mgutils.h"
38
39 #include "addritem.h"
40 #include "addrcache.h"
41
42 #include "ldapquery.h"
43
44 /*
45  * Key for thread specific data.
46  */
47 static pthread_key_t _queryThreadKey_;
48 static gboolean _queryThreadInit_ = FALSE;
49
50 gboolean callbackend (gpointer data)
51 {
52         LdapQuery *qry = (LdapQuery *)data;
53         qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
54         return FALSE;
55 }
56
57
58 /**
59  * Create new LDAP query object.
60  * \return Initialized query object.
61  */
62 LdapQuery *ldapqry_create( void ) {
63         LdapQuery *qry;
64
65         qry = g_new0( LdapQuery, 1 );
66         ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
67         ADDRQUERY_ID(qry) = 0;
68         ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
69         ADDRQUERY_NAME(qry) = NULL;
70         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
71         ADDRQUERY_FOLDER(qry) = NULL;
72         ADDRQUERY_SEARCHVALUE(qry) = NULL;
73         qry->control = NULL;
74         qry->server = NULL;
75         qry->entriesRead = 0;
76         qry->elapsedTime = 0;
77         qry->stopFlag = FALSE;
78         qry->busyFlag = FALSE;
79         qry->agedFlag = FALSE;
80         qry->completed = FALSE;
81         qry->thread = NULL;
82         qry->callBackEntry = NULL;
83         qry->callBackEnd = NULL;
84         qry->ldap = NULL;
85         qry->data = NULL;
86
87         /* Mutex to protect stop and busy flags */
88         qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
89         pthread_mutex_init( qry->mutexStop, NULL );
90         qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
91         pthread_mutex_init( qry->mutexBusy, NULL );
92
93         /* Mutex to protect critical section */
94         qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
95         pthread_mutex_init( qry->mutexEntry, NULL );
96
97         return qry;
98 }
99
100 /**
101  * Specify the reference to control data that will be used for the query. The calling
102  * module should be responsible for creating and destroying this control object.
103  * \param qry Query object.
104  * \param ctl Control object.
105  */
106 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
107         g_return_if_fail( qry != NULL );
108         qry->control = ctl;
109 }
110
111 /**
112  * Specify query name to be used.
113  * \param qry   Query object.
114  * \param value Name.
115  */
116 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
117         ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
118         g_strstrip( ADDRQUERY_NAME(qry) );
119 }
120
121 /**
122  * Specify search value to be used.
123  * \param qry Query object.
124  * \param value 
125  */
126 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
127         ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
128         g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
129 }
130
131 /**
132  * Specify error/status.
133  * \param qry   Query object.
134  * \param value Status.
135  */
136 void ldapqry_set_error_status( LdapQuery* qry, const gint value ) {
137         ADDRQUERY_RETVAL(qry) = value;
138 }
139
140 /**
141  * Specify query type.
142  * \param qry Query object.
143  * \param value Query type, either:
144  * <ul>
145  * <li><code>LDAPQUERY_NONE</code></li>
146  * <li><code>LDAPQUERY_STATIC</code></li>
147  * <li><code>LDAPQUERY_DYNAMIC</code></li>
148  * </ul>
149  */
150 /*
151 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
152         ADDRQUERY_TYPE(qry) = value;
153 }
154 */
155
156 /**
157  * Specify search type.
158  * \param qry   Query object.
159  * \param value Type.
160  */
161 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
162         g_return_if_fail( qry != NULL );
163         ADDRQUERY_SEARCHTYPE(qry) = value;
164 }
165
166 /**
167  * Specify query ID.
168  * \param qry Query object.
169  * \param value ID for the query.
170  */
171 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
172         ADDRQUERY_ID(qry) = value;
173 }
174
175 /**
176  * Specify maximum number of LDAP entries to retrieve.
177  * \param qry Query object.
178  * \param value Entries to read.
179  */
180 void ldapqry_set_entries_read( LdapQuery* qry, const gint value ) {
181         if( value > 0 ) {
182                 qry->entriesRead = value;
183         }
184         else {
185                 qry->entriesRead = 0;
186         }
187 }
188
189 /**
190  * Register a callback function that will be executed when each entry
191  * has been read and processed. When called, the function will be passed
192  * this query object and a GList of ItemEMail objects as arguments. An
193  * example of typical usage is shown below.
194  *
195  * <pre>
196  * ------------------------------------------------------------
197  * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
198  *   GList *node;
199  *
200  *   node = listEMail;
201  *   while( node ) {
202  *     ItemEMail *email = node->data;
203  *     ... process email object ...
204  *     node = g_list_next( node );
205  *   }
206  *   g_list_free( listEMail );
207  * }
208  * ...
209  * ...
210  * ldapqry_set_callback_entry( qry, myCallbackEntry );
211  * ------------------------------------------------------------
212  * </pre>
213  *
214  * \param qry Query object.
215  * \param func Function.
216  */
217 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
218         pthread_mutex_lock( qry->mutexEntry );
219         qry->callBackEntry = func;
220         pthread_mutex_unlock( qry->mutexEntry );
221 }
222
223 /**
224  * Register a callback function that will be executed when the search
225  * is complete. When called, the function will be passed this query
226  * object as an argument.
227  * \param qry Query object.
228  * \param func Function.
229  */
230 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
231         qry->callBackEnd = func;
232 }
233
234 /**
235  * Notify query to start/stop executing. This method should be called with a
236  * value if <i>TRUE</i> to terminate an existing running query.
237  *
238  * \param qry Query object.
239  * \param value Value of stop flag.
240  */
241 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
242         g_return_if_fail( qry != NULL );
243
244         pthread_mutex_lock( qry->mutexStop );
245         qry->stopFlag = value;
246         pthread_mutex_unlock( qry->mutexStop );
247 }
248
249 /**
250  * Test value of stop flag. This method should be used to determine whether a
251  * query has stopped running.
252  * \param qry Query object.
253  * \return Value of stop flag.
254  */
255 gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
256         gboolean value;
257         g_return_val_if_fail( qry != NULL, TRUE );
258
259         pthread_mutex_lock( qry->mutexStop );
260         value = qry->stopFlag;
261         pthread_mutex_unlock( qry->mutexStop );
262         return value;
263 }
264
265 /**
266  * Set busy flag.
267  * \param qry Query object.
268  * \param value Value of busy flag.
269  */
270 void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
271         g_return_if_fail( qry != NULL );
272
273         pthread_mutex_lock( qry->mutexBusy );
274         qry->busyFlag = value;
275         pthread_mutex_unlock( qry->mutexBusy );
276 }
277
278 /**
279  * Test value of busy flag. This method will return a value of <i>FALSE</i>
280  * when a query has completed running.
281  * \param qry Query object.
282  * \return Value of busy flag.
283  */
284 gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
285         gboolean value;
286         g_return_val_if_fail( qry != NULL, FALSE );
287
288         pthread_mutex_lock( qry->mutexBusy );
289         value = qry->busyFlag;
290         pthread_mutex_unlock( qry->mutexBusy );
291         return value;
292 }
293
294 /**
295  * Set query aged flag.
296  * \param qry Query object.
297  * \param value Value of aged flag.
298  */
299 void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
300         g_return_if_fail( qry != NULL );
301         qry->agedFlag = value;
302 }
303
304 /**
305  * Test value of aged flag.
306  * \param qry Query object.
307  * \return <i>TRUE</i> if query has been marked as aged (and can be retired).
308  */
309 gboolean ldapqry_get_aged_flag( LdapQuery *qry ) {
310         g_return_val_if_fail( qry != NULL, TRUE );
311         return qry->agedFlag;
312 }
313
314 /**
315  * Specify user data for query.
316  * \param qry Query object.
317  * \param value Data to set.
318  */
319 void ldapqry_set_data( LdapQuery *qry, const gpointer value ) {
320         g_return_if_fail( qry != NULL );
321         qry->data = value;
322 }
323
324 /**
325  * Retrieve user data associated with query.
326  * \param qry Query object.
327  * \return Data.
328  */
329 gpointer ldapqry_get_data( LdapQuery *qry ) {
330         g_return_val_if_fail( qry != NULL, NULL );
331         return qry->data;
332 }
333
334 /**
335  * Release the LDAP control data associated with the query.
336  * \param qry Query object to process.
337  */
338 void ldapqry_release_control( LdapQuery *qry ) {
339         g_return_if_fail( qry != NULL );
340         if( qry->control != NULL ) {
341                 ldapctl_free( qry->control );
342         }
343         qry->control = NULL;
344 }
345
346 /**
347  * Clear LDAP query member variables.
348  * \param qry Query object.
349  */
350 void ldapqry_clear( LdapQuery *qry ) {
351         g_return_if_fail( qry != NULL );
352
353         /* Free internal stuff */
354         g_free( ADDRQUERY_NAME(qry) );
355         g_free( ADDRQUERY_SEARCHVALUE(qry) );
356
357         /* Clear pointers and value */
358         ADDRQUERY_NAME(qry) = NULL;
359         ADDRQUERY_SEARCHVALUE(qry) = NULL;
360         ADDRQUERY_ID(qry) = 0;
361         ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
362         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
363         qry->entriesRead = 0;
364         qry->elapsedTime = 0;
365         qry->stopFlag = FALSE;
366         qry->busyFlag = FALSE;
367         qry->agedFlag = FALSE;
368         qry->completed = FALSE;
369         qry->callBackEntry = NULL;
370         qry->callBackEnd = NULL;
371         qry->ldap = NULL;
372         qry->data = NULL;
373 }
374
375 /**
376  * Free up LDAP query object by releasing internal memory. Note that
377  * the thread object will be freed by the OS.
378  * \param qry Query object to process.
379  */
380 void ldapqry_free( LdapQuery *qry ) {
381         g_return_if_fail( qry != NULL );
382
383         /* Clear out internal members */
384         ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
385         ldapqry_clear( qry );
386
387         /* Free the mutex */
388         pthread_mutex_destroy( qry->mutexStop );
389         pthread_mutex_destroy( qry->mutexBusy );
390         pthread_mutex_destroy( qry->mutexEntry );
391         g_free( qry->mutexStop );
392         g_free( qry->mutexBusy );
393         g_free( qry->mutexEntry );
394         qry->mutexEntry = NULL;
395         qry->mutexBusy = NULL;
396         qry->mutexStop = NULL;
397
398         /* Do not free folder - parent server object should free */     
399         ADDRQUERY_FOLDER(qry) = NULL;
400
401         /* Do not free thread - thread should be terminated before freeing */
402         qry->thread = NULL;
403
404         /* Do not free LDAP control - should be destroyed before freeing */
405         qry->control = NULL;
406
407         /* Now release object */
408         g_free( qry );
409 }
410
411 /**
412  * Display object to specified stream.
413  * \param qry    Query object to process.
414  * \param stream Output stream.
415  */
416 void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
417         g_return_if_fail( qry != NULL );
418
419         fprintf( stream, "LdapQuery:\n" );
420         fprintf( stream, "  control?: %s\n",   qry->control ? "yes" : "no" );
421         fprintf( stream, "err/status: %d\n",   ADDRQUERY_RETVAL(qry) );
422         fprintf( stream, "query type: %d\n",   ADDRQUERY_TYPE(qry) );
423         fprintf( stream, "searchType: %d\n",   ADDRQUERY_SEARCHTYPE(qry) );
424         fprintf( stream, "query name: '%s'\n", ADDRQUERY_NAME(qry) );
425         fprintf( stream, "search val: '%s'\n", ADDRQUERY_SEARCHVALUE(qry) );
426         fprintf( stream, "   queryID: %d\n",   ADDRQUERY_ID(qry) );
427         fprintf( stream, "   entries: %d\n",   qry->entriesRead );
428         fprintf( stream, "   elapsed: %d\n",   qry->elapsedTime );
429         fprintf( stream, " stop flag: %s\n",   qry->stopFlag  ? "yes" : "no" );
430         fprintf( stream, " busy flag: %s\n",   qry->busyFlag  ? "yes" : "no" );
431         fprintf( stream, " aged flag: %s\n",   qry->agedFlag  ? "yes" : "no" );
432         fprintf( stream, " completed: %s\n",   qry->completed ? "yes" : "no" );
433 }
434
435 /**
436  * Free linked lists of character strings.
437  * \param listName  List of common names.
438  * \param listAddr  List of addresses.
439  * \param listFirst List of first names.
440  * \param listLast  List of last names.
441  */
442 static void ldapqry_free_lists(
443                 GSList *listName, GSList *listAddr, GSList *listFirst,
444                 GSList *listLast )
445 {
446         mgu_free_list( listName );
447         mgu_free_list( listAddr );
448         mgu_free_list( listFirst );
449         mgu_free_list( listLast );
450 }
451
452 /**
453  * Add all LDAP attribute values to a list.
454  * \param ld LDAP handle.
455  * \param entry LDAP entry to process.
456  * \param attr  LDAP attribute.
457  * \return List of values.
458  */
459 static GSList *ldapqry_add_list_values(
460                 LDAP *ld, LDAPMessage *entry, char *attr )
461 {
462         GSList *list = NULL;
463         gint i;
464         gchar **vals;
465
466         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
467                 for( i = 0; vals[i] != NULL; i++ ) {
468                         /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
469                         list = g_slist_append( list, g_strdup( vals[i] ) );
470                 }
471         }
472         ldap_value_free( vals );
473         return list;
474 }
475
476 /**
477  * Add a single attribute value to a list.
478  * \param  ld    LDAP handle.
479  * \param  entry LDAP entry to process.
480  * \param  attr  LDAP attribute name to process.
481  * \return List of values; only one value will be present.
482  */
483 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
484         GSList *list = NULL;
485         gchar **vals;
486
487         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
488                 if( vals[0] != NULL ) {
489                         /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
490                         list = g_slist_append( list, g_strdup( vals[0] ) );
491                 }
492         }
493         ldap_value_free( vals );
494         return list;
495 }
496
497 /**
498  * Build an address list entry and append to list of address items. Name is formatted
499  * as "<first-name> <last-name>".
500  *
501  * \param  cache     Address cache to load.
502  * \param  qry Query object to process.
503  * \param  dn        DN for entry found on server.
504  * \param  listName  List of common names for entry; see notes below.
505  * \param  listAddr  List of EMail addresses for entry.
506  * \param  listFirst List of first names for entry.
507  * \param  listLast  List of last names for entry.
508  *
509  * \return List of ItemEMail objects.
510  *
511  * Notes:
512  * 1) Each LDAP server entry may have multiple LDAP attributes with the same
513  *    name. For example, a single entry for a person may have more than one
514  *    common name, email address, etc.
515 *
516  * 2) The DN for the entry is unique for the server.
517  */
518 static GList *ldapqry_build_items_fl(
519                 AddressCache *cache, LdapQuery *qry, gchar *dn,
520                 GSList *listName, GSList *listAddr, GSList *listFirst,
521                 GSList *listLast )
522 {
523         GSList *nodeAddress;
524         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
525         gboolean allocated;
526         ItemPerson *person;
527         ItemEMail *email;
528         ItemFolder *folder;
529         GList *listReturn = NULL;
530
531         folder = ADDRQUERY_FOLDER(qry);
532         if( folder == NULL ) return listReturn;
533         if( listAddr == NULL ) return listReturn;
534
535         /* Find longest first name in list */
536         firstName = mgu_slist_longest_entry( listFirst );
537
538         /* Format last name */
539         if( listLast ) {
540                 lastName = listLast->data;
541         }
542
543         /* Find longest common name */
544         allocated = FALSE;
545         fullName = mgu_slist_longest_entry( listName );
546         if( fullName == NULL ) {
547                 /* Format a full name from first and last names */
548                 if( firstName ) {
549                         if( lastName ) {
550                                 fullName = g_strdup_printf( "%s %s", firstName, lastName );
551                         }
552                         else {
553                                 fullName = g_strdup_printf( "%s", firstName );
554                         }
555                 }
556                 else {
557                         if( lastName ) {
558                                 fullName = g_strdup_printf( "%s", lastName );
559                         }
560                 }
561                 if( fullName ) {
562                         g_strchug( fullName ); g_strchomp( fullName );
563                         allocated = TRUE;
564                 }
565         }
566
567         /* Add person into folder */            
568         person = addritem_create_item_person();
569         addritem_person_set_common_name( person, fullName );
570         addritem_person_set_first_name( person, firstName );
571         addritem_person_set_last_name( person, lastName );
572         addrcache_id_person( cache, person );
573         addritem_person_set_external_id( person, dn );
574         addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
575
576         qry->entriesRead++;
577
578         /* Add each address item */
579         nodeAddress = listAddr;
580         while( nodeAddress ) {
581                 email = addritem_create_item_email();
582                 addritem_email_set_address( email, nodeAddress->data );
583                 addrcache_id_email( cache, email );
584                 addrcache_person_add_email( cache, person, email );
585                 addritem_person_add_email( person, email );
586                 listReturn = g_list_append( listReturn, email );
587                 nodeAddress = g_slist_next( nodeAddress );
588         }
589
590         /* Free any allocated memory */
591         if( allocated ) {
592                 g_free( fullName );
593         }
594         fullName = firstName = lastName = NULL;
595
596         return listReturn;
597 }
598
599 /**
600  * Process a single search entry.
601  * \param  cache Address cache to load.
602  * \param  qry   Query object to process.
603  * \param  ld    LDAP handle.
604  * \param  e     LDAP message.
605  * \return List of EMail objects found.
606  */
607 static GList *ldapqry_process_single_entry(
608                 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
609 {
610         char *dnEntry;
611         char *attribute;
612         LdapControl *ctl;
613         BerElement *ber;
614         GSList *listName = NULL, *listAddress = NULL;
615         GSList *listFirst = NULL, *listLast = NULL;
616         GList *listReturn;
617
618         listReturn = NULL;
619         ctl = qry->control;
620         dnEntry = ldap_get_dn( ld, e );
621         /* printf( "DN: %s\n", dnEntry ); */
622
623         /* Process all attributes */
624         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
625                 attribute = ldap_next_attribute( ld, e, ber ) ) {
626
627                 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
628                         listAddress = ldapqry_add_list_values( ld, e, attribute );
629                 }
630                 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
631                         listName = ldapqry_add_list_values( ld, e, attribute );
632                 }
633                 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
634                         listFirst = ldapqry_add_list_values( ld, e, attribute );
635                 }
636                 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
637                         listLast = ldapqry_add_single_value( ld, e, attribute );
638                 }
639
640                 /* Free memory used to store attribute */
641                 ldap_memfree( attribute );
642         }
643
644         /* Format and add items to cache */
645         listReturn = ldapqry_build_items_fl(
646                 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
647
648         /* Free up */
649         ldapqry_free_lists( listName, listAddress, listFirst, listLast );
650         listName = listAddress = listFirst = listLast = NULL;
651
652         if( ber != NULL ) {
653                 ber_free( ber, 0 );
654         }
655         g_free( dnEntry );
656
657         return listReturn;
658 }
659
660 /**
661  * Check parameters that are required for a search. This should
662  * be called before performing a search.
663  * \param  qry Query object to process.
664  * \return <i>TRUE</i> if search criteria appear OK.
665  */
666 gboolean ldapqry_check_search( LdapQuery *qry ) {
667         LdapControl *ctl;
668         ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
669
670         /* Test for control data */
671         ctl = qry->control;
672         if( ctl == NULL ) {
673                 return FALSE;
674         }
675
676         /* Test for search value */
677         if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
678                 return FALSE;
679         }
680         if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
681                 return FALSE;
682         }
683         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
684         return TRUE;
685 }
686
687 /**
688  * Touch the query. This nudges the touch time with the current time.
689  * \param qry Query object to process.
690  */
691 void ldapqry_touch( LdapQuery *qry ) {
692         qry->touchTime = time( NULL );
693         qry->agedFlag = FALSE;
694 }
695
696 /**
697  * Connect to LDAP server.
698  * \param  qry Query object to process.
699  * \return Error/status code.
700  */
701 static gint ldapqry_connect( LdapQuery *qry ) {
702         LdapControl *ctl;
703         LDAP *ld = NULL;
704         gint rc;
705         gint version;
706
707         /* Initialize connection */
708         /* printf( "===ldapqry_connect===\n" ); */
709         /* ldapqry_print( qry, stdout ); */
710         ctl = qry->control;
711         /* ldapctl_print( ctl, stdout ); */
712         /* printf( "======\n" ); */
713         ldapqry_touch( qry );
714         qry->startTime = qry->touchTime;
715         qry->elapsedTime = -1;
716         ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
717         if (!ctl->enableSSL) {
718                 ld = ldap_init( ctl->hostName, ctl->port );
719         } else {
720                 gchar *uri = g_strdup_printf("ldaps://%s:%d",
721                                 ctl->hostName, ctl->port);
722                 ldap_initialize(&ld, uri);
723                 g_free(uri);
724         }
725         if (ld == NULL)
726                 return ADDRQUERY_RETVAL(qry);
727
728         qry->ldap = ld;
729         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
730         if( ldapqry_get_stop_flag( qry ) ) {
731                 return ADDRQUERY_RETVAL(qry);
732         }
733         ldapqry_touch( qry );
734
735         /*
736         printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
737         */
738
739 #ifdef USE_LDAP_TLS
740         /* Handle TLS */
741         version = LDAP_VERSION3;
742         rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
743         if( rc == LDAP_OPT_SUCCESS ) {
744                 ctl->version = LDAP_VERSION3;
745         }
746
747         if( ctl->version == LDAP_VERSION3 ) {
748                 if( ctl->enableTLS && !ctl->enableSSL ) {
749                         ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
750                         rc = ldap_start_tls_s( ld, NULL, NULL );
751                         
752                         /* printf( "rc=%d\n", rc );
753                         printf( "LDAP Status: set_option: %s\n", ldap_err2string( rc ) ); */
754                         
755                         if( rc != LDAP_SUCCESS ) {
756                                 return ADDRQUERY_RETVAL(qry);
757                         }
758                 }
759         }
760 #endif
761
762         /* Bind to the server, if required */
763         ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
764         if( ctl->bindDN ) {
765                 if( * ctl->bindDN != '\0' ) {
766                         /* printf( "binding...\n" ); */
767                         rc = ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
768                         /* printf( "rc=%d\n", rc ); */
769                         if( rc != LDAP_SUCCESS ) {
770                                 /*
771                                 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
772                                         ldap_err2string( rc ) );
773                                 */
774                                 return ADDRQUERY_RETVAL(qry);
775                         }
776                 }
777         }
778         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
779         if( ldapqry_get_stop_flag( qry ) ) {
780                 return ADDRQUERY_RETVAL(qry);
781         }
782         ldapqry_touch( qry );
783
784         ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
785
786         return ADDRQUERY_RETVAL(qry);
787 }
788
789 /**
790  * Connect to LDAP server.
791  * \param  qry Query object to process.
792  * \return Error/status code.
793  */
794 static gint ldapqry_disconnect( LdapQuery *qry ) {
795         /* Disconnect */
796         if( qry->ldap ) ldap_unbind( qry->ldap );
797         qry->ldap = NULL;
798
799         ldapqry_touch( qry );
800         qry->elapsedTime = qry->touchTime - qry->startTime;
801
802         return ADDRQUERY_RETVAL(qry);
803 }
804
805 /**
806  * Perform the LDAP search, reading LDAP entries into cache.
807  * Note that one LDAP entry can have multiple values for many of its
808  * attributes. If these attributes are E-Mail addresses; these are
809  * broken out into separate address items. For any other attribute,
810  * only the first occurrence is read.
811  * 
812  * \param  qry Query object to process.
813  * \return Error/status code.
814  */
815 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
816         LdapControl *ctl;
817         LDAP *ld;
818         LDAPMessage *result, *e = NULL;
819         char **attribs;
820         gchar *criteria;
821         gboolean searchFlag;
822         gboolean entriesFound;
823         gboolean first;
824         struct timeval timeout;
825         gint rc;
826         AddressCache *cache;
827         GList *listEMail;
828
829         /* Initialize some variables */
830         ld = qry->ldap;
831         ctl = qry->control;
832         cache = qry->server->addressCache;
833         timeout.tv_sec = ctl->timeOut;
834         timeout.tv_usec = 0L;
835         entriesFound = FALSE;
836         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
837
838         /* Define all attributes we are interested in. */
839         attribs = ldapctl_attribute_array( ctl );
840
841         /* Create LDAP search string */
842         criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
843         /* printf( "Search criteria ::%s::\n", criteria ); */
844
845         /*
846          * Execute the search - this step may take some time to complete
847          * depending on network traffic and server response time.
848          */
849         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
850         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
851                 attribs, 0, NULL, NULL, &timeout, 0, &result );
852         ldapctl_free_attribute_array( attribs );
853         g_free( criteria );
854         criteria = NULL;
855         if( rc == LDAP_TIMEOUT ) {
856                 return ADDRQUERY_RETVAL(qry);
857         }
858         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
859
860         /* Test valid returns */
861         searchFlag = FALSE;
862         if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
863                 searchFlag = TRUE;
864         }
865         else if( rc == LDAP_SUCCESS ) {
866                 searchFlag = TRUE;
867         }
868         else if( rc == LDAP_PARTIAL_RESULTS ) {
869                 searchFlag = TRUE;
870         }
871         else {
872                 /*
873                 printf( "LDAP Error: ldap_search_st: %d\n", rc );
874                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
875                 */
876                 return ADDRQUERY_RETVAL(qry);
877         }
878         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
879
880         /*
881         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
882         */
883
884         /* Process results */
885         first = TRUE;
886         while( searchFlag ) {
887                 ldapqry_touch( qry );
888                 if( qry->entriesRead >= ctl->maxEntries ) break;                
889
890                 /* Test for stop */             
891                 if( ldapqry_get_stop_flag( qry ) ) {
892                         break;
893                 }
894
895                 /* Retrieve entry */            
896                 if( first ) {
897                         first = FALSE;
898                         e = ldap_first_entry( ld, result );
899                 }
900                 else {
901                         e = ldap_next_entry( ld, e );
902                 }
903                 if( e == NULL ) break;
904                 entriesFound = TRUE;
905
906                 /* Setup a critical section here */
907                 pthread_mutex_lock( qry->mutexEntry );
908
909                 /* Process entry */
910                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
911
912                 /* Process callback */
913                 if( qry->callBackEntry ) {
914                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
915                 }
916                 else {
917                         g_list_free( listEMail );
918                 }
919                 pthread_mutex_unlock( qry->mutexEntry );
920         }
921
922         /* Free up and disconnect */
923         ldap_msgfree( result );
924
925         if( searchFlag ) {
926                 if( entriesFound ) {
927                         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
928                 }
929                 else {
930                         ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
931                 }
932         }
933
934         return ADDRQUERY_RETVAL(qry);
935 }
936
937 /**
938  * Connection, perform search and disconnect.
939  * \param  qry Query object to process.
940  * \return Error/status code.
941  */
942 static gint ldapqry_perform_search( LdapQuery *qry ) {
943         /* Check search criteria */     
944         if( ! ldapqry_check_search( qry ) ) {
945                 return ADDRQUERY_RETVAL(qry);
946         }
947
948         /* Connect */
949         qry->ldap = NULL;
950         ldapqry_connect( qry );
951         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
952                 /* Perform search */
953                 ldapqry_search_retrieve( qry );
954         }
955         /* Disconnect */
956         ldapqry_disconnect( qry );
957         qry->ldap = NULL;
958
959         return ADDRQUERY_RETVAL(qry);
960 }
961
962 static gint ldapqry_perform_locate( LdapQuery *qry );
963
964 /**
965  * Wrapper around search.
966  * \param  qry Query object to process.
967  * \return Error/status code.
968  */
969 gint ldapqry_search( LdapQuery *qry ) {
970         gint retVal;
971
972         g_return_val_if_fail( qry != NULL, -1 );
973         g_return_val_if_fail( qry->control != NULL, -1 );
974
975         ldapqry_touch( qry );
976         qry->completed = FALSE;
977
978         /* Setup pointer to thread specific area */
979         pthread_setspecific( _queryThreadKey_, qry );
980
981         pthread_detach( pthread_self() );
982         
983         /* Now perform the search */
984         qry->entriesRead = 0;
985         ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
986         ldapqry_set_busy_flag( qry, TRUE );
987         ldapqry_set_stop_flag( qry, FALSE );
988         if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
989                 retVal = ldapqry_perform_locate( qry );
990         }
991         else {
992                 retVal = ldapqry_perform_search( qry );
993         }
994         if( retVal == LDAPRC_SUCCESS ) {
995                 qry->server->addressCache->dataRead = TRUE;
996                 qry->server->addressCache->accessFlag = FALSE;
997                 if( ldapqry_get_stop_flag( qry ) ) {
998                         /*
999                         printf( "Search was terminated prematurely\n" );
1000                         */
1001                 }
1002                 else {
1003                         ldapqry_touch( qry );
1004                         qry->completed = TRUE;
1005                         /*
1006                         printf( "Search ran to completion\n" );
1007                         */
1008                 }
1009         }
1010         ldapqry_set_stop_flag( qry, TRUE );
1011         ldapqry_set_busy_flag( qry, FALSE );
1012
1013         /* Process callback */  
1014         if( qry->callBackEnd ) {
1015                 g_timeout_add(0, callbackend, qry);
1016         }
1017
1018         return ADDRQUERY_RETVAL(qry);
1019 }
1020
1021 /**
1022  * Read data into list using a background thread. Callback function will be
1023  * notified when search is complete.
1024  * \param  qry Query object to process.
1025  * \return Error/status code.
1026  */
1027 gint ldapqry_read_data_th( LdapQuery *qry ) {
1028         g_return_val_if_fail( qry != NULL, -1 );
1029         g_return_val_if_fail( qry->control != NULL, -1 );
1030
1031         ldapqry_set_stop_flag( qry, FALSE );
1032         ldapqry_touch( qry );
1033         if( ldapqry_check_search( qry ) ) {
1034                 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1035                         /*
1036                         printf( "Starting LDAP search thread\n");
1037                         */
1038                         ldapqry_set_busy_flag( qry, TRUE );
1039                         qry->thread = g_malloc0( sizeof( pthread_t ) );
1040
1041                         /* Setup thread */                      
1042                         pthread_create( qry->thread, NULL,
1043                                 (void *) ldapqry_search, (void *) qry );
1044                 }
1045         }
1046         return ADDRQUERY_RETVAL(qry);
1047 }
1048
1049 /**
1050  * Cleanup LDAP thread data. This function will be called when each thread
1051  * exits. Note that the thread object will be freed by the kernel.
1052  * \param ptr Pointer to object being destroyed (a query object in this case).
1053  */
1054 static void ldapqry_destroyer( void * ptr ) {
1055         LdapQuery *qry;
1056
1057         qry = ( LdapQuery * ) ptr;
1058         /*
1059         printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1060         */
1061
1062         /* Perform any destruction here */
1063         if( qry->control != NULL ) {
1064                 ldapctl_free( qry->control );
1065         }
1066         qry->control = NULL;
1067         qry->thread = NULL;
1068         ldapqry_set_busy_flag( qry, FALSE );
1069 }
1070
1071 /**
1072  * Cancel thread associated with query.
1073  * \param qry Query object to process.
1074  */
1075 void ldapqry_cancel( LdapQuery *qry ) {
1076         g_return_if_fail( qry != NULL );
1077
1078         /*
1079         printf( "cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1080         */
1081         if( ldapqry_get_busy_flag( qry ) ) {
1082                 if( qry->thread ) {
1083                         /* printf( "calling pthread_cancel\n" ); */
1084                         pthread_cancel( * qry->thread );
1085                 }
1086         }
1087 }
1088
1089 /**
1090  * Initialize LDAP query. This function should be called once before executing
1091  * any LDAP queries to initialize thread specific data.
1092  */
1093 void ldapqry_initialize( void ) {
1094         /* printf( "ldapqry_initialize...\n" ); */
1095         if( ! _queryThreadInit_ ) {
1096                 /*
1097                 printf( "ldapqry_initialize::creating thread specific area\n" );
1098                 */
1099                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1100                 _queryThreadInit_ = TRUE;
1101         }
1102         /* printf( "ldapqry_initialize... done!\n" ); */
1103 }
1104
1105 /**
1106  * Age the query based on LDAP control parameters.
1107  * \param qry    Query object to process.
1108  * \param maxAge Maximum age of query (in seconds).
1109  */
1110 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1111         gint age;
1112
1113         g_return_if_fail( qry != NULL );
1114
1115         /* Limit the time that queries can hang around */       
1116         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1117
1118         /* Check age of query */
1119         age = time( NULL ) - qry->touchTime;
1120         if( age > maxAge ) {
1121                 qry->agedFlag = TRUE;
1122         }
1123 }
1124
1125 /**
1126  * Delete folder associated with query results.
1127  * \param qry Query object to process.
1128  */
1129 void ldapqry_delete_folder( LdapQuery *qry ) {
1130         AddressCache *cache;
1131         ItemFolder *folder;
1132
1133         g_return_if_fail( qry != NULL );
1134
1135         folder = ADDRQUERY_FOLDER(qry);
1136         if( folder ) {
1137                 cache = qry->server->addressCache;
1138                 folder = addrcache_remove_folder_delete( cache, folder );
1139                 if( folder ) {
1140                         addritem_free_item_folder( folder );
1141                 }
1142                 ADDRQUERY_FOLDER(qry) = NULL;
1143         }
1144 }
1145
1146 /**
1147  * Create a name/value pair object.
1148  * \param n Name.
1149  * \param v Value.
1150  * \return Initialized object.
1151  */
1152 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1153         NameValuePair *nvp = g_new0( NameValuePair, 1 );
1154
1155         nvp->name = g_strdup( n );
1156         nvp->value = g_strdup( v );
1157         return nvp;
1158 }
1159
1160 /**
1161  * Free up name/value pair object.
1162  * \param nvp Name/value object.
1163  */
1164 void ldapqry_free_name_value( NameValuePair *nvp ) {
1165         if( nvp ) {
1166                 g_free( nvp->name );
1167                 g_free( nvp->value );
1168                 nvp->name = nvp->value = NULL;
1169                 g_free( nvp );
1170         }
1171 }
1172
1173 /**
1174  * Print name/value pair object for debug.
1175  * \param nvp    Name/value object.
1176  * \param stream Output stream.
1177  */
1178 void ldapqry_print_name_value( NameValuePair *nvp, FILE *stream ) {
1179         if( nvp ) {
1180                 fprintf( stream, "n/v ::%s::%s::\n", nvp->name, nvp->value );
1181         }
1182 }
1183
1184 /**
1185  * Free up a list name/value pair objects.
1186  * \param list List of name/value objects.
1187  */
1188 void ldapqry_free_list_name_value( GList *list ) {
1189         GList *node;
1190
1191         node = list;
1192         while( node ) {
1193                 NameValuePair *nvp = ( NameValuePair * ) node->data;
1194                 ldapqry_free_name_value( nvp );
1195                 node->data = NULL;
1196                 node = g_list_next( node );
1197         }
1198         g_list_free( list );
1199 }
1200
1201 /**
1202  * Load a list of name/value pairs from LDAP attributes.
1203  * \param  ld          LDAP handle.
1204  * \param  e          LDAP message.
1205  * \param  attr       Attribute name.
1206  * \param  listValues List to populate.
1207  * \return List of attribute name/value pairs.
1208  */
1209 static GList *ldapqry_load_attrib_values(
1210                 LDAP *ld, LDAPMessage *entry, char *attr,
1211                 GList *listValues )
1212 {
1213         GList *list = NULL;
1214         gint i;
1215         gchar **vals;
1216         NameValuePair *nvp;
1217
1218         list = listValues;
1219         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
1220                 for( i = 0; vals[i] != NULL; i++ ) {
1221                         nvp = ldapqry_create_name_value( attr, vals[i] );
1222                         list = g_list_append( list, nvp );
1223                 }
1224         }
1225         ldap_value_free( vals );
1226         return list;
1227 }
1228
1229 /**
1230  * Fetch a list of all attributes.
1231  * \param  ld    LDAP handle.
1232  * \param  e     LDAP message.
1233  * \return List of attribute name/value pairs.
1234  */
1235 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1236 {
1237         char *attribute;
1238         BerElement *ber;
1239         GList *listValues = NULL;
1240
1241         /* Process all attributes */
1242         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1243                 attribute = ldap_next_attribute( ld, e, ber ) ) {
1244                 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1245                 ldap_memfree( attribute );
1246         }
1247
1248         /* Free up */
1249         if( ber != NULL ) {
1250                 ber_free( ber, 0 );
1251         }
1252         return listValues;
1253 }
1254
1255 #define CRITERIA_SINGLE "(objectclass=*)"
1256
1257 /**
1258  * Perform the data retrieval for a specific LDAP record.
1259  * 
1260  * \param  qry Query object to process.
1261  * \return Error/status code.
1262  */
1263 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1264         LdapControl *ctl;
1265         LDAP *ld;
1266         LDAPMessage *result, *e = NULL;
1267         gboolean entriesFound;
1268         gboolean first;
1269         struct timeval timeout;
1270         gint rc;
1271         gchar *dn;
1272         GList *listValues;
1273
1274         /* Initialize some variables */
1275         ld = qry->ldap;
1276         ctl = qry->control;
1277         dn = ADDRQUERY_SEARCHVALUE(qry);
1278         timeout.tv_sec = ctl->timeOut;
1279         timeout.tv_usec = 0L;
1280         entriesFound = FALSE;
1281
1282         /*
1283          * Execute the search - this step may take some time to complete
1284          * depending on network traffic and server response time.
1285          */
1286         ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1287         rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1288                 NULL, 0, NULL, NULL, &timeout, 0, &result );
1289         if( rc == LDAP_TIMEOUT ) {
1290                 return ADDRQUERY_RETVAL(qry);
1291         }
1292         ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1293         if( rc != LDAP_SUCCESS ) {
1294                 /*
1295                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
1296                 */
1297                 return ADDRQUERY_RETVAL(qry);
1298         }
1299
1300         /*
1301         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
1302         */
1303
1304         /* Process results */
1305         ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1306         first = TRUE;
1307         while( TRUE ) {
1308                 ldapqry_touch( qry );
1309                 if( qry->entriesRead >= ctl->maxEntries ) break;                
1310
1311                 /* Test for stop */
1312                 if( ldapqry_get_stop_flag( qry ) ) {
1313                         break;
1314                 }
1315
1316                 /* Retrieve entry */            
1317                 if( first ) {
1318                         first = FALSE;
1319                         e = ldap_first_entry( ld, result );
1320                 }
1321                 else {
1322                         e = ldap_next_entry( ld, e );
1323                 }
1324                 if( e == NULL ) break;
1325
1326                 entriesFound = TRUE;
1327
1328                 /* Setup a critical section here */
1329                 pthread_mutex_lock( qry->mutexEntry );
1330
1331                 /* Process entry */
1332                 listValues = ldapqry_fetch_attribs( ld, e );
1333
1334                 /* Process callback */
1335                 if( qry->callBackEntry ) {
1336                         qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1337                 }
1338                 ldapqry_free_list_name_value( listValues );
1339                 listValues = NULL;
1340
1341                 pthread_mutex_unlock( qry->mutexEntry );
1342         }
1343
1344         /* Free up and disconnect */
1345         ldap_msgfree( result );
1346
1347         if( entriesFound ) {
1348                 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1349         }
1350         else {
1351                 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1352         }
1353
1354         return ADDRQUERY_RETVAL(qry);
1355 }
1356
1357 /**
1358  * Perform the search to locate a specific LDAP record identified by
1359  * distinguished name (dn).
1360  * 
1361  * \param  qry Query object to process.
1362  * \return Error/status code.
1363  */
1364 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1365         /* Connect */
1366         qry->ldap = NULL;
1367         ldapqry_connect( qry );
1368         if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1369                 /* Perform search */
1370                 ldapqry_locate_retrieve( qry );
1371         }
1372         /* Disconnect */
1373         ldapqry_disconnect( qry );
1374         qry->ldap = NULL;
1375
1376         /* Process callback */  
1377         if( qry->callBackEnd ) {
1378                 g_timeout_add(0, callbackend, qry);
1379         }
1380
1381         return ADDRQUERY_RETVAL(qry);
1382 }
1383
1384 /**
1385  * Remove results (folder and data) for specified LDAP query.
1386  * \param  qry Query object to process.
1387  * \return TRUE if folder deleted successfully.
1388  */
1389 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1390         gboolean retVal = FALSE;
1391
1392         ldapqry_set_aged_flag( qry, TRUE );
1393
1394         if( ldapqry_get_busy_flag( qry ) ) {
1395                 ldapqry_set_stop_flag( qry, TRUE );
1396         }
1397         else {
1398                 LdapServer *server = qry->server;
1399                 server->listQuery = g_list_remove(server->listQuery, qry);
1400
1401                 retVal = TRUE;
1402         }
1403         return retVal;
1404 }
1405
1406 #endif  /* USE_LDAP */
1407
1408 /*
1409  * End of Source.
1410  */
1411
1412