0.8.11claws48
[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 thread associated with the query.
304  * \param qry Query object to process.
305  */
306 void ldapqry_release_thread( LdapQuery *qry ) {
307         g_return_if_fail( qry != NULL );
308         printf( "ldapqry_release_thread...\n" );
309         if( qry->thread != NULL ) {
310                 g_free( qry->thread );
311                 printf( "\t===========>done\n" );
312         }
313         qry->thread = NULL;
314 }
315
316 /**
317  * Release the LDAP control data associated with the query.
318  * \param qry Query object to process.
319  */
320 void ldapqry_release_control( LdapQuery *qry ) {
321         g_return_if_fail( qry != NULL );
322         if( qry->control != NULL ) {
323                 ldapctl_free( qry->control );
324         }
325         qry->control = NULL;
326 }
327
328 /**
329  * Clear LDAP query member variables.
330  * \param qry Query object.
331  */
332 void ldapqry_clear( LdapQuery *qry ) {
333         g_return_if_fail( qry != NULL );
334
335         /* Free internal stuff */
336         g_free( qry->queryName );
337         g_free( qry->searchValue );
338
339         /* Clear pointers and value */
340         qry->queryName = NULL;
341         qry->searchValue = NULL;
342         qry->retVal = LDAPRC_SUCCESS;
343         qry->queryType = LDAPQUERY_NONE;
344         qry->queryID = 0;
345         qry->entriesRead = 0;
346         qry->elapsedTime = 0;
347         qry->stopFlag = FALSE;
348         qry->busyFlag = FALSE;
349         qry->agedFlag = FALSE;
350         qry->completed = FALSE;
351         qry->callBackStart = NULL;
352         qry->callBackEntry = NULL;
353         qry->callBackEnd = NULL;
354 }
355
356 /**
357  * Free up LDAP query object by releasing internal memory.
358  * \param qry Query object to process.
359  */
360 void ldapqry_free( LdapQuery *qry ) {
361         g_return_if_fail( qry != NULL );
362
363         /* Clear out internal members */
364         ldapqry_clear( qry );
365
366         /* Free the mutex */
367         pthread_mutex_destroy( qry->mutexStop );
368         pthread_mutex_destroy( qry->mutexBusy );
369         pthread_mutex_destroy( qry->mutexEntry );
370         g_free( qry->mutexStop );
371         g_free( qry->mutexBusy );
372         g_free( qry->mutexEntry );
373         qry->mutexEntry = NULL;
374         qry->mutexBusy = NULL;
375         qry->mutexStop = NULL;
376
377         /* Do not free folder - parent server object should free */     
378         qry->folder = NULL;
379
380         /* Do not free thread - thread should be terminated before freeing */
381         qry->thread = NULL;
382
383         /* Do not free LDAP control - should be destroyed before freeing */
384         qry->control = NULL;
385
386         /* Now release object */
387         g_free( qry );
388 }
389
390 /**
391  * Display object to specified stream.
392  * \param qry    Query object to process.
393  * \param stream Output stream.
394  */
395 void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
396         g_return_if_fail( qry != NULL );
397
398         fprintf( stream, "LdapQuery:\n" );
399         fprintf( stream, "  control?: %s\n",   qry->control ? "yes" : "no" );
400         fprintf( stream, "err/status: %d\n",   qry->retVal );
401         fprintf( stream, "query type: %d\n",   qry->queryType );
402         fprintf( stream, "query name: '%s'\n", qry->queryName );
403         fprintf( stream, "search val: '%s'\n", qry->searchValue );
404         fprintf( stream, "   queryID: %d\n",   qry->queryID );
405         fprintf( stream, "   entries: %d\n",   qry->entriesRead );
406         fprintf( stream, "   elapsed: %d\n",   qry->elapsedTime );
407         fprintf( stream, " stop flag: %s\n",   qry->stopFlag  ? "yes" : "no" );
408         fprintf( stream, " busy flag: %s\n",   qry->busyFlag  ? "yes" : "no" );
409         fprintf( stream, " aged flag: %s\n",   qry->agedFlag  ? "yes" : "no" );
410         fprintf( stream, " completed: %s\n",   qry->completed ? "yes" : "no" );
411 }
412
413 /**
414  * Free linked lists of character strings.
415  * \param listName  List of common names.
416  * \param listAddr  List of addresses.
417  * \param listFirst List of first names.
418  * \param listLast  List of last names.
419  */
420 static void ldapqry_free_lists(
421                 GSList *listName, GSList *listAddr, GSList *listFirst,
422                 GSList *listLast )
423 {
424         mgu_free_list( listName );
425         mgu_free_list( listAddr );
426         mgu_free_list( listFirst );
427         mgu_free_list( listLast );
428 }
429
430 /**
431  * Add all LDAP attribute values to a list.
432  * \param ld LDAP handle.
433  * \param entry LDAP entry to process.
434  * \param attr  LDAP attribute.
435  * \return List of values.
436  */
437 static GSList *ldapqry_add_list_values(
438                 LDAP *ld, LDAPMessage *entry, char *attr )
439 {
440         GSList *list = NULL;
441         gint i;
442         gchar **vals;
443
444         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
445                 for( i = 0; vals[i] != NULL; i++ ) {
446                         /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
447                         list = g_slist_append( list, g_strdup( vals[i] ) );
448                 }
449         }
450         ldap_value_free( vals );
451         return list;
452 }
453
454 /**
455  * Add a single attribute value to a list.
456  * \param  ld    LDAP handle.
457  * \param  entry LDAP entry to process.
458  * \param  attr  LDAP attribute name to process.
459  * \return List of values; only one value will be present.
460  */
461 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
462         GSList *list = NULL;
463         gchar **vals;
464
465         if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
466                 if( vals[0] != NULL ) {
467                         /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
468                         list = g_slist_append( list, g_strdup( vals[0] ) );
469                 }
470         }
471         ldap_value_free( vals );
472         return list;
473 }
474
475 /**
476  * Build an address list entry and append to list of address items. Name is formatted
477  * as "<first-name> <last-name>".
478  *
479  * \param  cache     Address cache to load.
480  * \param  qry Query object to process.
481  * \param  dn        DN for entry found on server.
482  * \param  listName  List of common names for entry; see notes below.
483  * \param  listAddr  List of EMail addresses for entry.
484  * \param  listFirst List of first names for entry.
485  * \param  listLast  List of last names for entry.
486  *
487  * \return List of ItemEMail objects.
488  *
489  * Notes:
490  * 1) Each LDAP server entry may have multiple LDAP attributes with the same
491  *    name. For example, a single entry for a person may have more than one
492  *    common name, email address, etc.
493 *
494  * 2) The DN for the entry is unique for the server.
495  */
496 static GList *ldapqry_build_items_fl(
497                 AddressCache *cache, LdapQuery *qry, gchar *dn,
498                 GSList *listName, GSList *listAddr, GSList *listFirst,
499                 GSList *listLast )
500 {
501         GSList *nodeAddress;
502         gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
503         gboolean allocated;
504         ItemPerson *person;
505         ItemEMail *email;
506         ItemFolder *folder;
507         GList *listReturn;
508
509         listReturn = NULL;
510
511         /* Find longest first name in list */
512         firstName = mgu_slist_longest_entry( listFirst );
513
514         /* Format last name */
515         if( listLast ) {
516                 lastName = listLast->data;
517         }
518
519         /* Find longest common name */
520         allocated = FALSE;
521         fullName = mgu_slist_longest_entry( listName );
522         if( fullName == NULL ) {
523                 /* Format a full name from first and last names */
524                 if( firstName ) {
525                         if( lastName ) {
526                                 fullName = g_strdup_printf( "%s %s", firstName, lastName );
527                         }
528                         else {
529                                 fullName = g_strdup_printf( "%s", firstName );
530                         }
531                 }
532                 else {
533                         if( lastName ) {
534                                 fullName = g_strdup_printf( "%s", lastName );
535                         }
536                 }
537                 if( fullName ) {
538                         g_strchug( fullName ); g_strchomp( fullName );
539                         allocated = TRUE;
540                 }
541         }
542
543         if( listAddr ) {
544                 /* Create new folder for results */
545                 if( qry->folder == NULL ) {
546                         folder = addritem_create_item_folder();
547                         addritem_folder_set_name( folder, qry->queryName );
548                         addritem_folder_set_remarks( folder, "" );
549                         addrcache_id_folder( cache, folder );
550                         addrcache_add_folder( cache, folder );
551                         qry->folder = folder;
552
553                         /* Specify folder type and back reference */                    
554                         folder->folderType = ADDRFOLDER_LDAP_QUERY;
555                         folder->folderData = ( gpointer ) qry;
556                 }
557
558                 /* Add person into folder */            
559                 person = addritem_create_item_person();
560                 addritem_person_set_common_name( person, fullName );
561                 addritem_person_set_first_name( person, firstName );
562                 addritem_person_set_last_name( person, lastName );
563                 addrcache_id_person( cache, person );
564                 addritem_person_set_external_id( person, dn );
565                 addrcache_folder_add_person( cache, qry->folder, person );
566
567                 qry->entriesRead++;
568         }
569
570         /* Add each address item */
571         nodeAddress = listAddr;
572         while( nodeAddress ) {
573                 email = addritem_create_item_email();
574                 addritem_email_set_address( email, nodeAddress->data );
575                 addrcache_id_email( cache, email );
576                 addrcache_person_add_email( cache, person, email );
577                 addritem_person_add_email( person, email );
578                 listReturn = g_list_append( listReturn, email );
579                 nodeAddress = g_slist_next( nodeAddress );
580         }
581
582         /* Free any allocated memory */
583         if( allocated ) {
584                 g_free( fullName );
585         }
586         fullName = firstName = lastName = NULL;
587
588         return listReturn;
589 }
590
591 /**
592  * Process a single search entry.
593  * \param  cache Address cache to load.
594  * \param  qry   Query object to process.
595  * \param  ld    LDAP handle.
596  * \param  e     LDAP message.
597  * \return List of EMail objects found.
598  */
599 static GList *ldapqry_process_single_entry(
600                 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
601 {
602         char *dnEntry;
603         char *attribute;
604         LdapControl *ctl;
605         BerElement *ber;
606         GSList *listName = NULL, *listAddress = NULL;
607         GSList *listFirst = NULL, *listLast = NULL;
608         GList *listReturn;
609
610         listReturn = NULL;
611         ctl = qry->control;
612         dnEntry = ldap_get_dn( ld, e );
613         /* printf( "DN: %s\n", dnEntry ); */
614
615         /* Process all attributes */
616         for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
617                 attribute = ldap_next_attribute( ld, e, ber ) ) {
618
619                 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
620                         listAddress = ldapqry_add_list_values( ld, e, attribute );
621                 }
622                 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
623                         listName = ldapqry_add_list_values( ld, e, attribute );
624                 }
625                 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
626                         listFirst = ldapqry_add_list_values( ld, e, attribute );
627                 }
628                 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
629                         listLast = ldapqry_add_single_value( ld, e, attribute );
630                 }
631
632                 /* Free memory used to store attribute */
633                 ldap_memfree( attribute );
634         }
635
636         /* Format and add items to cache */
637         listReturn = ldapqry_build_items_fl(
638                 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
639
640         /* Free up */
641         ldapqry_free_lists( listName, listAddress, listFirst, listLast );
642         listName = listAddress = listFirst = listLast = NULL;
643
644         if( ber != NULL ) {
645                 ber_free( ber, 0 );
646         }
647         g_free( dnEntry );
648
649         return listReturn;
650 }
651
652 /**
653  * Check parameters that are required for a search. This should
654  * be called before performing a search.
655  * \param  qry Query object to process.
656  * \return <i>TRUE</i> if search criteria appear OK.
657  */
658 gboolean ldapqry_check_search( LdapQuery *qry ) {
659         LdapControl *ctl;
660         qry->retVal = LDAPRC_CRITERIA;
661
662         /* Test for control data */
663         ctl = qry->control;
664         if( ctl == NULL ) {
665                 return FALSE;
666         }
667
668         /* Test for search value */
669         if( qry->searchValue == NULL ) {
670                 return FALSE;
671         }
672         if( strlen( qry->searchValue ) < 1 ) {
673                 return FALSE;
674         }
675
676         qry->retVal = LDAPRC_SUCCESS;
677         return TRUE;
678 }
679
680 /**
681  * Touch the query. This nudges the touch time with the current time.
682  * \param qry Query object to process.
683  */
684 void ldapqry_touch( LdapQuery *qry ) {
685         qry->touchTime = time( NULL );
686         qry->agedFlag = FALSE;
687 }
688
689 /**
690  * Perform the LDAP search, reading LDAP entries into cache.
691  * Note that one LDAP entry can have multiple values for many of its
692  * attributes. If these attributes are E-Mail addresses; these are
693  * broken out into separate address items. For any other attribute,
694  * only the first occurrence is read.
695  * 
696  * \param  qry Query object to process.
697  * \return Error/status code.
698  */
699 static gint ldapqry_perform_search( LdapQuery *qry ) {
700         LdapControl *ctl;
701         LDAP *ld;
702         LDAPMessage *result, *e;
703         char **attribs;
704         gchar *criteria;
705         gboolean entriesFound;
706         gboolean first;
707         struct timeval timeout;
708         gint rc;
709         time_t tstart, tend;
710         AddressCache *cache;
711         GList *listEMail;
712
713         /* Initialize some variables */
714         ctl = qry->control;
715         cache = qry->server->addressCache;
716         timeout.tv_sec = ctl->timeOut;
717         timeout.tv_usec = 0L;
718         entriesFound = FALSE;
719         qry->elapsedTime = -1;
720         qry->retVal = LDAPRC_SUCCESS;
721
722         /* Check search criteria */     
723         if( ! ldapqry_check_search( qry ) ) {
724                 return qry->retVal;
725         }
726
727         /* Initialize connection */
728         ldapqry_touch( qry );
729         tstart = qry->touchTime;
730         tend = tstart - 1;
731         if( ( ld = ldap_init( ctl->hostName, ctl->port ) ) == NULL ) {
732                 qry->retVal = LDAPRC_INIT;
733                 return qry->retVal;
734         }
735         if( ldapqry_get_stop_flag( qry ) ) {
736                 qry->retVal = LDAPRC_SUCCESS;
737                 return qry->retVal;
738         }
739         ldapqry_touch( qry );
740
741         /*
742         printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
743         */
744
745         /* Bind to the server, if required */
746         if( ctl->bindDN ) {
747                 if( * ctl->bindDN != '\0' ) {
748                         /* printf( "binding...\n" ); */
749                         rc = ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
750                         /* printf( "rc=%d\n", rc ); */
751                         if( rc != LDAP_SUCCESS ) {
752                                 /*
753                                 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
754                                         ldap_err2string( rc ) );
755                                 */
756                                 ldap_unbind( ld );
757                                 qry->retVal = LDAPRC_BIND;
758                                 return qry->retVal;
759                         }
760                 }
761         }
762         if( ldapqry_get_stop_flag( qry ) ) {
763                 ldap_unbind( ld );
764                 qry->retVal = LDAPRC_SUCCESS;
765                 return qry->retVal;
766         }
767         ldapqry_touch( qry );
768
769         /* Define all attributes we are interested in. */
770         attribs = ldapctl_attribute_array( ctl );
771
772         /* Create LDAP search string */
773         criteria = ldapctl_format_criteria( ctl, qry->searchValue );
774         /* printf( "Search criteria ::%s::\n", criteria ); */
775
776         /*
777          * Execute the search - this step may take some time to complete
778          * depending on network traffic and server response time.
779          */
780         rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
781                 attribs, 0, NULL, NULL, &timeout, 0, &result );
782         ldapctl_free_attribute_array( attribs );
783         g_free( criteria );
784         criteria = NULL;
785         if( rc == LDAP_TIMEOUT ) {
786                 ldap_unbind( ld );
787                 qry->retVal = LDAPRC_TIMEOUT;
788                 return qry->retVal;
789         }
790         if( rc != LDAP_SUCCESS ) {
791                 /*
792                 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
793                 */
794                 ldap_unbind( ld );
795                 qry->retVal = LDAPRC_SEARCH;
796                 return qry->retVal;
797         }
798         if( ldapqry_get_stop_flag( qry ) ) {
799                 qry->retVal = LDAPRC_SUCCESS;
800                 return qry->retVal;
801         }
802         ldapqry_touch( qry );
803
804         /*
805         printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
806         */
807
808         if( ldapqry_get_stop_flag( qry ) ) {
809                 qry->retVal = LDAPRC_SUCCESS;
810                 return qry->retVal;
811         }
812
813         /* Process results */
814         first = TRUE;
815         while( TRUE ) {
816                 ldapqry_touch( qry );
817                 if( qry->entriesRead >= ctl->maxEntries ) break;                
818
819                 /* Test for stop */             
820                 if( ldapqry_get_stop_flag( qry ) ) {
821                         break;
822                 }
823
824                 /* Retrieve entry */            
825                 if( first ) {
826                         first = FALSE;
827                         e = ldap_first_entry( ld, result );
828                 }
829                 else {
830                         e = ldap_next_entry( ld, e );
831                 }
832                 if( e == NULL ) break;
833
834                 entriesFound = TRUE;
835
836                 /* Setup a critical section here */
837                 pthread_mutex_lock( qry->mutexEntry );
838
839                 /* Process entry */
840                 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
841
842                 /* Process callback */
843                 if( qry->callBackEntry ) {
844                         qry->callBackEntry( qry, listEMail );
845                 }
846                 else {
847                         g_list_free( listEMail );
848                 }
849
850                 pthread_mutex_unlock( qry->mutexEntry );
851         }
852
853         /* Free up and disconnect */
854         ldap_msgfree( result );
855         ldap_unbind( ld );
856         ldapqry_touch( qry );
857         tend = qry->touchTime;
858         qry->elapsedTime = tend - tstart;
859
860         if( entriesFound ) {
861                 qry->retVal = LDAPRC_SUCCESS;
862         }
863         else {
864                 qry->retVal = LDAPRC_NOENTRIES;
865         }
866
867         return qry->retVal;
868 }
869
870 /**
871  * Wrapper around search.
872  * \param  qry Query object to process.
873  * \return Error/status code.
874  */
875 gint ldapqry_search( LdapQuery *qry ) {
876         gint retVal;
877
878         g_return_val_if_fail( qry != NULL, -1 );
879         g_return_val_if_fail( qry->control != NULL, -1 );
880
881         ldapqry_touch( qry );
882         qry->completed = FALSE;
883
884         /* Process callback */  
885         if( qry->callBackStart ) {
886                 qry->callBackStart( qry );
887         }
888
889         /* Setup pointer to thread specific area */
890         pthread_setspecific( _queryThreadKey_, qry );
891
892         pthread_detach( pthread_self() );
893         
894         /* Now perform the search */
895         qry->entriesRead = 0;
896         qry->retVal = LDAPRC_SUCCESS;
897         ldapqry_set_busy_flag( qry, TRUE );
898         ldapqry_set_stop_flag( qry, FALSE );
899         retVal = ldapqry_perform_search( qry );
900         if( retVal == LDAPRC_SUCCESS ) {
901                 qry->server->addressCache->dataRead = TRUE;
902                 qry->server->addressCache->accessFlag = FALSE;
903                 if( ldapqry_get_stop_flag( qry ) ) {
904                         /*
905                         printf( "Search was terminated prematurely\n" );
906                         */
907                 }
908                 else {
909                         ldapqry_touch( qry );
910                         qry->completed = TRUE;
911                         /*
912                         printf( "Search ran to completion\n" );
913                         */
914                 }
915         }
916         ldapqry_set_stop_flag( qry, TRUE );
917         ldapqry_set_busy_flag( qry, FALSE );
918
919         /* Process callback */  
920         if( qry->callBackEnd ) {
921                 qry->callBackEnd( qry );
922         }
923
924         return qry->retVal;
925 }
926
927 /**
928  * Read data into list using a background thread. Callback function will be
929  * notified when search is complete.
930  * \param  qry Query object to process.
931  * \return Error/status code.
932  */
933 gint ldapqry_read_data_th( LdapQuery *qry ) {
934         g_return_val_if_fail( qry != NULL, -1 );
935         g_return_val_if_fail( qry->control != NULL, -1 );
936
937         ldapqry_set_stop_flag( qry, FALSE );
938         ldapqry_touch( qry );
939         if( ldapqry_check_search( qry ) ) {
940                 if( qry->retVal == LDAPRC_SUCCESS ) {
941                         /*
942                         printf( "Starting LDAP search thread\n");
943                         */
944                         ldapqry_set_busy_flag( qry, TRUE );
945                         qry->thread = g_malloc0( sizeof( pthread_t ) );
946
947                         /* Setup thread */                      
948                         pthread_create( qry->thread, NULL,
949                                 (void *) ldapqry_search, (void *) qry );
950                 }
951         }
952         return qry->retVal;
953 }
954
955 /**
956  * Join the thread associated with the query. This should probably be removed
957  * to prevent joining threads.
958  * \param qry Query object to process.
959  */
960 void ldapqry_join_thread( LdapQuery *qry ) {
961         g_return_if_fail( qry != NULL );
962
963         /* Wait for thread */
964         /* printf( "ldapqry_join_thread::Joining thread...\n" ); */
965         pthread_join( * qry->thread, NULL );
966         /* printf( "ldapqry_join_thread::Thread terminated\n" ); */
967 }
968
969 /**
970  * Cleanup LDAP thread data. This function will be called when each thread
971  * exits.
972  * \param ptr Pointer to object being destroyed (a query object in this case).
973  */
974 static void ldapqry_destroyer( void * ptr ) {
975         LdapQuery *qry;
976
977         qry = ( LdapQuery * ) ptr;
978         /*
979         printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), qry->queryName );
980         */
981
982         /* Perform any destruction here */
983         if( qry->control != NULL ) {
984                 ldapctl_free( qry->control );
985         }
986         qry->control = NULL;
987
988         if( qry->thread ) {
989                 g_free( qry->thread );
990         }
991         qry->thread = NULL;
992         ldapqry_set_busy_flag( qry, FALSE );
993         /*
994         printf( "...destroy exiting\n" );
995         */
996 }
997
998 /**
999  * Cancel thread associated with query.
1000  * \param qry Query object to process.
1001  */
1002 void ldapqry_cancel( LdapQuery *qry ) {
1003         g_return_if_fail( qry != NULL );
1004
1005         /*
1006         printf( "cancelling::%d::%s\n", (int) pthread_self(), qry->queryName );
1007         */
1008         if( ldapqry_get_busy_flag( qry ) ) {
1009                 if( qry->thread ) {
1010                         pthread_cancel( * qry->thread );
1011                 }
1012         }
1013 }
1014
1015 /**
1016  * Initialize LDAP query. This function should be called once before executing
1017  * any LDAP queries to initialize thread specific data.
1018  */
1019 void ldapqry_initialize( void ) {
1020         /* printf( "ldapqry_initialize...\n" ); */
1021         if( ! _queryThreadInit_ ) {
1022                 /*
1023                 printf( "ldapqry_initialize::creating thread specific area\n" );
1024                 */
1025                 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1026                 _queryThreadInit_ = TRUE;
1027         }
1028         /* printf( "ldapqry_initialize... done!\n" ); */
1029 }
1030
1031 /**
1032  * Age the query based on LDAP control parameters.
1033  * \param qry    Query object to process.
1034  * \param maxAge Maximum age of query (in seconds).
1035  */
1036 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1037         gint age;
1038
1039         g_return_if_fail( qry != NULL );
1040
1041         /* Limit the time that queries can hang around */       
1042         if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1043
1044         /* Check age of query */
1045         age = time( NULL ) - qry->touchTime;
1046         if( age > maxAge ) {
1047                 qry->agedFlag = TRUE;
1048         }
1049 }
1050
1051 /**
1052  * Delete folder associated with query results.
1053  * \param qry Query object to process.
1054  */
1055 void ldapqry_delete_folder( LdapQuery *qry ) {
1056         AddressCache *cache;
1057         ItemFolder *folder;
1058
1059         g_return_if_fail( qry != NULL );
1060
1061         folder = qry->folder;
1062         if( folder ) {
1063                 cache = qry->server->addressCache;
1064                 folder = addrcache_remove_folder_delete( cache, folder );
1065                 if( folder ) {
1066                         addritem_free_item_folder( folder );
1067                 }
1068                 qry->folder = NULL;
1069         }
1070 }
1071
1072 #endif  /* USE_LDAP */
1073
1074 /*
1075  * End of Source.
1076  */
1077
1078