2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2004 Match Grun
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 * Functions necessary to define and perform LDAP queries.
35 #include "ldapquery.h"
40 #include "addrcache.h"
42 #include "ldapquery.h"
45 * Key for thread specific data.
47 static pthread_key_t _queryThreadKey_;
48 static gboolean _queryThreadInit_ = FALSE;
51 * Create new LDAP query object.
52 * \return Initialized query object.
54 LdapQuery *ldapqry_create( void ) {
57 qry = g_new0( LdapQuery, 1 );
58 ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
59 ADDRQUERY_ID(qry) = 0;
60 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
61 ADDRQUERY_NAME(qry) = NULL;
62 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
63 ADDRQUERY_FOLDER(qry) = NULL;
64 ADDRQUERY_SEARCHVALUE(qry) = NULL;
69 qry->stopFlag = FALSE;
70 qry->busyFlag = FALSE;
71 qry->agedFlag = FALSE;
72 qry->completed = FALSE;
74 qry->callBackEntry = NULL;
75 qry->callBackEnd = NULL;
79 /* Mutex to protect stop and busy flags */
80 qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
81 pthread_mutex_init( qry->mutexStop, NULL );
82 qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
83 pthread_mutex_init( qry->mutexBusy, NULL );
85 /* Mutex to protect critical section */
86 qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
87 pthread_mutex_init( qry->mutexEntry, NULL );
93 * Specify the reference to control data that will be used for the query. The calling
94 * module should be responsible for creating and destroying this control object.
95 * \param qry Query object.
96 * \param ctl Control object.
98 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
99 g_return_if_fail( qry != NULL );
104 * Specify query name to be used.
105 * \param qry Query object.
108 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
109 ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
110 g_strstrip( ADDRQUERY_NAME(qry) );
114 * Specify search value to be used.
115 * \param qry Query object.
118 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
119 ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
120 g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
124 * Specify error/status.
125 * \param qry Query object.
126 * \param value Status.
128 void ldapqry_set_error_status( LdapQuery* qry, const gint value ) {
129 ADDRQUERY_RETVAL(qry) = value;
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 * Specify maximum number of LDAP entries to retrieve.
169 * \param qry Query object.
170 * \param value Entries to read.
172 void ldapqry_set_entries_read( LdapQuery* qry, const gint value ) {
174 qry->entriesRead = value;
177 qry->entriesRead = 0;
182 * Register a callback function that will be executed when each entry
183 * has been read and processed. When called, the function will be passed
184 * this query object and a GList of ItemEMail objects as arguments. An
185 * example of typical usage is shown below.
188 * ------------------------------------------------------------
189 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
194 * ItemEMail *email = node->data;
195 * ... process email object ...
196 * node = g_list_next( node );
198 * g_list_free( listEMail );
202 * ldapqry_set_callback_entry( qry, myCallbackEntry );
203 * ------------------------------------------------------------
206 * \param qry Query object.
207 * \param func Function.
209 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
210 pthread_mutex_lock( qry->mutexEntry );
211 qry->callBackEntry = func;
212 pthread_mutex_unlock( qry->mutexEntry );
216 * Register a callback function that will be executed when the search
217 * is complete. When called, the function will be passed this query
218 * object as an argument.
219 * \param qry Query object.
220 * \param func Function.
222 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
223 qry->callBackEnd = func;
227 * Notify query to start/stop executing. This method should be called with a
228 * value if <i>TRUE</i> to terminate an existing running query.
230 * \param qry Query object.
231 * \param value Value of stop flag.
233 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
234 g_return_if_fail( qry != NULL );
236 pthread_mutex_lock( qry->mutexStop );
237 qry->stopFlag = value;
238 pthread_mutex_unlock( qry->mutexStop );
242 * Test value of stop flag. This method should be used to determine whether a
243 * query has stopped running.
244 * \param qry Query object.
245 * \return Value of stop flag.
247 gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
249 g_return_if_fail( qry != NULL );
251 pthread_mutex_lock( qry->mutexStop );
252 value = qry->stopFlag;
253 pthread_mutex_unlock( qry->mutexStop );
259 * \param qry Query object.
260 * \param value Value of busy flag.
262 void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
263 g_return_if_fail( qry != NULL );
265 pthread_mutex_lock( qry->mutexBusy );
266 qry->busyFlag = value;
267 pthread_mutex_unlock( qry->mutexBusy );
271 * Test value of busy flag. This method will return a value of <i>FALSE</i>
272 * when a query has completed running.
273 * \param qry Query object.
274 * \return Value of busy flag.
276 gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
278 g_return_if_fail( qry != NULL );
280 pthread_mutex_lock( qry->mutexBusy );
281 value = qry->busyFlag;
282 pthread_mutex_unlock( qry->mutexBusy );
287 * Set query aged flag.
288 * \param qry Query object.
289 * \param value Value of aged flag.
291 void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
292 g_return_if_fail( qry != NULL );
293 qry->agedFlag = value;
297 * Test value of aged flag.
298 * \param qry Query object.
299 * \return <i>TRUE</i> if query has been marked as aged (and can be retired).
301 gboolean ldapqry_get_aged_flag( LdapQuery *qry ) {
302 g_return_if_fail( qry != NULL );
303 return qry->agedFlag;
307 * Specify user data for query.
308 * \param qry Query object.
309 * \param value Data to set.
311 void ldapqry_set_data( LdapQuery *qry, const gpointer value ) {
312 g_return_if_fail( qry != NULL );
317 * Retrieve user data associated with query.
318 * \param qry Query object.
321 gpointer ldapqry_get_data( LdapQuery *qry ) {
322 g_return_if_fail( qry != NULL );
327 * Release the LDAP control data associated with the query.
328 * \param qry Query object to process.
330 void ldapqry_release_control( LdapQuery *qry ) {
331 g_return_if_fail( qry != NULL );
332 if( qry->control != NULL ) {
333 ldapctl_free( qry->control );
339 * Clear LDAP query member variables.
340 * \param qry Query object.
342 void ldapqry_clear( LdapQuery *qry ) {
343 g_return_if_fail( qry != NULL );
345 /* Free internal stuff */
346 g_free( ADDRQUERY_NAME(qry) );
347 g_free( ADDRQUERY_SEARCHVALUE(qry) );
349 /* Clear pointers and value */
350 ADDRQUERY_NAME(qry) = NULL;
351 ADDRQUERY_SEARCHVALUE(qry) = NULL;
352 ADDRQUERY_ID(qry) = 0;
353 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
354 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
355 qry->entriesRead = 0;
356 qry->elapsedTime = 0;
357 qry->stopFlag = FALSE;
358 qry->busyFlag = FALSE;
359 qry->agedFlag = FALSE;
360 qry->completed = FALSE;
361 qry->callBackEntry = NULL;
362 qry->callBackEnd = NULL;
368 * Free up LDAP query object by releasing internal memory. Note that
369 * the thread object will be freed by the OS.
370 * \param qry Query object to process.
372 void ldapqry_free( LdapQuery *qry ) {
373 g_return_if_fail( qry != NULL );
375 /* Clear out internal members */
376 ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
377 ldapqry_clear( qry );
380 pthread_mutex_destroy( qry->mutexStop );
381 pthread_mutex_destroy( qry->mutexBusy );
382 pthread_mutex_destroy( qry->mutexEntry );
383 g_free( qry->mutexStop );
384 g_free( qry->mutexBusy );
385 g_free( qry->mutexEntry );
386 qry->mutexEntry = NULL;
387 qry->mutexBusy = NULL;
388 qry->mutexStop = NULL;
390 /* Do not free folder - parent server object should free */
391 ADDRQUERY_FOLDER(qry) = NULL;
393 /* Do not free thread - thread should be terminated before freeing */
396 /* Do not free LDAP control - should be destroyed before freeing */
399 /* Now release object */
404 * Display object to specified stream.
405 * \param qry Query object to process.
406 * \param stream Output stream.
408 void ldapqry_print( const LdapQuery *qry, FILE *stream ) {
409 g_return_if_fail( qry != NULL );
411 fprintf( stream, "LdapQuery:\n" );
412 fprintf( stream, " control?: %s\n", qry->control ? "yes" : "no" );
413 fprintf( stream, "err/status: %d\n", ADDRQUERY_RETVAL(qry) );
414 fprintf( stream, "query type: %d\n", ADDRQUERY_TYPE(qry) );
415 fprintf( stream, "searchType: %d\n", ADDRQUERY_SEARCHTYPE(qry) );
416 fprintf( stream, "query name: '%s'\n", ADDRQUERY_NAME(qry) );
417 fprintf( stream, "search val: '%s'\n", ADDRQUERY_SEARCHVALUE(qry) );
418 fprintf( stream, " queryID: %d\n", ADDRQUERY_ID(qry) );
419 fprintf( stream, " entries: %d\n", qry->entriesRead );
420 fprintf( stream, " elapsed: %d\n", qry->elapsedTime );
421 fprintf( stream, " stop flag: %s\n", qry->stopFlag ? "yes" : "no" );
422 fprintf( stream, " busy flag: %s\n", qry->busyFlag ? "yes" : "no" );
423 fprintf( stream, " aged flag: %s\n", qry->agedFlag ? "yes" : "no" );
424 fprintf( stream, " completed: %s\n", qry->completed ? "yes" : "no" );
428 * Free linked lists of character strings.
429 * \param listName List of common names.
430 * \param listAddr List of addresses.
431 * \param listFirst List of first names.
432 * \param listLast List of last names.
434 static void ldapqry_free_lists(
435 GSList *listName, GSList *listAddr, GSList *listFirst,
438 mgu_free_list( listName );
439 mgu_free_list( listAddr );
440 mgu_free_list( listFirst );
441 mgu_free_list( listLast );
445 * Add all LDAP attribute values to a list.
446 * \param ld LDAP handle.
447 * \param entry LDAP entry to process.
448 * \param attr LDAP attribute.
449 * \return List of values.
451 static GSList *ldapqry_add_list_values(
452 LDAP *ld, LDAPMessage *entry, char *attr )
458 if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
459 for( i = 0; vals[i] != NULL; i++ ) {
460 /* printf( "lv\t%s: %s\n", attr, vals[i] ); */
461 list = g_slist_append( list, g_strdup( vals[i] ) );
464 ldap_value_free( vals );
469 * Add a single attribute value to a list.
470 * \param ld LDAP handle.
471 * \param entry LDAP entry to process.
472 * \param attr LDAP attribute name to process.
473 * \return List of values; only one value will be present.
475 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
479 if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
480 if( vals[0] != NULL ) {
481 /* printf( "sv\t%s: %s\n", attr, vals[0] ); */
482 list = g_slist_append( list, g_strdup( vals[0] ) );
485 ldap_value_free( vals );
490 * Build an address list entry and append to list of address items. Name is formatted
491 * as "<first-name> <last-name>".
493 * \param cache Address cache to load.
494 * \param qry Query object to process.
495 * \param dn DN for entry found on server.
496 * \param listName List of common names for entry; see notes below.
497 * \param listAddr List of EMail addresses for entry.
498 * \param listFirst List of first names for entry.
499 * \param listLast List of last names for entry.
501 * \return List of ItemEMail objects.
504 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
505 * name. For example, a single entry for a person may have more than one
506 * common name, email address, etc.
508 * 2) The DN for the entry is unique for the server.
510 static GList *ldapqry_build_items_fl(
511 AddressCache *cache, LdapQuery *qry, gchar *dn,
512 GSList *listName, GSList *listAddr, GSList *listFirst,
516 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
521 GList *listReturn = NULL;
523 folder = ADDRQUERY_FOLDER(qry);
524 if( folder == NULL ) return listReturn;
525 if( listAddr == NULL ) return listReturn;
527 /* Find longest first name in list */
528 firstName = mgu_slist_longest_entry( listFirst );
530 /* Format last name */
532 lastName = listLast->data;
535 /* Find longest common name */
537 fullName = mgu_slist_longest_entry( listName );
538 if( fullName == NULL ) {
539 /* Format a full name from first and last names */
542 fullName = g_strdup_printf( "%s %s", firstName, lastName );
545 fullName = g_strdup_printf( "%s", firstName );
550 fullName = g_strdup_printf( "%s", lastName );
554 g_strchug( fullName ); g_strchomp( fullName );
559 /* Add person into folder */
560 person = addritem_create_item_person();
561 addritem_person_set_common_name( person, fullName );
562 addritem_person_set_first_name( person, firstName );
563 addritem_person_set_last_name( person, lastName );
564 addrcache_id_person( cache, person );
565 addritem_person_set_external_id( person, dn );
566 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
570 /* Add each address item */
571 nodeAddress = listAddr;
572 while( nodeAddress ) {
573 email = addritem_create_item_email();
574 addritem_email_set_address( email, nodeAddress->data );
575 addrcache_id_email( cache, email );
576 addrcache_person_add_email( cache, person, email );
577 addritem_person_add_email( person, email );
578 listReturn = g_list_append( listReturn, email );
579 nodeAddress = g_slist_next( nodeAddress );
582 /* Free any allocated memory */
586 fullName = firstName = lastName = NULL;
592 * Process a single search entry.
593 * \param cache Address cache to load.
594 * \param qry Query object to process.
595 * \param ld LDAP handle.
596 * \param e LDAP message.
597 * \return List of EMail objects found.
599 static GList *ldapqry_process_single_entry(
600 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
606 GSList *listName = NULL, *listAddress = NULL;
607 GSList *listFirst = NULL, *listLast = NULL;
612 dnEntry = ldap_get_dn( ld, e );
613 /* printf( "DN: %s\n", dnEntry ); */
615 /* Process all attributes */
616 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
617 attribute = ldap_next_attribute( ld, e, ber ) ) {
619 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
620 listAddress = ldapqry_add_list_values( ld, e, attribute );
622 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
623 listName = ldapqry_add_list_values( ld, e, attribute );
625 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
626 listFirst = ldapqry_add_list_values( ld, e, attribute );
628 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
629 listLast = ldapqry_add_single_value( ld, e, attribute );
632 /* Free memory used to store attribute */
633 ldap_memfree( attribute );
636 /* Format and add items to cache */
637 listReturn = ldapqry_build_items_fl(
638 cache, qry, dnEntry, listName, listAddress, listFirst, listLast );
641 ldapqry_free_lists( listName, listAddress, listFirst, listLast );
642 listName = listAddress = listFirst = listLast = NULL;
653 * Check parameters that are required for a search. This should
654 * be called before performing a search.
655 * \param qry Query object to process.
656 * \return <i>TRUE</i> if search criteria appear OK.
658 gboolean ldapqry_check_search( LdapQuery *qry ) {
660 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
662 /* Test for control data */
668 /* Test for search value */
669 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
672 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
675 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
680 * Touch the query. This nudges the touch time with the current time.
681 * \param qry Query object to process.
683 void ldapqry_touch( LdapQuery *qry ) {
684 qry->touchTime = time( NULL );
685 qry->agedFlag = FALSE;
689 * Connect to LDAP server.
690 * \param qry Query object to process.
691 * \return Error/status code.
693 static gint ldapqry_connect( LdapQuery *qry ) {
699 /* Initialize connection */
700 /* printf( "===ldapqry_connect===\n" ); */
701 /* ldapqry_print( qry, stdout ); */
703 /* ldapctl_print( ctl, stdout ); */
704 /* printf( "======\n" ); */
705 ldapqry_touch( qry );
706 qry->startTime = qry->touchTime;
707 qry->elapsedTime = -1;
708 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
709 if( ( ld = ldap_init( ctl->hostName, ctl->port ) ) == NULL ) {
710 return ADDRQUERY_RETVAL(qry);
713 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
714 if( ldapqry_get_stop_flag( qry ) ) {
715 return ADDRQUERY_RETVAL(qry);
717 ldapqry_touch( qry );
720 printf( "connected to LDAP host %s on port %d\n", ctl->hostName, ctl->port );
725 version = LDAP_VERSION3;
726 rc = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
727 if( rc == LDAP_OPT_SUCCESS ) {
728 ctl->version = LDAP_VERSION3;
731 if( ctl->version == LDAP_VERSION3 ) {
732 if( ctl->enableTLS ) {
733 ADDRQUERY_RETVAL(qry) = LDAPRC_TLS;
734 rc = ldap_start_tls_s( ld, NULL, NULL );
736 printf( "rc=%d\n", rc );
737 printf( "LDAP Status: set_option: %s\n", ldap_err2string( rc ) );
739 if( rc != LDAP_SUCCESS ) {
740 return ADDRQUERY_RETVAL(qry);
746 /* Bind to the server, if required */
747 ADDRQUERY_RETVAL(qry) = LDAPRC_BIND;
749 if( * ctl->bindDN != '\0' ) {
750 /* printf( "binding...\n" ); */
751 rc = ldap_simple_bind_s( ld, ctl->bindDN, ctl->bindPass );
752 /* printf( "rc=%d\n", rc ); */
753 if( rc != LDAP_SUCCESS ) {
755 printf( "LDAP Error: ldap_simple_bind_s: %s\n",
756 ldap_err2string( rc ) );
758 return ADDRQUERY_RETVAL(qry);
762 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
763 if( ldapqry_get_stop_flag( qry ) ) {
764 return ADDRQUERY_RETVAL(qry);
766 ldapqry_touch( qry );
768 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
770 return ADDRQUERY_RETVAL(qry);
774 * Connect to LDAP server.
775 * \param qry Query object to process.
776 * \return Error/status code.
778 static gint ldapqry_disconnect( LdapQuery *qry ) {
780 if( qry->ldap ) ldap_unbind( qry->ldap );
783 ldapqry_touch( qry );
784 qry->elapsedTime = qry->touchTime - qry->startTime;
786 return ADDRQUERY_RETVAL(qry);
790 * Perform the LDAP search, reading LDAP entries into cache.
791 * Note that one LDAP entry can have multiple values for many of its
792 * attributes. If these attributes are E-Mail addresses; these are
793 * broken out into separate address items. For any other attribute,
794 * only the first occurrence is read.
796 * \param qry Query object to process.
797 * \return Error/status code.
799 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
802 LDAPMessage *result, *e;
806 gboolean entriesFound;
808 struct timeval timeout;
813 /* Initialize some variables */
816 cache = qry->server->addressCache;
817 timeout.tv_sec = ctl->timeOut;
818 timeout.tv_usec = 0L;
819 entriesFound = FALSE;
820 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
822 /* Define all attributes we are interested in. */
823 attribs = ldapctl_attribute_array( ctl );
825 /* Create LDAP search string */
826 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
827 /* printf( "Search criteria ::%s::\n", criteria ); */
830 * Execute the search - this step may take some time to complete
831 * depending on network traffic and server response time.
833 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
834 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
835 attribs, 0, NULL, NULL, &timeout, 0, &result );
836 ldapctl_free_attribute_array( attribs );
839 if( rc == LDAP_TIMEOUT ) {
840 return ADDRQUERY_RETVAL(qry);
842 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
844 /* Test valid returns */
846 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
849 else if( rc == LDAP_SUCCESS ) {
852 else if( rc == LDAP_PARTIAL_RESULTS ) {
857 printf( "LDAP Error: ldap_search_st: %d\n", rc );
858 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
860 return ADDRQUERY_RETVAL(qry);
862 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
865 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
868 /* Process results */
870 while( searchFlag ) {
871 ldapqry_touch( qry );
872 if( qry->entriesRead >= ctl->maxEntries ) break;
875 if( ldapqry_get_stop_flag( qry ) ) {
882 e = ldap_first_entry( ld, result );
885 e = ldap_next_entry( ld, e );
887 if( e == NULL ) break;
890 /* Setup a critical section here */
891 pthread_mutex_lock( qry->mutexEntry );
894 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
896 /* Process callback */
897 if( qry->callBackEntry ) {
898 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
901 g_list_free( listEMail );
903 pthread_mutex_unlock( qry->mutexEntry );
906 /* Free up and disconnect */
907 ldap_msgfree( result );
911 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
914 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
918 return ADDRQUERY_RETVAL(qry);
922 * Connection, perform search and disconnect.
923 * \param qry Query object to process.
924 * \return Error/status code.
926 static gint ldapqry_perform_search( LdapQuery *qry ) {
927 /* Check search criteria */
928 if( ! ldapqry_check_search( qry ) ) {
929 return ADDRQUERY_RETVAL(qry);
934 ldapqry_connect( qry );
935 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
937 ldapqry_search_retrieve( qry );
940 ldapqry_disconnect( qry );
943 return ADDRQUERY_RETVAL(qry);
947 * Wrapper around search.
948 * \param qry Query object to process.
949 * \return Error/status code.
951 gint ldapqry_search( LdapQuery *qry ) {
954 g_return_val_if_fail( qry != NULL, -1 );
955 g_return_val_if_fail( qry->control != NULL, -1 );
957 ldapqry_touch( qry );
958 qry->completed = FALSE;
960 /* Setup pointer to thread specific area */
961 pthread_setspecific( _queryThreadKey_, qry );
963 pthread_detach( pthread_self() );
965 /* Now perform the search */
966 qry->entriesRead = 0;
967 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
968 ldapqry_set_busy_flag( qry, TRUE );
969 ldapqry_set_stop_flag( qry, FALSE );
970 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
971 retVal = ldapqry_perform_locate( qry );
974 retVal = ldapqry_perform_search( qry );
976 if( retVal == LDAPRC_SUCCESS ) {
977 qry->server->addressCache->dataRead = TRUE;
978 qry->server->addressCache->accessFlag = FALSE;
979 if( ldapqry_get_stop_flag( qry ) ) {
981 printf( "Search was terminated prematurely\n" );
985 ldapqry_touch( qry );
986 qry->completed = TRUE;
988 printf( "Search ran to completion\n" );
992 ldapqry_set_stop_flag( qry, TRUE );
993 ldapqry_set_busy_flag( qry, FALSE );
995 /* Process callback */
996 if( qry->callBackEnd ) {
997 qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
1000 return ADDRQUERY_RETVAL(qry);
1004 * Read data into list using a background thread. Callback function will be
1005 * notified when search is complete.
1006 * \param qry Query object to process.
1007 * \return Error/status code.
1009 gint ldapqry_read_data_th( LdapQuery *qry ) {
1010 g_return_val_if_fail( qry != NULL, -1 );
1011 g_return_val_if_fail( qry->control != NULL, -1 );
1013 ldapqry_set_stop_flag( qry, FALSE );
1014 ldapqry_touch( qry );
1015 if( ldapqry_check_search( qry ) ) {
1016 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1018 printf( "Starting LDAP search thread\n");
1020 ldapqry_set_busy_flag( qry, TRUE );
1021 qry->thread = g_malloc0( sizeof( pthread_t ) );
1024 pthread_create( qry->thread, NULL,
1025 (void *) ldapqry_search, (void *) qry );
1028 return ADDRQUERY_RETVAL(qry);
1032 * Cleanup LDAP thread data. This function will be called when each thread
1033 * exits. Note that the thread object will be freed by the kernel.
1034 * \param ptr Pointer to object being destroyed (a query object in this case).
1036 static void ldapqry_destroyer( void * ptr ) {
1039 qry = ( LdapQuery * ) ptr;
1041 printf( "ldapqry_destroyer::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1044 /* Perform any destruction here */
1045 if( qry->control != NULL ) {
1046 ldapctl_free( qry->control );
1048 qry->control = NULL;
1050 ldapqry_set_busy_flag( qry, FALSE );
1054 * Cancel thread associated with query.
1055 * \param qry Query object to process.
1057 void ldapqry_cancel( LdapQuery *qry ) {
1058 g_return_if_fail( qry != NULL );
1061 printf( "cancelling::%d::%s\n", (int) pthread_self(), ADDRQUERY_NAME(qry) );
1063 if( ldapqry_get_busy_flag( qry ) ) {
1065 /* printf( "calling pthread_cancel\n" ); */
1066 pthread_cancel( * qry->thread );
1072 * Initialize LDAP query. This function should be called once before executing
1073 * any LDAP queries to initialize thread specific data.
1075 void ldapqry_initialize( void ) {
1076 /* printf( "ldapqry_initialize...\n" ); */
1077 if( ! _queryThreadInit_ ) {
1079 printf( "ldapqry_initialize::creating thread specific area\n" );
1081 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1082 _queryThreadInit_ = TRUE;
1084 /* printf( "ldapqry_initialize... done!\n" ); */
1088 * Age the query based on LDAP control parameters.
1089 * \param qry Query object to process.
1090 * \param maxAge Maximum age of query (in seconds).
1092 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1095 g_return_if_fail( qry != NULL );
1097 /* Limit the time that queries can hang around */
1098 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1100 /* Check age of query */
1101 age = time( NULL ) - qry->touchTime;
1102 if( age > maxAge ) {
1103 qry->agedFlag = TRUE;
1108 * Delete folder associated with query results.
1109 * \param qry Query object to process.
1111 void ldapqry_delete_folder( LdapQuery *qry ) {
1112 AddressCache *cache;
1115 g_return_if_fail( qry != NULL );
1117 folder = ADDRQUERY_FOLDER(qry);
1119 cache = qry->server->addressCache;
1120 folder = addrcache_remove_folder_delete( cache, folder );
1122 addritem_free_item_folder( folder );
1124 ADDRQUERY_FOLDER(qry) = NULL;
1129 * Create a name/value pair object.
1132 * \return Initialized object.
1134 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1135 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1137 nvp->name = g_strdup( n );
1138 nvp->value = g_strdup( v );
1143 * Free up name/value pair object.
1144 * \param nvp Name/value object.
1146 void ldapqry_free_name_value( NameValuePair *nvp ) {
1148 g_free( nvp->name );
1149 g_free( nvp->value );
1150 nvp->name = nvp->value = NULL;
1156 * Print name/value pair object for debug.
1157 * \param nvp Name/value object.
1158 * \param stream Output stream.
1160 void ldapqry_print_name_value( NameValuePair *nvp, FILE *stream ) {
1162 fprintf( stream, "n/v ::%s::%s::\n", nvp->name, nvp->value );
1167 * Free up a list name/value pair objects.
1168 * \param list List of name/value objects.
1170 void ldapqry_free_list_name_value( GList *list ) {
1175 NameValuePair *nvp = ( NameValuePair * ) node->data;
1176 ldapqry_free_name_value( nvp );
1178 node = g_list_next( node );
1180 g_list_free( list );
1184 * Load a list of name/value pairs from LDAP attributes.
1185 * \param ld LDAP handle.
1186 * \param e LDAP message.
1187 * \param attr Attribute name.
1188 * \param listValues List to populate.
1189 * \return List of attribute name/value pairs.
1191 static GList *ldapqry_load_attrib_values(
1192 LDAP *ld, LDAPMessage *entry, char *attr,
1201 if( ( vals = ldap_get_values( ld, entry, attr ) ) != NULL ) {
1202 for( i = 0; vals[i] != NULL; i++ ) {
1203 nvp = ldapqry_create_name_value( attr, vals[i] );
1204 list = g_list_append( list, nvp );
1207 ldap_value_free( vals );
1212 * Fetch a list of all attributes.
1213 * \param ld LDAP handle.
1214 * \param e LDAP message.
1215 * \return List of attribute name/value pairs.
1217 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1221 GList *listValues = NULL;
1223 /* Process all attributes */
1224 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1225 attribute = ldap_next_attribute( ld, e, ber ) ) {
1226 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1227 ldap_memfree( attribute );
1237 #define CRITERIA_SINGLE "(objectclass=*)"
1240 * Perform the data retrieval for a specific LDAP record.
1242 * \param qry Query object to process.
1243 * \return Error/status code.
1245 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1248 LDAPMessage *result, *e;
1249 gboolean entriesFound;
1251 struct timeval timeout;
1256 /* Initialize some variables */
1259 dn = ADDRQUERY_SEARCHVALUE(qry);
1260 timeout.tv_sec = ctl->timeOut;
1261 timeout.tv_usec = 0L;
1262 entriesFound = FALSE;
1265 * Execute the search - this step may take some time to complete
1266 * depending on network traffic and server response time.
1268 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1269 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1270 NULL, 0, NULL, NULL, &timeout, 0, &result );
1271 if( rc == LDAP_TIMEOUT ) {
1272 return ADDRQUERY_RETVAL(qry);
1274 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1275 if( rc != LDAP_SUCCESS ) {
1277 printf( "LDAP Error: ldap_search_st: %s\n", ldap_err2string( rc ) );
1279 return ADDRQUERY_RETVAL(qry);
1283 printf( "Total results are: %d\n", ldap_count_entries( ld, result ) );
1286 /* Process results */
1287 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1290 ldapqry_touch( qry );
1291 if( qry->entriesRead >= ctl->maxEntries ) break;
1294 if( ldapqry_get_stop_flag( qry ) ) {
1298 /* Retrieve entry */
1301 e = ldap_first_entry( ld, result );
1304 e = ldap_next_entry( ld, e );
1306 if( e == NULL ) break;
1308 entriesFound = TRUE;
1310 /* Setup a critical section here */
1311 pthread_mutex_lock( qry->mutexEntry );
1314 listValues = ldapqry_fetch_attribs( ld, e );
1316 /* Process callback */
1317 if( qry->callBackEntry ) {
1318 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1320 ldapqry_free_list_name_value( listValues );
1323 pthread_mutex_unlock( qry->mutexEntry );
1326 /* Free up and disconnect */
1327 ldap_msgfree( result );
1329 if( entriesFound ) {
1330 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1333 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1336 return ADDRQUERY_RETVAL(qry);
1340 * Perform the search to locate a specific LDAP record identified by
1341 * distinguished name (dn).
1343 * \param qry Query object to process.
1344 * \return Error/status code.
1346 gint ldapqry_perform_locate( LdapQuery *qry ) {
1349 ldapqry_connect( qry );
1350 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1351 /* Perform search */
1352 ldapqry_locate_retrieve( qry );
1355 ldapqry_disconnect( qry );
1358 /* Process callback */
1359 if( qry->callBackEnd ) {
1360 qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
1363 return ADDRQUERY_RETVAL(qry);
1367 * Remove results (folder and data) for specified LDAP query.
1368 * \param qry Query object to process.
1369 * \return TRUE if folder deleted successfully.
1371 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1372 gboolean retVal = FALSE;
1374 ldapqry_set_aged_flag( qry, TRUE );
1376 if( ldapqry_get_busy_flag( qry ) ) {
1377 ldapqry_set_stop_flag( qry, TRUE );
1380 LdapServer *server = qry->server;
1381 server->listQuery = g_list_remove(server->listQuery, qry);
1388 #endif /* USE_LDAP */