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 ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
118 g_strstrip( ADDRQUERY_NAME(qry) );
122 * Specify search value to be used.
123 * \param qry Query object.
126 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
127 ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
128 g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
129 debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry));
133 * Specify query type.
134 * \param qry Query object.
135 * \param value Query type, either:
137 * <li><code>LDAPQUERY_NONE</code></li>
138 * <li><code>LDAPQUERY_STATIC</code></li>
139 * <li><code>LDAPQUERY_DYNAMIC</code></li>
143 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
144 ADDRQUERY_TYPE(qry) = value;
149 * Specify search type.
150 * \param qry Query object.
153 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
154 g_return_if_fail( qry != NULL );
155 ADDRQUERY_SEARCHTYPE(qry) = value;
160 * \param qry Query object.
161 * \param value ID for the query.
163 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
164 ADDRQUERY_ID(qry) = value;
168 * Register a callback function that will be executed when each entry
169 * has been read and processed. When called, the function will be passed
170 * this query object and a GList of ItemEMail objects as arguments. An
171 * example of typical usage is shown below.
174 * ------------------------------------------------------------
175 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
180 * ItemEMail *email = node->data;
181 * ... process email object ...
182 * node = g_list_next( node );
184 * g_list_free( listEMail );
188 * ldapqry_set_callback_entry( qry, myCallbackEntry );
189 * ------------------------------------------------------------
192 * \param qry Query object.
193 * \param func Function.
195 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
196 pthread_mutex_lock( qry->mutexEntry );
197 qry->callBackEntry = func;
198 pthread_mutex_unlock( qry->mutexEntry );
202 * Register a callback function that will be executed when the search
203 * is complete. When called, the function will be passed this query
204 * object as an argument.
205 * \param qry Query object.
206 * \param func Function.
208 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
209 qry->callBackEnd = func;
213 * Notify query to start/stop executing. This method should be called with a
214 * value if <i>TRUE</i> to terminate an existing running query.
216 * \param qry Query object.
217 * \param value Value of stop flag.
219 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
220 g_return_if_fail( qry != NULL );
222 pthread_mutex_lock( qry->mutexStop );
223 qry->stopFlag = value;
224 pthread_mutex_unlock( qry->mutexStop );
228 * Test value of stop flag. This method should be used to determine whether a
229 * query has stopped running.
230 * \param qry Query object.
231 * \return Value of stop flag.
233 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
235 g_return_val_if_fail( qry != NULL, TRUE );
237 pthread_mutex_lock( qry->mutexStop );
238 value = qry->stopFlag;
239 pthread_mutex_unlock( qry->mutexStop );
245 * \param qry Query object.
246 * \param value Value of busy flag.
248 static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
249 g_return_if_fail( qry != NULL );
250 if (qry->mutexBusy == NULL)
251 return; /* exiting, mutex already freed */
253 pthread_mutex_lock( qry->mutexBusy );
254 qry->busyFlag = value;
255 pthread_mutex_unlock( qry->mutexBusy );
259 * Test value of busy flag. This method will return a value of <i>FALSE</i>
260 * when a query has completed running.
261 * \param qry Query object.
262 * \return Value of busy flag.
264 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
266 g_return_val_if_fail( qry != NULL, FALSE );
268 pthread_mutex_lock( qry->mutexBusy );
269 value = qry->busyFlag;
270 pthread_mutex_unlock( qry->mutexBusy );
275 * Set query aged flag.
276 * \param qry Query object.
277 * \param value Value of aged flag.
279 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
280 g_return_if_fail( qry != NULL );
281 qry->agedFlag = value;
285 * Clear LDAP query member variables.
286 * \param qry Query object.
288 static void ldapqry_clear( LdapQuery *qry ) {
289 g_return_if_fail( qry != NULL );
291 /* Free internal stuff */
292 g_free( ADDRQUERY_NAME(qry) );
293 g_free( ADDRQUERY_SEARCHVALUE(qry) );
295 /* Clear pointers and value */
296 ADDRQUERY_NAME(qry) = NULL;
297 ADDRQUERY_SEARCHVALUE(qry) = NULL;
298 ADDRQUERY_ID(qry) = 0;
299 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
300 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
301 qry->entriesRead = 0;
302 qry->elapsedTime = 0;
303 qry->stopFlag = FALSE;
304 qry->busyFlag = FALSE;
305 qry->agedFlag = FALSE;
306 qry->completed = FALSE;
307 qry->callBackEntry = NULL;
308 qry->callBackEnd = NULL;
314 * Free up LDAP query object by releasing internal memory. Note that
315 * the thread object will be freed by the OS.
316 * \param qry Query object to process.
318 void ldapqry_free( LdapQuery *qry ) {
319 g_return_if_fail( qry != NULL );
321 /* Clear out internal members */
322 ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
323 ldapqry_clear( qry );
326 pthread_mutex_destroy( qry->mutexStop );
327 pthread_mutex_destroy( qry->mutexBusy );
328 pthread_mutex_destroy( qry->mutexEntry );
329 g_free( qry->mutexStop );
330 g_free( qry->mutexBusy );
331 g_free( qry->mutexEntry );
332 qry->mutexEntry = NULL;
333 qry->mutexBusy = NULL;
334 qry->mutexStop = NULL;
336 /* Do not free folder - parent server object should free */
337 ADDRQUERY_FOLDER(qry) = NULL;
339 /* Do not free thread - thread should be terminated before freeing */
342 /* Do not free LDAP control - should be destroyed before freeing */
345 /* Now release object */
350 * Free linked lists of character strings.
351 * \param listName List of common names.
352 * \param listAddr List of addresses.
353 * \param listFirst List of first names.
354 * \param listLast List of last names.
356 static void ldapqry_free_lists(
357 GSList *listName, GSList *listAddr, GSList *listFirst,
358 GSList *listLast, GSList *listDisplay, GSList *other_attrs )
360 GSList *cur = other_attrs;
361 mgu_free_list( listName );
362 mgu_free_list( listAddr );
363 mgu_free_list( listFirst );
364 mgu_free_list( listLast );
365 mgu_free_list( listDisplay );
366 for(;cur; cur = cur->next)
367 addritem_free_attribute((UserAttribute *)cur->data);
368 g_slist_free(other_attrs);
372 * Add all LDAP attribute values to a list.
373 * \param ld LDAP handle.
374 * \param entry LDAP entry to process.
375 * \param attr LDAP attribute.
376 * \return List of values.
378 static GSList *ldapqry_add_list_values(
379 LDAP *ld, LDAPMessage *entry, char *attr )
383 struct berval **vals;
385 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
386 for( i = 0; vals[i] != NULL; i++ ) {
387 /*debug_print("lv\t%s: %s\n", attr, vals[i]->bv_val);*/
388 list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
391 ldap_value_free_len( vals );
396 * Add a single attribute value to a list.
397 * \param ld LDAP handle.
398 * \param entry LDAP entry to process.
399 * \param attr LDAP attribute name to process.
400 * \return List of values; only one value will be present.
402 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
404 struct berval **vals;
406 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
407 if( vals[0] != NULL ) {
408 debug_print("sv\t%s: %s\n", attr, vals[0]->bv_val);
409 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
412 ldap_value_free_len( vals );
417 * Build an address list entry and append to list of address items. Name is formatted
418 * as "<first-name> <last-name>".
420 * \param cache Address cache to load.
421 * \param qry Query object to process.
422 * \param dn DN for entry found on server.
423 * \param listName List of common names for entry; see notes below.
424 * \param listAddr List of EMail addresses for entry.
425 * \param listFirst List of first names for entry.
426 * \param listLast List of last names for entry.
428 * \return List of ItemEMail objects.
431 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
432 * name. For example, a single entry for a person may have more than one
433 * common name, email address, etc.
435 * 2) The DN for the entry is unique for the server.
437 static GList *ldapqry_build_items_fl(
438 AddressCache *cache, LdapQuery *qry, gchar *dn,
439 GSList *listName, GSList *listAddr, GSList *listFirst,
440 GSList *listLast, GSList *listDisplay, GSList *attributes )
442 GSList *nodeAddress, *cur;
443 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
444 gboolean allocated = FALSE;
448 GList *listReturn = NULL;
450 folder = ADDRQUERY_FOLDER(qry);
451 if( folder == NULL ) return listReturn;
452 if( listAddr == NULL ) return listReturn;
456 fullName = listDisplay->data;
459 /* Find longest first name in list */
460 firstName = mgu_slist_longest_entry( listFirst );
462 /* Format last name */
464 lastName = listLast->data;
467 if ( fullName == NULL ) {
468 /* Find longest common name */
470 fullName = mgu_slist_longest_entry( listName );
471 if( fullName == NULL ) {
472 /* Format a full name from first and last names */
475 fullName = g_strdup_printf( "%s %s", firstName, lastName );
478 fullName = g_strdup_printf( "%s", firstName );
483 fullName = g_strdup_printf( "%s", lastName );
487 g_strchug( fullName ); g_strchomp( fullName );
493 /* Add person into folder */
494 person = addritem_create_item_person();
495 addritem_person_set_common_name( person, fullName );
496 addritem_person_set_first_name( person, firstName );
497 addritem_person_set_last_name( person, lastName );
498 addritem_person_set_nick_name( person, fullName );
499 addrcache_id_person( cache, person );
500 addritem_person_set_external_id( person, dn );
502 for (cur = attributes; cur; cur = cur->next) {
503 UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
504 addritem_person_add_attribute( person, attrib );
507 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
511 /* Add each address item */
512 nodeAddress = listAddr;
513 while( nodeAddress ) {
514 email = addritem_create_item_email();
515 addritem_email_set_address( email, nodeAddress->data );
516 addrcache_id_email( cache, email );
517 addrcache_person_add_email( cache, person, email );
518 addritem_person_add_email( person, email );
519 /*if (debug_get_mode()) {
520 addritem_print_item_email(email, stdout);
522 listReturn = g_list_append( listReturn, email );
523 nodeAddress = g_slist_next( nodeAddress );
526 /* Free any allocated memory */
530 fullName = firstName = lastName = NULL;
536 * Process a single search entry.
537 * \param cache Address cache to load.
538 * \param qry Query object to process.
539 * \param ld LDAP handle.
540 * \param e LDAP message.
541 * \return List of EMail objects found.
543 static GList *ldapqry_process_single_entry(
544 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
550 GSList *listName = NULL, *listAddress = NULL;
551 GSList *listFirst = NULL, *listLast = NULL;
552 GSList *listDisplay = NULL;
553 GSList *other_attrs = NULL;
558 dnEntry = ldap_get_dn( ld, e );
559 debug_print( "DN: %s\n", dnEntry );
561 /* Process all attributes */
562 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
563 attribute = ldap_next_attribute( ld, e, ber ) ) {
564 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
565 listAddress = ldapqry_add_list_values( ld, e, attribute );
567 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
568 listName = ldapqry_add_list_values( ld, e, attribute );
570 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
571 listFirst = ldapqry_add_list_values( ld, e, attribute );
573 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
574 listLast = ldapqry_add_single_value( ld, e, attribute );
575 } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
576 listDisplay = ldapqry_add_single_value( ld, e, attribute );
578 GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
579 UserAttribute *attrib = addritem_create_attribute();
580 const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
582 addritem_attrib_set_name( attrib, attribute );
583 addritem_attrib_set_value( attrib, attvalue );
584 other_attrs = g_slist_prepend(other_attrs, attrib);
586 mgu_free_list(attlist);
588 /* Free memory used to store attribute */
589 ldap_memfree( attribute );
592 /* Format and add items to cache */
593 listReturn = ldapqry_build_items_fl(
594 cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
597 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
598 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
609 * Check parameters that are required for a search. This should
610 * be called before performing a search.
611 * \param qry Query object to process.
612 * \return <i>TRUE</i> if search criteria appear OK.
614 gboolean ldapqry_check_search( LdapQuery *qry ) {
616 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
618 /* Test for control data */
624 /* Test for search value */
625 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
628 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
631 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
636 * Touch the query. This nudges the touch time with the current time.
637 * \param qry Query object to process.
639 void ldapqry_touch( LdapQuery *qry ) {
640 qry->touchTime = time( NULL );
641 qry->agedFlag = FALSE;
645 * Connect to LDAP server.
646 * \param qry Query object to process.
647 * \return Error/status code.
649 static gint ldapqry_connect( LdapQuery *qry ) {
656 /* Initialize connection */
657 if (debug_get_mode()) {
658 debug_print("===ldapqry_connect===\n");
659 /*ldapqry_print(qry, stdout);*/
662 /*if (debug_get_mode()) {
663 ldapctl_print(ctl, stdout);
664 debug_print("======\n");
666 ldapqry_touch( qry );
667 qry->startTime = qry->touchTime;
668 qry->elapsedTime = -1;
669 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
671 uri = g_strdup_printf("ldap%s://%s:%d",
672 ctl->enableSSL?"s":"",
673 ctl->hostName, ctl->port);
674 ldap_initialize(&ld, uri);
678 return ADDRQUERY_RETVAL(qry);
681 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
682 if( ldapqry_get_stop_flag( qry ) ) {
683 return ADDRQUERY_RETVAL(qry);
685 ldapqry_touch( qry );
687 debug_print("connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port);
691 version = LDAP_VERSION3;
692 rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
693 if( rc == LDAP_OPT_SUCCESS ) {
694 ctl->version = LDAP_VERSION3;
697 if( ctl->version == LDAP_VERSION3 ) {
698 if( ctl->enableTLS && !ctl->enableSSL ) {
699 ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
700 rc = ldap_start_tls_s( ld, NULL, NULL );
702 debug_print("rc=%d\n", rc);
703 debug_print("LDAP Status: set_option: %s\n", ldap_err2string( rc ) );
705 if( rc != LDAP_SUCCESS ) {
706 return ADDRQUERY_RETVAL(qry);
712 /* Bind to the server, if required */
713 ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
715 if( * ctl->bindDN != '\0' ) {
716 debug_print("binding...\n");
717 rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
718 debug_print("rc=%d\n", rc);
719 if( rc != LDAP_SUCCESS ) {
720 debug_print("LDAP Error: ldap_simple_bind_s: %s\n", ldap_err2string(rc));
721 return ADDRQUERY_RETVAL(qry);
725 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
726 if( ldapqry_get_stop_flag( qry ) ) {
727 return ADDRQUERY_RETVAL(qry);
729 ldapqry_touch( qry );
731 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
733 return ADDRQUERY_RETVAL(qry);
737 * Connect to LDAP server.
738 * \param qry Query object to process.
739 * \return Error/status code.
741 static gint ldapqry_disconnect( LdapQuery *qry ) {
743 if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
746 ldapqry_touch( qry );
747 qry->elapsedTime = qry->touchTime - qry->startTime;
749 return ADDRQUERY_RETVAL(qry);
753 * Perform the LDAP search, reading LDAP entries into cache.
754 * Note that one LDAP entry can have multiple values for many of its
755 * attributes. If these attributes are E-Mail addresses; these are
756 * broken out into separate address items. For any other attribute,
757 * only the first occurrence is read.
759 * \param qry Query object to process.
760 * \return Error/status code.
762 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
765 LDAPMessage *result, *e = NULL;
769 gboolean entriesFound;
771 struct timeval timeout;
776 /* Initialize some variables */
779 cache = qry->server->addressCache;
780 timeout.tv_sec = ctl->timeOut;
781 timeout.tv_usec = 0L;
782 entriesFound = FALSE;
783 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
785 /* Define all attributes we are interested in. */
786 attribs = ldapctl_full_attribute_array( ctl );
788 /* Create LDAP search string */
789 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
790 debug_print("Search criteria ::%s::\n", criteria);
793 * Execute the search - this step may take some time to complete
794 * depending on network traffic and server response time.
796 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
797 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
798 attribs, 0, NULL, NULL, &timeout, 0, &result );
799 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
800 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
801 ldapctl_free_attribute_array( attribs );
804 if( rc == LDAP_TIMEOUT ) {
805 return ADDRQUERY_RETVAL(qry);
807 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
809 /* Test valid returns */
811 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
814 else if( rc == LDAP_SUCCESS ) {
817 else if( rc == LDAP_PARTIAL_RESULTS ) {
821 debug_print("LDAP Error: ldap_search_st: %d\n", rc);
822 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
823 return ADDRQUERY_RETVAL(qry);
825 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
827 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
829 /* Process results */
831 while( searchFlag ) {
832 ldapqry_touch( qry );
833 if( qry->entriesRead >= ctl->maxEntries ) break;
836 if( ldapqry_get_stop_flag( qry ) ) {
843 e = ldap_first_entry( ld, result );
846 e = ldap_next_entry( ld, e );
848 if( e == NULL ) break;
851 /* Setup a critical section here */
852 pthread_mutex_lock( qry->mutexEntry );
855 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
857 /* Process callback */
858 if( qry->callBackEntry ) {
859 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
862 /*if (debug_get_mode()) {
863 GList *node = listEMail;
865 addritem_print_item_email(node->data, stdout);
866 node = g_list_next(node);
869 g_list_free( listEMail );
871 pthread_mutex_unlock( qry->mutexEntry );
874 /* Free up and disconnect */
875 ldap_msgfree( result );
879 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
882 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
886 return ADDRQUERY_RETVAL(qry);
890 * Connection, perform search and disconnect.
891 * \param qry Query object to process.
892 * \return Error/status code.
894 static gint ldapqry_perform_search( LdapQuery *qry ) {
895 /* Check search criteria */
896 if( ! ldapqry_check_search( qry ) ) {
897 return ADDRQUERY_RETVAL(qry);
902 ldapqry_connect( qry );
903 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
905 ldapqry_search_retrieve( qry );
908 ldapqry_disconnect( qry );
911 return ADDRQUERY_RETVAL(qry);
914 static gint ldapqry_perform_locate( LdapQuery *qry );
917 * Wrapper around search.
918 * \param qry Query object to process.
919 * \return Error/status code.
921 static gint ldapqry_search( LdapQuery *qry ) {
924 g_return_val_if_fail( qry != NULL, -1 );
925 g_return_val_if_fail( qry->control != NULL, -1 );
927 ldapqry_touch( qry );
928 qry->completed = FALSE;
930 /* Setup pointer to thread specific area */
931 pthread_setspecific( _queryThreadKey_, qry );
933 pthread_detach( pthread_self() );
935 /* Now perform the search */
936 qry->entriesRead = 0;
937 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
938 ldapqry_set_busy_flag( qry, TRUE );
939 ldapqry_set_stop_flag( qry, FALSE );
940 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
941 retVal = ldapqry_perform_locate( qry );
944 retVal = ldapqry_perform_search( qry );
946 if( retVal == LDAPRC_SUCCESS ) {
947 qry->server->addressCache->dataRead = TRUE;
948 qry->server->addressCache->accessFlag = FALSE;
949 if( ldapqry_get_stop_flag( qry ) ) {
950 debug_print("Search was terminated prematurely\n");
953 ldapqry_touch( qry );
954 qry->completed = TRUE;
955 debug_print("Search ran to completion\n");
958 ldapqry_set_stop_flag( qry, TRUE );
959 ldapqry_set_busy_flag( qry, FALSE );
961 /* Process callback */
962 if( qry->callBackEnd ) {
963 g_timeout_add(0, callbackend, qry);
966 return ADDRQUERY_RETVAL(qry);
970 * Read data into list using a background thread. Callback function will be
971 * notified when search is complete.
972 * \param qry Query object to process.
973 * \return Error/status code.
975 gint ldapqry_read_data_th( LdapQuery *qry ) {
976 g_return_val_if_fail( qry != NULL, -1 );
977 g_return_val_if_fail( qry->control != NULL, -1 );
979 ldapqry_set_stop_flag( qry, FALSE );
980 ldapqry_touch( qry );
981 if( ldapqry_check_search( qry ) ) {
982 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
983 debug_print("Starting LDAP search thread\n");
984 ldapqry_set_busy_flag( qry, TRUE );
985 qry->thread = g_malloc0( sizeof( pthread_t ) );
988 pthread_create( qry->thread, NULL,
989 (void *) ldapqry_search, (void *) qry );
992 return ADDRQUERY_RETVAL(qry);
996 * Cleanup LDAP thread data. This function will be called when each thread
997 * exits. Note that the thread object will be freed by the kernel.
998 * \param ptr Pointer to object being destroyed (a query object in this case).
1000 static void ldapqry_destroyer( void * ptr ) {
1003 qry = ( LdapQuery * ) ptr;
1004 debug_print("ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry));
1006 /* Perform any destruction here */
1007 if( qry->control != NULL ) {
1008 ldapctl_free( qry->control );
1010 qry->control = NULL;
1012 ldapqry_set_busy_flag( qry, FALSE );
1016 * Cancel thread associated with query.
1017 * \param qry Query object to process.
1019 void ldapqry_cancel( LdapQuery *qry ) {
1020 g_return_if_fail( qry != NULL );
1022 debug_print("cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry));
1023 if( ldapqry_get_busy_flag( qry ) ) {
1025 debug_print("calling pthread_cancel\n");
1026 pthread_cancel( * qry->thread );
1032 * Initialize LDAP query. This function should be called once before executing
1033 * any LDAP queries to initialize thread specific data.
1035 void ldapqry_initialize( void ) {
1036 debug_print("ldapqry_initialize...\n");
1037 if( ! _queryThreadInit_ ) {
1038 debug_print("ldapqry_initialize::creating thread specific area\n");
1039 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1040 _queryThreadInit_ = TRUE;
1042 debug_print("ldapqry_initialize... done!\n");
1046 * Age the query based on LDAP control parameters.
1047 * \param qry Query object to process.
1048 * \param maxAge Maximum age of query (in seconds).
1050 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1053 g_return_if_fail( qry != NULL );
1055 /* Limit the time that queries can hang around */
1056 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1058 /* Check age of query */
1059 age = time( NULL ) - qry->touchTime;
1060 if( age > maxAge ) {
1061 qry->agedFlag = TRUE;
1066 * Delete folder associated with query results.
1067 * \param qry Query object to process.
1069 void ldapqry_delete_folder( LdapQuery *qry ) {
1070 AddressCache *cache;
1073 g_return_if_fail( qry != NULL );
1075 folder = ADDRQUERY_FOLDER(qry);
1077 cache = qry->server->addressCache;
1078 folder = addrcache_remove_folder_delete( cache, folder );
1080 addritem_free_item_folder( folder );
1082 ADDRQUERY_FOLDER(qry) = NULL;
1087 * Create a name/value pair object.
1090 * \return Initialized object.
1092 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1093 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1095 nvp->name = g_strdup( n );
1096 nvp->value = g_strdup( v );
1101 * Free up name/value pair object.
1102 * \param nvp Name/value object.
1104 void ldapqry_free_name_value( NameValuePair *nvp ) {
1106 g_free( nvp->name );
1107 g_free( nvp->value );
1108 nvp->name = nvp->value = NULL;
1114 * Free up a list name/value pair objects.
1115 * \param list List of name/value objects.
1117 void ldapqry_free_list_name_value( GList *list ) {
1122 NameValuePair *nvp = ( NameValuePair * ) node->data;
1123 ldapqry_free_name_value( nvp );
1125 node = g_list_next( node );
1127 g_list_free( list );
1131 * Load a list of name/value pairs from LDAP attributes.
1132 * \param ld LDAP handle.
1133 * \param e LDAP message.
1134 * \param attr Attribute name.
1135 * \param listValues List to populate.
1136 * \return List of attribute name/value pairs.
1138 static GList *ldapqry_load_attrib_values(
1139 LDAP *ld, LDAPMessage *entry, char *attr,
1144 struct berval **vals;
1148 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1149 for( i = 0; vals[i] != NULL; i++ ) {
1150 gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1151 nvp = ldapqry_create_name_value( attr, tmp );
1153 list = g_list_append( list, nvp );
1156 ldap_value_free_len( vals );
1161 * Fetch a list of all attributes.
1162 * \param ld LDAP handle.
1163 * \param e LDAP message.
1164 * \return List of attribute name/value pairs.
1166 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1170 GList *listValues = NULL;
1172 /* Process all attributes */
1173 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1174 attribute = ldap_next_attribute( ld, e, ber ) ) {
1175 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1176 ldap_memfree( attribute );
1186 #define CRITERIA_SINGLE "(objectclass=*)"
1189 * Perform the data retrieval for a specific LDAP record.
1191 * \param qry Query object to process.
1192 * \return Error/status code.
1194 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1197 LDAPMessage *result, *e = NULL;
1198 gboolean entriesFound;
1200 struct timeval timeout;
1205 /* Initialize some variables */
1208 dn = ADDRQUERY_SEARCHVALUE(qry);
1209 timeout.tv_sec = ctl->timeOut;
1210 timeout.tv_usec = 0L;
1211 entriesFound = FALSE;
1214 * Execute the search - this step may take some time to complete
1215 * depending on network traffic and server response time.
1217 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1218 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1219 NULL, 0, NULL, NULL, &timeout, 0, &result );
1220 if( rc == LDAP_TIMEOUT ) {
1221 return ADDRQUERY_RETVAL(qry);
1223 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1224 if( rc != LDAP_SUCCESS ) {
1225 debug_print("LDAP Error: ldap_search_st: %s\n", ldap_err2string(rc));
1226 return ADDRQUERY_RETVAL(qry);
1229 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1231 /* Process results */
1232 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1235 ldapqry_touch( qry );
1236 if( qry->entriesRead >= ctl->maxEntries ) break;
1239 if( ldapqry_get_stop_flag( qry ) ) {
1243 /* Retrieve entry */
1246 e = ldap_first_entry( ld, result );
1249 e = ldap_next_entry( ld, e );
1251 if( e == NULL ) break;
1253 entriesFound = TRUE;
1255 /* Setup a critical section here */
1256 pthread_mutex_lock( qry->mutexEntry );
1259 listValues = ldapqry_fetch_attribs( ld, e );
1261 /* Process callback */
1262 if( qry->callBackEntry ) {
1263 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1265 ldapqry_free_list_name_value( listValues );
1268 pthread_mutex_unlock( qry->mutexEntry );
1271 /* Free up and disconnect */
1272 ldap_msgfree( result );
1274 if( entriesFound ) {
1275 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1278 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1281 return ADDRQUERY_RETVAL(qry);
1285 * Perform the search to locate a specific LDAP record identified by
1286 * distinguished name (dn).
1288 * \param qry Query object to process.
1289 * \return Error/status code.
1291 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1294 ldapqry_connect( qry );
1295 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1296 /* Perform search */
1297 ldapqry_locate_retrieve( qry );
1300 ldapqry_disconnect( qry );
1303 /* Process callback */
1304 if( qry->callBackEnd ) {
1305 g_timeout_add(0, callbackend, qry);
1308 return ADDRQUERY_RETVAL(qry);
1312 * Remove results (folder and data) for specified LDAP query.
1313 * \param qry Query object to process.
1314 * \return TRUE if folder deleted successfully.
1316 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1317 gboolean retVal = FALSE;
1319 ldapqry_set_aged_flag( qry, TRUE );
1321 if( ldapqry_get_busy_flag( qry ) ) {
1322 ldapqry_set_stop_flag( qry, TRUE );
1325 LdapServer *server = qry->server;
1326 server->listQuery = g_list_remove(server->listQuery, qry);
1333 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1334 ldapsvr_print_data(qry->server, stream);
1335 ldapctl_print(qry->control, stream);
1336 fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1337 fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1338 fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1339 fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1340 fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1341 fprintf(stream, "completed: %d\n", qry->completed);
1342 fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1343 fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1344 fprintf(stream, "data: %s\n", (gchar *) qry->data);
1347 #endif /* USE_LDAP */