add support for Ayatana indicator to Notification plugin
[claws.git] / src / ldapquery.c
blob429988d110e0891cb0105914f07a4908a478adf2
1 /*
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.
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #include "claws-features.h"
26 #endif
28 #ifdef USE_LDAP
30 #include <glib.h>
31 #include <glib/gi18n.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"
41 #include "file-utils.h"
43 #include "addritem.h"
44 #include "addrcache.h"
45 #include "common/utils.h"
46 #include "log.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 );
58 return FALSE;
62 /**
63 * Create new LDAP query object.
64 * \return Initialized query object.
66 LdapQuery *ldapqry_create( void ) {
67 LdapQuery *qry;
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;
77 qry->control = NULL;
78 qry->server = NULL;
79 qry->entriesRead = 0;
80 qry->elapsedTime = 0;
81 qry->stopFlag = FALSE;
82 qry->busyFlag = FALSE;
83 qry->agedFlag = FALSE;
84 qry->completed = FALSE;
85 qry->thread = NULL;
86 qry->callBackEntry = NULL;
87 qry->callBackEnd = NULL;
88 qry->ldap = NULL;
89 qry->data = 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 );
101 return qry;
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 );
112 qry->control = ctl;
116 * Specify query name to be used.
117 * \param qry Query object.
118 * \param value Name.
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)
124 return;
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.
132 * \param value
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)
138 return;
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:
147 * <ul>
148 * <li><code>LDAPQUERY_NONE</code></li>
149 * <li><code>LDAPQUERY_STATIC</code></li>
150 * <li><code>LDAPQUERY_DYNAMIC</code></li>
151 * </ul>
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.
162 * \param value Type.
164 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
165 cm_return_if_fail( qry != NULL );
166 ADDRQUERY_SEARCHTYPE(qry) = value;
170 * Specify query ID.
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.
185 * <pre>
186 * ------------------------------------------------------------
187 * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
188 * GList *node;
190 * node = listEMail;
191 * while( node ) {
192 * ItemEMail *email = node->data;
193 * ... process email object ...
194 * node = g_list_next( node );
196 * g_list_free( listEMail );
198 * ...
199 * ...
200 * ldapqry_set_callback_entry( qry, myCallbackEntry );
201 * ------------------------------------------------------------
202 * </pre>
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 ) {
246 gboolean value;
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 );
252 return value;
256 * Set busy flag.
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 ) {
277 gboolean value;
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 );
283 return value;
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;
321 qry->ldap = NULL;
322 qry->data = 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 );
337 /* Free the mutex */
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 */
352 qry->thread = NULL;
354 /* Do not free LDAP control - should be destroyed before freeing */
355 qry->control = NULL;
357 /* Now release object */
358 g_free( qry );
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 )
393 GSList *list = NULL;
394 gint i;
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 );
405 return list;
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 ) {
416 GSList *list = NULL;
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 ));
425 } else {
426 char *file = get_tmp_file();
427 FILE *fp = claws_fopen(file, "wb");
428 if (fp) {
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 );
437 return list;
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.
454 * Notes:
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;
469 ItemPerson *person;
470 ItemEMail *email;
471 ItemFolder *folder;
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;
479 if ( listDisplay ) {
480 allocated = FALSE;
481 fullName = listDisplay->data;
484 /* Find longest first name in list */
485 firstName = mgu_slist_longest_entry( listFirst );
487 /* Format last name */
488 if( listLast ) {
489 lastName = listLast->data;
492 if ( fullName == NULL ) {
493 /* Find longest common name */
494 allocated = FALSE;
495 fullName = mgu_slist_longest_entry( listName );
496 if( fullName == NULL ) {
497 /* Format a full name from first and last names */
498 if( firstName ) {
499 if( lastName ) {
500 fullName = g_strdup_printf( "%s %s", firstName, lastName );
502 else {
503 fullName = g_strdup_printf( "%s", firstName );
506 else {
507 if( lastName ) {
508 fullName = g_strdup_printf( "%s", lastName );
511 if( fullName ) {
512 g_strstrip( fullName );
513 allocated = TRUE;
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 );
531 } else {
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",
538 dn);
539 picfile = g_strdup_printf("%s%s.png", dir, filename);
540 addritem_person_set_picture( person, filename );
541 rename_force(attrib->value, picfile);
542 g_free(filename);
543 g_free(picfile);
544 g_free(dir);
549 addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
551 qry->entriesRead++;
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 */
569 if( allocated ) {
570 g_free( fullName );
572 fullName = firstName = lastName = NULL;
574 return listReturn;
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 )
588 char *dnEntry;
589 char *attribute;
590 LdapControl *ctl;
591 BerElement *ber;
592 GSList *listName = NULL, *listAddress = NULL;
593 GSList *listFirst = NULL, *listLast = NULL;
594 GSList *listDisplay = NULL;
595 GSList *other_attrs = NULL;
596 GList *listReturn;
598 listReturn = NULL;
599 ctl = qry->control;
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 );
619 } else {
620 GSList *attlist = ldapqry_add_single_value( ld, e, attribute );
621 const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
622 if (attvalue) {
623 UserAttribute *attrib = addritem_create_attribute();
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 );
638 /* Free up */
639 ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
640 listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
642 if( ber != NULL ) {
643 ber_free( ber, 0 );
645 ldap_memfree( dnEntry );
647 return listReturn;
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 ) {
657 LdapControl *ctl;
658 ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
660 /* Test for control data */
661 ctl = qry->control;
662 if( ctl == NULL ) {
663 return FALSE;
666 /* Test for search value */
667 if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
668 return FALSE;
670 if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
671 return FALSE;
673 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
674 return TRUE;
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 ) {
692 LdapControl *ctl;
693 LDAP *ld = NULL;
695 /* Initialize connection */
696 if (debug_get_mode()) {
697 debug_print("===ldapqry_connect===\n");
698 #ifdef DEBUG_LDAP
699 ldapqry_print(qry, stdout);
700 #endif
702 ctl = qry->control;
703 #ifdef DEBUG_LDAP
704 if (debug_get_mode()) {
705 ldapctl_print(ctl, stdout);
706 debug_print("======\n");
708 #endif
709 ldapqry_touch( qry );
710 qry->startTime = qry->touchTime;
711 qry->elapsedTime = -1;
712 ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
714 ld = ldapsvr_connect(ctl);
716 if (ld == NULL)
717 return ADDRQUERY_RETVAL(qry);
719 qry->ldap = ld;
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 ) {
746 gint rc;
747 /* Disconnect */
748 if( qry->ldap ) {
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));
753 } else {
754 log_print(LOG_PROTOCOL, _("LDAP (unbind): successful\n"));
757 qry->ldap = NULL;
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 ) {
776 LdapControl *ctl;
777 LDAP *ld;
778 LDAPMessage *result = NULL, *e = NULL;
779 char **attribs;
780 gchar *criteria;
781 gboolean searchFlag;
782 gboolean entriesFound;
783 gboolean first;
784 struct timeval timeout;
785 gint rc;
786 AddressCache *cache;
787 GList *listEMail;
789 /* Initialize some variables */
790 ld = qry->ldap;
791 ctl = qry->control;
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 );
814 g_free( criteria );
815 criteria = NULL;
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 */
823 searchFlag = FALSE;
824 if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
825 log_warning(LOG_PROTOCOL, _("LDAP (search): server limits exceeded\n"));
826 searchFlag = TRUE;
828 else if( rc == LDAP_SUCCESS ) {
829 log_print(LOG_PROTOCOL, _("LDAP (search): successful\n"));
830 searchFlag = TRUE;
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"));
834 searchFlag = TRUE;
836 else {
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;
842 #ifdef G_OS_WIN32
843 debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
844 #else
845 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
846 #endif
848 /* Process results */
849 first = TRUE;
850 while( searchFlag ) {
851 ldapqry_touch( qry );
852 if( qry->entriesRead >= ctl->maxEntries ) break;
854 /* Test for stop */
855 if( ldapqry_get_stop_flag( qry ) ) {
856 break;
859 /* Retrieve entry */
860 if( first ) {
861 first = FALSE;
862 e = ldap_first_entry( ld, result );
864 else {
865 e = ldap_next_entry( ld, e );
867 if( e == NULL ) break;
868 entriesFound = TRUE;
870 /* Setup a critical section here */
871 pthread_mutex_lock( qry->mutexEntry );
873 /* Process entry */
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 );
879 else
880 g_list_free( listEMail );
881 pthread_mutex_unlock( qry->mutexEntry );
884 /* Free up and disconnect */
885 ldap_msgfree( result );
887 if( searchFlag ) {
888 if( entriesFound ) {
889 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
891 else {
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);
910 /* Connect */
911 qry->ldap = NULL;
912 ldapqry_connect( qry );
913 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
914 /* Perform search */
915 ldapqry_search_retrieve( qry );
917 /* Disconnect */
918 ldapqry_disconnect( qry );
919 qry->ldap = NULL;
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 ) {
932 gint retVal;
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 );
953 else {
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");
962 else {
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 ) );
997 /* Setup thread */
998 if (pthread_create( qry->thread, NULL,
999 (void *) ldapqry_search, (void *) qry ) != 0) {
1000 g_free(qry->thread);
1001 qry->thread = NULL;
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 ) {
1015 LdapQuery *qry;
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;
1025 qry->thread = 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 ) ) {
1037 if( qry->thread ) {
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 ) {
1064 gint age;
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;
1084 ItemFolder *folder;
1086 cm_return_if_fail( qry != NULL );
1088 folder = ADDRQUERY_FOLDER(qry);
1089 if( folder ) {
1090 cache = qry->server->addressCache;
1091 folder = addrcache_remove_folder_delete( cache, folder );
1092 if( folder ) {
1093 addritem_free_item_folder( folder );
1095 ADDRQUERY_FOLDER(qry) = NULL;
1100 * Create a name/value pair object.
1101 * \param n Name.
1102 * \param v Value.
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 );
1110 return nvp;
1114 * Free up name/value pair object.
1115 * \param nvp Name/value object.
1117 void ldapqry_free_name_value( NameValuePair *nvp ) {
1118 if( nvp ) {
1119 g_free( nvp->name );
1120 g_free( nvp->value );
1121 nvp->name = nvp->value = NULL;
1122 g_free( nvp );
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 ) {
1131 GList *node;
1133 node = list;
1134 while( node ) {
1135 NameValuePair *nvp = ( NameValuePair * ) node->data;
1136 ldapqry_free_name_value( nvp );
1137 node->data = NULL;
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,
1153 GList *listValues )
1155 GList *list = NULL;
1156 gint i;
1157 struct berval **vals;
1158 NameValuePair *nvp;
1160 list = listValues;
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 );
1165 g_free(tmp);
1166 list = g_list_append( list, nvp );
1169 ldap_value_free_len( vals );
1170 return list;
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 )
1181 char *attribute;
1182 BerElement *ber;
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 );
1192 /* Free up */
1193 if( ber != NULL ) {
1194 ber_free( ber, 0 );
1196 return listValues;
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 ) {
1208 LdapControl *ctl;
1209 LDAP *ld;
1210 LDAPMessage *result, *e = NULL;
1211 gboolean entriesFound;
1212 gboolean first;
1213 struct timeval timeout;
1214 gint rc;
1215 gchar *dn;
1216 GList *listValues;
1218 /* Initialize some variables */
1219 ld = qry->ldap;
1220 ctl = qry->control;
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);
1243 } else {
1244 log_print(LOG_PROTOCOL, _("LDAP (search): successful\n"));
1247 #ifdef G_OS_WIN32
1248 debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
1249 #else
1250 debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1251 #endif
1253 /* Process results */
1254 ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1255 first = TRUE;
1256 while( TRUE ) {
1257 ldapqry_touch( qry );
1258 if( qry->entriesRead >= ctl->maxEntries ) break;
1260 /* Test for stop */
1261 if( ldapqry_get_stop_flag( qry ) ) {
1262 break;
1265 /* Retrieve entry */
1266 if( first ) {
1267 first = FALSE;
1268 e = ldap_first_entry( ld, result );
1270 else {
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 );
1280 /* Process entry */
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 );
1288 listValues = NULL;
1290 pthread_mutex_unlock( qry->mutexEntry );
1293 /* Free up and disconnect */
1294 ldap_msgfree( result );
1296 if( entriesFound ) {
1297 ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1299 else {
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 ) {
1314 /* Connect */
1315 qry->ldap = NULL;
1316 ldapqry_connect( qry );
1317 if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1318 /* Perform search */
1319 ldapqry_locate_retrieve( qry );
1321 /* Disconnect */
1322 ldapqry_disconnect( qry );
1323 qry->ldap = NULL;
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 );
1346 else {
1347 LdapServer *server = qry->server;
1348 server->listQuery = g_list_remove(server->listQuery, qry);
1350 retVal = TRUE;
1352 return retVal;
1355 #ifdef DEBUG_LDAP
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");
1371 #endif
1373 #endif /* USE_LDAP */
1376 * End of Source.