2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2007 Match Grun and the Claws Mail team
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.
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.
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.
21 * Functions necessary to define and perform LDAP queries.
36 #include "ldapquery.h"
41 #include "addrcache.h"
44 * Key for thread specific data.
46 static pthread_key_t _queryThreadKey_;
47 static gboolean _queryThreadInit_ = FALSE;
49 static gboolean callbackend (gpointer data)
51 LdapQuery *qry = (LdapQuery *)data;
52 qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
58 * Create new LDAP query object.
59 * \return Initialized query object.
61 LdapQuery *ldapqry_create( void ) {
64 qry = g_new0( LdapQuery, 1 );
65 ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
66 ADDRQUERY_ID(qry) = 0;
67 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
68 ADDRQUERY_NAME(qry) = NULL;
69 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
70 ADDRQUERY_FOLDER(qry) = NULL;
71 ADDRQUERY_SEARCHVALUE(qry) = NULL;
76 qry->stopFlag = FALSE;
77 qry->busyFlag = FALSE;
78 qry->agedFlag = FALSE;
79 qry->completed = FALSE;
81 qry->callBackEntry = NULL;
82 qry->callBackEnd = NULL;
86 /* Mutex to protect stop and busy flags */
87 qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
88 pthread_mutex_init( qry->mutexStop, NULL );
89 qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
90 pthread_mutex_init( qry->mutexBusy, NULL );
92 /* Mutex to protect critical section */
93 qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
94 pthread_mutex_init( qry->mutexEntry, NULL );
100 * Specify the reference to control data that will be used for the query. The calling
101 * module should be responsible for creating and destroying this control object.
102 * \param qry Query object.
103 * \param ctl Control object.
105 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
106 g_return_if_fail( qry != NULL );
111 * Specify query name to be used.
112 * \param qry Query object.
115 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
116 ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
117 g_strstrip( ADDRQUERY_NAME(qry) );
121 * Specify search value to be used.
122 * \param qry Query object.
125 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
126 ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
127 g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
131 * Specify query type.
132 * \param qry Query object.
133 * \param value Query type, either:
135 * <li><code>LDAPQUERY_NONE</code></li>
136 * <li><code>LDAPQUERY_STATIC</code></li>
137 * <li><code>LDAPQUERY_DYNAMIC</code></li>
141 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
142 ADDRQUERY_TYPE(qry) = value;
147 * Specify search type.
148 * \param qry Query object.
151 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
152 g_return_if_fail( qry != NULL );
153 ADDRQUERY_SEARCHTYPE(qry) = value;
158 * \param qry Query object.
159 * \param value ID for the query.
161 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
162 ADDRQUERY_ID(qry) = value;
166 * Register a callback function that will be executed when each entry
167 * has been read and processed. When called, the function will be passed
168 * this query object and a GList of ItemEMail objects as arguments. An
169 * example of typical usage is shown below.
172 * ------------------------------------------------------------
173 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
178 * ItemEMail *email = node->data;
179 * ... process email object ...
180 * node = g_list_next( node );
182 * g_list_free( listEMail );
186 * ldapqry_set_callback_entry( qry, myCallbackEntry );
187 * ------------------------------------------------------------
190 * \param qry Query object.
191 * \param func Function.
193 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
194 pthread_mutex_lock( qry->mutexEntry );
195 qry->callBackEntry = func;
196 pthread_mutex_unlock( qry->mutexEntry );
200 * Register a callback function that will be executed when the search
201 * is complete. When called, the function will be passed this query
202 * object as an argument.
203 * \param qry Query object.
204 * \param func Function.
206 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
207 qry->callBackEnd = func;
211 * Notify query to start/stop executing. This method should be called with a
212 * value if <i>TRUE</i> to terminate an existing running query.
214 * \param qry Query object.
215 * \param value Value of stop flag.
217 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
218 g_return_if_fail( qry != NULL );
220 pthread_mutex_lock( qry->mutexStop );
221 qry->stopFlag = value;
222 pthread_mutex_unlock( qry->mutexStop );
226 * Test value of stop flag. This method should be used to determine whether a
227 * query has stopped running.
228 * \param qry Query object.
229 * \return Value of stop flag.
231 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
233 g_return_val_if_fail( qry != NULL, TRUE );
235 pthread_mutex_lock( qry->mutexStop );
236 value = qry->stopFlag;
237 pthread_mutex_unlock( qry->mutexStop );
243 * \param qry Query object.
244 * \param value Value of busy flag.
246 static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
247 g_return_if_fail( qry != NULL );
248 if (qry->mutexBusy == NULL)
249 return; /* exiting, mutex already freed */
251 pthread_mutex_lock( qry->mutexBusy );
252 qry->busyFlag = value;
253 pthread_mutex_unlock( qry->mutexBusy );
257 * Test value of busy flag. This method will return a value of <i>FALSE</i>
258 * when a query has completed running.
259 * \param qry Query object.
260 * \return Value of busy flag.
262 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
264 g_return_val_if_fail( qry != NULL, FALSE );
266 pthread_mutex_lock( qry->mutexBusy );
267 value = qry->busyFlag;
268 pthread_mutex_unlock( qry->mutexBusy );
273 * Set query aged flag.
274 * \param qry Query object.
275 * \param value Value of aged flag.
277 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
278 g_return_if_fail( qry != NULL );
279 qry->agedFlag = value;
283 * Clear LDAP query member variables.
284 * \param qry Query object.
286 static void ldapqry_clear( LdapQuery *qry ) {
287 g_return_if_fail( qry != NULL );
289 /* Free internal stuff */
290 g_free( ADDRQUERY_NAME(qry) );
291 g_free( ADDRQUERY_SEARCHVALUE(qry) );
293 /* Clear pointers and value */
294 ADDRQUERY_NAME(qry) = NULL;
295 ADDRQUERY_SEARCHVALUE(qry) = NULL;
296 ADDRQUERY_ID(qry) = 0;
297 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
298 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
299 qry->entriesRead = 0;
300 qry->elapsedTime = 0;
301 qry->stopFlag = FALSE;
302 qry->busyFlag = FALSE;
303 qry->agedFlag = FALSE;
304 qry->completed = FALSE;
305 qry->callBackEntry = NULL;
306 qry->callBackEnd = NULL;
312 * Free up LDAP query object by releasing internal memory. Note that
313 * the thread object will be freed by the OS.
314 * \param qry Query object to process.
316 void ldapqry_free( LdapQuery *qry ) {
317 g_return_if_fail( qry != NULL );
319 /* Clear out internal members */
320 ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
321 ldapqry_clear( qry );
324 pthread_mutex_destroy( qry->mutexStop );
325 pthread_mutex_destroy( qry->mutexBusy );
326 pthread_mutex_destroy( qry->mutexEntry );
327 g_free( qry->mutexStop );
328 g_free( qry->mutexBusy );
329 g_free( qry->mutexEntry );
330 qry->mutexEntry = NULL;
331 qry->mutexBusy = NULL;
332 qry->mutexStop = NULL;
334 /* Do not free folder - parent server object should free */
335 ADDRQUERY_FOLDER(qry) = NULL;
337 /* Do not free thread - thread should be terminated before freeing */
340 /* Do not free LDAP control - should be destroyed before freeing */
343 /* Now release object */
348 * Free linked lists of character strings.
349 * \param listName List of common names.
350 * \param listAddr List of addresses.
351 * \param listFirst List of first names.
352 * \param listLast List of last names.
354 static void ldapqry_free_lists(
355 GSList *listName, GSList *listAddr, GSList *listFirst,
358 mgu_free_list( listName );
359 mgu_free_list( listAddr );
360 mgu_free_list( listFirst );
361 mgu_free_list( listLast );
365 * Add all LDAP attribute values to a list.
366 * \param ld LDAP handle.
367 * \param entry LDAP entry to process.
368 * \param attr LDAP attribute.
369 * \return List of values.
371 static GSList *ldapqry_add_list_values(
372 LDAP *ld, LDAPMessage *entry, char *attr )
376 struct berval **vals;
378 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
379 for( i = 0; vals[i] != NULL; i++ ) {
380 /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
381 list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
384 ldap_value_free_len( vals );
389 * Add a single attribute value to a list.
390 * \param ld LDAP handle.
391 * \param entry LDAP entry to process.
392 * \param attr LDAP attribute name to process.
393 * \return List of values; only one value will be present.
395 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
397 struct berval **vals;
399 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
400 if( vals[0] != NULL ) {
401 /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
402 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
405 ldap_value_free_len( vals );
410 * Build an address list entry and append to list of address items. Name is formatted
411 * as "<first-name> <last-name>".
413 * \param cache Address cache to load.
414 * \param qry Query object to process.
415 * \param dn DN for entry found on server.
416 * \param listName List of common names for entry; see notes below.
417 * \param listAddr List of EMail addresses for entry.
418 * \param listFirst List of first names for entry.
419 * \param listLast List of last names for entry.
421 * \return List of ItemEMail objects.
424 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
425 * name. For example, a single entry for a person may have more than one
426 * common name, email address, etc.
428 * 2) The DN for the entry is unique for the server.
430 static GList *ldapqry_build_items_fl(
431 AddressCache *cache, LdapQuery *qry, gchar *dn,
432 GSList *listName, GSList *listAddr, GSList *listFirst,
436 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
441 GList *listReturn = NULL;
443 folder = ADDRQUERY_FOLDER(qry);
444 if( folder == NULL ) return listReturn;
445 if( listAddr == NULL ) return listReturn;
447 /* Find longest first name in list */
448 firstName = mgu_slist_longest_entry( listFirst );
450 /* Format last name */
452 lastName = listLast->data;
455 /* Find longest common name */
457 fullName = mgu_slist_longest_entry( listName );
458 if( fullName == NULL ) {
459 /* Format a full name from first and last names */
462 fullName = g_strdup_printf( "%s %s", firstName, lastName );
465 fullName = g_strdup_printf( "%s", firstName );
470 fullName = g_strdup_printf( "%s", lastName );
474 g_strchug( fullName ); g_strchomp( fullName );
479 /* Add person into folder */
480 person = addritem_create_item_person();
481 addritem_person_set_common_name( person, fullName );
482 addritem_person_set_first_name( person, firstName );
483 addritem_person_set_last_name( person, lastName );
484 addrcache_id_person( cache, person );
485 addritem_person_set_external_id( person, dn );
486 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
490 /* Add each address item */
491 nodeAddress = listAddr;
492 while( nodeAddress ) {
493 email = addritem_create_item_email();
494 addritem_email_set_address( email, nodeAddress->data );
495 addrcache_id_email( cache, email );
496 addrcache_person_add_email( cache, person, email );
497 addritem_person_add_email( person, email );
498 listReturn = g_list_append( listReturn, email );
499 nodeAddress = g_slist_next( nodeAddress );
502 /* Free any allocated memory */
506 fullName = firstName = lastName = NULL;
512 * Process a single search entry.
513 * \param cache Address cache to load.
514 * \param qry Query object to process.
515 * \param ld LDAP handle.
516 * \param e LDAP message.
517 * \return List of EMail objects found.
519 static GList *ldapqry_process_single_entry(
520 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
526 GSList *listName = NULL, *listAddress = NULL;
527 GSList *listFirst = NULL, *listLast = NULL;
532 dnEntry = ldap_get_dn( ld, e );
533 /* printf( "DN: %s\n", dnEntry ); */
535 /* Process all attributes */
536 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
537 attribute = ldap_next_attribute( ld, e, ber ) ) {
539 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
540 listAddress = ldapqry_add_list_values( ld, e, attribute );
542 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
543 listName = ldapqry_add_list_values( ld, e, attribute );
545 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
546 listFirst = ldapqry_add_list_values( ld, e, attribute );
548 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
549 listLast = ldapqry_add_single_value( ld, e, attribute );
552 /* Free memory used to store attribute */
553 ldap_memfree( attribute );
556 /* Format and add items to cache */
557 listReturn = ldapqry_build_items_fl(
558 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
561 ldapqry_free_lists( listName, listAddress, listFirst, listLast );
562 listName = listAddress = listFirst = listLast = NULL;
573 * Check parameters that are required for a search. This should
574 * be called before performing a search.
575 * \param qry Query object to process.
576 * \return <i>TRUE</i> if search criteria appear OK.
578 gboolean ldapqry_check_search( LdapQuery *qry ) {
580 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
582 /* Test for control data */
588 /* Test for search value */
589 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
592 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
595 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
600 * Touch the query. This nudges the touch time with the current time.
601 * \param qry Query object to process.
603 void ldapqry_touch( LdapQuery *qry ) {
604 qry->touchTime = time( NULL );
605 qry->agedFlag = FALSE;
609 * Connect to LDAP server.
610 * \param qry Query object to process.
611 * \return Error/status code.
613 static gint ldapqry_connect( LdapQuery *qry ) {
620 /* Initialize connection */
621 /* printf( "===ldapqry_connect===\n" ); */
622 /* ldapqry_print( qry, stdout ); */
624 /* ldapctl_print( ctl, stdout ); */
625 /* printf( "======\n" ); */
626 ldapqry_touch( qry );
627 qry->startTime = qry->touchTime;
628 qry->elapsedTime = -1;
629 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
631 uri = g_strdup_printf("ldap%s://%s:%d",
632 ctl->enableSSL?"s":"",
633 ctl->hostName, ctl->port);
634 ldap_initialize(&ld, uri);
638 return ADDRQUERY_RETVAL(qry);
641 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
642 if( ldapqry_get_stop_flag( qry ) ) {
643 return ADDRQUERY_RETVAL(qry);
645 ldapqry_touch( qry );
648 printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
653 version = LDAP_VERSION3;
654 rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
655 if( rc == LDAP_OPT_SUCCESS ) {
656 ctl->version = LDAP_VERSION3;
659 if( ctl->version == LDAP_VERSION3 ) {
660 if( ctl->enableTLS && !ctl->enableSSL ) {
661 ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
662 rc = ldap_start_tls_s( ld, NULL, NULL );
664 /* printf( "rc=%d\n", rc );
665 printf( "LDAP Status: set_option: %s\n", ldap_err2string( rc ) ); */
667 if( rc != LDAP_SUCCESS ) {
668 return ADDRQUERY_RETVAL(qry);
674 /* Bind to the server, if required */
675 ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
677 if( * ctl->bindDN != '\0' ) {
678 /* printf( "binding...\n" ); */
679 rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
680 /* printf( "rc=%d\n", rc ); */
681 if( rc != LDAP_SUCCESS ) {
683 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
684 ldap_err2string( rc ) );
686 return ADDRQUERY_RETVAL(qry);
690 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
691 if( ldapqry_get_stop_flag( qry ) ) {
692 return ADDRQUERY_RETVAL(qry);
694 ldapqry_touch( qry );
696 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
698 return ADDRQUERY_RETVAL(qry);
702 * Connect to LDAP server.
703 * \param qry Query object to process.
704 * \return Error/status code.
706 static gint ldapqry_disconnect( LdapQuery *qry ) {
708 if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
711 ldapqry_touch( qry );
712 qry->elapsedTime = qry->touchTime - qry->startTime;
714 return ADDRQUERY_RETVAL(qry);
718 * Perform the LDAP search, reading LDAP entries into cache.
719 * Note that one LDAP entry can have multiple values for many of its
720 * attributes. If these attributes are E-Mail addresses; these are
721 * broken out into separate address items. For any other attribute,
722 * only the first occurrence is read.
724 * \param qry Query object to process.
725 * \return Error/status code.
727 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
730 LDAPMessage *result, *e = NULL;
734 gboolean entriesFound;
736 struct timeval timeout;
741 /* Initialize some variables */
744 cache = qry->server->addressCache;
745 timeout.tv_sec = ctl->timeOut;
746 timeout.tv_usec = 0L;
747 entriesFound = FALSE;
748 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
750 /* Define all attributes we are interested in. */
751 attribs = ldapctl_attribute_array( ctl );
753 /* Create LDAP search string */
754 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
755 /* printf( "Search criteria ::%s::\n", criteria ); */
758 * Execute the search - this step may take some time to complete
759 * depending on network traffic and server response time.
761 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
762 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
763 attribs, 0, NULL, NULL, &timeout, 0, &result );
764 ldapctl_free_attribute_array( attribs );
767 if( rc == LDAP_TIMEOUT ) {
768 return ADDRQUERY_RETVAL(qry);
770 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
772 /* Test valid returns */
774 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
777 else if( rc == LDAP_SUCCESS ) {
780 else if( rc == LDAP_PARTIAL_RESULTS ) {
785 printf( "LDAP Error: ldap_search_st: %d\n", rc );
786 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
788 return ADDRQUERY_RETVAL(qry);
790 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
793 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
796 /* Process results */
798 while( searchFlag ) {
799 ldapqry_touch( qry );
800 if( qry->entriesRead >= ctl->maxEntries ) break;
803 if( ldapqry_get_stop_flag( qry ) ) {
810 e = ldap_first_entry( ld, result );
813 e = ldap_next_entry( ld, e );
815 if( e == NULL ) break;
818 /* Setup a critical section here */
819 pthread_mutex_lock( qry->mutexEntry );
822 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
824 /* Process callback */
825 if( qry->callBackEntry ) {
826 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
829 g_list_free( listEMail );
831 pthread_mutex_unlock( qry->mutexEntry );
834 /* Free up and disconnect */
835 ldap_msgfree( result );
839 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
842 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
846 return ADDRQUERY_RETVAL(qry);
850 * Connection, perform search and disconnect.
851 * \param qry Query object to process.
852 * \return Error/status code.
854 static gint ldapqry_perform_search( LdapQuery *qry ) {
855 /* Check search criteria */
856 if( ! ldapqry_check_search( qry ) ) {
857 return ADDRQUERY_RETVAL(qry);
862 ldapqry_connect( qry );
863 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
865 ldapqry_search_retrieve( qry );
868 ldapqry_disconnect( qry );
871 return ADDRQUERY_RETVAL(qry);
874 static gint ldapqry_perform_locate( LdapQuery *qry );
877 * Wrapper around search.
878 * \param qry Query object to process.
879 * \return Error/status code.
881 static gint ldapqry_search( LdapQuery *qry ) {
884 g_return_val_if_fail( qry != NULL, -1 );
885 g_return_val_if_fail( qry->control != NULL, -1 );
887 ldapqry_touch( qry );
888 qry->completed = FALSE;
890 /* Setup pointer to thread specific area */
891 pthread_setspecific( _queryThreadKey_, qry );
893 pthread_detach( pthread_self() );
895 /* Now perform the search */
896 qry->entriesRead = 0;
897 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
898 ldapqry_set_busy_flag( qry, TRUE );
899 ldapqry_set_stop_flag( qry, FALSE );
900 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
901 retVal = ldapqry_perform_locate( qry );
904 retVal = ldapqry_perform_search( qry );
906 if( retVal == LDAPRC_SUCCESS ) {
907 qry->server->addressCache->dataRead = TRUE;
908 qry->server->addressCache->accessFlag = FALSE;
909 if( ldapqry_get_stop_flag( qry ) ) {
911 printf( "Search was terminated prematurely\n" );
915 ldapqry_touch( qry );
916 qry->completed = TRUE;
918 printf( "Search ran to completion\n" );
922 ldapqry_set_stop_flag( qry, TRUE );
923 ldapqry_set_busy_flag( qry, FALSE );
925 /* Process callback */
926 if( qry->callBackEnd ) {
927 g_timeout_add(0, callbackend, qry);
930 return ADDRQUERY_RETVAL(qry);
934 * Read data into list using a background thread. Callback function will be
935 * notified when search is complete.
936 * \param qry Query object to process.
937 * \return Error/status code.
939 gint ldapqry_read_data_th( LdapQuery *qry ) {
940 g_return_val_if_fail( qry != NULL, -1 );
941 g_return_val_if_fail( qry->control != NULL, -1 );
943 ldapqry_set_stop_flag( qry, FALSE );
944 ldapqry_touch( qry );
945 if( ldapqry_check_search( qry ) ) {
946 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
948 printf( "Starting LDAP search thread\n");
950 ldapqry_set_busy_flag( qry, TRUE );
951 qry->thread = g_malloc0( sizeof( pthread_t ) );
954 pthread_create( qry->thread, NULL,
955 (void *) ldapqry_search, (void *) qry );
958 return ADDRQUERY_RETVAL(qry);
962 * Cleanup LDAP thread data. This function will be called when each thread
963 * exits. Note that the thread object will be freed by the kernel.
964 * \param ptr Pointer to object being destroyed (a query object in this case).
966 static void ldapqry_destroyer( void * ptr ) {
969 qry = ( LdapQuery * ) ptr;
971 printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
974 /* Perform any destruction here */
975 if( qry->control != NULL ) {
976 ldapctl_free( qry->control );
980 ldapqry_set_busy_flag( qry, FALSE );
984 * Cancel thread associated with query.
985 * \param qry Query object to process.
987 void ldapqry_cancel( LdapQuery *qry ) {
988 g_return_if_fail( qry != NULL );
991 printf( "cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
993 if( ldapqry_get_busy_flag( qry ) ) {
995 /* printf( "calling pthread_cancel\n" ); */
996 pthread_cancel( * qry->thread );
1002 * Initialize LDAP query. This function should be called once before executing
1003 * any LDAP queries to initialize thread specific data.
1005 void ldapqry_initialize( void ) {
1006 /* printf( "ldapqry_initialize...\n" ); */
1007 if( ! _queryThreadInit_ ) {
1009 printf( "ldapqry_initialize::creating thread specific area\n" );
1011 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1012 _queryThreadInit_ = TRUE;
1014 /* printf( "ldapqry_initialize... done!\n" ); */
1018 * Age the query based on LDAP control parameters.
1019 * \param qry Query object to process.
1020 * \param maxAge Maximum age of query (in seconds).
1022 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1025 g_return_if_fail( qry != NULL );
1027 /* Limit the time that queries can hang around */
1028 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1030 /* Check age of query */
1031 age = time( NULL ) - qry->touchTime;
1032 if( age > maxAge ) {
1033 qry->agedFlag = TRUE;
1038 * Delete folder associated with query results.
1039 * \param qry Query object to process.
1041 void ldapqry_delete_folder( LdapQuery *qry ) {
1042 AddressCache *cache;
1045 g_return_if_fail( qry != NULL );
1047 folder = ADDRQUERY_FOLDER(qry);
1049 cache = qry->server->addressCache;
1050 folder = addrcache_remove_folder_delete( cache, folder );
1052 addritem_free_item_folder( folder );
1054 ADDRQUERY_FOLDER(qry) = NULL;
1059 * Create a name/value pair object.
1062 * \return Initialized object.
1064 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1065 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1067 nvp->name = g_strdup( n );
1068 nvp->value = g_strdup( v );
1073 * Free up name/value pair object.
1074 * \param nvp Name/value object.
1076 void ldapqry_free_name_value( NameValuePair *nvp ) {
1078 g_free( nvp->name );
1079 g_free( nvp->value );
1080 nvp->name = nvp->value = NULL;
1086 * Free up a list name/value pair objects.
1087 * \param list List of name/value objects.
1089 void ldapqry_free_list_name_value( GList *list ) {
1094 NameValuePair *nvp = ( NameValuePair * ) node->data;
1095 ldapqry_free_name_value( nvp );
1097 node = g_list_next( node );
1099 g_list_free( list );
1103 * Load a list of name/value pairs from LDAP attributes.
1104 * \param ld LDAP handle.
1105 * \param e LDAP message.
1106 * \param attr Attribute name.
1107 * \param listValues List to populate.
1108 * \return List of attribute name/value pairs.
1110 static GList *ldapqry_load_attrib_values(
1111 LDAP *ld, LDAPMessage *entry, char *attr,
1116 struct berval **vals;
1120 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1121 for( i = 0; vals[i] != NULL; i++ ) {
1122 gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1123 nvp = ldapqry_create_name_value( attr, tmp );
1125 list = g_list_append( list, nvp );
1128 ldap_value_free_len( vals );
1133 * Fetch a list of all attributes.
1134 * \param ld LDAP handle.
1135 * \param e LDAP message.
1136 * \return List of attribute name/value pairs.
1138 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1142 GList *listValues = NULL;
1144 /* Process all attributes */
1145 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1146 attribute = ldap_next_attribute( ld, e, ber ) ) {
1147 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1148 ldap_memfree( attribute );
1158 #define CRITERIA_SINGLE "(objectclass=*)"
1161 * Perform the data retrieval for a specific LDAP record.
1163 * \param qry Query object to process.
1164 * \return Error/status code.
1166 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1169 LDAPMessage *result, *e = NULL;
1170 gboolean entriesFound;
1172 struct timeval timeout;
1177 /* Initialize some variables */
1180 dn = ADDRQUERY_SEARCHVALUE(qry);
1181 timeout.tv_sec = ctl->timeOut;
1182 timeout.tv_usec = 0L;
1183 entriesFound = FALSE;
1186 * Execute the search - this step may take some time to complete
1187 * depending on network traffic and server response time.
1189 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1190 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1191 NULL, 0, NULL, NULL, &timeout, 0, &result );
1192 if( rc == LDAP_TIMEOUT ) {
1193 return ADDRQUERY_RETVAL(qry);
1195 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1196 if( rc != LDAP_SUCCESS ) {
1198 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
1200 return ADDRQUERY_RETVAL(qry);
1204 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
1207 /* Process results */
1208 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1211 ldapqry_touch( qry );
1212 if( qry->entriesRead >= ctl->maxEntries ) break;
1215 if( ldapqry_get_stop_flag( qry ) ) {
1219 /* Retrieve entry */
1222 e = ldap_first_entry( ld, result );
1225 e = ldap_next_entry( ld, e );
1227 if( e == NULL ) break;
1229 entriesFound = TRUE;
1231 /* Setup a critical section here */
1232 pthread_mutex_lock( qry->mutexEntry );
1235 listValues = ldapqry_fetch_attribs( ld, e );
1237 /* Process callback */
1238 if( qry->callBackEntry ) {
1239 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1241 ldapqry_free_list_name_value( listValues );
1244 pthread_mutex_unlock( qry->mutexEntry );
1247 /* Free up and disconnect */
1248 ldap_msgfree( result );
1250 if( entriesFound ) {
1251 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1254 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1257 return ADDRQUERY_RETVAL(qry);
1261 * Perform the search to locate a specific LDAP record identified by
1262 * distinguished name (dn).
1264 * \param qry Query object to process.
1265 * \return Error/status code.
1267 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1270 ldapqry_connect( qry );
1271 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1272 /* Perform search */
1273 ldapqry_locate_retrieve( qry );
1276 ldapqry_disconnect( qry );
1279 /* Process callback */
1280 if( qry->callBackEnd ) {
1281 g_timeout_add(0, callbackend, qry);
1284 return ADDRQUERY_RETVAL(qry);
1288 * Remove results (folder and data) for specified LDAP query.
1289 * \param qry Query object to process.
1290 * \return TRUE if folder deleted successfully.
1292 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1293 gboolean retVal = FALSE;
1295 ldapqry_set_aged_flag( qry, TRUE );
1297 if( ldapqry_get_busy_flag( qry ) ) {
1298 ldapqry_set_stop_flag( qry, TRUE );
1301 LdapServer *server = qry->server;
1302 server->listQuery = g_list_remove(server->listQuery, qry);
1309 #endif /* USE_LDAP */