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"
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,
356 GSList *listLast, GSList *listDisplay, GSList *other_attrs )
358 GSList *cur = other_attrs;
359 mgu_free_list( listName );
360 mgu_free_list( listAddr );
361 mgu_free_list( listFirst );
362 mgu_free_list( listLast );
363 mgu_free_list( listDisplay );
364 for(;cur; cur = cur->next)
365 addritem_free_attribute((UserAttribute *)cur->data);
366 g_slist_free(other_attrs);
370 * Add all LDAP attribute values to a list.
371 * \param ld LDAP handle.
372 * \param entry LDAP entry to process.
373 * \param attr LDAP attribute.
374 * \return List of values.
376 static GSList *ldapqry_add_list_values(
377 LDAP *ld, LDAPMessage *entry, char *attr )
381 struct berval **vals;
383 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
384 for( i = 0; vals[i] != NULL; i++ ) {
385 /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
386 list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
389 ldap_value_free_len( vals );
394 * Add a single attribute value to a list.
395 * \param ld LDAP handle.
396 * \param entry LDAP entry to process.
397 * \param attr LDAP attribute name to process.
398 * \return List of values; only one value will be present.
400 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
402 struct berval **vals;
404 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
405 if( vals[0] != NULL ) {
406 /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
407 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
410 ldap_value_free_len( vals );
415 * Build an address list entry and append to list of address items. Name is formatted
416 * as "<first-name> <last-name>".
418 * \param cache Address cache to load.
419 * \param qry Query object to process.
420 * \param dn DN for entry found on server.
421 * \param listName List of common names for entry; see notes below.
422 * \param listAddr List of EMail addresses for entry.
423 * \param listFirst List of first names for entry.
424 * \param listLast List of last names for entry.
426 * \return List of ItemEMail objects.
429 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
430 * name. For example, a single entry for a person may have more than one
431 * common name, email address, etc.
433 * 2) The DN for the entry is unique for the server.
435 static GList *ldapqry_build_items_fl(
436 AddressCache *cache, LdapQuery *qry, gchar *dn,
437 GSList *listName, GSList *listAddr, GSList *listFirst,
438 GSList *listLast, GSList *listDisplay, GSList *attributes )
440 GSList *nodeAddress, *cur;
441 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
442 gboolean allocated = FALSE;
446 GList *listReturn = NULL;
448 folder = ADDRQUERY_FOLDER(qry);
449 if( folder == NULL ) return listReturn;
450 if( listAddr == NULL ) return listReturn;
454 fullName = listDisplay->data;
457 /* Find longest first name in list */
458 firstName = mgu_slist_longest_entry( listFirst );
460 /* Format last name */
462 lastName = listLast->data;
465 if ( fullName == NULL ) {
466 /* Find longest common name */
468 fullName = mgu_slist_longest_entry( listName );
469 if( fullName == NULL ) {
470 /* Format a full name from first and last names */
473 fullName = g_strdup_printf( "%s %s", firstName, lastName );
476 fullName = g_strdup_printf( "%s", firstName );
481 fullName = g_strdup_printf( "%s", lastName );
485 g_strchug( fullName ); g_strchomp( fullName );
491 /* Add person into folder */
492 person = addritem_create_item_person();
493 addritem_person_set_common_name( person, fullName );
494 addritem_person_set_first_name( person, firstName );
495 addritem_person_set_last_name( person, lastName );
496 addritem_person_set_nick_name( person, fullName );
497 addrcache_id_person( cache, person );
498 addritem_person_set_external_id( person, dn );
500 for (cur = attributes; cur; cur = cur->next) {
501 UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
502 addritem_person_add_attribute( person, attrib );
505 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
509 /* Add each address item */
510 nodeAddress = listAddr;
511 while( nodeAddress ) {
512 email = addritem_create_item_email();
513 addritem_email_set_address( email, nodeAddress->data );
514 addrcache_id_email( cache, email );
515 addrcache_person_add_email( cache, person, email );
516 addritem_person_add_email( person, email );
517 /*addritem_print_item_email(email, stdout);*/
518 listReturn = g_list_append( listReturn, email );
519 nodeAddress = g_slist_next( nodeAddress );
522 /* Free any allocated memory */
526 fullName = firstName = lastName = NULL;
532 * Process a single search entry.
533 * \param cache Address cache to load.
534 * \param qry Query object to process.
535 * \param ld LDAP handle.
536 * \param e LDAP message.
537 * \return List of EMail objects found.
539 static GList *ldapqry_process_single_entry(
540 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
546 GSList *listName = NULL, *listAddress = NULL;
547 GSList *listFirst = NULL, *listLast = NULL;
548 GSList *listDisplay = NULL;
549 GSList *other_attrs = NULL;
554 dnEntry = ldap_get_dn( ld, e );
555 /* printf( "DN: %s\n", dnEntry ); */
557 /* Process all attributes */
558 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
559 attribute = ldap_next_attribute( ld, e, ber ) ) {
560 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
561 listAddress = ldapqry_add_list_values( ld, e, attribute );
563 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
564 listName = ldapqry_add_list_values( ld, e, attribute );
566 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
567 listFirst = ldapqry_add_list_values( ld, e, attribute );
569 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
570 listLast = ldapqry_add_single_value( ld, e, attribute );
571 } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
572 listDisplay = ldapqry_add_single_value( ld, e, attribute );
574 GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
575 UserAttribute *attrib = addritem_create_attribute();
576 const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
578 addritem_attrib_set_name( attrib, attribute );
579 addritem_attrib_set_value( attrib, attvalue );
580 other_attrs = g_slist_prepend(other_attrs, attrib);
582 mgu_free_list(attlist);
584 /* Free memory used to store attribute */
585 ldap_memfree( attribute );
588 /* Format and add items to cache */
589 listReturn = ldapqry_build_items_fl(
590 cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
593 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
594 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
605 * Check parameters that are required for a search. This should
606 * be called before performing a search.
607 * \param qry Query object to process.
608 * \return <i>TRUE</i> if search criteria appear OK.
610 gboolean ldapqry_check_search( LdapQuery *qry ) {
612 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
614 /* Test for control data */
620 /* Test for search value */
621 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
624 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
627 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
632 * Touch the query. This nudges the touch time with the current time.
633 * \param qry Query object to process.
635 void ldapqry_touch( LdapQuery *qry ) {
636 qry->touchTime = time( NULL );
637 qry->agedFlag = FALSE;
641 * Connect to LDAP server.
642 * \param qry Query object to process.
643 * \return Error/status code.
645 static gint ldapqry_connect( LdapQuery *qry ) {
652 /* Initialize connection */
653 /* printf( "===ldapqry_connect===\n" ); */
654 /* ldapqry_print( qry, stdout ); */
656 /* ldapctl_print( ctl, stdout ); */
657 /* printf( "======\n" ); */
658 ldapqry_touch( qry );
659 qry->startTime = qry->touchTime;
660 qry->elapsedTime = -1;
661 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
663 uri = g_strdup_printf("ldap%s://%s:%d",
664 ctl->enableSSL?"s":"",
665 ctl->hostName, ctl->port);
666 ldap_initialize(&ld, uri);
670 return ADDRQUERY_RETVAL(qry);
673 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
674 if( ldapqry_get_stop_flag( qry ) ) {
675 return ADDRQUERY_RETVAL(qry);
677 ldapqry_touch( qry );
680 printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
685 version = LDAP_VERSION3;
686 rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
687 if( rc == LDAP_OPT_SUCCESS ) {
688 ctl->version = LDAP_VERSION3;
691 if( ctl->version == LDAP_VERSION3 ) {
692 if( ctl->enableTLS && !ctl->enableSSL ) {
693 ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
694 rc = ldap_start_tls_s( ld, NULL, NULL );
696 /* printf( "rc=%d\n", rc );
697 printf( "LDAP Status: set_option: %s\n", ldap_err2string( rc ) ); */
699 if( rc != LDAP_SUCCESS ) {
700 return ADDRQUERY_RETVAL(qry);
706 /* Bind to the server, if required */
707 ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
709 if( * ctl->bindDN != '\0' ) {
710 /* printf( "binding...\n" ); */
711 rc = claws_ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
712 /* printf( "rc=%d\n", rc ); */
713 if( rc != LDAP_SUCCESS ) {
715 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
716 ldap_err2string( rc ) );
718 return ADDRQUERY_RETVAL(qry);
722 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
723 if( ldapqry_get_stop_flag( qry ) ) {
724 return ADDRQUERY_RETVAL(qry);
726 ldapqry_touch( qry );
728 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
730 return ADDRQUERY_RETVAL(qry);
734 * Connect to LDAP server.
735 * \param qry Query object to process.
736 * \return Error/status code.
738 static gint ldapqry_disconnect( LdapQuery *qry ) {
740 if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
743 ldapqry_touch( qry );
744 qry->elapsedTime = qry->touchTime - qry->startTime;
746 return ADDRQUERY_RETVAL(qry);
750 * Perform the LDAP search, reading LDAP entries into cache.
751 * Note that one LDAP entry can have multiple values for many of its
752 * attributes. If these attributes are E-Mail addresses; these are
753 * broken out into separate address items. For any other attribute,
754 * only the first occurrence is read.
756 * \param qry Query object to process.
757 * \return Error/status code.
759 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
762 LDAPMessage *result, *e = NULL;
766 gboolean entriesFound;
768 struct timeval timeout;
773 /* Initialize some variables */
776 cache = qry->server->addressCache;
777 timeout.tv_sec = ctl->timeOut;
778 timeout.tv_usec = 0L;
779 entriesFound = FALSE;
780 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
782 /* Define all attributes we are interested in. */
783 attribs = ldapctl_attribute_array( ctl );
785 /* Create LDAP search string */
786 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
787 /* printf( "Search criteria ::%s::\n", criteria ); */
790 * Execute the search - this step may take some time to complete
791 * depending on network traffic and server response time.
793 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
794 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
795 attribs, 0, NULL, NULL, &timeout, 0, &result );
796 ldapctl_free_attribute_array( attribs );
799 if( rc == LDAP_TIMEOUT ) {
800 return ADDRQUERY_RETVAL(qry);
802 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
804 /* Test valid returns */
806 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
809 else if( rc == LDAP_SUCCESS ) {
812 else if( rc == LDAP_PARTIAL_RESULTS ) {
817 printf( "LDAP Error: ldap_search_st: %d\n", rc );
818 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
820 return ADDRQUERY_RETVAL(qry);
822 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
825 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
828 /* Process results */
830 while( searchFlag ) {
831 ldapqry_touch( qry );
832 if( qry->entriesRead >= ctl->maxEntries ) break;
835 if( ldapqry_get_stop_flag( qry ) ) {
842 e = ldap_first_entry( ld, result );
845 e = ldap_next_entry( ld, e );
847 if( e == NULL ) break;
850 /* Setup a critical section here */
851 pthread_mutex_lock( qry->mutexEntry );
854 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
856 /* Process callback */
857 if( qry->callBackEntry ) {
858 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
861 /*GList *node = listEMail;
863 addritem_print_item_email(node->data, stdout);
864 node = g_list_next(node);
866 g_list_free( listEMail );
868 pthread_mutex_unlock( qry->mutexEntry );
871 /* Free up and disconnect */
872 ldap_msgfree( result );
876 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
879 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
883 return ADDRQUERY_RETVAL(qry);
887 * Connection, perform search and disconnect.
888 * \param qry Query object to process.
889 * \return Error/status code.
891 static gint ldapqry_perform_search( LdapQuery *qry ) {
892 /* Check search criteria */
893 if( ! ldapqry_check_search( qry ) ) {
894 return ADDRQUERY_RETVAL(qry);
899 ldapqry_connect( qry );
900 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
902 ldapqry_search_retrieve( qry );
905 ldapqry_disconnect( qry );
908 return ADDRQUERY_RETVAL(qry);
911 static gint ldapqry_perform_locate( LdapQuery *qry );
914 * Wrapper around search.
915 * \param qry Query object to process.
916 * \return Error/status code.
918 static gint ldapqry_search( LdapQuery *qry ) {
921 g_return_val_if_fail( qry != NULL, -1 );
922 g_return_val_if_fail( qry->control != NULL, -1 );
924 ldapqry_touch( qry );
925 qry->completed = FALSE;
927 /* Setup pointer to thread specific area */
928 pthread_setspecific( _queryThreadKey_, qry );
930 pthread_detach( pthread_self() );
932 /* Now perform the search */
933 qry->entriesRead = 0;
934 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
935 ldapqry_set_busy_flag( qry, TRUE );
936 ldapqry_set_stop_flag( qry, FALSE );
937 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
938 retVal = ldapqry_perform_locate( qry );
941 retVal = ldapqry_perform_search( qry );
943 if( retVal == LDAPRC_SUCCESS ) {
944 qry->server->addressCache->dataRead = TRUE;
945 qry->server->addressCache->accessFlag = FALSE;
946 if( ldapqry_get_stop_flag( qry ) ) {
948 printf( "Search was terminated prematurely\n" );
952 ldapqry_touch( qry );
953 qry->completed = TRUE;
955 printf( "Search ran to completion\n" );
959 ldapqry_set_stop_flag( qry, TRUE );
960 ldapqry_set_busy_flag( qry, FALSE );
962 /* Process callback */
963 if( qry->callBackEnd ) {
964 g_timeout_add(0, callbackend, qry);
967 return ADDRQUERY_RETVAL(qry);
971 * Read data into list using a background thread. Callback function will be
972 * notified when search is complete.
973 * \param qry Query object to process.
974 * \return Error/status code.
976 gint ldapqry_read_data_th( LdapQuery *qry ) {
977 g_return_val_if_fail( qry != NULL, -1 );
978 g_return_val_if_fail( qry->control != NULL, -1 );
980 ldapqry_set_stop_flag( qry, FALSE );
981 ldapqry_touch( qry );
982 if( ldapqry_check_search( qry ) ) {
983 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
985 printf( "Starting LDAP search thread\n");
987 ldapqry_set_busy_flag( qry, TRUE );
988 qry->thread = g_malloc0( sizeof( pthread_t ) );
991 pthread_create( qry->thread, NULL,
992 (void *) ldapqry_search, (void *) qry );
995 return ADDRQUERY_RETVAL(qry);
999 * Cleanup LDAP thread data. This function will be called when each thread
1000 * exits. Note that the thread object will be freed by the kernel.
1001 * \param ptr Pointer to object being destroyed (a query object in this case).
1003 static void ldapqry_destroyer( void * ptr ) {
1006 qry = ( LdapQuery * ) ptr;
1008 printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1011 /* Perform any destruction here */
1012 if( qry->control != NULL ) {
1013 ldapctl_free( qry->control );
1015 qry->control = NULL;
1017 ldapqry_set_busy_flag( qry, FALSE );
1021 * Cancel thread associated with query.
1022 * \param qry Query object to process.
1024 void ldapqry_cancel( LdapQuery *qry ) {
1025 g_return_if_fail( qry != NULL );
1028 printf( "cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1030 if( ldapqry_get_busy_flag( qry ) ) {
1032 /* printf( "calling pthread_cancel\n" ); */
1033 pthread_cancel( * qry->thread );
1039 * Initialize LDAP query. This function should be called once before executing
1040 * any LDAP queries to initialize thread specific data.
1042 void ldapqry_initialize( void ) {
1043 /* printf( "ldapqry_initialize...\n" ); */
1044 if( ! _queryThreadInit_ ) {
1046 printf( "ldapqry_initialize::creating thread specific area\n" );
1048 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1049 _queryThreadInit_ = TRUE;
1051 /* printf( "ldapqry_initialize... done!\n" ); */
1055 * Age the query based on LDAP control parameters.
1056 * \param qry Query object to process.
1057 * \param maxAge Maximum age of query (in seconds).
1059 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1062 g_return_if_fail( qry != NULL );
1064 /* Limit the time that queries can hang around */
1065 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1067 /* Check age of query */
1068 age = time( NULL ) - qry->touchTime;
1069 if( age > maxAge ) {
1070 qry->agedFlag = TRUE;
1075 * Delete folder associated with query results.
1076 * \param qry Query object to process.
1078 void ldapqry_delete_folder( LdapQuery *qry ) {
1079 AddressCache *cache;
1082 g_return_if_fail( qry != NULL );
1084 folder = ADDRQUERY_FOLDER(qry);
1086 cache = qry->server->addressCache;
1087 folder = addrcache_remove_folder_delete( cache, folder );
1089 addritem_free_item_folder( folder );
1091 ADDRQUERY_FOLDER(qry) = NULL;
1096 * Create a name/value pair object.
1099 * \return Initialized object.
1101 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1102 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1104 nvp->name = g_strdup( n );
1105 nvp->value = g_strdup( v );
1110 * Free up name/value pair object.
1111 * \param nvp Name/value object.
1113 void ldapqry_free_name_value( NameValuePair *nvp ) {
1115 g_free( nvp->name );
1116 g_free( nvp->value );
1117 nvp->name = nvp->value = NULL;
1123 * Free up a list name/value pair objects.
1124 * \param list List of name/value objects.
1126 void ldapqry_free_list_name_value( GList *list ) {
1131 NameValuePair *nvp = ( NameValuePair * ) node->data;
1132 ldapqry_free_name_value( nvp );
1134 node = g_list_next( node );
1136 g_list_free( list );
1140 * Load a list of name/value pairs from LDAP attributes.
1141 * \param ld LDAP handle.
1142 * \param e LDAP message.
1143 * \param attr Attribute name.
1144 * \param listValues List to populate.
1145 * \return List of attribute name/value pairs.
1147 static GList *ldapqry_load_attrib_values(
1148 LDAP *ld, LDAPMessage *entry, char *attr,
1153 struct berval **vals;
1157 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1158 for( i = 0; vals[i] != NULL; i++ ) {
1159 gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1160 nvp = ldapqry_create_name_value( attr, tmp );
1162 list = g_list_append( list, nvp );
1165 ldap_value_free_len( vals );
1170 * Fetch a list of all attributes.
1171 * \param ld LDAP handle.
1172 * \param e LDAP message.
1173 * \return List of attribute name/value pairs.
1175 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1179 GList *listValues = NULL;
1181 /* Process all attributes */
1182 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1183 attribute = ldap_next_attribute( ld, e, ber ) ) {
1184 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1185 ldap_memfree( attribute );
1195 #define CRITERIA_SINGLE "(objectclass=*)"
1198 * Perform the data retrieval for a specific LDAP record.
1200 * \param qry Query object to process.
1201 * \return Error/status code.
1203 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1206 LDAPMessage *result, *e = NULL;
1207 gboolean entriesFound;
1209 struct timeval timeout;
1214 /* Initialize some variables */
1217 dn = ADDRQUERY_SEARCHVALUE(qry);
1218 timeout.tv_sec = ctl->timeOut;
1219 timeout.tv_usec = 0L;
1220 entriesFound = FALSE;
1223 * Execute the search - this step may take some time to complete
1224 * depending on network traffic and server response time.
1226 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1227 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1228 NULL, 0, NULL, NULL, &timeout, 0, &result );
1229 if( rc == LDAP_TIMEOUT ) {
1230 return ADDRQUERY_RETVAL(qry);
1232 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1233 if( rc != LDAP_SUCCESS ) {
1235 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
1237 return ADDRQUERY_RETVAL(qry);
1241 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
1244 /* Process results */
1245 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1248 ldapqry_touch( qry );
1249 if( qry->entriesRead >= ctl->maxEntries ) break;
1252 if( ldapqry_get_stop_flag( qry ) ) {
1256 /* Retrieve entry */
1259 e = ldap_first_entry( ld, result );
1262 e = ldap_next_entry( ld, e );
1264 if( e == NULL ) break;
1266 entriesFound = TRUE;
1268 /* Setup a critical section here */
1269 pthread_mutex_lock( qry->mutexEntry );
1272 listValues = ldapqry_fetch_attribs( ld, e );
1274 /* Process callback */
1275 if( qry->callBackEntry ) {
1276 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1278 ldapqry_free_list_name_value( listValues );
1281 pthread_mutex_unlock( qry->mutexEntry );
1284 /* Free up and disconnect */
1285 ldap_msgfree( result );
1287 if( entriesFound ) {
1288 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1291 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1294 return ADDRQUERY_RETVAL(qry);
1298 * Perform the search to locate a specific LDAP record identified by
1299 * distinguished name (dn).
1301 * \param qry Query object to process.
1302 * \return Error/status code.
1304 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1307 ldapqry_connect( qry );
1308 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1309 /* Perform search */
1310 ldapqry_locate_retrieve( qry );
1313 ldapqry_disconnect( qry );
1316 /* Process callback */
1317 if( qry->callBackEnd ) {
1318 g_timeout_add(0, callbackend, qry);
1321 return ADDRQUERY_RETVAL(qry);
1325 * Remove results (folder and data) for specified LDAP query.
1326 * \param qry Query object to process.
1327 * \return TRUE if folder deleted successfully.
1329 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1330 gboolean retVal = FALSE;
1332 ldapqry_set_aged_flag( qry, TRUE );
1334 if( ldapqry_get_busy_flag( qry ) ) {
1335 ldapqry_set_stop_flag( qry, TRUE );
1338 LdapServer *server = qry->server;
1339 server->listQuery = g_list_remove(server->listQuery, qry);
1346 #endif /* USE_LDAP */