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 3 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, see <http://www.gnu.org/licenses/>.
21 * Functions necessary to define and perform LDAP queries.
36 #include "ldapquery.h"
41 #include "addrcache.h"
42 #include "common/utils.h"
45 * Key for thread specific data.
47 static pthread_key_t _queryThreadKey_;
48 static gboolean _queryThreadInit_ = FALSE;
50 static gboolean callbackend (gpointer data)
52 LdapQuery *qry = (LdapQuery *)data;
53 qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
59 * Create new LDAP query object.
60 * \return Initialized query object.
62 LdapQuery *ldapqry_create( void ) {
65 qry = g_new0( LdapQuery, 1 );
66 ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
67 ADDRQUERY_ID(qry) = 0;
68 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
69 ADDRQUERY_NAME(qry) = NULL;
70 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
71 ADDRQUERY_FOLDER(qry) = NULL;
72 ADDRQUERY_SEARCHVALUE(qry) = NULL;
77 qry->stopFlag = FALSE;
78 qry->busyFlag = FALSE;
79 qry->agedFlag = FALSE;
80 qry->completed = FALSE;
82 qry->callBackEntry = NULL;
83 qry->callBackEnd = NULL;
87 /* Mutex to protect stop and busy flags */
88 qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
89 pthread_mutex_init( qry->mutexStop, NULL );
90 qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
91 pthread_mutex_init( qry->mutexBusy, NULL );
93 /* Mutex to protect critical section */
94 qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
95 pthread_mutex_init( qry->mutexEntry, NULL );
101 * Specify the reference to control data that will be used for the query. The calling
102 * module should be responsible for creating and destroying this control object.
103 * \param qry Query object.
104 * \param ctl Control object.
106 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
107 g_return_if_fail( qry != NULL );
112 * Specify query name to be used.
113 * \param qry Query object.
116 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
117 g_return_if_fail( qry != NULL );
118 g_return_if_fail( ADDRQUERY_NAME(qry) != NULL );
119 ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
120 g_strstrip( ADDRQUERY_NAME(qry) );
121 debug_print("set name: %s\n", ADDRQUERY_NAME(qry));
125 * Specify search value to be used.
126 * \param qry Query object.
129 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
130 g_return_if_fail( qry != NULL );
131 g_return_if_fail( ADDRQUERY_SEARCHVALUE(qry) != NULL );
132 ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
133 g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
134 debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry));
138 * Specify query type.
139 * \param qry Query object.
140 * \param value Query type, either:
142 * <li><code>LDAPQUERY_NONE</code></li>
143 * <li><code>LDAPQUERY_STATIC</code></li>
144 * <li><code>LDAPQUERY_DYNAMIC</code></li>
148 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
149 ADDRQUERY_TYPE(qry) = value;
154 * Specify search type.
155 * \param qry Query object.
158 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
159 g_return_if_fail( qry != NULL );
160 g_return_if_fail( ADDRQUERY_SEARCHTYPE(qry) != NULL );
161 ADDRQUERY_SEARCHTYPE(qry) = value;
166 * \param qry Query object.
167 * \param value ID for the query.
169 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
170 g_return_if_fail( qry != NULL );
171 g_return_if_fail( ADDRQUERY_ID(qry) != NULL );
172 ADDRQUERY_ID(qry) = value;
176 * Register a callback function that will be executed when each entry
177 * has been read and processed. When called, the function will be passed
178 * this query object and a GList of ItemEMail objects as arguments. An
179 * example of typical usage is shown below.
182 * ------------------------------------------------------------
183 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
188 * ItemEMail *email = node->data;
189 * ... process email object ...
190 * node = g_list_next( node );
192 * g_list_free( listEMail );
196 * ldapqry_set_callback_entry( qry, myCallbackEntry );
197 * ------------------------------------------------------------
200 * \param qry Query object.
201 * \param func Function.
203 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
204 pthread_mutex_lock( qry->mutexEntry );
205 qry->callBackEntry = func;
206 pthread_mutex_unlock( qry->mutexEntry );
210 * Register a callback function that will be executed when the search
211 * is complete. When called, the function will be passed this query
212 * object as an argument.
213 * \param qry Query object.
214 * \param func Function.
216 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
217 qry->callBackEnd = func;
221 * Notify query to start/stop executing. This method should be called with a
222 * value if <i>TRUE</i> to terminate an existing running query.
224 * \param qry Query object.
225 * \param value Value of stop flag.
227 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
228 g_return_if_fail( qry != NULL );
230 pthread_mutex_lock( qry->mutexStop );
231 qry->stopFlag = value;
232 pthread_mutex_unlock( qry->mutexStop );
236 * Test value of stop flag. This method should be used to determine whether a
237 * query has stopped running.
238 * \param qry Query object.
239 * \return Value of stop flag.
241 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
243 g_return_val_if_fail( qry != NULL, TRUE );
245 pthread_mutex_lock( qry->mutexStop );
246 value = qry->stopFlag;
247 pthread_mutex_unlock( qry->mutexStop );
253 * \param qry Query object.
254 * \param value Value of busy flag.
256 static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
257 g_return_if_fail( qry != NULL );
258 if (qry->mutexBusy == NULL)
259 return; /* exiting, mutex already freed */
261 pthread_mutex_lock( qry->mutexBusy );
262 qry->busyFlag = value;
263 pthread_mutex_unlock( qry->mutexBusy );
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.
272 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
274 g_return_val_if_fail( qry != NULL, FALSE );
276 pthread_mutex_lock( qry->mutexBusy );
277 value = qry->busyFlag;
278 pthread_mutex_unlock( qry->mutexBusy );
283 * Set query aged flag.
284 * \param qry Query object.
285 * \param value Value of aged flag.
287 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
288 g_return_if_fail( qry != NULL );
289 qry->agedFlag = value;
293 * Clear LDAP query member variables.
294 * \param qry Query object.
296 static void ldapqry_clear( LdapQuery *qry ) {
297 g_return_if_fail( qry != NULL );
299 /* Free internal stuff */
300 g_free( ADDRQUERY_NAME(qry) );
301 g_free( ADDRQUERY_SEARCHVALUE(qry) );
303 /* Clear pointers and value */
304 ADDRQUERY_NAME(qry) = NULL;
305 ADDRQUERY_SEARCHVALUE(qry) = NULL;
306 ADDRQUERY_ID(qry) = 0;
307 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
308 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
309 qry->entriesRead = 0;
310 qry->elapsedTime = 0;
311 qry->stopFlag = FALSE;
312 qry->busyFlag = FALSE;
313 qry->agedFlag = FALSE;
314 qry->completed = FALSE;
315 qry->callBackEntry = NULL;
316 qry->callBackEnd = NULL;
322 * Free up LDAP query object by releasing internal memory. Note that
323 * the thread object will be freed by the OS.
324 * \param qry Query object to process.
326 void ldapqry_free( LdapQuery *qry ) {
327 g_return_if_fail( qry != NULL );
329 /* Clear out internal members */
330 ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
331 ldapqry_clear( qry );
334 pthread_mutex_destroy( qry->mutexStop );
335 pthread_mutex_destroy( qry->mutexBusy );
336 pthread_mutex_destroy( qry->mutexEntry );
337 g_free( qry->mutexStop );
338 g_free( qry->mutexBusy );
339 g_free( qry->mutexEntry );
340 qry->mutexEntry = NULL;
341 qry->mutexBusy = NULL;
342 qry->mutexStop = NULL;
344 /* Do not free folder - parent server object should free */
345 ADDRQUERY_FOLDER(qry) = NULL;
347 /* Do not free thread - thread should be terminated before freeing */
350 /* Do not free LDAP control - should be destroyed before freeing */
353 /* Now release object */
358 * Free linked lists of character strings.
359 * \param listName List of common names.
360 * \param listAddr List of addresses.
361 * \param listFirst List of first names.
362 * \param listLast List of last names.
364 static void ldapqry_free_lists(
365 GSList *listName, GSList *listAddr, GSList *listFirst,
366 GSList *listLast, GSList *listDisplay, GSList *other_attrs )
368 GSList *cur = other_attrs;
369 mgu_free_list( listName );
370 mgu_free_list( listAddr );
371 mgu_free_list( listFirst );
372 mgu_free_list( listLast );
373 mgu_free_list( listDisplay );
374 for(;cur; cur = cur->next)
375 addritem_free_attribute((UserAttribute *)cur->data);
376 g_slist_free(other_attrs);
380 * Add all LDAP attribute values to a list.
381 * \param ld LDAP handle.
382 * \param entry LDAP entry to process.
383 * \param attr LDAP attribute.
384 * \return List of values.
386 static GSList *ldapqry_add_list_values(
387 LDAP *ld, LDAPMessage *entry, char *attr )
391 struct berval **vals;
393 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
394 for( i = 0; vals[i] != NULL; i++ ) {
395 /*debug_print("lv\t%s: %s\n", attr?attr:"null",
396 vals[i]->bv_val?vals[i]->bv_val:"null");*/
397 list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
400 ldap_value_free_len( vals );
405 * Add a single attribute value to a list.
406 * \param ld LDAP handle.
407 * \param entry LDAP entry to process.
408 * \param attr LDAP attribute name to process.
409 * \return List of values; only one value will be present.
411 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
413 struct berval **vals;
415 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
416 if( vals[0] != NULL ) {
417 debug_print("sv\t%s: %s\n", attr?attr:"null",
418 vals[0]->bv_val?vals[0]->bv_val:"null");
419 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
422 ldap_value_free_len( vals );
427 * Build an address list entry and append to list of address items. Name is formatted
428 * as "<first-name> <last-name>".
430 * \param cache Address cache to load.
431 * \param qry Query object to process.
432 * \param dn DN for entry found on server.
433 * \param listName List of common names for entry; see notes below.
434 * \param listAddr List of EMail addresses for entry.
435 * \param listFirst List of first names for entry.
436 * \param listLast List of last names for entry.
438 * \return List of ItemEMail objects.
441 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
442 * name. For example, a single entry for a person may have more than one
443 * common name, email address, etc.
445 * 2) The DN for the entry is unique for the server.
447 static GList *ldapqry_build_items_fl(
448 AddressCache *cache, LdapQuery *qry, gchar *dn,
449 GSList *listName, GSList *listAddr, GSList *listFirst,
450 GSList *listLast, GSList *listDisplay, GSList *attributes )
452 GSList *nodeAddress, *cur;
453 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
454 gboolean allocated = FALSE;
458 GList *listReturn = NULL;
460 folder = ADDRQUERY_FOLDER(qry);
461 if( folder == NULL ) return listReturn;
462 if( listAddr == NULL ) return listReturn;
466 fullName = listDisplay->data;
469 /* Find longest first name in list */
470 firstName = mgu_slist_longest_entry( listFirst );
472 /* Format last name */
474 lastName = listLast->data;
477 if ( fullName == NULL ) {
478 /* Find longest common name */
480 fullName = mgu_slist_longest_entry( listName );
481 if( fullName == NULL ) {
482 /* Format a full name from first and last names */
485 fullName = g_strdup_printf( "%s %s", firstName, lastName );
488 fullName = g_strdup_printf( "%s", firstName );
493 fullName = g_strdup_printf( "%s", lastName );
497 g_strchug( fullName ); g_strchomp( fullName );
503 /* Add person into folder */
504 person = addritem_create_item_person();
505 addritem_person_set_common_name( person, fullName );
506 addritem_person_set_first_name( person, firstName );
507 addritem_person_set_last_name( person, lastName );
508 addritem_person_set_nick_name( person, fullName );
509 addrcache_id_person( cache, person );
510 addritem_person_set_external_id( person, dn );
512 for (cur = attributes; cur; cur = cur->next) {
513 UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
514 addritem_person_add_attribute( person, attrib );
517 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
521 /* Add each address item */
522 nodeAddress = listAddr;
523 while( nodeAddress ) {
524 email = addritem_create_item_email();
525 addritem_email_set_address( email, nodeAddress->data );
526 addrcache_id_email( cache, email );
527 addrcache_person_add_email( cache, person, email );
528 addritem_person_add_email( person, email );
529 /*if (debug_get_mode()) {
530 addritem_print_item_email(email, stdout);
532 listReturn = g_list_append( listReturn, email );
533 nodeAddress = g_slist_next( nodeAddress );
536 /* Free any allocated memory */
540 fullName = firstName = lastName = NULL;
546 * Process a single search entry.
547 * \param cache Address cache to load.
548 * \param qry Query object to process.
549 * \param ld LDAP handle.
550 * \param e LDAP message.
551 * \return List of EMail objects found.
553 static GList *ldapqry_process_single_entry(
554 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
560 GSList *listName = NULL, *listAddress = NULL;
561 GSList *listFirst = NULL, *listLast = NULL;
562 GSList *listDisplay = NULL;
563 GSList *other_attrs = NULL;
568 dnEntry = ldap_get_dn( ld, e );
569 debug_print( "DN: %s\n", dnEntry?dnEntry:"null" );
571 /* Process all attributes */
572 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
573 attribute = ldap_next_attribute( ld, e, ber ) ) {
574 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
575 listAddress = ldapqry_add_list_values( ld, e, attribute );
577 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
578 listName = ldapqry_add_list_values( ld, e, attribute );
580 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
581 listFirst = ldapqry_add_list_values( ld, e, attribute );
583 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
584 listLast = ldapqry_add_single_value( ld, e, attribute );
585 } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
586 listDisplay = ldapqry_add_single_value( ld, e, attribute );
588 GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
589 UserAttribute *attrib = addritem_create_attribute();
590 const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
592 addritem_attrib_set_name( attrib, attribute );
593 addritem_attrib_set_value( attrib, attvalue );
594 other_attrs = g_slist_prepend(other_attrs, attrib);
596 mgu_free_list(attlist);
598 /* Free memory used to store attribute */
599 ldap_memfree( attribute );
602 /* Format and add items to cache */
603 listReturn = ldapqry_build_items_fl(
604 cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
607 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
608 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
619 * Check parameters that are required for a search. This should
620 * be called before performing a search.
621 * \param qry Query object to process.
622 * \return <i>TRUE</i> if search criteria appear OK.
624 gboolean ldapqry_check_search( LdapQuery *qry ) {
626 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
628 /* Test for control data */
634 /* Test for search value */
635 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
638 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
641 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
646 * Touch the query. This nudges the touch time with the current time.
647 * \param qry Query object to process.
649 void ldapqry_touch( LdapQuery *qry ) {
650 qry->touchTime = time( NULL );
651 qry->agedFlag = FALSE;
655 * Connect to LDAP server.
656 * \param qry Query object to process.
657 * \return Error/status code.
659 static gint ldapqry_connect( LdapQuery *qry ) {
666 /* Initialize connection */
667 if (debug_get_mode()) {
668 debug_print("===ldapqry_connect===\n");
669 /*ldapqry_print(qry, stdout);*/
672 /*if (debug_get_mode()) {
673 ldapctl_print(ctl, stdout);
674 debug_print("======\n");
676 ldapqry_touch( qry );
677 qry->startTime = qry->touchTime;
678 qry->elapsedTime = -1;
679 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
681 uri = g_strdup_printf("ldap%s://%s:%d",
682 ctl->enableSSL?"s":"",
683 ctl->hostName, ctl->port);
684 ldap_initialize(&ld, uri);
688 return ADDRQUERY_RETVAL(qry);
691 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
692 if( ldapqry_get_stop_flag( qry ) ) {
693 return ADDRQUERY_RETVAL(qry);
695 ldapqry_touch( qry );
697 debug_print("connected to LDAP host %s on port %d\n",
698 ctl->hostName?ctl->hostName:"null", ctl->port);
702 version = LDAP_VERSION3;
703 rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
704 if( rc == LDAP_OPT_SUCCESS ) {
705 ctl->version = LDAP_VERSION3;
708 if( ctl->version == LDAP_VERSION3 ) {
709 if( ctl->enableTLS && !ctl->enableSSL ) {
710 ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
711 rc = ldap_start_tls_s( ld, NULL, NULL );
713 debug_print("rc=%d\n", rc);
714 debug_print("LDAP Status: set_option: %s\n", ldap_err2string(rc));
716 if( rc != LDAP_SUCCESS ) {
717 return ADDRQUERY_RETVAL(qry);
723 /* Bind to the server, if required */
724 ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
726 if( * ctl->bindDN != '\0' ) {
727 debug_print("binding...\n");
728 rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
729 debug_print("rc=%d\n", rc);
730 if( rc != LDAP_SUCCESS ) {
731 debug_print("LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string(rc));
732 return ADDRQUERY_RETVAL(qry);
736 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
737 if( ldapqry_get_stop_flag( qry ) ) {
738 return ADDRQUERY_RETVAL(qry);
740 ldapqry_touch( qry );
742 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
744 return ADDRQUERY_RETVAL(qry);
748 * Connect to LDAP server.
749 * \param qry Query object to process.
750 * \return Error/status code.
752 static gint ldapqry_disconnect( LdapQuery *qry ) {
754 if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
757 ldapqry_touch( qry );
758 qry->elapsedTime = qry->touchTime - qry->startTime;
760 return ADDRQUERY_RETVAL(qry);
764 * Perform the LDAP search, reading LDAP entries into cache.
765 * Note that one LDAP entry can have multiple values for many of its
766 * attributes. If these attributes are E-Mail addresses; these are
767 * broken out into separate address items. For any other attribute,
768 * only the first occurrence is read.
770 * \param qry Query object to process.
771 * \return Error/status code.
773 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
776 LDAPMessage *result, *e = NULL;
780 gboolean entriesFound;
782 struct timeval timeout;
787 /* Initialize some variables */
790 cache = qry->server->addressCache;
791 timeout.tv_sec = ctl->timeOut;
792 timeout.tv_usec = 0L;
793 entriesFound = FALSE;
794 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
796 /* Define all attributes we are interested in. */
797 attribs = ldapctl_full_attribute_array( ctl );
799 /* Create LDAP search string */
800 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
801 debug_print("Search criteria ::%s::\n", criteria?criteria:"null");
804 * Execute the search - this step may take some time to complete
805 * depending on network traffic and server response time.
807 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
808 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
809 attribs, 0, NULL, NULL, &timeout, 0, &result );
810 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
811 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
812 ldapctl_free_attribute_array( attribs );
815 if( rc == LDAP_TIMEOUT ) {
816 return ADDRQUERY_RETVAL(qry);
818 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
820 /* Test valid returns */
822 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
825 else if( rc == LDAP_SUCCESS ) {
828 else if( rc == LDAP_PARTIAL_RESULTS ) {
832 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
833 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
834 return ADDRQUERY_RETVAL(qry);
836 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
838 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
840 /* Process results */
842 while( searchFlag ) {
843 ldapqry_touch( qry );
844 if( qry->entriesRead >= ctl->maxEntries ) break;
847 if( ldapqry_get_stop_flag( qry ) ) {
854 e = ldap_first_entry( ld, result );
857 e = ldap_next_entry( ld, e );
859 if( e == NULL ) break;
862 /* Setup a critical section here */
863 pthread_mutex_lock( qry->mutexEntry );
866 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
868 /* Process callback */
869 if( qry->callBackEntry ) {
870 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
873 /*if (debug_get_mode()) {
874 GList *node = listEMail;
876 addritem_print_item_email(node->data, stdout);
877 node = g_list_next(node);
880 g_list_free( listEMail );
882 pthread_mutex_unlock( qry->mutexEntry );
885 /* Free up and disconnect */
886 ldap_msgfree( result );
890 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
893 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
897 return ADDRQUERY_RETVAL(qry);
901 * Connection, perform search and disconnect.
902 * \param qry Query object to process.
903 * \return Error/status code.
905 static gint ldapqry_perform_search( LdapQuery *qry ) {
906 /* Check search criteria */
907 if( ! ldapqry_check_search( qry ) ) {
908 return ADDRQUERY_RETVAL(qry);
913 ldapqry_connect( qry );
914 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
916 ldapqry_search_retrieve( qry );
919 ldapqry_disconnect( qry );
922 return ADDRQUERY_RETVAL(qry);
925 static gint ldapqry_perform_locate( LdapQuery *qry );
928 * Wrapper around search.
929 * \param qry Query object to process.
930 * \return Error/status code.
932 static gint ldapqry_search( LdapQuery *qry ) {
935 g_return_val_if_fail( qry != NULL, -1 );
936 g_return_val_if_fail( qry->control != NULL, -1 );
938 ldapqry_touch( qry );
939 qry->completed = FALSE;
941 /* Setup pointer to thread specific area */
942 pthread_setspecific( _queryThreadKey_, qry );
944 pthread_detach( pthread_self() );
946 /* Now perform the search */
947 qry->entriesRead = 0;
948 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
949 ldapqry_set_busy_flag( qry, TRUE );
950 ldapqry_set_stop_flag( qry, FALSE );
951 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
952 retVal = ldapqry_perform_locate( qry );
955 retVal = ldapqry_perform_search( qry );
957 if( retVal == LDAPRC_SUCCESS ) {
958 qry->server->addressCache->dataRead = TRUE;
959 qry->server->addressCache->accessFlag = FALSE;
960 if( ldapqry_get_stop_flag( qry ) ) {
961 debug_print("Search was terminated prematurely\n");
964 ldapqry_touch( qry );
965 qry->completed = TRUE;
966 debug_print("Search ran to completion\n");
969 ldapqry_set_stop_flag( qry, TRUE );
970 ldapqry_set_busy_flag( qry, FALSE );
972 /* Process callback */
973 if( qry->callBackEnd ) {
974 g_timeout_add(0, callbackend, qry);
977 return ADDRQUERY_RETVAL(qry);
981 * Read data into list using a background thread. Callback function will be
982 * notified when search is complete.
983 * \param qry Query object to process.
984 * \return Error/status code.
986 gint ldapqry_read_data_th( LdapQuery *qry ) {
987 g_return_val_if_fail( qry != NULL, -1 );
988 g_return_val_if_fail( qry->control != NULL, -1 );
990 ldapqry_set_stop_flag( qry, FALSE );
991 ldapqry_touch( qry );
992 if( ldapqry_check_search( qry ) ) {
993 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
994 debug_print("Starting LDAP search thread\n");
995 ldapqry_set_busy_flag( qry, TRUE );
996 qry->thread = g_malloc0( sizeof( pthread_t ) );
999 pthread_create( qry->thread, NULL,
1000 (void *) ldapqry_search, (void *) qry );
1003 return ADDRQUERY_RETVAL(qry);
1007 * Cleanup LDAP thread data. This function will be called when each thread
1008 * exits. Note that the thread object will be freed by the kernel.
1009 * \param ptr Pointer to object being destroyed (a query object in this case).
1011 static void ldapqry_destroyer( void * ptr ) {
1014 qry = ( LdapQuery * ) ptr;
1015 g_return_if_fail( qry != NULL );
1016 g_return_if_fail( ADDRQUERY_NAME(qry) != NULL );
1018 debug_print("ldapqry_destroyer::%d::%s\n", (int) pthread_self(),
1019 ADDRQUERY_NAME(qry));
1021 /* Perform any destruction here */
1022 if( qry->control != NULL ) {
1023 ldapctl_free( qry->control );
1025 qry->control = NULL;
1027 ldapqry_set_busy_flag( qry, FALSE );
1031 * Cancel thread associated with query.
1032 * \param qry Query object to process.
1034 void ldapqry_cancel( LdapQuery *qry ) {
1035 g_return_if_fail( qry != NULL );
1037 debug_print("cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry));
1038 if( ldapqry_get_busy_flag( qry ) ) {
1040 debug_print("calling pthread_cancel\n");
1041 pthread_cancel( * qry->thread );
1047 * Initialize LDAP query. This function should be called once before executing
1048 * any LDAP queries to initialize thread specific data.
1050 void ldapqry_initialize( void ) {
1051 debug_print("ldapqry_initialize...\n");
1052 if( ! _queryThreadInit_ ) {
1053 debug_print("ldapqry_initialize::creating thread specific area\n");
1054 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1055 _queryThreadInit_ = TRUE;
1057 debug_print("ldapqry_initialize... done!\n");
1061 * Age the query based on LDAP control parameters.
1062 * \param qry Query object to process.
1063 * \param maxAge Maximum age of query (in seconds).
1065 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1068 g_return_if_fail( qry != NULL );
1070 /* Limit the time that queries can hang around */
1071 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1073 /* Check age of query */
1074 age = time( NULL ) - qry->touchTime;
1075 if( age > maxAge ) {
1076 qry->agedFlag = TRUE;
1081 * Delete folder associated with query results.
1082 * \param qry Query object to process.
1084 void ldapqry_delete_folder( LdapQuery *qry ) {
1085 AddressCache *cache;
1088 g_return_if_fail( qry != NULL );
1090 folder = ADDRQUERY_FOLDER(qry);
1092 cache = qry->server->addressCache;
1093 folder = addrcache_remove_folder_delete( cache, folder );
1095 addritem_free_item_folder( folder );
1097 ADDRQUERY_FOLDER(qry) = NULL;
1102 * Create a name/value pair object.
1105 * \return Initialized object.
1107 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1108 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1110 nvp->name = g_strdup( n );
1111 nvp->value = g_strdup( v );
1116 * Free up name/value pair object.
1117 * \param nvp Name/value object.
1119 void ldapqry_free_name_value( NameValuePair *nvp ) {
1121 g_free( nvp->name );
1122 g_free( nvp->value );
1123 nvp->name = nvp->value = NULL;
1129 * Free up a list name/value pair objects.
1130 * \param list List of name/value objects.
1132 void ldapqry_free_list_name_value( GList *list ) {
1137 NameValuePair *nvp = ( NameValuePair * ) node->data;
1138 ldapqry_free_name_value( nvp );
1140 node = g_list_next( node );
1142 g_list_free( list );
1146 * Load a list of name/value pairs from LDAP attributes.
1147 * \param ld LDAP handle.
1148 * \param e LDAP message.
1149 * \param attr Attribute name.
1150 * \param listValues List to populate.
1151 * \return List of attribute name/value pairs.
1153 static GList *ldapqry_load_attrib_values(
1154 LDAP *ld, LDAPMessage *entry, char *attr,
1159 struct berval **vals;
1163 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1164 for( i = 0; vals[i] != NULL; i++ ) {
1165 gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1166 nvp = ldapqry_create_name_value( attr, tmp );
1168 list = g_list_append( list, nvp );
1171 ldap_value_free_len( vals );
1176 * Fetch a list of all attributes.
1177 * \param ld LDAP handle.
1178 * \param e LDAP message.
1179 * \return List of attribute name/value pairs.
1181 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1185 GList *listValues = NULL;
1187 /* Process all attributes */
1188 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1189 attribute = ldap_next_attribute( ld, e, ber ) ) {
1190 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1191 ldap_memfree( attribute );
1201 #define CRITERIA_SINGLE "(objectclass=*)"
1204 * Perform the data retrieval for a specific LDAP record.
1206 * \param qry Query object to process.
1207 * \return Error/status code.
1209 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1212 LDAPMessage *result, *e = NULL;
1213 gboolean entriesFound;
1215 struct timeval timeout;
1220 /* Initialize some variables */
1223 dn = ADDRQUERY_SEARCHVALUE(qry);
1224 timeout.tv_sec = ctl->timeOut;
1225 timeout.tv_usec = 0L;
1226 entriesFound = FALSE;
1229 * Execute the search - this step may take some time to complete
1230 * depending on network traffic and server response time.
1232 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1233 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1234 NULL, 0, NULL, NULL, &timeout, 0, &result );
1235 if( rc == LDAP_TIMEOUT ) {
1236 return ADDRQUERY_RETVAL(qry);
1238 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1239 if( rc != LDAP_SUCCESS ) {
1240 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
1241 return ADDRQUERY_RETVAL(qry);
1244 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1246 /* Process results */
1247 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1250 ldapqry_touch( qry );
1251 if( qry->entriesRead >= ctl->maxEntries ) break;
1254 if( ldapqry_get_stop_flag( qry ) ) {
1258 /* Retrieve entry */
1261 e = ldap_first_entry( ld, result );
1264 e = ldap_next_entry( ld, e );
1266 if( e == NULL ) break;
1268 entriesFound = TRUE;
1270 /* Setup a critical section here */
1271 pthread_mutex_lock( qry->mutexEntry );
1274 listValues = ldapqry_fetch_attribs( ld, e );
1276 /* Process callback */
1277 if( qry->callBackEntry ) {
1278 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1280 ldapqry_free_list_name_value( listValues );
1283 pthread_mutex_unlock( qry->mutexEntry );
1286 /* Free up and disconnect */
1287 ldap_msgfree( result );
1289 if( entriesFound ) {
1290 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1293 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1296 return ADDRQUERY_RETVAL(qry);
1300 * Perform the search to locate a specific LDAP record identified by
1301 * distinguished name (dn).
1303 * \param qry Query object to process.
1304 * \return Error/status code.
1306 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1309 ldapqry_connect( qry );
1310 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1311 /* Perform search */
1312 ldapqry_locate_retrieve( qry );
1315 ldapqry_disconnect( qry );
1318 /* Process callback */
1319 if( qry->callBackEnd ) {
1320 g_timeout_add(0, callbackend, qry);
1323 return ADDRQUERY_RETVAL(qry);
1327 * Remove results (folder and data) for specified LDAP query.
1328 * \param qry Query object to process.
1329 * \return TRUE if folder deleted successfully.
1331 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1332 gboolean retVal = FALSE;
1334 ldapqry_set_aged_flag( qry, TRUE );
1336 if( ldapqry_get_busy_flag( qry ) ) {
1337 ldapqry_set_stop_flag( qry, TRUE );
1340 LdapServer *server = qry->server;
1341 server->listQuery = g_list_remove(server->listQuery, qry);
1348 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1349 g_return_if_fail( qry != NULL );
1351 ldapsvr_print_data(qry->server, stream);
1352 ldapctl_print(qry->control, stream);
1353 fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1354 fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1355 fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1356 fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1357 fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1358 fprintf(stream, "completed: %d\n", qry->completed);
1359 fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1360 fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1361 fprintf(stream, "data: %s\n", qry->data?(gchar *)qry->data:"null");
1364 #endif /* USE_LDAP */