LDAP static search.
[claws.git] / src / ldapquery.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003 Match Grun
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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 <ldap.h>
34 #include <lber.h>
35
36 #include "ldapquery.h"
37 #include "ldapctrl.h"
38 #include "mgutils.h"
39
40 #include "addritem.h"
41 #include "addrcache.h"
42
43 /*
44  * Key for thread specific data.
45  */
46 static pthread_key_t _queryThreadKey_;
47 static gboolean _queryThreadInit_ = FALSE;
48
49 /**
50  * Create new LDAP query object.
51  * \return Initialized query object.
52  */
53 LdapQuery *ldapqry_create( void ) {
54         LdapQuery *qry;
55
56         qry = g_new0( LdapQuery, 1 );
57         qry->control = NULL;
58         qry->retVal = LDAPRC_SUCCESS;
59         qry->queryType = LDAPQUERY_NONE;
60         qry->queryName = NULL;
61         qry->searchValue = NULL;
62         qry->queryID = 0;
63         qry->entriesRead = 0;
64         qry->elapsedTime = 0;
65         qry->stopFlag = FALSE;
66         qry->busyFlag = FALSE;
67         qry->agedFlag = FALSE;
68         qry->completed = FALSE;
69         qry->thread = NULL;
70         qry->callBackStart = NULL;
71         qry->callBackEntry = NULL;
72         qry->callBackEnd = NULL;
73         qry->folder = NULL;
74         qry->server = NULL;
75
76         /* Mutex to protect stop and busy flags */
77         qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
78         pthread_mutex_init( qry->mutexStop, NULL );
79         qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
80         pthread_mutex_init( qry->mutexBusy, NULL );
81
82         /* Mutex to protect critical section */
83         qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
84         pthread_mutex_init( qry->mutexEntry, NULL );
85
86         return qry;
87 }
88
89 /**
90  * Specify the reference to control data that will be used for the query. The calling
91  * module should be responsible for creating and destroying this control object.
92  * \param qry Query object.
93  * \param ctl Control object.
94  */
95 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
96         g_return_if_fail( qry != NULL );
97         qry->control = ctl;
98 }
99
100 /**
101  * Specify query name to be used.
102  * \param qry   Query object.
103  * \param value Name.
104  */
105 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
106         qry->queryName = mgu_replace_string( qry->queryName, value );
107         g_strstrip( qry->queryName );
108 }
109
110 /**
111  * Specify search value to be used.
112  * \param qry Query object.
113  * \param value 
114  */
115 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
116         qry->searchValue = mgu_replace_string( qry->searchValue, value );
117         g_strstrip( qry->searchValue );
118 }
119
120 /**
121  * Specify error/status.
122  * \param qry   Query object.
123  * \param value Status.
124  */
125 void ldapqry_set_error_status( LdapQuery* qry, const gint value ) {
126         qry->retVal = value;
127 }
128
129 /**
130  * Specify query type.
131  * \param qry Query object.
132  * \param value Query type, either:
133  * <ul>
134  * <li><code>LDAPQUERY_NONE</code></li>
135  * <li><code>LDAPQUERY_STATIC</code></li>
136  * <li><code>LDAPQUERY_DYNAMIC</code></li>
137  * </ul>
138  */
139 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
140         qry->queryType = value;
141 }
142
143 /**
144  * Specify query ID.
145  * \param qry Query object.
146  * \param value ID for the query.
147  */
148 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
149         qry->queryID = value;
150 }
151
152 /**
153  * Specify maximum number of LDAP entries to retrieve.
154  * \param qry Query object.
155  * \param value Entries to read.
156  */
157 void ldapqry_set_entries_read( LdapQuery* qry, const gint value ) {
158         if( value > 0 ) {
159                 qry->entriesRead = value;
160         }
161         else {
162                 qry->entriesRead = 0;
163         }
164 }
165
166 /**
167  * Register a callback function that will be executed when the search
168  * starts. When called, the function will be passed this query object
169  * as an argument.
170  * \param qry Query object.
171  * \param func Function.
172  */
173 void ldapqry_set_callback_start( LdapQuery *qry, void *func ) {
174         qry->callBackStart = func;
175 }
176
177 /**
178  * Register a callback function that will be executed when each entry
179  * has been read and processed. When called, the function will be passed
180  * this query object and a GList of ItemEMail objects as arguments. An
181  * example of typical usage is shown below.
182  *
183  * <pre>
184  * ------------------------------------------------------------
185  * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
186  *   GList *node;
187  *
188  *   node = listEMail;
189  *   while( node ) {
190  *     ItemEMail *email = node->data;
191  *     ... process email object ...
192  *     node = g_list_next( node );
193  *   }
194  *   g_list_free( listEMail );
195  * }
196  * ...
197  * ...
198  * ldapqry_set_callback_entry( qry, myCallbackEntry );
199  * ------------------------------------------------------------
200  * </pre>
201  *
202  * \param qry Query object.
203  * \param func Function.
204  */
205 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
206         pthread_mutex_lock( qry->mutexEntry );
207         qry->callBackEntry = func;
208         pthread_mutex_unlock( qry->mutexEntry );
209 }
210
211 /**
212  * Register a callback function that will be executed when the search
213  * is complete. When called, the function will be passed this query
214  * object as an argument.
215  * \param qry Query object.
216  * \param func Function.
217  */
218 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
219         qry->callBackEnd = func;
220 }
221
222 /**
223  * Notify query to start/stop executing. This method should be called with a
224  * value if <i>TRUE</i> to terminate an existing running query.
225  *
226  * \param qry Query object.
227  * \param value Value of stop flag.
228  */
229 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
230         g_return_if_fail( qry != NULL );
231
232         pthread_mutex_lock( qry->mutexStop );
233         qry->stopFlag = value;
234         pthread_mutex_unlock( qry->mutexStop );
235 }
236
237 /**
238  * Test value of stop flag. This method should be used to determine whether a
239  * query has stopped running.
240  * \param qry Query object.
241  * \return Value of stop flag.
242  */
243 gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
244         gboolean value;
245         g_return_if_fail( qry != NULL );
246
247         pthread_mutex_lock( qry->mutexStop );
248         value = qry->stopFlag;
249         pthread_mutex_unlock( qry->mutexStop );
250         return value;
251 }
252
253 /**
254  * Set busy flag.
255  * \param qry Query object.
256  * \param value Value of busy flag.
257  */
258 void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
259         g_return_if_fail( qry != NULL );
260
261         pthread_mutex_lock( qry->mutexBusy );
262         qry->busyFlag = value;
263         pthread_mutex_unlock( qry->mutexBusy );
264 }
265
266 /**
267  * Test value of busy flag. This method will return a value of <i>FALSE</i>
268  * when a query has completed running.
269  * \param qry Query object.
270  * \return Value of busy flag.
271  */
272 gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
273         gboolean value;
274         g_return_if_fail( qry != NULL );
275
276         pthread_mutex_lock( qry->mutexBusy );
277         value = qry->busyFlag;
278         pthread_mutex_unlock( qry->mutexBusy );
279         return value;
280 }
281
282 /**
283  * Set query aged flag.
284  * \param qry Query object.
285  * \param value Value of aged flag.
286  */
287 void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
288         g_return_if_fail( qry != NULL );
289         qry->agedFlag = value;
290 }
291
292 /**
293  * Test value of aged flag.
294  * \param qry Query object.
295  * \return <i>TRUE</i> if query has been marked as aged (and can be retired).
296  */
297 gboolean ldapqry_get_aged_flag( LdapQuery *qry ) {
298         g_return_if_fail( qry != NULL );
299         return qry->agedFlag;
300 }
301
302 /**
303  * Release the LDAP control data associated with the query.
304  * \param qry Query object to process.
305  */
306 void ldapqry_release_control( LdapQuery *qry ) {
307         g_return_if_fail( qry != NULL );
308         if( qry->control != NULL ) {
309                 ldapctl_free( qry->control );
310         }
311         qry->control = NULL;
312 }
313
314 /**
315  * Clear LDAP query member variables.
316  * \param qry Query object.
317  */
318 void ldapqry_clear( LdapQuery *qry ) {
319         g_return_if_fail( qry != NULL );
320
321         /* Free internal stuff */
322         g_free( qry->queryName );
323         g_free( qry->searchValue );
324
325         /* Clear pointers and value */
326         qry->queryName = NULL;
327         qry->searchValue = NULL;
328         qry->retVal = LDAPRC_SUCCESS;
329         qry->queryType = LDAPQUERY_NONE;
330         qry->queryID = 0;
331         qry->entriesRead = 0;
332         qry->elapsedTime = 0;
333         qry->stopFlag = FALSE;
334         qry->busyFlag = FALSE;
335         qry->agedFlag = FALSE;
336         qry->completed = FALSE;
337         qry->callBackStart = NULL;
338         qry->callBackEntry = NULL;
339         qry->callBackEnd = NULL;
340 }
341
342 /**
343  * Free up LDAP query object by releasing internal memory. Note that
344  * the thread object will be freed by the OS.
345  * \param qry Query object to process.
346  */
347 void ldapqry_free( LdapQuery *qry ) {
348         g_return_if_fail( qry != NULL );
349
350         /* Clear out internal members */
351         ldapqry_clear( qry );
352
353         /* Free the mutex */
354         pthread_mutex_destroy( qry->mutexStop );
355         pthread_mutex_destroy( qry->mutexBusy );
356         pthread_mutex_destroy( qry->mutexEntry );
357         g_free( qry->mutexStop );
358         g_free( qry->mutexBusy );
359         g_free( qry->mutexEntry );
360         qry->mutexEntry = NULL;
361         qry->mutexBusy = NULL;
362         qry->mutexStop = NULL;
363
364         /* Do not free folder - parent server object should free */     
365         qry->folder = NULL;
366
367         /* Do not free thread - thread should be terminated before freeing */
368         qry->thread = NULL;
369
370         /* Do not free LDAP control - should be destroyed before freeing */
371         qry->control = NULL;
372
373         /* Now release object */
374         g_free( qry );
375 }
376
377 /**
378  * Display object to specified stream.
379  * \param qry    Query object to process.
380  * \param stream Output stream.
381  */
382 void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
383         g_return_if_fail( qry != NULL );
384
385         fprintf( stream, "LdapQuery:\n" );
386         fprintf( stream, "  control?: %s\n",   qry->control ? "yes" : "no" );
387         fprintf( stream, "err/status: %d\n",   qry->retVal );
388         fprintf( stream, "query type: %d\n",   qry->queryType );
389         fprintf( stream, "query name: '%s'\n", qry->queryName );
390         fprintf( stream, "search val: '%s'\n", qry->searchValue );
391         fprintf( stream, "   queryID: %d\n",   qry->queryID );
392         fprintf( stream, "   entries: %d\n",   qry->entriesRead );
393         fprintf( stream, "   elapsed: %d\n",   qry->elapsedTime );
394         fprintf( stream, " stop flag: %s\n",   qry->stopFlag  ? "yes" : "no" );
395         fprintf( stream, " busy flag: %s\n",   qry->busyFlag  ? "yes" : "no" );
396         fprintf( stream, " aged flag: %s\n",   qry->agedFlag  ? "yes" : "no" );
397         fprintf( stream, " completed: %s\n",   qry->completed ? "yes" : "no" );
398 }
399
400 /**
401  * Free linked lists of character strings.
402  * \param listName  List of common names.
403  * \param listAddr  List of addresses.
404  * \param listFirst List of first names.
405  * \param listLast  List of last names.
406  */
407 static void ldapqry_free_lists(
408                 GSList *listName, GSList *listAddr, GSList *listFirst,
409                 GSList *listLast )
410 {
411         mgu_free_list( listName );
412         mgu_free_list( listAddr );
413         mgu_free_list( listFirst );
414         mgu_free_list( listLast );
415 }
416
417 /**
418  * Add all LDAP attribute values to a list.
419  * \param ld LDAP handle.
420  * \param entry LDAP entry to process.
421  * \param attr  LDAP attribute.
422  * \return List of values.
423  */
424 static GSList *ldapqry_add_list_values(
425                 LDAP *ld, LDAPMessage *entry, char *attr )
426 {
427         GSList *list = NULL;
428         gint i;
429         gchar **vals;
430
431         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
432                 for( i = 0; vals[i] != NULL; i++ ) {
433                         /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
434                         list = g_slist_append( list, g_strdup( vals[i] ) );
435                 }
436         }
437         ldap_value_free( vals );
438         return list;
439 }
440
441 /**
442  * Add a single attribute value to a list.
443  * \param  ld    LDAP handle.
444  * \param  entry LDAP entry to process.
445  * \param  attr  LDAP attribute name to process.
446  * \return List of values; only one value will be present.
447  */
448 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
449         GSList *list = NULL;
450         gchar **vals;
451
452         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
453                 if( vals[0] != NULL ) {
454                         /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
455                         list = g_slist_append( list, g_strdup( vals[0] ) );
456                 }
457         }
458         ldap_value_free( vals );
459         return list;
460 }
461
462 /**
463  * Build an address list entry and append to list of address items. Name is formatted
464  * as "<first-name> <last-name>".
465  *
466  * \param  cache     Address cache to load.
467  * \param  qry Query object to process.
468  * \param  dn        DN for entry found on server.
469  * \param  listName  List of common names for entry; see notes below.
470  * \param  listAddr  List of EMail addresses for entry.
471  * \param  listFirst List of first names for entry.
472  * \param  listLast  List of last names for entry.
473  *
474  * \return List of ItemEMail objects.
475  *
476  * Notes:
477  * 1) Each LDAP server entry may have multiple LDAP attributes with the same
478  *    name. For example, a single entry for a person may have more than one
479  *    common name, email address, etc.
480 *
481  * 2) The DN for the entry is unique for the server.
482  */
483 static GList *ldapqry_build_items_fl(
484                 AddressCache *cache, LdapQuery *qry, gchar *dn,
485                 GSList *listName, GSList *listAddr, GSList *listFirst,
486                 GSList *listLast )
487 {
488         GSList *nodeAddress;
489         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
490         gboolean allocated;
491         ItemPerson *person;
492         ItemEMail *email;
493         ItemFolder *folder;
494         GList *listReturn;
495
496         listReturn = NULL;
497         if( listAddr == NULL ) return listReturn;
498
499         /* Find longest first name in list */
500         firstName = mgu_slist_longest_entry( listFirst );
501
502         /* Format last name */
503         if( listLast ) {
504                 lastName = listLast->data;
505         }
506
507         /* Find longest common name */
508         allocated = FALSE;
509         fullName = mgu_slist_longest_entry( listName );
510         if( fullName == NULL ) {
511                 /* Format a full name from first and last names */
512                 if( firstName ) {
513                         if( lastName ) {
514                                 fullName = g_strdup_printf( "%s %s", firstName, lastName );
515                         }
516                         else {
517                                 fullName = g_strdup_printf( "%s", firstName );
518                         }
519                 }
520                 else {
521                         if( lastName ) {
522                                 fullName = g_strdup_printf( "%s", lastName );
523                         }
524                 }
525                 if( fullName ) {
526                         g_strchug( fullName ); g_strchomp( fullName );
527                         allocated = TRUE;
528                 }
529         }
530
531         /* Create new folder for results */
532         if( qry->folder == NULL ) {
533                 folder = addritem_create_item_folder();
534                 addritem_folder_set_name( folder, qry->queryName );
535                 addritem_folder_set_remarks( folder, "" );
536                 addrcache_id_folder( cache, folder );
537                 addrcache_add_folder( cache, folder );
538                 qry->folder = folder;
539
540                 /* Specify folder type and back reference */                    
541                 folder->folderType = ADDRFOLDER_LDAP_QUERY;
542                 folder->folderData = ( gpointer ) qry;
543                 folder->isHidden = TRUE;
544         }
545
546         /* Add person into folder */            
547         person = addritem_create_item_person();
548         addritem_person_set_common_name( person, fullName );
549         addritem_person_set_first_name( person, firstName );
550         addritem_person_set_last_name( person, lastName );
551         addrcache_id_person( cache, person );
552         addritem_person_set_external_id( person, dn );
553         addrcache_folder_add_person( cache, qry->folder, person );
554
555         qry->entriesRead++;
556
557         /* Add each address item */
558         nodeAddress = listAddr;
559         while( nodeAddress ) {
560                 email = addritem_create_item_email();
561                 addritem_email_set_address( email, nodeAddress->data );
562                 addrcache_id_email( cache, email );
563                 addrcache_person_add_email( cache, person, email );
564                 addritem_person_add_email( person, email );
565                 listReturn = g_list_append( listReturn, email );
566                 nodeAddress = g_slist_next( nodeAddress );
567         }
568
569         /* Free any allocated memory */
570         if( allocated ) {
571                 g_free( fullName );
572         }
573         fullName = firstName = lastName = NULL;
574
575         return listReturn;
576 }
577
578 /**
579  * Process a single search entry.
580  * \param  cache Address cache to load.
581  * \param  qry   Query object to process.
582  * \param  ld    LDAP handle.
583  * \param  e     LDAP message.
584  * \return List of EMail objects found.
585  */
586 static GList *ldapqry_process_single_entry(
587                 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
588 {
589         char *dnEntry;
590         char *attribute;
591         LdapControl *ctl;
592         BerElement *ber;
593         GSList *listName = NULL, *listAddress = NULL;
594         GSList *listFirst = NULL, *listLast = NULL;
595         GList *listReturn;
596
597         listReturn = NULL;
598         ctl = qry->control;
599         dnEntry = ldap_get_dn( ld, e );
600         /* printf( "DN: %s\n", dnEntry ); */
601
602         /* Process all attributes */
603         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
604                 attribute = ldap_next_attribute( ld, e, ber ) ) {
605
606                 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
607                         listAddress = ldapqry_add_list_values( ld, e, attribute );
608                 }
609                 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
610                         listName = ldapqry_add_list_values( ld, e, attribute );
611                 }
612                 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
613                         listFirst = ldapqry_add_list_values( ld, e, attribute );
614                 }
615                 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
616                         listLast = ldapqry_add_single_value( ld, e, attribute );
617                 }
618
619                 /* Free memory used to store attribute */
620                 ldap_memfree( attribute );
621         }
622
623         /* Format and add items to cache */
624         listReturn = ldapqry_build_items_fl(
625                 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
626
627         /* Free up */
628         ldapqry_free_lists( listName, listAddress, listFirst, listLast );
629         listName = listAddress = listFirst = listLast = NULL;
630
631         if( ber != NULL ) {
632                 ber_free( ber, 0 );
633         }
634         g_free( dnEntry );
635
636         return listReturn;
637 }
638
639 /**
640  * Check parameters that are required for a search. This should
641  * be called before performing a search.
642  * \param  qry Query object to process.
643  * \return <i>TRUE</i> if search criteria appear OK.
644  */
645 gboolean ldapqry_check_search( LdapQuery *qry ) {
646         LdapControl *ctl;
647         qry->retVal = LDAPRC_CRITERIA;
648
649         /* Test for control data */
650         ctl = qry->control;
651         if( ctl == NULL ) {
652                 return FALSE;
653         }
654
655         /* Test for search value */
656         if( qry->searchValue == NULL ) {
657                 return FALSE;
658         }
659         if( strlen( qry->searchValue ) < 1 ) {
660                 return FALSE;
661         }
662
663         qry->retVal = LDAPRC_SUCCESS;
664         return TRUE;
665 }
666
667 /**
668  * Touch the query. This nudges the touch time with the current time.
669  * \param qry Query object to process.
670  */
671 void ldapqry_touch( LdapQuery *qry ) {
672         qry->touchTime = time( NULL );
673         qry->agedFlag = FALSE;
674 }
675
676 /**
677  * Perform the LDAP search, reading LDAP entries into cache.
678  * Note that one LDAP entry can have multiple values for many of its
679  * attributes. If these attributes are E-Mail addresses; these are
680  * broken out into separate address items. For any other attribute,
681  * only the first occurrence is read.
682  * 
683  * \param  qry Query object to process.
684  * \return Error/status code.
685  */
686 static gint ldapqry_perform_search( LdapQuery *qry ) {
687         LdapControl *ctl;
688         LDAP *ld;
689         LDAPMessage *result, *e;
690         char **attribs;
691         gchar *criteria;
692         gboolean entriesFound;
693         gboolean first;
694         struct timeval timeout;
695         gint rc;
696         time_t tstart, tend;
697         AddressCache *cache;
698         GList *listEMail;
699
700         /* Initialize some variables */
701         ctl = qry->control;
702         cache = qry->server->addressCache;
703         timeout.tv_sec = ctl->timeOut;
704         timeout.tv_usec = 0L;
705         entriesFound = FALSE;
706         qry->elapsedTime = -1;
707         qry->retVal = LDAPRC_SUCCESS;
708
709         /* Check search criteria */     
710         if( ! ldapqry_check_search( qry ) ) {
711                 return qry->retVal;
712         }
713
714         /* Initialize connection */
715         ldapqry_touch( qry );
716         tstart = qry->touchTime;
717         tend = tstart - 1;
718         if( ( ld = ldap_init( ctl->hostName, ctl->port ) ) == NULL ) {
719                 qry->retVal = LDAPRC_INIT;
720                 return qry->retVal;
721         }
722         if( ldapqry_get_stop_flag( qry ) ) {
723                 qry->retVal = LDAPRC_SUCCESS;
724                 return qry->retVal;
725         }
726         ldapqry_touch( qry );
727
728         /*
729         printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
730         */
731
732         /* Bind to the server, if required */
733         if( ctl->bindDN ) {
734                 if( * ctl->bindDN != '\0' ) {
735                         /* printf( "binding...\n" ); */
736                         rc = ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
737                         /* printf( "rc=%d\n", rc ); */
738                         if( rc != LDAP_SUCCESS ) {
739                                 /*
740                                 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
741                                         ldap_err2string( rc ) );
742                                 */
743                                 ldap_unbind( ld );
744                                 qry->retVal = LDAPRC_BIND;
745                                 return qry->retVal;
746                         }
747                 }
748         }
749         if( ldapqry_get_stop_flag( qry ) ) {
750                 ldap_unbind( ld );
751                 qry->retVal = LDAPRC_SUCCESS;
752                 return qry->retVal;
753         }
754         ldapqry_touch( qry );
755
756         /* Define all attributes we are interested in. */
757         attribs = ldapctl_attribute_array( ctl );
758
759         /* Create LDAP search string */
760         criteria = ldapctl_format_criteria( ctl, qry->searchValue );
761         /* printf( "Search criteria ::%s::\n", criteria ); */
762
763         /*
764          * Execute the search - this step may take some time to complete
765          * depending on network traffic and server response time.
766          */
767         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
768                 attribs, 0, NULL, NULL, &timeout, 0, &result );
769         ldapctl_free_attribute_array( attribs );
770         g_free( criteria );
771         criteria = NULL;
772         if( rc == LDAP_TIMEOUT ) {
773                 ldap_unbind( ld );
774                 qry->retVal = LDAPRC_TIMEOUT;
775                 return qry->retVal;
776         }
777         if( rc != LDAP_SUCCESS ) {
778                 /*
779                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
780                 */
781                 ldap_unbind( ld );
782                 qry->retVal = LDAPRC_SEARCH;
783                 return qry->retVal;
784         }
785         if( ldapqry_get_stop_flag( qry ) ) {
786                 qry->retVal = LDAPRC_SUCCESS;
787                 return qry->retVal;
788         }
789         ldapqry_touch( qry );
790
791         /*
792         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
793         */
794
795         if( ldapqry_get_stop_flag( qry ) ) {
796                 qry->retVal = LDAPRC_SUCCESS;
797                 return qry->retVal;
798         }
799
800         /* Process results */
801         first = TRUE;
802         while( TRUE ) {
803                 ldapqry_touch( qry );
804                 if( qry->entriesRead >= ctl->maxEntries ) break;                
805
806                 /* Test for stop */             
807                 if( ldapqry_get_stop_flag( qry ) ) {
808                         break;
809                 }
810
811                 /* Retrieve entry */            
812                 if( first ) {
813                         first = FALSE;
814                         e = ldap_first_entry( ld, result );
815                 }
816                 else {
817                         e = ldap_next_entry( ld, e );
818                 }
819                 if( e == NULL ) break;
820
821                 entriesFound = TRUE;
822
823                 /* Setup a critical section here */
824                 pthread_mutex_lock( qry->mutexEntry );
825
826                 /* Process entry */
827                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
828
829                 /* Process callback */
830                 if( qry->callBackEntry ) {
831                         qry->callBackEntry( qry, listEMail );
832                 }
833                 else {
834                         g_list_free( listEMail );
835                 }
836
837                 pthread_mutex_unlock( qry->mutexEntry );
838         }
839
840         /* Free up and disconnect */
841         ldap_msgfree( result );
842         ldap_unbind( ld );
843         ldapqry_touch( qry );
844         tend = qry->touchTime;
845         qry->elapsedTime = tend - tstart;
846
847         if( entriesFound ) {
848                 qry->retVal = LDAPRC_SUCCESS;
849         }
850         else {
851                 qry->retVal = LDAPRC_NOENTRIES;
852         }
853
854         return qry->retVal;
855 }
856
857 /**
858  * Wrapper around search.
859  * \param  qry Query object to process.
860  * \return Error/status code.
861  */
862 gint ldapqry_search( LdapQuery *qry ) {
863         gint retVal;
864
865         g_return_val_if_fail( qry != NULL, -1 );
866         g_return_val_if_fail( qry->control != NULL, -1 );
867
868         ldapqry_touch( qry );
869         qry->completed = FALSE;
870
871         /* Process callback */  
872         if( qry->callBackStart ) {
873                 qry->callBackStart( qry );
874         }
875
876         /* Setup pointer to thread specific area */
877         pthread_setspecific( _queryThreadKey_, qry );
878
879         pthread_detach( pthread_self() );
880         
881         /* Now perform the search */
882         qry->entriesRead = 0;
883         qry->retVal = LDAPRC_SUCCESS;
884         ldapqry_set_busy_flag( qry, TRUE );
885         ldapqry_set_stop_flag( qry, FALSE );
886         retVal = ldapqry_perform_search( qry );
887         if( retVal == LDAPRC_SUCCESS ) {
888                 qry->server->addressCache->dataRead = TRUE;
889                 qry->server->addressCache->accessFlag = FALSE;
890                 if( ldapqry_get_stop_flag( qry ) ) {
891                         /*
892                         printf( "Search was terminated prematurely\n" );
893                         */
894                 }
895                 else {
896                         ldapqry_touch( qry );
897                         qry->completed = TRUE;
898                         /*
899                         printf( "Search ran to completion\n" );
900                         */
901                 }
902         }
903         ldapqry_set_stop_flag( qry, TRUE );
904         ldapqry_set_busy_flag( qry, FALSE );
905
906         /* Process callback */  
907         if( qry->callBackEnd ) {
908                 qry->callBackEnd( qry );
909         }
910
911         return qry->retVal;
912 }
913
914 /**
915  * Read data into list using a background thread. Callback function will be
916  * notified when search is complete.
917  * \param  qry Query object to process.
918  * \return Error/status code.
919  */
920 gint ldapqry_read_data_th( LdapQuery *qry ) {
921         g_return_val_if_fail( qry != NULL, -1 );
922         g_return_val_if_fail( qry->control != NULL, -1 );
923
924         ldapqry_set_stop_flag( qry, FALSE );
925         ldapqry_touch( qry );
926         if( ldapqry_check_search( qry ) ) {
927                 if( qry->retVal == LDAPRC_SUCCESS ) {
928                         /*
929                         printf( "Starting LDAP search thread\n");
930                         */
931                         ldapqry_set_busy_flag( qry, TRUE );
932                         qry->thread = g_malloc0( sizeof( pthread_t ) );
933
934                         /* Setup thread */                      
935                         pthread_create( qry->thread, NULL,
936                                 (void *) ldapqry_search, (void *) qry );
937                 }
938         }
939         return qry->retVal;
940 }
941
942 /**
943  * Join the thread associated with the query. This should probably be removed
944  * to prevent joining threads.
945  * \param qry Query object to process.
946  */
947 void ldapqry_join_threadX( LdapQuery *qry ) {
948         g_return_if_fail( qry != NULL );
949
950         /* Wait for thread */
951         /* printf( "ldapqry_join_thread::Joining thread...\n" ); */
952         pthread_join( * qry->thread, NULL );
953         /* printf( "ldapqry_join_thread::Thread terminated\n" ); */
954 }
955
956 /**
957  * Cleanup LDAP thread data. This function will be called when each thread
958  * exits. Note that the thread object will be freed by the kernel.
959  * \param ptr Pointer to object being destroyed (a query object in this case).
960  */
961 static void ldapqry_destroyer( void * ptr ) {
962         LdapQuery *qry;
963
964         qry = ( LdapQuery * ) ptr;
965         /*
966         printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), qry->queryName );
967         */
968
969         /* Perform any destruction here */
970         if( qry->control != NULL ) {
971                 ldapctl_free( qry->control );
972         }
973         qry->control = NULL;
974         qry->thread = NULL;
975         ldapqry_set_busy_flag( qry, FALSE );
976         /*
977         printf( "...destroy exiting\n" );
978         */
979 }
980
981 /**
982  * Cancel thread associated with query.
983  * \param qry Query object to process.
984  */
985 void ldapqry_cancel( LdapQuery *qry ) {
986         g_return_if_fail( qry != NULL );
987
988         /*
989         printf( "cancelling::%d::%s\n", (int) pthread_self(), qry->queryName );
990         */
991         if( ldapqry_get_busy_flag( qry ) ) {
992                 if( qry->thread ) {
993                         pthread_cancel( * qry->thread );
994                 }
995         }
996 }
997
998 /**
999  * Initialize LDAP query. This function should be called once before executing
1000  * any LDAP queries to initialize thread specific data.
1001  */
1002 void ldapqry_initialize( void ) {
1003         /* printf( "ldapqry_initialize...\n" ); */
1004         if( ! _queryThreadInit_ ) {
1005                 /*
1006                 printf( "ldapqry_initialize::creating thread specific area\n" );
1007                 */
1008                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1009                 _queryThreadInit_ = TRUE;
1010         }
1011         /* printf( "ldapqry_initialize... done!\n" ); */
1012 }
1013
1014 /**
1015  * Age the query based on LDAP control parameters.
1016  * \param qry    Query object to process.
1017  * \param maxAge Maximum age of query (in seconds).
1018  */
1019 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1020         gint age;
1021
1022         g_return_if_fail( qry != NULL );
1023
1024         /* Limit the time that queries can hang around */       
1025         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1026
1027         /* Check age of query */
1028         age = time( NULL ) - qry->touchTime;
1029         if( age > maxAge ) {
1030                 qry->agedFlag = TRUE;
1031         }
1032 }
1033
1034 /**
1035  * Delete folder associated with query results.
1036  * \param qry Query object to process.
1037  */
1038 void ldapqry_delete_folder( LdapQuery *qry ) {
1039         AddressCache *cache;
1040         ItemFolder *folder;
1041
1042         g_return_if_fail( qry != NULL );
1043
1044         folder = qry->folder;
1045         if( folder ) {
1046                 cache = qry->server->addressCache;
1047                 folder = addrcache_remove_folder_delete( cache, folder );
1048                 if( folder ) {
1049                         addritem_free_item_folder( folder );
1050                 }
1051                 qry->folder = NULL;
1052         }
1053 }
1054
1055 #endif  /* USE_LDAP */
1056
1057 /*
1058  * End of Source.
1059  */
1060
1061