2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2022 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"
41 #include "file-utils.h"
44 #include "addrcache.h"
45 #include "common/utils.h"
49 * Key for thread specific data.
51 static pthread_key_t _queryThreadKey_
;
52 static gboolean _queryThreadInit_
= FALSE
;
54 static gboolean
callbackend (gpointer data
)
56 LdapQuery
*qry
= (LdapQuery
*)data
;
57 qry
->callBackEnd( qry
, ADDRQUERY_ID(qry
), ADDRQUERY_RETVAL(qry
), qry
->data
);
63 * Create new LDAP query object.
64 * \return Initialized query object.
66 LdapQuery
*ldapqry_create( void ) {
69 qry
= g_new0( LdapQuery
, 1 );
70 ADDRQUERY_TYPE(qry
) = ADDRQUERY_LDAP
;
71 ADDRQUERY_ID(qry
) = 0;
72 ADDRQUERY_SEARCHTYPE(qry
) = ADDRSEARCH_NONE
;
73 ADDRQUERY_NAME(qry
) = NULL
;
74 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
75 ADDRQUERY_FOLDER(qry
) = NULL
;
76 ADDRQUERY_SEARCHVALUE(qry
) = NULL
;
81 qry
->stopFlag
= FALSE
;
82 qry
->busyFlag
= FALSE
;
83 qry
->agedFlag
= FALSE
;
84 qry
->completed
= FALSE
;
86 qry
->callBackEntry
= NULL
;
87 qry
->callBackEnd
= NULL
;
91 /* Mutex to protect stop and busy flags */
92 qry
->mutexStop
= g_malloc0( sizeof( pthread_mutex_t
) );
93 pthread_mutex_init( qry
->mutexStop
, NULL
);
94 qry
->mutexBusy
= g_malloc0( sizeof( pthread_mutex_t
) );
95 pthread_mutex_init( qry
->mutexBusy
, NULL
);
97 /* Mutex to protect critical section */
98 qry
->mutexEntry
= g_malloc0( sizeof( pthread_mutex_t
) );
99 pthread_mutex_init( qry
->mutexEntry
, NULL
);
105 * Specify the reference to control data that will be used for the query. The calling
106 * module should be responsible for creating and destroying this control object.
107 * \param qry Query object.
108 * \param ctl Control object.
110 void ldapqry_set_control( LdapQuery
*qry
, LdapControl
*ctl
) {
111 cm_return_if_fail( qry
!= NULL
);
116 * Specify query name to be used.
117 * \param qry Query object.
120 void ldapqry_set_name( LdapQuery
* qry
, const gchar
*value
) {
121 cm_return_if_fail( qry
!= NULL
);
122 ADDRQUERY_NAME(qry
) = mgu_replace_string( ADDRQUERY_NAME(qry
), value
);
123 if (ADDRQUERY_NAME(qry
) == NULL
)
125 g_strstrip( ADDRQUERY_NAME(qry
) );
126 debug_print("set name: %s\n", ADDRQUERY_NAME(qry
));
130 * Specify search value to be used.
131 * \param qry Query object.
134 void ldapqry_set_search_value( LdapQuery
*qry
, const gchar
*value
) {
135 cm_return_if_fail( qry
!= NULL
);
136 ADDRQUERY_SEARCHVALUE(qry
) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry
), value
);
137 if (ADDRQUERY_SEARCHVALUE(qry
) == NULL
)
139 g_strstrip( ADDRQUERY_SEARCHVALUE(qry
) );
140 debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry
));
144 * Specify query type.
145 * \param qry Query object.
146 * \param value Query type, either:
148 * <li><code>LDAPQUERY_NONE</code></li>
149 * <li><code>LDAPQUERY_STATIC</code></li>
150 * <li><code>LDAPQUERY_DYNAMIC</code></li>
154 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
155 ADDRQUERY_TYPE(qry) = value;
160 * Specify search type.
161 * \param qry Query object.
164 void ldapqry_set_search_type( LdapQuery
*qry
, const AddrSearchType value
) {
165 cm_return_if_fail( qry
!= NULL
);
166 ADDRQUERY_SEARCHTYPE(qry
) = value
;
171 * \param qry Query object.
172 * \param value ID for the query.
174 void ldapqry_set_query_id( LdapQuery
* qry
, const gint value
) {
175 cm_return_if_fail( qry
!= NULL
);
176 ADDRQUERY_ID(qry
) = value
;
180 * Register a callback function that will be executed when each entry
181 * has been read and processed. When called, the function will be passed
182 * this query object and a GList of ItemEMail objects as arguments. An
183 * example of typical usage is shown below.
186 * ------------------------------------------------------------
187 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
192 * ItemEMail *email = node->data;
193 * ... process email object ...
194 * node = g_list_next( node );
196 * g_list_free( listEMail );
200 * ldapqry_set_callback_entry( qry, myCallbackEntry );
201 * ------------------------------------------------------------
204 * \param qry Query object.
205 * \param func Function.
207 void ldapqry_set_callback_entry( LdapQuery
*qry
, void *func
) {
208 pthread_mutex_lock( qry
->mutexEntry
);
209 qry
->callBackEntry
= func
;
210 pthread_mutex_unlock( qry
->mutexEntry
);
214 * Register a callback function that will be executed when the search
215 * is complete. When called, the function will be passed this query
216 * object as an argument.
217 * \param qry Query object.
218 * \param func Function.
220 void ldapqry_set_callback_end( LdapQuery
*qry
, void *func
) {
221 qry
->callBackEnd
= func
;
225 * Notify query to start/stop executing. This method should be called with a
226 * value if <i>TRUE</i> to terminate an existing running query.
228 * \param qry Query object.
229 * \param value Value of stop flag.
231 void ldapqry_set_stop_flag( LdapQuery
*qry
, const gboolean value
) {
232 cm_return_if_fail( qry
!= NULL
);
234 pthread_mutex_lock( qry
->mutexStop
);
235 qry
->stopFlag
= value
;
236 pthread_mutex_unlock( qry
->mutexStop
);
240 * Test value of stop flag. This method should be used to determine whether a
241 * query has stopped running.
242 * \param qry Query object.
243 * \return Value of stop flag.
245 static gboolean
ldapqry_get_stop_flag( LdapQuery
*qry
) {
247 cm_return_val_if_fail( qry
!= NULL
, TRUE
);
249 pthread_mutex_lock( qry
->mutexStop
);
250 value
= qry
->stopFlag
;
251 pthread_mutex_unlock( qry
->mutexStop
);
257 * \param qry Query object.
258 * \param value Value of busy flag.
260 static void ldapqry_set_busy_flag( LdapQuery
*qry
, const gboolean value
) {
261 cm_return_if_fail( qry
!= NULL
);
262 if (qry
->mutexBusy
== NULL
)
263 return; /* exiting, mutex already freed */
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 static gboolean
ldapqry_get_busy_flag( LdapQuery
*qry
) {
278 cm_return_val_if_fail( qry
!= NULL
, FALSE
);
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 static void ldapqry_set_aged_flag( LdapQuery
*qry
, const gboolean value
) {
292 cm_return_if_fail( qry
!= NULL
);
293 qry
->agedFlag
= value
;
297 * Clear LDAP query member variables.
298 * \param qry Query object.
300 static void ldapqry_clear( LdapQuery
*qry
) {
301 cm_return_if_fail( qry
!= NULL
);
303 /* Free internal stuff */
304 g_free( ADDRQUERY_NAME(qry
) );
305 g_free( ADDRQUERY_SEARCHVALUE(qry
) );
307 /* Clear pointers and value */
308 ADDRQUERY_NAME(qry
) = NULL
;
309 ADDRQUERY_SEARCHVALUE(qry
) = NULL
;
310 ADDRQUERY_ID(qry
) = 0;
311 ADDRQUERY_SEARCHTYPE(qry
) = ADDRSEARCH_NONE
;
312 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
313 qry
->entriesRead
= 0;
314 qry
->elapsedTime
= 0;
315 qry
->stopFlag
= FALSE
;
316 qry
->busyFlag
= FALSE
;
317 qry
->agedFlag
= FALSE
;
318 qry
->completed
= FALSE
;
319 qry
->callBackEntry
= NULL
;
320 qry
->callBackEnd
= NULL
;
326 * Free up LDAP query object by releasing internal memory. Note that
327 * the thread object will be freed by the OS.
328 * \param qry Query object to process.
330 void ldapqry_free( LdapQuery
*qry
) {
331 cm_return_if_fail( qry
!= NULL
);
333 /* Clear out internal members */
334 ADDRQUERY_TYPE(qry
) = ADDRQUERY_NONE
;
335 ldapqry_clear( qry
);
338 pthread_mutex_destroy( qry
->mutexStop
);
339 pthread_mutex_destroy( qry
->mutexBusy
);
340 pthread_mutex_destroy( qry
->mutexEntry
);
341 g_free( qry
->mutexStop
);
342 g_free( qry
->mutexBusy
);
343 g_free( qry
->mutexEntry
);
344 qry
->mutexEntry
= NULL
;
345 qry
->mutexBusy
= NULL
;
346 qry
->mutexStop
= NULL
;
348 /* Do not free folder - parent server object should free */
349 ADDRQUERY_FOLDER(qry
) = NULL
;
351 /* Do not free thread - thread should be terminated before freeing */
354 /* Do not free LDAP control - should be destroyed before freeing */
357 /* Now release object */
362 * Free linked lists of character strings.
363 * \param listName List of common names.
364 * \param listAddr List of addresses.
365 * \param listFirst List of first names.
366 * \param listLast List of last names.
368 static void ldapqry_free_lists(
369 GSList
*listName
, GSList
*listAddr
, GSList
*listFirst
,
370 GSList
*listLast
, GSList
*listDisplay
, GSList
*other_attrs
)
372 GSList
*cur
= other_attrs
;
373 g_slist_free_full( listName
, g_free
);
374 g_slist_free_full( listAddr
, g_free
);
375 g_slist_free_full( listFirst
, g_free
);
376 g_slist_free_full( listLast
, g_free
);
377 g_slist_free_full( listDisplay
, g_free
);
378 for(;cur
; cur
= cur
->next
)
379 addritem_free_attribute((UserAttribute
*)cur
->data
);
380 g_slist_free(other_attrs
);
384 * Add all LDAP attribute values to a list.
385 * \param ld LDAP handle.
386 * \param entry LDAP entry to process.
387 * \param attr LDAP attribute.
388 * \return List of values.
390 static GSList
*ldapqry_add_list_values(
391 LDAP
*ld
, LDAPMessage
*entry
, char *attr
)
395 struct berval
**vals
;
397 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
398 for( i
= 0; vals
[i
] != NULL
; i
++ ) {
399 /*debug_print("lv\t%s: %s\n", attr?attr:"null",
400 vals[i]->bv_val?vals[i]->bv_val:"null");*/
401 list
= g_slist_append( list
, g_strndup( vals
[i
]->bv_val
, vals
[i
]->bv_len
) );
404 ldap_value_free_len( vals
);
409 * Add a single attribute value to a list.
410 * \param ld LDAP handle.
411 * \param entry LDAP entry to process.
412 * \param attr LDAP attribute name to process.
413 * \return List of values; only one value will be present.
415 static GSList
*ldapqry_add_single_value( LDAP
*ld
, LDAPMessage
*entry
, char *attr
) {
417 struct berval
**vals
;
419 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
420 if( vals
[0] != NULL
) {
421 if (strcmp(attr
, "jpegPhoto")) {
422 debug_print("sv\t%s: %s\n", attr
?attr
:"null",
423 vals
[0]->bv_val
?vals
[0]->bv_val
:"null");
424 list
= g_slist_append( list
, g_strndup( vals
[0]->bv_val
, vals
[0]->bv_len
));
426 char *file
= get_tmp_file();
427 FILE *fp
= claws_fopen(file
, "wb");
429 claws_fwrite(vals
[0]->bv_val
, 1, vals
[0]->bv_len
, fp
);
430 claws_safe_fclose(fp
);
432 list
= g_slist_append( list
, file
);
436 ldap_value_free_len( vals
);
441 * Build an address list entry and append to list of address items. Name is formatted
442 * as "<first-name> <last-name>".
444 * \param cache Address cache to load.
445 * \param qry Query object to process.
446 * \param dn DN for entry found on server.
447 * \param listName List of common names for entry; see notes below.
448 * \param listAddr List of EMail addresses for entry.
449 * \param listFirst List of first names for entry.
450 * \param listLast List of last names for entry.
452 * \return List of ItemEMail objects.
455 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
456 * name. For example, a single entry for a person may have more than one
457 * common name, email address, etc.
459 * 2) The DN for the entry is unique for the server.
461 static GList
*ldapqry_build_items_fl(
462 AddressCache
*cache
, LdapQuery
*qry
, gchar
*dn
,
463 GSList
*listName
, GSList
*listAddr
, GSList
*listFirst
,
464 GSList
*listLast
, GSList
*listDisplay
, GSList
*attributes
)
466 GSList
*nodeAddress
, *cur
;
467 gchar
*firstName
= NULL
, *lastName
= NULL
, *fullName
= NULL
;
468 gboolean allocated
= FALSE
;
472 gchar
*picfile
= NULL
;
473 GList
*listReturn
= NULL
;
475 folder
= ADDRQUERY_FOLDER(qry
);
476 if( folder
== NULL
) return listReturn
;
477 if( listAddr
== NULL
) return listReturn
;
481 fullName
= listDisplay
->data
;
484 /* Find longest first name in list */
485 firstName
= mgu_slist_longest_entry( listFirst
);
487 /* Format last name */
489 lastName
= listLast
->data
;
492 if ( fullName
== NULL
) {
493 /* Find longest common name */
495 fullName
= mgu_slist_longest_entry( listName
);
496 if( fullName
== NULL
) {
497 /* Format a full name from first and last names */
500 fullName
= g_strdup_printf( "%s %s", firstName
, lastName
);
503 fullName
= g_strdup_printf( "%s", firstName
);
508 fullName
= g_strdup_printf( "%s", lastName
);
512 g_strstrip( fullName
);
518 /* Add person into folder */
519 person
= addritem_create_item_person();
520 addritem_person_set_common_name( person
, fullName
);
521 addritem_person_set_first_name( person
, firstName
);
522 addritem_person_set_last_name( person
, lastName
);
523 addritem_person_set_nick_name( person
, fullName
);
524 addrcache_id_person( cache
, person
);
525 addritem_person_set_external_id( person
, dn
);
527 for (cur
= attributes
; cur
; cur
= cur
->next
) {
528 UserAttribute
*attrib
= addritem_copy_attribute((UserAttribute
*)cur
->data
);
529 if (attrib
->name
&& strcmp(attrib
->name
, "jpegPhoto")) {
530 addritem_person_add_attribute( person
, attrib
);
532 if (qry
->server
&& qry
->server
->control
) {
533 gchar
*dir
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
,
534 ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
, NULL
);
535 gchar
*filename
= g_strdup_printf("%s-%s-%s",
536 qry
->server
->control
->hostName
?qry
->server
->control
->hostName
:"nohost",
537 qry
->server
->control
->baseDN
?qry
->server
->control
->baseDN
:"nobase",
539 picfile
= g_strdup_printf("%s%s.png", dir
, filename
);
540 addritem_person_set_picture( person
, filename
);
541 rename_force(attrib
->value
, picfile
);
549 addrcache_folder_add_person( cache
, ADDRQUERY_FOLDER(qry
), person
);
553 /* Add each address item */
554 nodeAddress
= listAddr
;
555 while( nodeAddress
) {
556 email
= addritem_create_item_email();
557 addritem_email_set_address( email
, nodeAddress
->data
);
558 addrcache_id_email( cache
, email
);
559 addrcache_person_add_email( cache
, person
, email
);
560 addritem_person_add_email( person
, email
);
561 /*if (debug_get_mode()) {
562 addritem_print_item_email(email, stdout);
564 listReturn
= g_list_append( listReturn
, email
);
565 nodeAddress
= g_slist_next( nodeAddress
);
568 /* Free any allocated memory */
572 fullName
= firstName
= lastName
= NULL
;
578 * Process a single search entry.
579 * \param cache Address cache to load.
580 * \param qry Query object to process.
581 * \param ld LDAP handle.
582 * \param e LDAP message.
583 * \return List of EMail objects found.
585 static GList
*ldapqry_process_single_entry(
586 AddressCache
*cache
, LdapQuery
*qry
, LDAP
*ld
, LDAPMessage
*e
)
592 GSList
*listName
= NULL
, *listAddress
= NULL
;
593 GSList
*listFirst
= NULL
, *listLast
= NULL
;
594 GSList
*listDisplay
= NULL
;
595 GSList
*other_attrs
= NULL
;
600 dnEntry
= ldap_get_dn( ld
, e
);
601 debug_print( "DN: %s\n", dnEntry
?dnEntry
:"null" );
603 /* Process all attributes */
604 for( attribute
= ldap_first_attribute( ld
, e
, &ber
); attribute
!= NULL
;
605 attribute
= ldap_next_attribute( ld
, e
, ber
) ) {
606 if( strcasecmp( attribute
, ctl
->attribEMail
) == 0 ) {
607 listAddress
= ldapqry_add_list_values( ld
, e
, attribute
);
609 else if( strcasecmp( attribute
, ctl
->attribCName
) == 0 ) {
610 listName
= ldapqry_add_list_values( ld
, e
, attribute
);
612 else if( strcasecmp( attribute
, ctl
->attribFName
) == 0 ) {
613 listFirst
= ldapqry_add_list_values( ld
, e
, attribute
);
615 else if( strcasecmp( attribute
, ctl
->attribLName
) == 0 ) {
616 listLast
= ldapqry_add_single_value( ld
, e
, attribute
);
617 } else if( strcasecmp( attribute
, ctl
->attribDName
) == 0 ) {
618 listDisplay
= ldapqry_add_single_value( ld
, e
, attribute
);
620 GSList
*attlist
= ldapqry_add_single_value( ld
, e
, attribute
);
621 UserAttribute
*attrib
= addritem_create_attribute();
622 const gchar
*attvalue
= attlist
?((gchar
*)attlist
->data
):NULL
;
624 addritem_attrib_set_name( attrib
, attribute
);
625 addritem_attrib_set_value( attrib
, attvalue
);
626 other_attrs
= g_slist_prepend(other_attrs
, attrib
);
628 g_slist_free_full(attlist
, g_free
);
630 /* Free memory used to store attribute */
631 ldap_memfree( attribute
);
634 /* Format and add items to cache */
635 listReturn
= ldapqry_build_items_fl(
636 cache
, qry
, dnEntry
, listName
, listAddress
, listFirst
, listLast
, listDisplay
, other_attrs
);
639 ldapqry_free_lists( listName
, listAddress
, listFirst
, listLast
, listDisplay
, other_attrs
);
640 listName
= listAddress
= listFirst
= listLast
= listDisplay
= other_attrs
= NULL
;
645 ldap_memfree( dnEntry
);
651 * Check parameters that are required for a search. This should
652 * be called before performing a search.
653 * \param qry Query object to process.
654 * \return <i>TRUE</i> if search criteria appear OK.
656 gboolean
ldapqry_check_search( LdapQuery
*qry
) {
658 ADDRQUERY_RETVAL(qry
) = LDAPRC_CRITERIA
;
660 /* Test for control data */
666 /* Test for search value */
667 if( ADDRQUERY_SEARCHVALUE(qry
) == NULL
) {
670 if( strlen( ADDRQUERY_SEARCHVALUE(qry
) ) < 1 ) {
673 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
678 * Touch the query. This nudges the touch time with the current time.
679 * \param qry Query object to process.
681 void ldapqry_touch( LdapQuery
*qry
) {
682 qry
->touchTime
= time( NULL
);
683 qry
->agedFlag
= FALSE
;
687 * Connect to LDAP server.
688 * \param qry Query object to process.
689 * \return Error/status code.
691 static gint
ldapqry_connect( LdapQuery
*qry
) {
695 /* Initialize connection */
696 if (debug_get_mode()) {
697 debug_print("===ldapqry_connect===\n");
699 ldapqry_print(qry
, stdout
);
704 if (debug_get_mode()) {
705 ldapctl_print(ctl
, stdout
);
706 debug_print("======\n");
709 ldapqry_touch( qry
);
710 qry
->startTime
= qry
->touchTime
;
711 qry
->elapsedTime
= -1;
712 ADDRQUERY_RETVAL(qry
) = LDAPRC_INIT
;
714 ld
= ldapsvr_connect(ctl
);
717 return ADDRQUERY_RETVAL(qry
);
720 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
721 if( ldapqry_get_stop_flag( qry
) ) {
722 return ADDRQUERY_RETVAL(qry
);
724 ldapqry_touch( qry
);
726 debug_print("connected to LDAP host %s on port %d\n",
727 ctl
->hostName
?ctl
->hostName
:"null", ctl
->port
);
729 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
730 if( ldapqry_get_stop_flag( qry
) ) {
731 return ADDRQUERY_RETVAL(qry
);
733 ldapqry_touch( qry
);
735 ADDRQUERY_RETVAL(qry
) = LDAP_SUCCESS
;
737 return ADDRQUERY_RETVAL(qry
);
741 * Connect to LDAP server.
742 * \param qry Query object to process.
743 * \return Error/status code.
745 static gint
ldapqry_disconnect( LdapQuery
*qry
) {
749 rc
= ldap_unbind_ext( qry
->ldap
, NULL
, NULL
);
750 if (rc
!= LDAP_SUCCESS
) {
751 log_error(LOG_PROTOCOL
, _("LDAP error (unbind): %d (%s)\n"),
752 rc
, ldaputil_get_error(qry
->ldap
));
754 log_print(LOG_PROTOCOL
, _("LDAP (unbind): successful\n"));
759 ldapqry_touch( qry
);
760 qry
->elapsedTime
= qry
->touchTime
- qry
->startTime
;
762 return ADDRQUERY_RETVAL(qry
);
766 * Perform the LDAP search, reading LDAP entries into cache.
767 * Note that one LDAP entry can have multiple values for many of its
768 * attributes. If these attributes are E-Mail addresses; these are
769 * broken out into separate address items. For any other attribute,
770 * only the first occurrence is read.
772 * \param qry Query object to process.
773 * \return Error/status code.
775 static gint
ldapqry_search_retrieve( LdapQuery
*qry
) {
778 LDAPMessage
*result
= NULL
, *e
= NULL
;
782 gboolean entriesFound
;
784 struct timeval timeout
;
789 /* Initialize some variables */
792 cache
= qry
->server
->addressCache
;
793 timeout
.tv_sec
= ctl
->timeOut
;
794 timeout
.tv_usec
= 0L;
795 entriesFound
= FALSE
;
796 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
798 /* Define all attributes we are interested in. */
799 attribs
= ldapctl_full_attribute_array( ctl
);
801 /* Create LDAP search string */
802 criteria
= ldapctl_format_criteria( ctl
, ADDRQUERY_SEARCHVALUE(qry
) );
803 debug_print("Search criteria ::%s::\n", criteria
?criteria
:"null");
806 * Execute the search - this step may take some time to complete
807 * depending on network traffic and server response time.
809 ADDRQUERY_RETVAL(qry
) = LDAPRC_TIMEOUT
;
810 rc
= ldap_search_ext_s( ld
, ctl
->baseDN
, LDAP_SCOPE_SUBTREE
, criteria
,
811 attribs
, 0, NULL
, NULL
, &timeout
, 0, &result
);
812 debug_print("LDAP ldap_search_ext_s: %d (%s)\n", rc
, ldaputil_get_error(ld
));
813 ldapctl_free_attribute_array( attribs
);
816 if( rc
== LDAP_TIMEOUT
) {
817 log_warning(LOG_PROTOCOL
, _("LDAP (search): timeout\n"));
818 return ADDRQUERY_RETVAL(qry
);
820 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
822 /* Test valid returns */
824 if( rc
== LDAP_ADMINLIMIT_EXCEEDED
) {
825 log_warning(LOG_PROTOCOL
, _("LDAP (search): server limits exceeded\n"));
828 else if( rc
== LDAP_SUCCESS
) {
829 log_print(LOG_PROTOCOL
, _("LDAP (search): successful\n"));
832 else if( rc
== LDAP_PARTIAL_RESULTS
|| (result
&& ldap_count_entries(ld
, result
) > 0) ) {
833 log_print(LOG_PROTOCOL
, _("LDAP (search): successful (partial results)\n"));
837 log_error(LOG_PROTOCOL
, _("LDAP error (search): %d (%s)\n"), rc
, ldaputil_get_error(ld
));
838 return ADDRQUERY_RETVAL(qry
);
840 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
843 debug_print("Total results are: %lu\n", ldap_count_entries(ld
, result
));
845 debug_print("Total results are: %d\n", ldap_count_entries(ld
, result
));
848 /* Process results */
850 while( searchFlag
) {
851 ldapqry_touch( qry
);
852 if( qry
->entriesRead
>= ctl
->maxEntries
) break;
855 if( ldapqry_get_stop_flag( qry
) ) {
862 e
= ldap_first_entry( ld
, result
);
865 e
= ldap_next_entry( ld
, e
);
867 if( e
== NULL
) break;
870 /* Setup a critical section here */
871 pthread_mutex_lock( qry
->mutexEntry
);
874 listEMail
= ldapqry_process_single_entry( cache
, qry
, ld
, e
);
876 /* Process callback */
877 if( qry
->callBackEntry
)
878 qry
->callBackEntry( qry
, ADDRQUERY_ID(qry
), listEMail
, qry
->data
);
880 g_list_free( listEMail
);
881 pthread_mutex_unlock( qry
->mutexEntry
);
884 /* Free up and disconnect */
885 ldap_msgfree( result
);
889 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
892 ADDRQUERY_RETVAL(qry
) = LDAPRC_NOENTRIES
;
896 return ADDRQUERY_RETVAL(qry
);
900 * Connection, perform search and disconnect.
901 * \param qry Query object to process.
902 * \return Error/status code.
904 static gint
ldapqry_perform_search( LdapQuery
*qry
) {
905 /* Check search criteria */
906 if( ! ldapqry_check_search( qry
) ) {
907 return ADDRQUERY_RETVAL(qry
);
912 ldapqry_connect( qry
);
913 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
915 ldapqry_search_retrieve( qry
);
918 ldapqry_disconnect( qry
);
921 return ADDRQUERY_RETVAL(qry
);
924 static gint
ldapqry_perform_locate( LdapQuery
*qry
);
927 * Wrapper around search.
928 * \param qry Query object to process.
929 * \return Error/status code.
931 static gint
ldapqry_search( LdapQuery
*qry
) {
934 cm_return_val_if_fail( qry
!= NULL
, -1 );
935 cm_return_val_if_fail( qry
->control
!= NULL
, -1 );
937 ldapqry_touch( qry
);
938 qry
->completed
= FALSE
;
940 /* Setup pointer to thread specific area */
941 pthread_setspecific( _queryThreadKey_
, qry
);
943 pthread_detach( pthread_self() );
945 /* Now perform the search */
946 qry
->entriesRead
= 0;
947 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
948 ldapqry_set_busy_flag( qry
, TRUE
);
949 ldapqry_set_stop_flag( qry
, FALSE
);
950 if( ADDRQUERY_SEARCHTYPE(qry
) == ADDRSEARCH_LOCATE
) {
951 retVal
= ldapqry_perform_locate( qry
);
954 retVal
= ldapqry_perform_search( qry
);
956 if( retVal
== LDAPRC_SUCCESS
) {
957 qry
->server
->addressCache
->dataRead
= TRUE
;
958 qry
->server
->addressCache
->accessFlag
= FALSE
;
959 if( ldapqry_get_stop_flag( qry
) ) {
960 debug_print("Search was terminated prematurely\n");
963 ldapqry_touch( qry
);
964 qry
->completed
= TRUE
;
965 debug_print("Search ran to completion\n");
968 ldapqry_set_stop_flag( qry
, TRUE
);
969 ldapqry_set_busy_flag( qry
, FALSE
);
971 /* Process callback */
972 if( qry
->callBackEnd
) {
973 g_timeout_add(0, callbackend
, qry
);
976 return ADDRQUERY_RETVAL(qry
);
980 * Read data into list using a background thread. Callback function will be
981 * notified when search is complete.
982 * \param qry Query object to process.
983 * \return Error/status code.
985 gint
ldapqry_read_data_th( LdapQuery
*qry
) {
986 cm_return_val_if_fail( qry
!= NULL
, -1 );
987 cm_return_val_if_fail( qry
->control
!= NULL
, -1 );
989 ldapqry_set_stop_flag( qry
, FALSE
);
990 ldapqry_touch( qry
);
991 if( ldapqry_check_search( qry
) ) {
992 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
993 debug_print("Starting LDAP search thread\n");
994 ldapqry_set_busy_flag( qry
, TRUE
);
995 qry
->thread
= g_malloc0( sizeof( pthread_t
) );
998 if (pthread_create( qry
->thread
, NULL
,
999 (void *) ldapqry_search
, (void *) qry
) != 0) {
1000 g_free(qry
->thread
);
1002 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
1006 return ADDRQUERY_RETVAL(qry
);
1010 * Cleanup LDAP thread data. This function will be called when each thread
1011 * exits. Note that the thread object will be freed by the kernel.
1012 * \param ptr Pointer to object being destroyed (a query object in this case).
1014 static void ldapqry_destroyer( void * ptr
) {
1017 qry
= ( LdapQuery
* ) ptr
;
1018 cm_return_if_fail( qry
!= NULL
);
1020 /* Perform any destruction here */
1021 if( qry
->control
!= NULL
) {
1022 ldapctl_free( qry
->control
);
1024 qry
->control
= NULL
;
1026 ldapqry_set_busy_flag( qry
, FALSE
);
1030 * Cancel thread associated with query.
1031 * \param qry Query object to process.
1033 void ldapqry_cancel( LdapQuery
*qry
) {
1034 cm_return_if_fail( qry
!= NULL
);
1036 if( ldapqry_get_busy_flag( qry
) ) {
1038 debug_print("calling pthread_cancel\n");
1039 pthread_cancel( * qry
->thread
);
1045 * Initialize LDAP query. This function should be called once before executing
1046 * any LDAP queries to initialize thread specific data.
1048 void ldapqry_initialize( void ) {
1049 debug_print("ldapqry_initialize...\n");
1050 if( ! _queryThreadInit_
) {
1051 debug_print("ldapqry_initialize::creating thread specific area\n");
1052 pthread_key_create( &_queryThreadKey_
, ldapqry_destroyer
);
1053 _queryThreadInit_
= TRUE
;
1055 debug_print("ldapqry_initialize... done!\n");
1059 * Age the query based on LDAP control parameters.
1060 * \param qry Query object to process.
1061 * \param maxAge Maximum age of query (in seconds).
1063 void ldapqry_age( LdapQuery
*qry
, gint maxAge
) {
1066 cm_return_if_fail( qry
!= NULL
);
1068 /* Limit the time that queries can hang around */
1069 if( maxAge
< 1 ) maxAge
= LDAPCTL_MAX_QUERY_AGE
;
1071 /* Check age of query */
1072 age
= time( NULL
) - qry
->touchTime
;
1073 if( age
> maxAge
) {
1074 qry
->agedFlag
= TRUE
;
1079 * Delete folder associated with query results.
1080 * \param qry Query object to process.
1082 void ldapqry_delete_folder( LdapQuery
*qry
) {
1083 AddressCache
*cache
;
1086 cm_return_if_fail( qry
!= NULL
);
1088 folder
= ADDRQUERY_FOLDER(qry
);
1090 cache
= qry
->server
->addressCache
;
1091 folder
= addrcache_remove_folder_delete( cache
, folder
);
1093 addritem_free_item_folder( folder
);
1095 ADDRQUERY_FOLDER(qry
) = NULL
;
1100 * Create a name/value pair object.
1103 * \return Initialized object.
1105 static NameValuePair
*ldapqry_create_name_value( const gchar
*n
, const gchar
*v
) {
1106 NameValuePair
*nvp
= g_new0( NameValuePair
, 1 );
1108 nvp
->name
= g_strdup( n
);
1109 nvp
->value
= g_strdup( v
);
1114 * Free up name/value pair object.
1115 * \param nvp Name/value object.
1117 void ldapqry_free_name_value( NameValuePair
*nvp
) {
1119 g_free( nvp
->name
);
1120 g_free( nvp
->value
);
1121 nvp
->name
= nvp
->value
= NULL
;
1127 * Free up a list name/value pair objects.
1128 * \param list List of name/value objects.
1130 void ldapqry_free_list_name_value( GList
*list
) {
1135 NameValuePair
*nvp
= ( NameValuePair
* ) node
->data
;
1136 ldapqry_free_name_value( nvp
);
1138 node
= g_list_next( node
);
1140 g_list_free( list
);
1144 * Load a list of name/value pairs from LDAP attributes.
1145 * \param ld LDAP handle.
1146 * \param e LDAP message.
1147 * \param attr Attribute name.
1148 * \param listValues List to populate.
1149 * \return List of attribute name/value pairs.
1151 static GList
*ldapqry_load_attrib_values(
1152 LDAP
*ld
, LDAPMessage
*entry
, char *attr
,
1157 struct berval
**vals
;
1161 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
1162 for( i
= 0; vals
[i
] != NULL
; i
++ ) {
1163 gchar
*tmp
= g_strndup( vals
[i
]->bv_val
, vals
[i
]->bv_len
);
1164 nvp
= ldapqry_create_name_value( attr
, tmp
);
1166 list
= g_list_append( list
, nvp
);
1169 ldap_value_free_len( vals
);
1174 * Fetch a list of all attributes.
1175 * \param ld LDAP handle.
1176 * \param e LDAP message.
1177 * \return List of attribute name/value pairs.
1179 static GList
*ldapqry_fetch_attribs( LDAP
*ld
, LDAPMessage
*e
)
1183 GList
*listValues
= NULL
;
1185 /* Process all attributes */
1186 for( attribute
= ldap_first_attribute( ld
, e
, &ber
); attribute
!= NULL
;
1187 attribute
= ldap_next_attribute( ld
, e
, ber
) ) {
1188 listValues
= ldapqry_load_attrib_values( ld
, e
, attribute
, listValues
);
1189 ldap_memfree( attribute
);
1199 #define CRITERIA_SINGLE "(objectclass=*)"
1202 * Perform the data retrieval for a specific LDAP record.
1204 * \param qry Query object to process.
1205 * \return Error/status code.
1207 static gint
ldapqry_locate_retrieve( LdapQuery
*qry
) {
1210 LDAPMessage
*result
, *e
= NULL
;
1211 gboolean entriesFound
;
1213 struct timeval timeout
;
1218 /* Initialize some variables */
1221 dn
= ADDRQUERY_SEARCHVALUE(qry
);
1222 timeout
.tv_sec
= ctl
->timeOut
;
1223 timeout
.tv_usec
= 0L;
1224 entriesFound
= FALSE
;
1227 * Execute the search - this step may take some time to complete
1228 * depending on network traffic and server response time.
1230 ADDRQUERY_RETVAL(qry
) = LDAPRC_TIMEOUT
;
1231 rc
= ldap_search_ext_s( ld
, dn
, LDAP_SCOPE_BASE
, CRITERIA_SINGLE
,
1232 NULL
, 0, NULL
, NULL
, &timeout
, 0, &result
);
1233 if( rc
== LDAP_TIMEOUT
) {
1234 return ADDRQUERY_RETVAL(qry
);
1236 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
1237 if( rc
!= LDAP_SUCCESS
) {
1238 log_error(LOG_PROTOCOL
, _("LDAP error (search): %d (%s)\n"),
1239 rc
, ldaputil_get_error(ld
));
1240 debug_print("LDAP Error: ldap_search_ext_s: %d (%s)\n",
1241 rc
, ldaputil_get_error(ld
));
1242 return ADDRQUERY_RETVAL(qry
);
1244 log_print(LOG_PROTOCOL
, _("LDAP (search): successful\n"));
1248 debug_print("Total results are: %lu\n", ldap_count_entries(ld
, result
));
1250 debug_print("Total results are: %d\n", ldap_count_entries(ld
, result
));
1253 /* Process results */
1254 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
1257 ldapqry_touch( qry
);
1258 if( qry
->entriesRead
>= ctl
->maxEntries
) break;
1261 if( ldapqry_get_stop_flag( qry
) ) {
1265 /* Retrieve entry */
1268 e
= ldap_first_entry( ld
, result
);
1271 e
= ldap_next_entry( ld
, e
);
1273 if( e
== NULL
) break;
1275 entriesFound
= TRUE
;
1277 /* Setup a critical section here */
1278 pthread_mutex_lock( qry
->mutexEntry
);
1281 listValues
= ldapqry_fetch_attribs( ld
, e
);
1283 /* Process callback */
1284 if( qry
->callBackEntry
) {
1285 qry
->callBackEntry( qry
, ADDRQUERY_ID(qry
), listValues
, qry
->data
);
1287 ldapqry_free_list_name_value( listValues
);
1290 pthread_mutex_unlock( qry
->mutexEntry
);
1293 /* Free up and disconnect */
1294 ldap_msgfree( result
);
1296 if( entriesFound
) {
1297 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
1300 ADDRQUERY_RETVAL(qry
) = LDAPRC_NOENTRIES
;
1303 return ADDRQUERY_RETVAL(qry
);
1307 * Perform the search to locate a specific LDAP record identified by
1308 * distinguished name (dn).
1310 * \param qry Query object to process.
1311 * \return Error/status code.
1313 static gint
ldapqry_perform_locate( LdapQuery
*qry
) {
1316 ldapqry_connect( qry
);
1317 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
1318 /* Perform search */
1319 ldapqry_locate_retrieve( qry
);
1322 ldapqry_disconnect( qry
);
1325 /* Process callback */
1326 if( qry
->callBackEnd
) {
1327 g_timeout_add(0, callbackend
, qry
);
1330 return ADDRQUERY_RETVAL(qry
);
1334 * Remove results (folder and data) for specified LDAP query.
1335 * \param qry Query object to process.
1336 * \return TRUE if folder deleted successfully.
1338 gboolean
ldapquery_remove_results( LdapQuery
*qry
) {
1339 gboolean retVal
= FALSE
;
1341 ldapqry_set_aged_flag( qry
, TRUE
);
1343 if( ldapqry_get_busy_flag( qry
) ) {
1344 ldapqry_set_stop_flag( qry
, TRUE
);
1347 LdapServer
*server
= qry
->server
;
1348 server
->listQuery
= g_list_remove(server
->listQuery
, qry
);
1356 void ldapqry_print(LdapQuery
*qry
, FILE *stream
) {
1357 cm_return_if_fail( qry
!= NULL
);
1359 ldapsvr_print_data(qry
->server
, stream
);
1360 ldapctl_print(qry
->control
, stream
);
1361 fprintf(stream
, "entriesRead: %d\n", qry
->entriesRead
);
1362 fprintf(stream
, "elapsedTime: %d\n", qry
->elapsedTime
);
1363 fprintf(stream
, "stopFlag: %d\n", qry
->stopFlag
);
1364 fprintf(stream
, "busyFlag: %d\n", qry
->busyFlag
);
1365 fprintf(stream
, "agedFlag: %d\n", qry
->agedFlag
);
1366 fprintf(stream
, "completed: %d\n", qry
->completed
);
1367 fprintf(stream
, "startTime: %d\n", (int) qry
->startTime
);
1368 fprintf(stream
, "touchTime: %d\n", (int) qry
->touchTime
);
1369 fprintf(stream
, "data: %s\n", qry
->data
?(gchar
*)qry
->data
:"null");
1373 #endif /* USE_LDAP */