2 * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2018 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/>.
20 * Functions necessary to define and perform LDAP queries.
25 #include "claws-features.h"
31 #include <glib/gi18n.h>
37 #include "ldapquery.h"
39 #include "ldapserver.h"
43 #include "addrcache.h"
44 #include "common/utils.h"
48 * Key for thread specific data.
50 static pthread_key_t _queryThreadKey_;
51 static gboolean _queryThreadInit_ = FALSE;
53 static gboolean callbackend (gpointer data)
55 LdapQuery *qry = (LdapQuery *)data;
56 qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
62 * Create new LDAP query object.
63 * \return Initialized query object.
65 LdapQuery *ldapqry_create( void ) {
68 qry = g_new0( LdapQuery, 1 );
69 ADDRQUERY_TYPE(qry) = ADDRQUERY_LDAP;
70 ADDRQUERY_ID(qry) = 0;
71 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
72 ADDRQUERY_NAME(qry) = NULL;
73 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
74 ADDRQUERY_FOLDER(qry) = NULL;
75 ADDRQUERY_SEARCHVALUE(qry) = NULL;
80 qry->stopFlag = FALSE;
81 qry->busyFlag = FALSE;
82 qry->agedFlag = FALSE;
83 qry->completed = FALSE;
85 qry->callBackEntry = NULL;
86 qry->callBackEnd = NULL;
90 /* Mutex to protect stop and busy flags */
91 qry->mutexStop = g_malloc0( sizeof( pthread_mutex_t ) );
92 pthread_mutex_init( qry->mutexStop, NULL );
93 qry->mutexBusy = g_malloc0( sizeof( pthread_mutex_t ) );
94 pthread_mutex_init( qry->mutexBusy, NULL );
96 /* Mutex to protect critical section */
97 qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
98 pthread_mutex_init( qry->mutexEntry, NULL );
104 * Specify the reference to control data that will be used for the query. The calling
105 * module should be responsible for creating and destroying this control object.
106 * \param qry Query object.
107 * \param ctl Control object.
109 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
110 cm_return_if_fail( qry != NULL );
115 * Specify query name to be used.
116 * \param qry Query object.
119 void ldapqry_set_name( LdapQuery* qry, const gchar *value ) {
120 cm_return_if_fail( qry != NULL );
121 ADDRQUERY_NAME(qry) = mgu_replace_string( ADDRQUERY_NAME(qry), value );
122 if (ADDRQUERY_NAME(qry) == NULL)
124 g_strstrip( ADDRQUERY_NAME(qry) );
125 debug_print("set name: %s\n", ADDRQUERY_NAME(qry));
129 * Specify search value to be used.
130 * \param qry Query object.
133 void ldapqry_set_search_value( LdapQuery *qry, const gchar *value ) {
134 cm_return_if_fail( qry != NULL );
135 ADDRQUERY_SEARCHVALUE(qry) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry), value );
136 if (ADDRQUERY_SEARCHVALUE(qry) == NULL)
138 g_strstrip( ADDRQUERY_SEARCHVALUE(qry) );
139 debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry));
143 * Specify query type.
144 * \param qry Query object.
145 * \param value Query type, either:
147 * <li><code>LDAPQUERY_NONE</code></li>
148 * <li><code>LDAPQUERY_STATIC</code></li>
149 * <li><code>LDAPQUERY_DYNAMIC</code></li>
153 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
154 ADDRQUERY_TYPE(qry) = value;
159 * Specify search type.
160 * \param qry Query object.
163 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
164 cm_return_if_fail( qry != NULL );
165 ADDRQUERY_SEARCHTYPE(qry) = value;
170 * \param qry Query object.
171 * \param value ID for the query.
173 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
174 cm_return_if_fail( qry != NULL );
175 ADDRQUERY_ID(qry) = value;
179 * Register a callback function that will be executed when each entry
180 * has been read and processed. When called, the function will be passed
181 * this query object and a GList of ItemEMail objects as arguments. An
182 * example of typical usage is shown below.
185 * ------------------------------------------------------------
186 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
191 * ItemEMail *email = node->data;
192 * ... process email object ...
193 * node = g_list_next( node );
195 * g_list_free( listEMail );
199 * ldapqry_set_callback_entry( qry, myCallbackEntry );
200 * ------------------------------------------------------------
203 * \param qry Query object.
204 * \param func Function.
206 void ldapqry_set_callback_entry( LdapQuery *qry, void *func ) {
207 pthread_mutex_lock( qry->mutexEntry );
208 qry->callBackEntry = func;
209 pthread_mutex_unlock( qry->mutexEntry );
213 * Register a callback function that will be executed when the search
214 * is complete. When called, the function will be passed this query
215 * object as an argument.
216 * \param qry Query object.
217 * \param func Function.
219 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
220 qry->callBackEnd = func;
224 * Notify query to start/stop executing. This method should be called with a
225 * value if <i>TRUE</i> to terminate an existing running query.
227 * \param qry Query object.
228 * \param value Value of stop flag.
230 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
231 cm_return_if_fail( qry != NULL );
233 pthread_mutex_lock( qry->mutexStop );
234 qry->stopFlag = value;
235 pthread_mutex_unlock( qry->mutexStop );
239 * Test value of stop flag. This method should be used to determine whether a
240 * query has stopped running.
241 * \param qry Query object.
242 * \return Value of stop flag.
244 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
246 cm_return_val_if_fail( qry != NULL, TRUE );
248 pthread_mutex_lock( qry->mutexStop );
249 value = qry->stopFlag;
250 pthread_mutex_unlock( qry->mutexStop );
256 * \param qry Query object.
257 * \param value Value of busy flag.
259 static void ldapqry_set_busy_flag( LdapQuery *qry, const gboolean value ) {
260 cm_return_if_fail( qry != NULL );
261 if (qry->mutexBusy == NULL)
262 return; /* exiting, mutex already freed */
264 pthread_mutex_lock( qry->mutexBusy );
265 qry->busyFlag = value;
266 pthread_mutex_unlock( qry->mutexBusy );
270 * Test value of busy flag. This method will return a value of <i>FALSE</i>
271 * when a query has completed running.
272 * \param qry Query object.
273 * \return Value of busy flag.
275 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
277 cm_return_val_if_fail( qry != NULL, FALSE );
279 pthread_mutex_lock( qry->mutexBusy );
280 value = qry->busyFlag;
281 pthread_mutex_unlock( qry->mutexBusy );
286 * Set query aged flag.
287 * \param qry Query object.
288 * \param value Value of aged flag.
290 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
291 cm_return_if_fail( qry != NULL );
292 qry->agedFlag = value;
296 * Clear LDAP query member variables.
297 * \param qry Query object.
299 static void ldapqry_clear( LdapQuery *qry ) {
300 cm_return_if_fail( qry != NULL );
302 /* Free internal stuff */
303 g_free( ADDRQUERY_NAME(qry) );
304 g_free( ADDRQUERY_SEARCHVALUE(qry) );
306 /* Clear pointers and value */
307 ADDRQUERY_NAME(qry) = NULL;
308 ADDRQUERY_SEARCHVALUE(qry) = NULL;
309 ADDRQUERY_ID(qry) = 0;
310 ADDRQUERY_SEARCHTYPE(qry) = ADDRSEARCH_NONE;
311 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
312 qry->entriesRead = 0;
313 qry->elapsedTime = 0;
314 qry->stopFlag = FALSE;
315 qry->busyFlag = FALSE;
316 qry->agedFlag = FALSE;
317 qry->completed = FALSE;
318 qry->callBackEntry = NULL;
319 qry->callBackEnd = NULL;
325 * Free up LDAP query object by releasing internal memory. Note that
326 * the thread object will be freed by the OS.
327 * \param qry Query object to process.
329 void ldapqry_free( LdapQuery *qry ) {
330 cm_return_if_fail( qry != NULL );
332 /* Clear out internal members */
333 ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
334 ldapqry_clear( qry );
337 pthread_mutex_destroy( qry->mutexStop );
338 pthread_mutex_destroy( qry->mutexBusy );
339 pthread_mutex_destroy( qry->mutexEntry );
340 g_free( qry->mutexStop );
341 g_free( qry->mutexBusy );
342 g_free( qry->mutexEntry );
343 qry->mutexEntry = NULL;
344 qry->mutexBusy = NULL;
345 qry->mutexStop = NULL;
347 /* Do not free folder - parent server object should free */
348 ADDRQUERY_FOLDER(qry) = NULL;
350 /* Do not free thread - thread should be terminated before freeing */
353 /* Do not free LDAP control - should be destroyed before freeing */
356 /* Now release object */
361 * Free linked lists of character strings.
362 * \param listName List of common names.
363 * \param listAddr List of addresses.
364 * \param listFirst List of first names.
365 * \param listLast List of last names.
367 static void ldapqry_free_lists(
368 GSList *listName, GSList *listAddr, GSList *listFirst,
369 GSList *listLast, GSList *listDisplay, GSList *other_attrs )
371 GSList *cur = other_attrs;
372 mgu_free_list( listName );
373 mgu_free_list( listAddr );
374 mgu_free_list( listFirst );
375 mgu_free_list( listLast );
376 mgu_free_list( listDisplay );
377 for(;cur; cur = cur->next)
378 addritem_free_attribute((UserAttribute *)cur->data);
379 g_slist_free(other_attrs);
383 * Add all LDAP attribute values to a list.
384 * \param ld LDAP handle.
385 * \param entry LDAP entry to process.
386 * \param attr LDAP attribute.
387 * \return List of values.
389 static GSList *ldapqry_add_list_values(
390 LDAP *ld, LDAPMessage *entry, char *attr )
394 struct berval **vals;
396 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
397 for( i = 0; vals[i] != NULL; i++ ) {
398 /*debug_print("lv\t%s: %s\n", attr?attr:"null",
399 vals[i]->bv_val?vals[i]->bv_val:"null");*/
400 list = g_slist_append( list, g_strndup( vals[i]->bv_val, vals[i]->bv_len) );
403 ldap_value_free_len( vals );
408 * Add a single attribute value to a list.
409 * \param ld LDAP handle.
410 * \param entry LDAP entry to process.
411 * \param attr LDAP attribute name to process.
412 * \return List of values; only one value will be present.
414 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
416 struct berval **vals;
418 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
419 if( vals[0] != NULL ) {
420 if (strcmp(attr, "jpegPhoto")) {
421 debug_print("sv\t%s: %s\n", attr?attr:"null",
422 vals[0]->bv_val?vals[0]->bv_val:"null");
423 list = g_slist_append( list, g_strndup( vals[0]->bv_val, vals[0]->bv_len ));
425 char *file = get_tmp_file();
426 FILE *fp = g_fopen(file, "wb");
428 fwrite(vals[0]->bv_val, 1, vals[0]->bv_len, fp);
431 list = g_slist_append( list, file);
435 ldap_value_free_len( vals );
440 * Build an address list entry and append to list of address items. Name is formatted
441 * as "<first-name> <last-name>".
443 * \param cache Address cache to load.
444 * \param qry Query object to process.
445 * \param dn DN for entry found on server.
446 * \param listName List of common names for entry; see notes below.
447 * \param listAddr List of EMail addresses for entry.
448 * \param listFirst List of first names for entry.
449 * \param listLast List of last names for entry.
451 * \return List of ItemEMail objects.
454 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
455 * name. For example, a single entry for a person may have more than one
456 * common name, email address, etc.
458 * 2) The DN for the entry is unique for the server.
460 static GList *ldapqry_build_items_fl(
461 AddressCache *cache, LdapQuery *qry, gchar *dn,
462 GSList *listName, GSList *listAddr, GSList *listFirst,
463 GSList *listLast, GSList *listDisplay, GSList *attributes )
465 GSList *nodeAddress, *cur;
466 gchar *firstName = NULL, *lastName = NULL, *fullName = NULL;
467 gboolean allocated = FALSE;
471 gchar *picfile = NULL;
472 GList *listReturn = NULL;
474 folder = ADDRQUERY_FOLDER(qry);
475 if( folder == NULL ) return listReturn;
476 if( listAddr == NULL ) return listReturn;
480 fullName = listDisplay->data;
483 /* Find longest first name in list */
484 firstName = mgu_slist_longest_entry( listFirst );
486 /* Format last name */
488 lastName = listLast->data;
491 if ( fullName == NULL ) {
492 /* Find longest common name */
494 fullName = mgu_slist_longest_entry( listName );
495 if( fullName == NULL ) {
496 /* Format a full name from first and last names */
499 fullName = g_strdup_printf( "%s %s", firstName, lastName );
502 fullName = g_strdup_printf( "%s", firstName );
507 fullName = g_strdup_printf( "%s", lastName );
511 g_strstrip( fullName );
517 /* Add person into folder */
518 person = addritem_create_item_person();
519 addritem_person_set_common_name( person, fullName );
520 addritem_person_set_first_name( person, firstName );
521 addritem_person_set_last_name( person, lastName );
522 addritem_person_set_nick_name( person, fullName );
523 addrcache_id_person( cache, person );
524 addritem_person_set_external_id( person, dn );
526 for (cur = attributes; cur; cur = cur->next) {
527 UserAttribute *attrib = addritem_copy_attribute((UserAttribute *)cur->data);
528 if (attrib->name && strcmp(attrib->name, "jpegPhoto")) {
529 addritem_person_add_attribute( person, attrib );
531 if (qry->server && qry->server->control) {
532 gchar *dir = g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S,
533 ADDRBOOK_DIR, G_DIR_SEPARATOR_S, NULL );
534 gchar *filename = g_strdup_printf("%s-%s-%s",
535 qry->server->control->hostName?qry->server->control->hostName:"nohost",
536 qry->server->control->baseDN?qry->server->control->baseDN:"nobase",
538 picfile = g_strdup_printf("%s%s.png", dir, filename);
539 addritem_person_set_picture( person, filename );
540 rename_force(attrib->value, picfile);
548 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
552 /* Add each address item */
553 nodeAddress = listAddr;
554 while( nodeAddress ) {
555 email = addritem_create_item_email();
556 addritem_email_set_address( email, nodeAddress->data );
557 addrcache_id_email( cache, email );
558 addrcache_person_add_email( cache, person, email );
559 addritem_person_add_email( person, email );
560 /*if (debug_get_mode()) {
561 addritem_print_item_email(email, stdout);
563 listReturn = g_list_append( listReturn, email );
564 nodeAddress = g_slist_next( nodeAddress );
567 /* Free any allocated memory */
571 fullName = firstName = lastName = NULL;
577 * Process a single search entry.
578 * \param cache Address cache to load.
579 * \param qry Query object to process.
580 * \param ld LDAP handle.
581 * \param e LDAP message.
582 * \return List of EMail objects found.
584 static GList *ldapqry_process_single_entry(
585 AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
591 GSList *listName = NULL, *listAddress = NULL;
592 GSList *listFirst = NULL, *listLast = NULL;
593 GSList *listDisplay = NULL;
594 GSList *other_attrs = NULL;
599 dnEntry = ldap_get_dn( ld, e );
600 debug_print( "DN: %s\n", dnEntry?dnEntry:"null" );
602 /* Process all attributes */
603 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
604 attribute = ldap_next_attribute( ld, e, ber ) ) {
605 if( strcasecmp( attribute, ctl->attribEMail ) == 0 ) {
606 listAddress = ldapqry_add_list_values( ld, e, attribute );
608 else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
609 listName = ldapqry_add_list_values( ld, e, attribute );
611 else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
612 listFirst = ldapqry_add_list_values( ld, e, attribute );
614 else if( strcasecmp( attribute, ctl->attribLName ) == 0 ) {
615 listLast = ldapqry_add_single_value( ld, e, attribute );
616 } else if( strcasecmp( attribute, ctl->attribDName ) == 0 ) {
617 listDisplay = ldapqry_add_single_value( ld, e, attribute );
619 GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
620 UserAttribute *attrib = addritem_create_attribute();
621 const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
623 addritem_attrib_set_name( attrib, attribute );
624 addritem_attrib_set_value( attrib, attvalue );
625 other_attrs = g_slist_prepend(other_attrs, attrib);
627 mgu_free_list(attlist);
629 /* Free memory used to store attribute */
630 ldap_memfree( attribute );
633 /* Format and add items to cache */
634 listReturn = ldapqry_build_items_fl(
635 cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
638 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
639 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
644 ldap_memfree( dnEntry );
650 * Check parameters that are required for a search. This should
651 * be called before performing a search.
652 * \param qry Query object to process.
653 * \return <i>TRUE</i> if search criteria appear OK.
655 gboolean ldapqry_check_search( LdapQuery *qry ) {
657 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
659 /* Test for control data */
665 /* Test for search value */
666 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
669 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
672 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
677 * Touch the query. This nudges the touch time with the current time.
678 * \param qry Query object to process.
680 void ldapqry_touch( LdapQuery *qry ) {
681 qry->touchTime = time( NULL );
682 qry->agedFlag = FALSE;
686 * Connect to LDAP server.
687 * \param qry Query object to process.
688 * \return Error/status code.
690 static gint ldapqry_connect( LdapQuery *qry ) {
694 /* Initialize connection */
695 if (debug_get_mode()) {
696 debug_print("===ldapqry_connect===\n");
697 /*ldapqry_print(qry, stdout);*/
700 /*if (debug_get_mode()) {
701 ldapctl_print(ctl, stdout);
702 debug_print("======\n");
704 ldapqry_touch( qry );
705 qry->startTime = qry->touchTime;
706 qry->elapsedTime = -1;
707 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
709 ld = ldapsvr_connect(ctl);
712 return ADDRQUERY_RETVAL(qry);
715 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
716 if( ldapqry_get_stop_flag( qry ) ) {
717 return ADDRQUERY_RETVAL(qry);
719 ldapqry_touch( qry );
721 debug_print("connected to LDAP host %s on port %d\n",
722 ctl->hostName?ctl->hostName:"null", ctl->port);
724 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
725 if( ldapqry_get_stop_flag( qry ) ) {
726 return ADDRQUERY_RETVAL(qry);
728 ldapqry_touch( qry );
730 ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
732 return ADDRQUERY_RETVAL(qry);
736 * Connect to LDAP server.
737 * \param qry Query object to process.
738 * \return Error/status code.
740 static gint ldapqry_disconnect( LdapQuery *qry ) {
744 rc = ldap_unbind_ext( qry->ldap, NULL, NULL );
745 if (rc != LDAP_SUCCESS) {
746 log_error(LOG_PROTOCOL, _("LDAP error (unbind): %d (%s)\n"),
747 rc, ldaputil_get_error(qry->ldap));
749 log_message(LOG_PROTOCOL, _("LDAP (unbind): successful\n"));
754 ldapqry_touch( qry );
755 qry->elapsedTime = qry->touchTime - qry->startTime;
757 return ADDRQUERY_RETVAL(qry);
761 * Perform the LDAP search, reading LDAP entries into cache.
762 * Note that one LDAP entry can have multiple values for many of its
763 * attributes. If these attributes are E-Mail addresses; these are
764 * broken out into separate address items. For any other attribute,
765 * only the first occurrence is read.
767 * \param qry Query object to process.
768 * \return Error/status code.
770 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
773 LDAPMessage *result = NULL, *e = NULL;
777 gboolean entriesFound;
779 struct timeval timeout;
784 /* Initialize some variables */
787 cache = qry->server->addressCache;
788 timeout.tv_sec = ctl->timeOut;
789 timeout.tv_usec = 0L;
790 entriesFound = FALSE;
791 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
793 /* Define all attributes we are interested in. */
794 attribs = ldapctl_full_attribute_array( ctl );
796 /* Create LDAP search string */
797 criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
798 debug_print("Search criteria ::%s::\n", criteria?criteria:"null");
801 * Execute the search - this step may take some time to complete
802 * depending on network traffic and server response time.
804 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
805 rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
806 attribs, 0, NULL, NULL, &timeout, 0, &result );
807 debug_print("LDAP ldap_search_ext_s: %d (%s)\n", rc, ldaputil_get_error(ld));
808 ldapctl_free_attribute_array( attribs );
811 if( rc == LDAP_TIMEOUT ) {
812 log_warning(LOG_PROTOCOL, _("LDAP (search): timeout\n"));
813 return ADDRQUERY_RETVAL(qry);
815 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
817 /* Test valid returns */
819 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
820 log_warning(LOG_PROTOCOL, _("LDAP (search): server limits exceeded\n"));
823 else if( rc == LDAP_SUCCESS ) {
824 log_message(LOG_PROTOCOL, _("LDAP (search): successful\n"));
827 else if( rc == LDAP_PARTIAL_RESULTS || (result && ldap_count_entries(ld, result) > 0) ) {
828 log_message(LOG_PROTOCOL, _("LDAP (search): successful (partial results)\n"));
832 log_error(LOG_PROTOCOL, _("LDAP error (search): %d (%s)\n"), rc, ldaputil_get_error(ld));
833 return ADDRQUERY_RETVAL(qry);
835 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
838 debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
840 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
843 /* Process results */
845 while( searchFlag ) {
846 ldapqry_touch( qry );
847 if( qry->entriesRead >= ctl->maxEntries ) break;
850 if( ldapqry_get_stop_flag( qry ) ) {
857 e = ldap_first_entry( ld, result );
860 e = ldap_next_entry( ld, e );
862 if( e == NULL ) break;
865 /* Setup a critical section here */
866 pthread_mutex_lock( qry->mutexEntry );
869 listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
871 /* Process callback */
872 if( qry->callBackEntry )
873 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
875 g_list_free( listEMail );
876 pthread_mutex_unlock( qry->mutexEntry );
879 /* Free up and disconnect */
880 ldap_msgfree( result );
884 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
887 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
891 return ADDRQUERY_RETVAL(qry);
895 * Connection, perform search and disconnect.
896 * \param qry Query object to process.
897 * \return Error/status code.
899 static gint ldapqry_perform_search( LdapQuery *qry ) {
900 /* Check search criteria */
901 if( ! ldapqry_check_search( qry ) ) {
902 return ADDRQUERY_RETVAL(qry);
907 ldapqry_connect( qry );
908 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
910 ldapqry_search_retrieve( qry );
913 ldapqry_disconnect( qry );
916 return ADDRQUERY_RETVAL(qry);
919 static gint ldapqry_perform_locate( LdapQuery *qry );
922 * Wrapper around search.
923 * \param qry Query object to process.
924 * \return Error/status code.
926 static gint ldapqry_search( LdapQuery *qry ) {
929 cm_return_val_if_fail( qry != NULL, -1 );
930 cm_return_val_if_fail( qry->control != NULL, -1 );
932 ldapqry_touch( qry );
933 qry->completed = FALSE;
935 /* Setup pointer to thread specific area */
936 pthread_setspecific( _queryThreadKey_, qry );
938 pthread_detach( pthread_self() );
940 /* Now perform the search */
941 qry->entriesRead = 0;
942 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
943 ldapqry_set_busy_flag( qry, TRUE );
944 ldapqry_set_stop_flag( qry, FALSE );
945 if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
946 retVal = ldapqry_perform_locate( qry );
949 retVal = ldapqry_perform_search( qry );
951 if( retVal == LDAPRC_SUCCESS ) {
952 qry->server->addressCache->dataRead = TRUE;
953 qry->server->addressCache->accessFlag = FALSE;
954 if( ldapqry_get_stop_flag( qry ) ) {
955 debug_print("Search was terminated prematurely\n");
958 ldapqry_touch( qry );
959 qry->completed = TRUE;
960 debug_print("Search ran to completion\n");
963 ldapqry_set_stop_flag( qry, TRUE );
964 ldapqry_set_busy_flag( qry, FALSE );
966 /* Process callback */
967 if( qry->callBackEnd ) {
968 g_timeout_add(0, callbackend, qry);
971 return ADDRQUERY_RETVAL(qry);
975 * Read data into list using a background thread. Callback function will be
976 * notified when search is complete.
977 * \param qry Query object to process.
978 * \return Error/status code.
980 gint ldapqry_read_data_th( LdapQuery *qry ) {
981 cm_return_val_if_fail( qry != NULL, -1 );
982 cm_return_val_if_fail( qry->control != NULL, -1 );
984 ldapqry_set_stop_flag( qry, FALSE );
985 ldapqry_touch( qry );
986 if( ldapqry_check_search( qry ) ) {
987 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
988 debug_print("Starting LDAP search thread\n");
989 ldapqry_set_busy_flag( qry, TRUE );
990 qry->thread = g_malloc0( sizeof( pthread_t ) );
993 if (pthread_create( qry->thread, NULL,
994 (void *) ldapqry_search, (void *) qry ) != 0) {
997 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1001 return ADDRQUERY_RETVAL(qry);
1005 * Cleanup LDAP thread data. This function will be called when each thread
1006 * exits. Note that the thread object will be freed by the kernel.
1007 * \param ptr Pointer to object being destroyed (a query object in this case).
1009 static void ldapqry_destroyer( void * ptr ) {
1012 qry = ( LdapQuery * ) ptr;
1013 cm_return_if_fail( qry != NULL );
1015 /* Perform any destruction here */
1016 if( qry->control != NULL ) {
1017 ldapctl_free( qry->control );
1019 qry->control = NULL;
1021 ldapqry_set_busy_flag( qry, FALSE );
1025 * Cancel thread associated with query.
1026 * \param qry Query object to process.
1028 void ldapqry_cancel( LdapQuery *qry ) {
1029 cm_return_if_fail( qry != NULL );
1031 if( ldapqry_get_busy_flag( qry ) ) {
1033 debug_print("calling pthread_cancel\n");
1034 pthread_cancel( * qry->thread );
1040 * Initialize LDAP query. This function should be called once before executing
1041 * any LDAP queries to initialize thread specific data.
1043 void ldapqry_initialize( void ) {
1044 debug_print("ldapqry_initialize...\n");
1045 if( ! _queryThreadInit_ ) {
1046 debug_print("ldapqry_initialize::creating thread specific area\n");
1047 pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1048 _queryThreadInit_ = TRUE;
1050 debug_print("ldapqry_initialize... done!\n");
1054 * Age the query based on LDAP control parameters.
1055 * \param qry Query object to process.
1056 * \param maxAge Maximum age of query (in seconds).
1058 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1061 cm_return_if_fail( qry != NULL );
1063 /* Limit the time that queries can hang around */
1064 if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1066 /* Check age of query */
1067 age = time( NULL ) - qry->touchTime;
1068 if( age > maxAge ) {
1069 qry->agedFlag = TRUE;
1074 * Delete folder associated with query results.
1075 * \param qry Query object to process.
1077 void ldapqry_delete_folder( LdapQuery *qry ) {
1078 AddressCache *cache;
1081 cm_return_if_fail( qry != NULL );
1083 folder = ADDRQUERY_FOLDER(qry);
1085 cache = qry->server->addressCache;
1086 folder = addrcache_remove_folder_delete( cache, folder );
1088 addritem_free_item_folder( folder );
1090 ADDRQUERY_FOLDER(qry) = NULL;
1095 * Create a name/value pair object.
1098 * \return Initialized object.
1100 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1101 NameValuePair *nvp = g_new0( NameValuePair, 1 );
1103 nvp->name = g_strdup( n );
1104 nvp->value = g_strdup( v );
1109 * Free up name/value pair object.
1110 * \param nvp Name/value object.
1112 void ldapqry_free_name_value( NameValuePair *nvp ) {
1114 g_free( nvp->name );
1115 g_free( nvp->value );
1116 nvp->name = nvp->value = NULL;
1122 * Free up a list name/value pair objects.
1123 * \param list List of name/value objects.
1125 void ldapqry_free_list_name_value( GList *list ) {
1130 NameValuePair *nvp = ( NameValuePair * ) node->data;
1131 ldapqry_free_name_value( nvp );
1133 node = g_list_next( node );
1135 g_list_free( list );
1139 * Load a list of name/value pairs from LDAP attributes.
1140 * \param ld LDAP handle.
1141 * \param e LDAP message.
1142 * \param attr Attribute name.
1143 * \param listValues List to populate.
1144 * \return List of attribute name/value pairs.
1146 static GList *ldapqry_load_attrib_values(
1147 LDAP *ld, LDAPMessage *entry, char *attr,
1152 struct berval **vals;
1156 if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1157 for( i = 0; vals[i] != NULL; i++ ) {
1158 gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1159 nvp = ldapqry_create_name_value( attr, tmp );
1161 list = g_list_append( list, nvp );
1164 ldap_value_free_len( vals );
1169 * Fetch a list of all attributes.
1170 * \param ld LDAP handle.
1171 * \param e LDAP message.
1172 * \return List of attribute name/value pairs.
1174 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1178 GList *listValues = NULL;
1180 /* Process all attributes */
1181 for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1182 attribute = ldap_next_attribute( ld, e, ber ) ) {
1183 listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1184 ldap_memfree( attribute );
1194 #define CRITERIA_SINGLE "(objectclass=*)"
1197 * Perform the data retrieval for a specific LDAP record.
1199 * \param qry Query object to process.
1200 * \return Error/status code.
1202 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1205 LDAPMessage *result, *e = NULL;
1206 gboolean entriesFound;
1208 struct timeval timeout;
1213 /* Initialize some variables */
1216 dn = ADDRQUERY_SEARCHVALUE(qry);
1217 timeout.tv_sec = ctl->timeOut;
1218 timeout.tv_usec = 0L;
1219 entriesFound = FALSE;
1222 * Execute the search - this step may take some time to complete
1223 * depending on network traffic and server response time.
1225 ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1226 rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1227 NULL, 0, NULL, NULL, &timeout, 0, &result );
1228 if( rc == LDAP_TIMEOUT ) {
1229 return ADDRQUERY_RETVAL(qry);
1231 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1232 if( rc != LDAP_SUCCESS ) {
1233 log_error(LOG_PROTOCOL, _("LDAP error (search): %d (%s)\n"),
1234 rc, ldaputil_get_error(ld));
1235 debug_print("LDAP Error: ldap_search_ext_s: %d (%s)\n",
1236 rc, ldaputil_get_error(ld));
1237 return ADDRQUERY_RETVAL(qry);
1239 log_message(LOG_PROTOCOL, _("LDAP (search): successful\n"));
1243 debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
1245 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1248 /* Process results */
1249 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1252 ldapqry_touch( qry );
1253 if( qry->entriesRead >= ctl->maxEntries ) break;
1256 if( ldapqry_get_stop_flag( qry ) ) {
1260 /* Retrieve entry */
1263 e = ldap_first_entry( ld, result );
1266 e = ldap_next_entry( ld, e );
1268 if( e == NULL ) break;
1270 entriesFound = TRUE;
1272 /* Setup a critical section here */
1273 pthread_mutex_lock( qry->mutexEntry );
1276 listValues = ldapqry_fetch_attribs( ld, e );
1278 /* Process callback */
1279 if( qry->callBackEntry ) {
1280 qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1282 ldapqry_free_list_name_value( listValues );
1285 pthread_mutex_unlock( qry->mutexEntry );
1288 /* Free up and disconnect */
1289 ldap_msgfree( result );
1291 if( entriesFound ) {
1292 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1295 ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1298 return ADDRQUERY_RETVAL(qry);
1302 * Perform the search to locate a specific LDAP record identified by
1303 * distinguished name (dn).
1305 * \param qry Query object to process.
1306 * \return Error/status code.
1308 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1311 ldapqry_connect( qry );
1312 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1313 /* Perform search */
1314 ldapqry_locate_retrieve( qry );
1317 ldapqry_disconnect( qry );
1320 /* Process callback */
1321 if( qry->callBackEnd ) {
1322 g_timeout_add(0, callbackend, qry);
1325 return ADDRQUERY_RETVAL(qry);
1329 * Remove results (folder and data) for specified LDAP query.
1330 * \param qry Query object to process.
1331 * \return TRUE if folder deleted successfully.
1333 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1334 gboolean retVal = FALSE;
1336 ldapqry_set_aged_flag( qry, TRUE );
1338 if( ldapqry_get_busy_flag( qry ) ) {
1339 ldapqry_set_stop_flag( qry, TRUE );
1342 LdapServer *server = qry->server;
1343 server->listQuery = g_list_remove(server->listQuery, qry);
1350 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1351 cm_return_if_fail( qry != NULL );
1353 ldapsvr_print_data(qry->server, stream);
1354 ldapctl_print(qry->control, stream);
1355 fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1356 fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1357 fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1358 fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1359 fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1360 fprintf(stream, "completed: %d\n", qry->completed);
1361 fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1362 fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1363 fprintf(stream, "data: %s\n", qry->data?(gchar *)qry->data:"null");
1366 #endif /* USE_LDAP */