2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 2003-2012 Match Grun and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * Functions necessary to define and perform LDAP queries.
26 #include "claws-features.h"
37 #include "ldapquery.h"
39 #include "ldapserver.h"
43 #include "addrcache.h"
44 #include "common/utils.h"
47 * Key for thread specific data.
49 static pthread_key_t _queryThreadKey_
;
50 static gboolean _queryThreadInit_
= FALSE
;
52 static gboolean
callbackend (gpointer data
)
54 LdapQuery
*qry
= (LdapQuery
*)data
;
55 qry
->callBackEnd( qry
, ADDRQUERY_ID(qry
), ADDRQUERY_RETVAL(qry
), qry
->data
);
61 * Create new LDAP query object.
62 * \return Initialized query object.
64 LdapQuery
*ldapqry_create( void ) {
67 qry
= g_new0( LdapQuery
, 1 );
68 ADDRQUERY_TYPE(qry
) = ADDRQUERY_LDAP
;
69 ADDRQUERY_ID(qry
) = 0;
70 ADDRQUERY_SEARCHTYPE(qry
) = ADDRSEARCH_NONE
;
71 ADDRQUERY_NAME(qry
) = NULL
;
72 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
73 ADDRQUERY_FOLDER(qry
) = NULL
;
74 ADDRQUERY_SEARCHVALUE(qry
) = NULL
;
79 qry
->stopFlag
= FALSE
;
80 qry
->busyFlag
= FALSE
;
81 qry
->agedFlag
= FALSE
;
82 qry
->completed
= FALSE
;
84 qry
->callBackEntry
= NULL
;
85 qry
->callBackEnd
= NULL
;
89 /* Mutex to protect stop and busy flags */
90 qry
->mutexStop
= g_malloc0( sizeof( pthread_mutex_t
) );
91 pthread_mutex_init( qry
->mutexStop
, NULL
);
92 qry
->mutexBusy
= g_malloc0( sizeof( pthread_mutex_t
) );
93 pthread_mutex_init( qry
->mutexBusy
, NULL
);
95 /* Mutex to protect critical section */
96 qry
->mutexEntry
= g_malloc0( sizeof( pthread_mutex_t
) );
97 pthread_mutex_init( qry
->mutexEntry
, NULL
);
103 * Specify the reference to control data that will be used for the query. The calling
104 * module should be responsible for creating and destroying this control object.
105 * \param qry Query object.
106 * \param ctl Control object.
108 void ldapqry_set_control( LdapQuery
*qry
, LdapControl
*ctl
) {
109 cm_return_if_fail( qry
!= NULL
);
114 * Specify query name to be used.
115 * \param qry Query object.
118 void ldapqry_set_name( LdapQuery
* qry
, const gchar
*value
) {
119 cm_return_if_fail( qry
!= NULL
);
120 ADDRQUERY_NAME(qry
) = mgu_replace_string( ADDRQUERY_NAME(qry
), value
);
121 if (ADDRQUERY_NAME(qry
) == NULL
)
123 g_strstrip( ADDRQUERY_NAME(qry
) );
124 debug_print("set name: %s\n", ADDRQUERY_NAME(qry
));
128 * Specify search value to be used.
129 * \param qry Query object.
132 void ldapqry_set_search_value( LdapQuery
*qry
, const gchar
*value
) {
133 cm_return_if_fail( qry
!= NULL
);
134 ADDRQUERY_SEARCHVALUE(qry
) = mgu_replace_string( ADDRQUERY_SEARCHVALUE(qry
), value
);
135 if (ADDRQUERY_SEARCHVALUE(qry
) == NULL
)
137 g_strstrip( ADDRQUERY_SEARCHVALUE(qry
) );
138 debug_print("search value: %s\n", ADDRQUERY_SEARCHVALUE(qry
));
142 * Specify query type.
143 * \param qry Query object.
144 * \param value Query type, either:
146 * <li><code>LDAPQUERY_NONE</code></li>
147 * <li><code>LDAPQUERY_STATIC</code></li>
148 * <li><code>LDAPQUERY_DYNAMIC</code></li>
152 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
153 ADDRQUERY_TYPE(qry) = value;
158 * Specify search type.
159 * \param qry Query object.
162 void ldapqry_set_search_type( LdapQuery
*qry
, const AddrSearchType value
) {
163 cm_return_if_fail( qry
!= NULL
);
164 ADDRQUERY_SEARCHTYPE(qry
) = value
;
169 * \param qry Query object.
170 * \param value ID for the query.
172 void ldapqry_set_query_id( LdapQuery
* qry
, const gint value
) {
173 cm_return_if_fail( qry
!= NULL
);
174 ADDRQUERY_ID(qry
) = value
;
178 * Register a callback function that will be executed when each entry
179 * has been read and processed. When called, the function will be passed
180 * this query object and a GList of ItemEMail objects as arguments. An
181 * example of typical usage is shown below.
184 * ------------------------------------------------------------
185 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
190 * ItemEMail *email = node->data;
191 * ... process email object ...
192 * node = g_list_next( node );
194 * g_list_free( listEMail );
198 * ldapqry_set_callback_entry( qry, myCallbackEntry );
199 * ------------------------------------------------------------
202 * \param qry Query object.
203 * \param func Function.
205 void ldapqry_set_callback_entry( LdapQuery
*qry
, void *func
) {
206 pthread_mutex_lock( qry
->mutexEntry
);
207 qry
->callBackEntry
= func
;
208 pthread_mutex_unlock( qry
->mutexEntry
);
212 * Register a callback function that will be executed when the search
213 * is complete. When called, the function will be passed this query
214 * object as an argument.
215 * \param qry Query object.
216 * \param func Function.
218 void ldapqry_set_callback_end( LdapQuery
*qry
, void *func
) {
219 qry
->callBackEnd
= func
;
223 * Notify query to start/stop executing. This method should be called with a
224 * value if <i>TRUE</i> to terminate an existing running query.
226 * \param qry Query object.
227 * \param value Value of stop flag.
229 void ldapqry_set_stop_flag( LdapQuery
*qry
, const gboolean value
) {
230 cm_return_if_fail( qry
!= NULL
);
232 pthread_mutex_lock( qry
->mutexStop
);
233 qry
->stopFlag
= value
;
234 pthread_mutex_unlock( qry
->mutexStop
);
238 * Test value of stop flag. This method should be used to determine whether a
239 * query has stopped running.
240 * \param qry Query object.
241 * \return Value of stop flag.
243 static gboolean
ldapqry_get_stop_flag( LdapQuery
*qry
) {
245 cm_return_val_if_fail( qry
!= NULL
, TRUE
);
247 pthread_mutex_lock( qry
->mutexStop
);
248 value
= qry
->stopFlag
;
249 pthread_mutex_unlock( qry
->mutexStop
);
255 * \param qry Query object.
256 * \param value Value of busy flag.
258 static void ldapqry_set_busy_flag( LdapQuery
*qry
, const gboolean value
) {
259 cm_return_if_fail( qry
!= NULL
);
260 if (qry
->mutexBusy
== NULL
)
261 return; /* exiting, mutex already freed */
263 pthread_mutex_lock( qry
->mutexBusy
);
264 qry
->busyFlag
= value
;
265 pthread_mutex_unlock( qry
->mutexBusy
);
269 * Test value of busy flag. This method will return a value of <i>FALSE</i>
270 * when a query has completed running.
271 * \param qry Query object.
272 * \return Value of busy flag.
274 static gboolean
ldapqry_get_busy_flag( LdapQuery
*qry
) {
276 cm_return_val_if_fail( qry
!= NULL
, FALSE
);
278 pthread_mutex_lock( qry
->mutexBusy
);
279 value
= qry
->busyFlag
;
280 pthread_mutex_unlock( qry
->mutexBusy
);
285 * Set query aged flag.
286 * \param qry Query object.
287 * \param value Value of aged flag.
289 static void ldapqry_set_aged_flag( LdapQuery
*qry
, const gboolean value
) {
290 cm_return_if_fail( qry
!= NULL
);
291 qry
->agedFlag
= value
;
295 * Clear LDAP query member variables.
296 * \param qry Query object.
298 static void ldapqry_clear( LdapQuery
*qry
) {
299 cm_return_if_fail( qry
!= NULL
);
301 /* Free internal stuff */
302 g_free( ADDRQUERY_NAME(qry
) );
303 g_free( ADDRQUERY_SEARCHVALUE(qry
) );
305 /* Clear pointers and value */
306 ADDRQUERY_NAME(qry
) = NULL
;
307 ADDRQUERY_SEARCHVALUE(qry
) = NULL
;
308 ADDRQUERY_ID(qry
) = 0;
309 ADDRQUERY_SEARCHTYPE(qry
) = ADDRSEARCH_NONE
;
310 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
311 qry
->entriesRead
= 0;
312 qry
->elapsedTime
= 0;
313 qry
->stopFlag
= FALSE
;
314 qry
->busyFlag
= FALSE
;
315 qry
->agedFlag
= FALSE
;
316 qry
->completed
= FALSE
;
317 qry
->callBackEntry
= NULL
;
318 qry
->callBackEnd
= NULL
;
324 * Free up LDAP query object by releasing internal memory. Note that
325 * the thread object will be freed by the OS.
326 * \param qry Query object to process.
328 void ldapqry_free( LdapQuery
*qry
) {
329 cm_return_if_fail( qry
!= NULL
);
331 /* Clear out internal members */
332 ADDRQUERY_TYPE(qry
) = ADDRQUERY_NONE
;
333 ldapqry_clear( qry
);
336 pthread_mutex_destroy( qry
->mutexStop
);
337 pthread_mutex_destroy( qry
->mutexBusy
);
338 pthread_mutex_destroy( qry
->mutexEntry
);
339 g_free( qry
->mutexStop
);
340 g_free( qry
->mutexBusy
);
341 g_free( qry
->mutexEntry
);
342 qry
->mutexEntry
= NULL
;
343 qry
->mutexBusy
= NULL
;
344 qry
->mutexStop
= NULL
;
346 /* Do not free folder - parent server object should free */
347 ADDRQUERY_FOLDER(qry
) = NULL
;
349 /* Do not free thread - thread should be terminated before freeing */
352 /* Do not free LDAP control - should be destroyed before freeing */
355 /* Now release object */
360 * Free linked lists of character strings.
361 * \param listName List of common names.
362 * \param listAddr List of addresses.
363 * \param listFirst List of first names.
364 * \param listLast List of last names.
366 static void ldapqry_free_lists(
367 GSList
*listName
, GSList
*listAddr
, GSList
*listFirst
,
368 GSList
*listLast
, GSList
*listDisplay
, GSList
*other_attrs
)
370 GSList
*cur
= other_attrs
;
371 mgu_free_list( listName
);
372 mgu_free_list( listAddr
);
373 mgu_free_list( listFirst
);
374 mgu_free_list( listLast
);
375 mgu_free_list( listDisplay
);
376 for(;cur
; cur
= cur
->next
)
377 addritem_free_attribute((UserAttribute
*)cur
->data
);
378 g_slist_free(other_attrs
);
382 * Add all LDAP attribute values to a list.
383 * \param ld LDAP handle.
384 * \param entry LDAP entry to process.
385 * \param attr LDAP attribute.
386 * \return List of values.
388 static GSList
*ldapqry_add_list_values(
389 LDAP
*ld
, LDAPMessage
*entry
, char *attr
)
393 struct berval
**vals
;
395 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
396 for( i
= 0; vals
[i
] != NULL
; i
++ ) {
397 /*debug_print("lv\t%s: %s\n", attr?attr:"null",
398 vals[i]->bv_val?vals[i]->bv_val:"null");*/
399 list
= g_slist_append( list
, g_strndup( vals
[i
]->bv_val
, vals
[i
]->bv_len
) );
402 ldap_value_free_len( vals
);
407 * Add a single attribute value to a list.
408 * \param ld LDAP handle.
409 * \param entry LDAP entry to process.
410 * \param attr LDAP attribute name to process.
411 * \return List of values; only one value will be present.
413 static GSList
*ldapqry_add_single_value( LDAP
*ld
, LDAPMessage
*entry
, char *attr
) {
415 struct berval
**vals
;
417 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
418 if( vals
[0] != NULL
) {
419 if (strcmp(attr
, "jpegPhoto")) {
420 debug_print("sv\t%s: %s\n", attr
?attr
:"null",
421 vals
[0]->bv_val
?vals
[0]->bv_val
:"null");
422 list
= g_slist_append( list
, g_strndup( vals
[0]->bv_val
, vals
[0]->bv_len
));
424 char *file
= get_tmp_file();
425 FILE *fp
= g_fopen(file
, "wb");
427 fwrite(vals
[0]->bv_val
, 1, vals
[0]->bv_len
, fp
);
430 list
= g_slist_append( list
, file
);
434 ldap_value_free_len( vals
);
439 * Build an address list entry and append to list of address items. Name is formatted
440 * as "<first-name> <last-name>".
442 * \param cache Address cache to load.
443 * \param qry Query object to process.
444 * \param dn DN for entry found on server.
445 * \param listName List of common names for entry; see notes below.
446 * \param listAddr List of EMail addresses for entry.
447 * \param listFirst List of first names for entry.
448 * \param listLast List of last names for entry.
450 * \return List of ItemEMail objects.
453 * 1) Each LDAP server entry may have multiple LDAP attributes with the same
454 * name. For example, a single entry for a person may have more than one
455 * common name, email address, etc.
457 * 2) The DN for the entry is unique for the server.
459 static GList
*ldapqry_build_items_fl(
460 AddressCache
*cache
, LdapQuery
*qry
, gchar
*dn
,
461 GSList
*listName
, GSList
*listAddr
, GSList
*listFirst
,
462 GSList
*listLast
, GSList
*listDisplay
, GSList
*attributes
)
464 GSList
*nodeAddress
, *cur
;
465 gchar
*firstName
= NULL
, *lastName
= NULL
, *fullName
= NULL
;
466 gboolean allocated
= FALSE
;
470 gchar
*picfile
= NULL
;
471 GList
*listReturn
= NULL
;
473 folder
= ADDRQUERY_FOLDER(qry
);
474 if( folder
== NULL
) return listReturn
;
475 if( listAddr
== NULL
) return listReturn
;
479 fullName
= listDisplay
->data
;
482 /* Find longest first name in list */
483 firstName
= mgu_slist_longest_entry( listFirst
);
485 /* Format last name */
487 lastName
= listLast
->data
;
490 if ( fullName
== NULL
) {
491 /* Find longest common name */
493 fullName
= mgu_slist_longest_entry( listName
);
494 if( fullName
== NULL
) {
495 /* Format a full name from first and last names */
498 fullName
= g_strdup_printf( "%s %s", firstName
, lastName
);
501 fullName
= g_strdup_printf( "%s", firstName
);
506 fullName
= g_strdup_printf( "%s", lastName
);
510 g_strchug( fullName
); g_strchomp( fullName
);
516 /* Add person into folder */
517 person
= addritem_create_item_person();
518 addritem_person_set_common_name( person
, fullName
);
519 addritem_person_set_first_name( person
, firstName
);
520 addritem_person_set_last_name( person
, lastName
);
521 addritem_person_set_nick_name( person
, fullName
);
522 addrcache_id_person( cache
, person
);
523 addritem_person_set_external_id( person
, dn
);
525 for (cur
= attributes
; cur
; cur
= cur
->next
) {
526 UserAttribute
*attrib
= addritem_copy_attribute((UserAttribute
*)cur
->data
);
527 if (attrib
->name
&& strcmp(attrib
->name
, "jpegPhoto")) {
528 addritem_person_add_attribute( person
, attrib
);
530 if (qry
->server
&& qry
->server
->control
) {
531 gchar
*dir
= g_strconcat( get_rc_dir(), G_DIR_SEPARATOR_S
,
532 ADDRBOOK_DIR
, G_DIR_SEPARATOR_S
, NULL
);
533 gchar
*filename
= g_strdup_printf("%s-%s-%s",
534 qry
->server
->control
->hostName
?qry
->server
->control
->hostName
:"nohost",
535 qry
->server
->control
->baseDN
?qry
->server
->control
->baseDN
:"nobase",
537 picfile
= g_strdup_printf("%s%s.png", dir
, filename
);
538 addritem_person_set_picture( person
, filename
);
539 rename_force(attrib
->value
, picfile
);
547 addrcache_folder_add_person( cache
, ADDRQUERY_FOLDER(qry
), person
);
551 /* Add each address item */
552 nodeAddress
= listAddr
;
553 while( nodeAddress
) {
554 email
= addritem_create_item_email();
555 addritem_email_set_address( email
, nodeAddress
->data
);
556 addrcache_id_email( cache
, email
);
557 addrcache_person_add_email( cache
, person
, email
);
558 addritem_person_add_email( person
, email
);
559 /*if (debug_get_mode()) {
560 addritem_print_item_email(email, stdout);
562 listReturn
= g_list_append( listReturn
, email
);
563 nodeAddress
= g_slist_next( nodeAddress
);
566 /* Free any allocated memory */
570 fullName
= firstName
= lastName
= NULL
;
576 * Process a single search entry.
577 * \param cache Address cache to load.
578 * \param qry Query object to process.
579 * \param ld LDAP handle.
580 * \param e LDAP message.
581 * \return List of EMail objects found.
583 static GList
*ldapqry_process_single_entry(
584 AddressCache
*cache
, LdapQuery
*qry
, LDAP
*ld
, LDAPMessage
*e
)
590 GSList
*listName
= NULL
, *listAddress
= NULL
;
591 GSList
*listFirst
= NULL
, *listLast
= NULL
;
592 GSList
*listDisplay
= NULL
;
593 GSList
*other_attrs
= NULL
;
598 dnEntry
= ldap_get_dn( ld
, e
);
599 debug_print( "DN: %s\n", dnEntry
?dnEntry
:"null" );
601 /* Process all attributes */
602 for( attribute
= ldap_first_attribute( ld
, e
, &ber
); attribute
!= NULL
;
603 attribute
= ldap_next_attribute( ld
, e
, ber
) ) {
604 if( strcasecmp( attribute
, ctl
->attribEMail
) == 0 ) {
605 listAddress
= ldapqry_add_list_values( ld
, e
, attribute
);
607 else if( strcasecmp( attribute
, ctl
->attribCName
) == 0 ) {
608 listName
= ldapqry_add_list_values( ld
, e
, attribute
);
610 else if( strcasecmp( attribute
, ctl
->attribFName
) == 0 ) {
611 listFirst
= ldapqry_add_list_values( ld
, e
, attribute
);
613 else if( strcasecmp( attribute
, ctl
->attribLName
) == 0 ) {
614 listLast
= ldapqry_add_single_value( ld
, e
, attribute
);
615 } else if( strcasecmp( attribute
, ctl
->attribDName
) == 0 ) {
616 listDisplay
= ldapqry_add_single_value( ld
, e
, attribute
);
618 GSList
*attlist
= ldapqry_add_single_value( ld
, e
, attribute
);
619 UserAttribute
*attrib
= addritem_create_attribute();
620 const gchar
*attvalue
= attlist
?((gchar
*)attlist
->data
):NULL
;
622 addritem_attrib_set_name( attrib
, attribute
);
623 addritem_attrib_set_value( attrib
, attvalue
);
624 other_attrs
= g_slist_prepend(other_attrs
, attrib
);
626 mgu_free_list(attlist
);
628 /* Free memory used to store attribute */
629 ldap_memfree( attribute
);
632 /* Format and add items to cache */
633 listReturn
= ldapqry_build_items_fl(
634 cache
, qry
, dnEntry
, listName
, listAddress
, listFirst
, listLast
, listDisplay
, other_attrs
);
637 ldapqry_free_lists( listName
, listAddress
, listFirst
, listLast
, listDisplay
, other_attrs
);
638 listName
= listAddress
= listFirst
= listLast
= listDisplay
= other_attrs
= NULL
;
649 * Check parameters that are required for a search. This should
650 * be called before performing a search.
651 * \param qry Query object to process.
652 * \return <i>TRUE</i> if search criteria appear OK.
654 gboolean
ldapqry_check_search( LdapQuery
*qry
) {
656 ADDRQUERY_RETVAL(qry
) = LDAPRC_CRITERIA
;
658 /* Test for control data */
664 /* Test for search value */
665 if( ADDRQUERY_SEARCHVALUE(qry
) == NULL
) {
668 if( strlen( ADDRQUERY_SEARCHVALUE(qry
) ) < 1 ) {
671 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
676 * Touch the query. This nudges the touch time with the current time.
677 * \param qry Query object to process.
679 void ldapqry_touch( LdapQuery
*qry
) {
680 qry
->touchTime
= time( NULL
);
681 qry
->agedFlag
= FALSE
;
685 * Connect to LDAP server.
686 * \param qry Query object to process.
687 * \return Error/status code.
689 static gint
ldapqry_connect( LdapQuery
*qry
) {
693 /* Initialize connection */
694 if (debug_get_mode()) {
695 debug_print("===ldapqry_connect===\n");
696 /*ldapqry_print(qry, stdout);*/
699 /*if (debug_get_mode()) {
700 ldapctl_print(ctl, stdout);
701 debug_print("======\n");
703 ldapqry_touch( qry
);
704 qry
->startTime
= qry
->touchTime
;
705 qry
->elapsedTime
= -1;
706 ADDRQUERY_RETVAL(qry
) = LDAPRC_INIT
;
708 ld
= ldapsvr_connect(ctl
);
711 return ADDRQUERY_RETVAL(qry
);
714 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
715 if( ldapqry_get_stop_flag( qry
) ) {
716 return ADDRQUERY_RETVAL(qry
);
718 ldapqry_touch( qry
);
720 debug_print("connected to LDAP host %s on port %d\n",
721 ctl
->hostName
?ctl
->hostName
:"null", ctl
->port
);
723 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
724 if( ldapqry_get_stop_flag( qry
) ) {
725 return ADDRQUERY_RETVAL(qry
);
727 ldapqry_touch( qry
);
729 ADDRQUERY_RETVAL(qry
) = LDAP_SUCCESS
;
731 return ADDRQUERY_RETVAL(qry
);
735 * Connect to LDAP server.
736 * \param qry Query object to process.
737 * \return Error/status code.
739 static gint
ldapqry_disconnect( LdapQuery
*qry
) {
741 if( qry
->ldap
) ldap_unbind_ext( qry
->ldap
, NULL
, NULL
);
744 ldapqry_touch( qry
);
745 qry
->elapsedTime
= qry
->touchTime
- qry
->startTime
;
747 return ADDRQUERY_RETVAL(qry
);
751 * Perform the LDAP search, reading LDAP entries into cache.
752 * Note that one LDAP entry can have multiple values for many of its
753 * attributes. If these attributes are E-Mail addresses; these are
754 * broken out into separate address items. For any other attribute,
755 * only the first occurrence is read.
757 * \param qry Query object to process.
758 * \return Error/status code.
760 static gint
ldapqry_search_retrieve( LdapQuery
*qry
) {
763 LDAPMessage
*result
= NULL
, *e
= NULL
;
767 gboolean entriesFound
;
769 struct timeval timeout
;
774 /* Initialize some variables */
777 cache
= qry
->server
->addressCache
;
778 timeout
.tv_sec
= ctl
->timeOut
;
779 timeout
.tv_usec
= 0L;
780 entriesFound
= FALSE
;
781 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
783 /* Define all attributes we are interested in. */
784 attribs
= ldapctl_full_attribute_array( ctl
);
786 /* Create LDAP search string */
787 criteria
= ldapctl_format_criteria( ctl
, ADDRQUERY_SEARCHVALUE(qry
) );
788 debug_print("Search criteria ::%s::\n", criteria
?criteria
:"null");
791 * Execute the search - this step may take some time to complete
792 * depending on network traffic and server response time.
794 ADDRQUERY_RETVAL(qry
) = LDAPRC_TIMEOUT
;
795 rc
= ldap_search_ext_s( ld
, ctl
->baseDN
, LDAP_SCOPE_SUBTREE
, criteria
,
796 attribs
, 0, NULL
, NULL
, &timeout
, 0, &result
);
797 debug_print("LDAP Error: ldap_search_st: %d\n", rc
);
798 debug_print("LDAP Error: ldap_search_st: %s\n", ldaputil_get_error(ld
));
799 ldapctl_free_attribute_array( attribs
);
802 if( rc
== LDAP_TIMEOUT
) {
803 return ADDRQUERY_RETVAL(qry
);
805 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
807 /* Test valid returns */
809 if( rc
== LDAP_ADMINLIMIT_EXCEEDED
) {
812 else if( rc
== LDAP_SUCCESS
) {
815 else if( rc
== LDAP_PARTIAL_RESULTS
|| (result
&& ldap_count_entries(ld
, result
) > 0) ) {
819 debug_print("LDAP Error: ldap_search_st: %d\n", rc
);
820 debug_print("LDAP Error: ldap_search_st: %s\n", ldaputil_get_error(ld
));
821 return ADDRQUERY_RETVAL(qry
);
823 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
825 debug_print("Total results are: %d\n", ldap_count_entries(ld
, result
));
827 /* Process results */
829 while( searchFlag
) {
830 ldapqry_touch( qry
);
831 if( qry
->entriesRead
>= ctl
->maxEntries
) break;
834 if( ldapqry_get_stop_flag( qry
) ) {
841 e
= ldap_first_entry( ld
, result
);
844 e
= ldap_next_entry( ld
, e
);
846 if( e
== NULL
) break;
849 /* Setup a critical section here */
850 pthread_mutex_lock( qry
->mutexEntry
);
853 listEMail
= ldapqry_process_single_entry( cache
, qry
, ld
, e
);
855 /* Process callback */
856 if( qry
->callBackEntry
)
857 qry
->callBackEntry( qry
, ADDRQUERY_ID(qry
), listEMail
, qry
->data
);
859 g_list_free( listEMail
);
860 pthread_mutex_unlock( qry
->mutexEntry
);
863 /* Free up and disconnect */
864 ldap_msgfree( result
);
868 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
871 ADDRQUERY_RETVAL(qry
) = LDAPRC_NOENTRIES
;
875 return ADDRQUERY_RETVAL(qry
);
879 * Connection, perform search and disconnect.
880 * \param qry Query object to process.
881 * \return Error/status code.
883 static gint
ldapqry_perform_search( LdapQuery
*qry
) {
884 /* Check search criteria */
885 if( ! ldapqry_check_search( qry
) ) {
886 return ADDRQUERY_RETVAL(qry
);
891 ldapqry_connect( qry
);
892 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
894 ldapqry_search_retrieve( qry
);
897 ldapqry_disconnect( qry
);
900 return ADDRQUERY_RETVAL(qry
);
903 static gint
ldapqry_perform_locate( LdapQuery
*qry
);
906 * Wrapper around search.
907 * \param qry Query object to process.
908 * \return Error/status code.
910 static gint
ldapqry_search( LdapQuery
*qry
) {
913 cm_return_val_if_fail( qry
!= NULL
, -1 );
914 cm_return_val_if_fail( qry
->control
!= NULL
, -1 );
916 ldapqry_touch( qry
);
917 qry
->completed
= FALSE
;
919 /* Setup pointer to thread specific area */
920 pthread_setspecific( _queryThreadKey_
, qry
);
922 pthread_detach( pthread_self() );
924 /* Now perform the search */
925 qry
->entriesRead
= 0;
926 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
927 ldapqry_set_busy_flag( qry
, TRUE
);
928 ldapqry_set_stop_flag( qry
, FALSE
);
929 if( ADDRQUERY_SEARCHTYPE(qry
) == ADDRSEARCH_LOCATE
) {
930 retVal
= ldapqry_perform_locate( qry
);
933 retVal
= ldapqry_perform_search( qry
);
935 if( retVal
== LDAPRC_SUCCESS
) {
936 qry
->server
->addressCache
->dataRead
= TRUE
;
937 qry
->server
->addressCache
->accessFlag
= FALSE
;
938 if( ldapqry_get_stop_flag( qry
) ) {
939 debug_print("Search was terminated prematurely\n");
942 ldapqry_touch( qry
);
943 qry
->completed
= TRUE
;
944 debug_print("Search ran to completion\n");
947 ldapqry_set_stop_flag( qry
, TRUE
);
948 ldapqry_set_busy_flag( qry
, FALSE
);
950 /* Process callback */
951 if( qry
->callBackEnd
) {
952 g_timeout_add(0, callbackend
, qry
);
955 return ADDRQUERY_RETVAL(qry
);
959 * Read data into list using a background thread. Callback function will be
960 * notified when search is complete.
961 * \param qry Query object to process.
962 * \return Error/status code.
964 gint
ldapqry_read_data_th( LdapQuery
*qry
) {
965 cm_return_val_if_fail( qry
!= NULL
, -1 );
966 cm_return_val_if_fail( qry
->control
!= NULL
, -1 );
968 ldapqry_set_stop_flag( qry
, FALSE
);
969 ldapqry_touch( qry
);
970 if( ldapqry_check_search( qry
) ) {
971 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
972 debug_print("Starting LDAP search thread\n");
973 ldapqry_set_busy_flag( qry
, TRUE
);
974 qry
->thread
= g_malloc0( sizeof( pthread_t
) );
977 if (pthread_create( qry
->thread
, NULL
,
978 (void *) ldapqry_search
, (void *) qry
) != 0) {
981 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
985 return ADDRQUERY_RETVAL(qry
);
989 * Cleanup LDAP thread data. This function will be called when each thread
990 * exits. Note that the thread object will be freed by the kernel.
991 * \param ptr Pointer to object being destroyed (a query object in this case).
993 static void ldapqry_destroyer( void * ptr
) {
996 qry
= ( LdapQuery
* ) ptr
;
997 cm_return_if_fail( qry
!= NULL
);
999 /* Perform any destruction here */
1000 if( qry
->control
!= NULL
) {
1001 ldapctl_free( qry
->control
);
1003 qry
->control
= NULL
;
1005 ldapqry_set_busy_flag( qry
, FALSE
);
1009 * Cancel thread associated with query.
1010 * \param qry Query object to process.
1012 void ldapqry_cancel( LdapQuery
*qry
) {
1013 cm_return_if_fail( qry
!= NULL
);
1015 if( ldapqry_get_busy_flag( qry
) ) {
1017 debug_print("calling pthread_cancel\n");
1018 pthread_cancel( * qry
->thread
);
1024 * Initialize LDAP query. This function should be called once before executing
1025 * any LDAP queries to initialize thread specific data.
1027 void ldapqry_initialize( void ) {
1028 debug_print("ldapqry_initialize...\n");
1029 if( ! _queryThreadInit_
) {
1030 debug_print("ldapqry_initialize::creating thread specific area\n");
1031 pthread_key_create( &_queryThreadKey_
, ldapqry_destroyer
);
1032 _queryThreadInit_
= TRUE
;
1034 debug_print("ldapqry_initialize... done!\n");
1038 * Age the query based on LDAP control parameters.
1039 * \param qry Query object to process.
1040 * \param maxAge Maximum age of query (in seconds).
1042 void ldapqry_age( LdapQuery
*qry
, gint maxAge
) {
1045 cm_return_if_fail( qry
!= NULL
);
1047 /* Limit the time that queries can hang around */
1048 if( maxAge
< 1 ) maxAge
= LDAPCTL_MAX_QUERY_AGE
;
1050 /* Check age of query */
1051 age
= time( NULL
) - qry
->touchTime
;
1052 if( age
> maxAge
) {
1053 qry
->agedFlag
= TRUE
;
1058 * Delete folder associated with query results.
1059 * \param qry Query object to process.
1061 void ldapqry_delete_folder( LdapQuery
*qry
) {
1062 AddressCache
*cache
;
1065 cm_return_if_fail( qry
!= NULL
);
1067 folder
= ADDRQUERY_FOLDER(qry
);
1069 cache
= qry
->server
->addressCache
;
1070 folder
= addrcache_remove_folder_delete( cache
, folder
);
1072 addritem_free_item_folder( folder
);
1074 ADDRQUERY_FOLDER(qry
) = NULL
;
1079 * Create a name/value pair object.
1082 * \return Initialized object.
1084 static NameValuePair
*ldapqry_create_name_value( const gchar
*n
, const gchar
*v
) {
1085 NameValuePair
*nvp
= g_new0( NameValuePair
, 1 );
1087 nvp
->name
= g_strdup( n
);
1088 nvp
->value
= g_strdup( v
);
1093 * Free up name/value pair object.
1094 * \param nvp Name/value object.
1096 void ldapqry_free_name_value( NameValuePair
*nvp
) {
1098 g_free( nvp
->name
);
1099 g_free( nvp
->value
);
1100 nvp
->name
= nvp
->value
= NULL
;
1106 * Free up a list name/value pair objects.
1107 * \param list List of name/value objects.
1109 void ldapqry_free_list_name_value( GList
*list
) {
1114 NameValuePair
*nvp
= ( NameValuePair
* ) node
->data
;
1115 ldapqry_free_name_value( nvp
);
1117 node
= g_list_next( node
);
1119 g_list_free( list
);
1123 * Load a list of name/value pairs from LDAP attributes.
1124 * \param ld LDAP handle.
1125 * \param e LDAP message.
1126 * \param attr Attribute name.
1127 * \param listValues List to populate.
1128 * \return List of attribute name/value pairs.
1130 static GList
*ldapqry_load_attrib_values(
1131 LDAP
*ld
, LDAPMessage
*entry
, char *attr
,
1136 struct berval
**vals
;
1140 if( ( vals
= ldap_get_values_len( ld
, entry
, attr
) ) != NULL
) {
1141 for( i
= 0; vals
[i
] != NULL
; i
++ ) {
1142 gchar
*tmp
= g_strndup( vals
[i
]->bv_val
, vals
[i
]->bv_len
);
1143 nvp
= ldapqry_create_name_value( attr
, tmp
);
1145 list
= g_list_append( list
, nvp
);
1148 ldap_value_free_len( vals
);
1153 * Fetch a list of all attributes.
1154 * \param ld LDAP handle.
1155 * \param e LDAP message.
1156 * \return List of attribute name/value pairs.
1158 static GList
*ldapqry_fetch_attribs( LDAP
*ld
, LDAPMessage
*e
)
1162 GList
*listValues
= NULL
;
1164 /* Process all attributes */
1165 for( attribute
= ldap_first_attribute( ld
, e
, &ber
); attribute
!= NULL
;
1166 attribute
= ldap_next_attribute( ld
, e
, ber
) ) {
1167 listValues
= ldapqry_load_attrib_values( ld
, e
, attribute
, listValues
);
1168 ldap_memfree( attribute
);
1178 #define CRITERIA_SINGLE "(objectclass=*)"
1181 * Perform the data retrieval for a specific LDAP record.
1183 * \param qry Query object to process.
1184 * \return Error/status code.
1186 static gint
ldapqry_locate_retrieve( LdapQuery
*qry
) {
1189 LDAPMessage
*result
, *e
= NULL
;
1190 gboolean entriesFound
;
1192 struct timeval timeout
;
1197 /* Initialize some variables */
1200 dn
= ADDRQUERY_SEARCHVALUE(qry
);
1201 timeout
.tv_sec
= ctl
->timeOut
;
1202 timeout
.tv_usec
= 0L;
1203 entriesFound
= FALSE
;
1206 * Execute the search - this step may take some time to complete
1207 * depending on network traffic and server response time.
1209 ADDRQUERY_RETVAL(qry
) = LDAPRC_TIMEOUT
;
1210 rc
= ldap_search_ext_s( ld
, dn
, LDAP_SCOPE_BASE
, CRITERIA_SINGLE
,
1211 NULL
, 0, NULL
, NULL
, &timeout
, 0, &result
);
1212 if( rc
== LDAP_TIMEOUT
) {
1213 return ADDRQUERY_RETVAL(qry
);
1215 ADDRQUERY_RETVAL(qry
) = LDAPRC_SEARCH
;
1216 if( rc
!= LDAP_SUCCESS
) {
1217 debug_print("LDAP Error: ldap_search_st: %s\n", ldaputil_get_error(ld
));
1218 return ADDRQUERY_RETVAL(qry
);
1221 debug_print("Total results are: %d\n", ldap_count_entries(ld
, result
));
1223 /* Process results */
1224 ADDRQUERY_RETVAL(qry
) = LDAPRC_STOP_FLAG
;
1227 ldapqry_touch( qry
);
1228 if( qry
->entriesRead
>= ctl
->maxEntries
) break;
1231 if( ldapqry_get_stop_flag( qry
) ) {
1235 /* Retrieve entry */
1238 e
= ldap_first_entry( ld
, result
);
1241 e
= ldap_next_entry( ld
, e
);
1243 if( e
== NULL
) break;
1245 entriesFound
= TRUE
;
1247 /* Setup a critical section here */
1248 pthread_mutex_lock( qry
->mutexEntry
);
1251 listValues
= ldapqry_fetch_attribs( ld
, e
);
1253 /* Process callback */
1254 if( qry
->callBackEntry
) {
1255 qry
->callBackEntry( qry
, ADDRQUERY_ID(qry
), listValues
, qry
->data
);
1257 ldapqry_free_list_name_value( listValues
);
1260 pthread_mutex_unlock( qry
->mutexEntry
);
1263 /* Free up and disconnect */
1264 ldap_msgfree( result
);
1266 if( entriesFound
) {
1267 ADDRQUERY_RETVAL(qry
) = LDAPRC_SUCCESS
;
1270 ADDRQUERY_RETVAL(qry
) = LDAPRC_NOENTRIES
;
1273 return ADDRQUERY_RETVAL(qry
);
1277 * Perform the search to locate a specific LDAP record identified by
1278 * distinguished name (dn).
1280 * \param qry Query object to process.
1281 * \return Error/status code.
1283 static gint
ldapqry_perform_locate( LdapQuery
*qry
) {
1286 ldapqry_connect( qry
);
1287 if( ADDRQUERY_RETVAL(qry
) == LDAPRC_SUCCESS
) {
1288 /* Perform search */
1289 ldapqry_locate_retrieve( qry
);
1292 ldapqry_disconnect( qry
);
1295 /* Process callback */
1296 if( qry
->callBackEnd
) {
1297 g_timeout_add(0, callbackend
, qry
);
1300 return ADDRQUERY_RETVAL(qry
);
1304 * Remove results (folder and data) for specified LDAP query.
1305 * \param qry Query object to process.
1306 * \return TRUE if folder deleted successfully.
1308 gboolean
ldapquery_remove_results( LdapQuery
*qry
) {
1309 gboolean retVal
= FALSE
;
1311 ldapqry_set_aged_flag( qry
, TRUE
);
1313 if( ldapqry_get_busy_flag( qry
) ) {
1314 ldapqry_set_stop_flag( qry
, TRUE
);
1317 LdapServer
*server
= qry
->server
;
1318 server
->listQuery
= g_list_remove(server
->listQuery
, qry
);
1325 void ldapqry_print(LdapQuery
*qry
, FILE *stream
) {
1326 cm_return_if_fail( qry
!= NULL
);
1328 ldapsvr_print_data(qry
->server
, stream
);
1329 ldapctl_print(qry
->control
, stream
);
1330 fprintf(stream
, "entriesRead: %d\n", qry
->entriesRead
);
1331 fprintf(stream
, "elapsedTime: %d\n", qry
->elapsedTime
);
1332 fprintf(stream
, "stopFlag: %d\n", qry
->stopFlag
);
1333 fprintf(stream
, "busyFlag: %d\n", qry
->busyFlag
);
1334 fprintf(stream
, "agedFlag: %d\n", qry
->agedFlag
);
1335 fprintf(stream
, "completed: %d\n", qry
->completed
);
1336 fprintf(stream
, "startTime: %d\n", (int) qry
->startTime
);
1337 fprintf(stream
, "touchTime: %d\n", (int) qry
->touchTime
);
1338 fprintf(stream
, "data: %s\n", qry
->data
?(gchar
*)qry
->data
:"null");
1341 #endif /* USE_LDAP */