add user specified stylesheet option
[claws.git] / src / ldapquery.c
blobae2789d4ba0fa970b84fec3e57c7364a68eda192
1 /*
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.
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #include "claws-features.h"
27 #endif
29 #ifdef USE_LDAP
31 #include <glib.h>
32 #include <sys/time.h>
33 #include <string.h>
35 #include "defs.h"
36 #include "ldaputil.h"
37 #include "ldapquery.h"
38 #include "ldapctrl.h"
39 #include "ldapserver.h"
40 #include "mgutils.h"
42 #include "addritem.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 );
56 return FALSE;
60 /**
61 * Create new LDAP query object.
62 * \return Initialized query object.
64 LdapQuery *ldapqry_create( void ) {
65 LdapQuery *qry;
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;
75 qry->control = NULL;
76 qry->server = NULL;
77 qry->entriesRead = 0;
78 qry->elapsedTime = 0;
79 qry->stopFlag = FALSE;
80 qry->busyFlag = FALSE;
81 qry->agedFlag = FALSE;
82 qry->completed = FALSE;
83 qry->thread = NULL;
84 qry->callBackEntry = NULL;
85 qry->callBackEnd = NULL;
86 qry->ldap = NULL;
87 qry->data = 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 );
99 return qry;
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 );
110 qry->control = ctl;
114 * Specify query name to be used.
115 * \param qry Query object.
116 * \param value Name.
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)
122 return;
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.
130 * \param value
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)
136 return;
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:
145 * <ul>
146 * <li><code>LDAPQUERY_NONE</code></li>
147 * <li><code>LDAPQUERY_STATIC</code></li>
148 * <li><code>LDAPQUERY_DYNAMIC</code></li>
149 * </ul>
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.
160 * \param value Type.
162 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
163 cm_return_if_fail( qry != NULL );
164 ADDRQUERY_SEARCHTYPE(qry) = value;
168 * Specify query ID.
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.
183 * <pre>
184 * ------------------------------------------------------------
185 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
186 * GList *node;
188 * node = listEMail;
189 * while( node ) {
190 * ItemEMail *email = node->data;
191 * ... process email object ...
192 * node = g_list_next( node );
194 * g_list_free( listEMail );
196 * ...
197 * ...
198 * ldapqry_set_callback_entry( qry, myCallbackEntry );
199 * ------------------------------------------------------------
200 * </pre>
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 ) {
244 gboolean value;
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 );
250 return value;
254 * Set busy flag.
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 ) {
275 gboolean value;
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 );
281 return value;
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;
319 qry->ldap = NULL;
320 qry->data = 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 );
335 /* Free the mutex */
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 */
350 qry->thread = NULL;
352 /* Do not free LDAP control - should be destroyed before freeing */
353 qry->control = NULL;
355 /* Now release object */
356 g_free( qry );
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 )
391 GSList *list = NULL;
392 gint i;
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 );
403 return list;
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 ) {
414 GSList *list = NULL;
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 ));
423 } else {
424 char *file = get_tmp_file();
425 FILE *fp = g_fopen(file, "wb");
426 if (fp) {
427 fwrite(vals[0]->bv_val, 1, vals[0]->bv_len, fp);
428 fclose(fp);
430 list = g_slist_append( list, file);
434 ldap_value_free_len( vals );
435 return list;
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.
452 * Notes:
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;
467 ItemPerson *person;
468 ItemEMail *email;
469 ItemFolder *folder;
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;
477 if ( listDisplay ) {
478 allocated = FALSE;
479 fullName = listDisplay->data;
482 /* Find longest first name in list */
483 firstName = mgu_slist_longest_entry( listFirst );
485 /* Format last name */
486 if( listLast ) {
487 lastName = listLast->data;
490 if ( fullName == NULL ) {
491 /* Find longest common name */
492 allocated = FALSE;
493 fullName = mgu_slist_longest_entry( listName );
494 if( fullName == NULL ) {
495 /* Format a full name from first and last names */
496 if( firstName ) {
497 if( lastName ) {
498 fullName = g_strdup_printf( "%s %s", firstName, lastName );
500 else {
501 fullName = g_strdup_printf( "%s", firstName );
504 else {
505 if( lastName ) {
506 fullName = g_strdup_printf( "%s", lastName );
509 if( fullName ) {
510 g_strchug( fullName ); g_strchomp( fullName );
511 allocated = TRUE;
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 );
529 } else {
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",
536 dn);
537 picfile = g_strdup_printf("%s%s.png", dir, filename);
538 addritem_person_set_picture( person, filename );
539 rename_force(attrib->value, picfile);
540 g_free(filename);
541 g_free(picfile);
542 g_free(dir);
547 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
549 qry->entriesRead++;
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 */
567 if( allocated ) {
568 g_free( fullName );
570 fullName = firstName = lastName = NULL;
572 return listReturn;
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 )
586 char *dnEntry;
587 char *attribute;
588 LdapControl *ctl;
589 BerElement *ber;
590 GSList *listName = NULL, *listAddress = NULL;
591 GSList *listFirst = NULL, *listLast = NULL;
592 GSList *listDisplay = NULL;
593 GSList *other_attrs = NULL;
594 GList *listReturn;
596 listReturn = NULL;
597 ctl = qry->control;
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 );
617 } else {
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;
621 if (attvalue) {
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 );
636 /* Free up */
637 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
638 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
640 if( ber != NULL ) {
641 ber_free( ber, 0 );
643 g_free( dnEntry );
645 return listReturn;
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 ) {
655 LdapControl *ctl;
656 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
658 /* Test for control data */
659 ctl = qry->control;
660 if( ctl == NULL ) {
661 return FALSE;
664 /* Test for search value */
665 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
666 return FALSE;
668 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
669 return FALSE;
671 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
672 return TRUE;
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 ) {
690 LdapControl *ctl;
691 LDAP *ld = NULL;
693 /* Initialize connection */
694 if (debug_get_mode()) {
695 debug_print("===ldapqry_connect===\n");
696 /*ldapqry_print(qry, stdout);*/
698 ctl = qry->control;
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);
710 if (ld == NULL)
711 return ADDRQUERY_RETVAL(qry);
713 qry->ldap = ld;
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 ) {
740 /* Disconnect */
741 if( qry->ldap ) ldap_unbind_ext( qry->ldap, NULL, NULL );
742 qry->ldap = 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 ) {
761 LdapControl *ctl;
762 LDAP *ld;
763 LDAPMessage *result = NULL, *e = NULL;
764 char **attribs;
765 gchar *criteria;
766 gboolean searchFlag;
767 gboolean entriesFound;
768 gboolean first;
769 struct timeval timeout;
770 gint rc;
771 AddressCache *cache;
772 GList *listEMail;
774 /* Initialize some variables */
775 ld = qry->ldap;
776 ctl = qry->control;
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 );
800 g_free( criteria );
801 criteria = NULL;
802 if( rc == LDAP_TIMEOUT ) {
803 return ADDRQUERY_RETVAL(qry);
805 ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
807 /* Test valid returns */
808 searchFlag = FALSE;
809 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
810 searchFlag = TRUE;
812 else if( rc == LDAP_SUCCESS ) {
813 searchFlag = TRUE;
815 else if( rc == LDAP_PARTIAL_RESULTS || (result && ldap_count_entries(ld, result) > 0) ) {
816 searchFlag = TRUE;
818 else {
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 */
828 first = TRUE;
829 while( searchFlag ) {
830 ldapqry_touch( qry );
831 if( qry->entriesRead >= ctl->maxEntries ) break;
833 /* Test for stop */
834 if( ldapqry_get_stop_flag( qry ) ) {
835 break;
838 /* Retrieve entry */
839 if( first ) {
840 first = FALSE;
841 e = ldap_first_entry( ld, result );
843 else {
844 e = ldap_next_entry( ld, e );
846 if( e == NULL ) break;
847 entriesFound = TRUE;
849 /* Setup a critical section here */
850 pthread_mutex_lock( qry->mutexEntry );
852 /* Process entry */
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 );
858 else
859 g_list_free( listEMail );
860 pthread_mutex_unlock( qry->mutexEntry );
863 /* Free up and disconnect */
864 ldap_msgfree( result );
866 if( searchFlag ) {
867 if( entriesFound ) {
868 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
870 else {
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);
889 /* Connect */
890 qry->ldap = NULL;
891 ldapqry_connect( qry );
892 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
893 /* Perform search */
894 ldapqry_search_retrieve( qry );
896 /* Disconnect */
897 ldapqry_disconnect( qry );
898 qry->ldap = NULL;
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 ) {
911 gint retVal;
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 );
932 else {
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");
941 else {
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 ) );
976 /* Setup thread */
977 if (pthread_create( qry->thread, NULL,
978 (void *) ldapqry_search, (void *) qry ) != 0) {
979 g_free(qry->thread);
980 qry->thread = NULL;
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 ) {
994 LdapQuery *qry;
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;
1004 qry->thread = 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 ) ) {
1016 if( qry->thread ) {
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 ) {
1043 gint age;
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;
1063 ItemFolder *folder;
1065 cm_return_if_fail( qry != NULL );
1067 folder = ADDRQUERY_FOLDER(qry);
1068 if( folder ) {
1069 cache = qry->server->addressCache;
1070 folder = addrcache_remove_folder_delete( cache, folder );
1071 if( folder ) {
1072 addritem_free_item_folder( folder );
1074 ADDRQUERY_FOLDER(qry) = NULL;
1079 * Create a name/value pair object.
1080 * \param n Name.
1081 * \param v Value.
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 );
1089 return nvp;
1093 * Free up name/value pair object.
1094 * \param nvp Name/value object.
1096 void ldapqry_free_name_value( NameValuePair *nvp ) {
1097 if( nvp ) {
1098 g_free( nvp->name );
1099 g_free( nvp->value );
1100 nvp->name = nvp->value = NULL;
1101 g_free( nvp );
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 ) {
1110 GList *node;
1112 node = list;
1113 while( node ) {
1114 NameValuePair *nvp = ( NameValuePair * ) node->data;
1115 ldapqry_free_name_value( nvp );
1116 node->data = NULL;
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,
1132 GList *listValues )
1134 GList *list = NULL;
1135 gint i;
1136 struct berval **vals;
1137 NameValuePair *nvp;
1139 list = listValues;
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 );
1144 g_free(tmp);
1145 list = g_list_append( list, nvp );
1148 ldap_value_free_len( vals );
1149 return list;
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 )
1160 char *attribute;
1161 BerElement *ber;
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 );
1171 /* Free up */
1172 if( ber != NULL ) {
1173 ber_free( ber, 0 );
1175 return listValues;
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 ) {
1187 LdapControl *ctl;
1188 LDAP *ld;
1189 LDAPMessage *result, *e = NULL;
1190 gboolean entriesFound;
1191 gboolean first;
1192 struct timeval timeout;
1193 gint rc;
1194 gchar *dn;
1195 GList *listValues;
1197 /* Initialize some variables */
1198 ld = qry->ldap;
1199 ctl = qry->control;
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;
1225 first = TRUE;
1226 while( TRUE ) {
1227 ldapqry_touch( qry );
1228 if( qry->entriesRead >= ctl->maxEntries ) break;
1230 /* Test for stop */
1231 if( ldapqry_get_stop_flag( qry ) ) {
1232 break;
1235 /* Retrieve entry */
1236 if( first ) {
1237 first = FALSE;
1238 e = ldap_first_entry( ld, result );
1240 else {
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 );
1250 /* Process entry */
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 );
1258 listValues = NULL;
1260 pthread_mutex_unlock( qry->mutexEntry );
1263 /* Free up and disconnect */
1264 ldap_msgfree( result );
1266 if( entriesFound ) {
1267 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1269 else {
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 ) {
1284 /* Connect */
1285 qry->ldap = NULL;
1286 ldapqry_connect( qry );
1287 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1288 /* Perform search */
1289 ldapqry_locate_retrieve( qry );
1291 /* Disconnect */
1292 ldapqry_disconnect( qry );
1293 qry->ldap = NULL;
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 );
1316 else {
1317 LdapServer *server = qry->server;
1318 server->listQuery = g_list_remove(server->listQuery, qry);
1320 retVal = TRUE;
1322 return retVal;
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 */
1344 * End of Source.