1 /*
2  * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 2003-2018 Match Grun and the Claws Mail team
4  *
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.
9  *
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.
14  *
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/>.
17  */
18 
19 /*
20  * Functions necessary to define and perform LDAP queries.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #include "claws-features.h"
26 #endif
27 
28 #ifdef USE_LDAP
29 
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <sys/time.h>
33 #include <string.h>
34 
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"
42 
43 #include "addritem.h"
44 #include "addrcache.h"
45 #include "common/utils.h"
46 #include "log.h"
47 
48 /*
49  * Key for thread specific data.
50  */
51 static pthread_key_t _queryThreadKey_;
52 static gboolean _queryThreadInit_ = FALSE;
53 
callbackend(gpointer data)54 static gboolean callbackend (gpointer data)
55 {
56 	LdapQuery *qry = (LdapQuery *)data;
57 	qry->callBackEnd( qry, ADDRQUERY_ID(qry), ADDRQUERY_RETVAL(qry), qry->data );
58 	return FALSE;
59 }
60 
61 
62 /**
63  * Create new LDAP query object.
64  * \return Initialized query object.
65  */
ldapqry_create(void)66 LdapQuery *ldapqry_create( void ) {
67 	LdapQuery *qry;
68 
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;
90 
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 );
96 
97 	/* Mutex to protect critical section */
98 	qry->mutexEntry = g_malloc0( sizeof( pthread_mutex_t ) );
99 	pthread_mutex_init( qry->mutexEntry, NULL );
100 
101 	return qry;
102 }
103 
104 /**
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.
109  */
ldapqry_set_control(LdapQuery * qry,LdapControl * ctl)110 void ldapqry_set_control( LdapQuery *qry, LdapControl *ctl ) {
111 	cm_return_if_fail( qry != NULL );
112 	qry->control = ctl;
113 }
114 
115 /**
116  * Specify query name to be used.
117  * \param qry   Query object.
118  * \param value Name.
119  */
ldapqry_set_name(LdapQuery * qry,const gchar * value)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));
127 }
128 
129 /**
130  * Specify search value to be used.
131  * \param qry Query object.
132  * \param value
133  */
ldapqry_set_search_value(LdapQuery * qry,const gchar * 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));
141 }
142 
143 /**
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>
152  */
153 /*
154 void ldapqry_set_query_type( LdapQuery* qry, const gint value ) {
155 	ADDRQUERY_TYPE(qry) = value;
156 }
157 */
158 
159 /**
160  * Specify search type.
161  * \param qry   Query object.
162  * \param value Type.
163  */
ldapqry_set_search_type(LdapQuery * qry,const AddrSearchType value)164 void ldapqry_set_search_type( LdapQuery *qry, const AddrSearchType value ) {
165 	cm_return_if_fail( qry != NULL );
166 	ADDRQUERY_SEARCHTYPE(qry) = value;
167 }
168 
169 /**
170  * Specify query ID.
171  * \param qry Query object.
172  * \param value ID for the query.
173  */
ldapqry_set_query_id(LdapQuery * qry,const gint value)174 void ldapqry_set_query_id( LdapQuery* qry, const gint value ) {
175 	cm_return_if_fail( qry != NULL );
176 	ADDRQUERY_ID(qry) = value;
177 }
178 
179 /**
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.
184  *
185  * <pre>
186  * ------------------------------------------------------------
187  * void myCallbackEntry( LdapQuery *qry, GList *listEMail ) {
188  *   GList *node;
189  *
190  *   node = listEMail;
191  *   while( node ) {
192  *     ItemEMail *email = node->data;
193  *     ... process email object ...
194  *     node = g_list_next( node );
195  *   }
196  *   g_list_free( listEMail );
197  * }
198  * ...
199  * ...
200  * ldapqry_set_callback_entry( qry, myCallbackEntry );
201  * ------------------------------------------------------------
202  * </pre>
203  *
204  * \param qry Query object.
205  * \param func Function.
206  */
ldapqry_set_callback_entry(LdapQuery * qry,void * func)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 );
211 }
212 
213 /**
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.
219  */
ldapqry_set_callback_end(LdapQuery * qry,void * func)220 void ldapqry_set_callback_end( LdapQuery *qry, void *func ) {
221 	qry->callBackEnd = func;
222 }
223 
224 /**
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.
227  *
228  * \param qry Query object.
229  * \param value Value of stop flag.
230  */
ldapqry_set_stop_flag(LdapQuery * qry,const gboolean value)231 void ldapqry_set_stop_flag( LdapQuery *qry, const gboolean value ) {
232 	cm_return_if_fail( qry != NULL );
233 
234 	pthread_mutex_lock( qry->mutexStop );
235 	qry->stopFlag = value;
236 	pthread_mutex_unlock( qry->mutexStop );
237 }
238 
239 /**
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.
244  */
ldapqry_get_stop_flag(LdapQuery * qry)245 static gboolean ldapqry_get_stop_flag( LdapQuery *qry ) {
246 	gboolean value;
247 	cm_return_val_if_fail( qry != NULL, TRUE );
248 
249 	pthread_mutex_lock( qry->mutexStop );
250 	value = qry->stopFlag;
251 	pthread_mutex_unlock( qry->mutexStop );
252 	return value;
253 }
254 
255 /**
256  * Set busy flag.
257  * \param qry Query object.
258  * \param value Value of busy flag.
259  */
ldapqry_set_busy_flag(LdapQuery * qry,const gboolean value)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 */
264 
265 	pthread_mutex_lock( qry->mutexBusy );
266 	qry->busyFlag = value;
267 	pthread_mutex_unlock( qry->mutexBusy );
268 }
269 
270 /**
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.
275  */
ldapqry_get_busy_flag(LdapQuery * qry)276 static gboolean ldapqry_get_busy_flag( LdapQuery *qry ) {
277 	gboolean value;
278 	cm_return_val_if_fail( qry != NULL, FALSE );
279 
280 	pthread_mutex_lock( qry->mutexBusy );
281 	value = qry->busyFlag;
282 	pthread_mutex_unlock( qry->mutexBusy );
283 	return value;
284 }
285 
286 /**
287  * Set query aged flag.
288  * \param qry Query object.
289  * \param value Value of aged flag.
290  */
ldapqry_set_aged_flag(LdapQuery * qry,const gboolean value)291 static void ldapqry_set_aged_flag( LdapQuery *qry, const gboolean value ) {
292 	cm_return_if_fail( qry != NULL );
293 	qry->agedFlag = value;
294 }
295 
296 /**
297  * Clear LDAP query member variables.
298  * \param qry Query object.
299  */
ldapqry_clear(LdapQuery * qry)300 static void ldapqry_clear( LdapQuery *qry ) {
301 	cm_return_if_fail( qry != NULL );
302 
303 	/* Free internal stuff */
304 	g_free( ADDRQUERY_NAME(qry) );
305 	g_free( ADDRQUERY_SEARCHVALUE(qry) );
306 
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;
323 }
324 
325 /**
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.
329  */
ldapqry_free(LdapQuery * qry)330 void ldapqry_free( LdapQuery *qry ) {
331 	cm_return_if_fail( qry != NULL );
332 
333 	/* Clear out internal members */
334 	ADDRQUERY_TYPE(qry) = ADDRQUERY_NONE;
335 	ldapqry_clear( qry );
336 
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;
347 
348 	/* Do not free folder - parent server object should free */
349 	ADDRQUERY_FOLDER(qry) = NULL;
350 
351 	/* Do not free thread - thread should be terminated before freeing */
352 	qry->thread = NULL;
353 
354 	/* Do not free LDAP control - should be destroyed before freeing */
355 	qry->control = NULL;
356 
357 	/* Now release object */
358 	g_free( qry );
359 }
360 
361 /**
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.
367  */
ldapqry_free_lists(GSList * listName,GSList * listAddr,GSList * listFirst,GSList * listLast,GSList * listDisplay,GSList * other_attrs)368 static void ldapqry_free_lists(
369 		GSList *listName, GSList *listAddr, GSList *listFirst,
370 		GSList *listLast, GSList *listDisplay, GSList *other_attrs )
371 {
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);
381 }
382 
383 /**
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.
389  */
ldapqry_add_list_values(LDAP * ld,LDAPMessage * entry,char * attr)390 static GSList *ldapqry_add_list_values(
391 		LDAP *ld, LDAPMessage *entry, char *attr )
392 {
393 	GSList *list = NULL;
394 	gint i;
395 	struct berval **vals;
396 
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) );
402 		}
403 	}
404 	ldap_value_free_len( vals );
405 	return list;
406 }
407 
408 /**
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.
414  */
ldapqry_add_single_value(LDAP * ld,LDAPMessage * entry,char * attr)415 static GSList *ldapqry_add_single_value( LDAP *ld, LDAPMessage *entry, char *attr ) {
416 	GSList *list = NULL;
417 	struct berval **vals;
418 
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);
431 				}
432 				list = g_slist_append( list, file);
433 			}
434 		}
435 	}
436 	ldap_value_free_len( vals );
437 	return list;
438 }
439 
440 /**
441  * Build an address list entry and append to list of address items. Name is formatted
442  * as "<first-name> <last-name>".
443  *
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.
451  *
452  * \return List of ItemEMail objects.
453  *
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.
458 *
459  * 2) The DN for the entry is unique for the server.
460  */
ldapqry_build_items_fl(AddressCache * cache,LdapQuery * qry,gchar * dn,GSList * listName,GSList * listAddr,GSList * listFirst,GSList * listLast,GSList * listDisplay,GSList * attributes)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 )
465 {
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;
474 
475 	folder = ADDRQUERY_FOLDER(qry);
476 	if( folder == NULL ) return listReturn;
477 	if( listAddr == NULL ) return listReturn;
478 
479 	if ( listDisplay ) {
480 		allocated = FALSE;
481 		fullName = listDisplay->data;
482 	}
483 
484 	/* Find longest first name in list */
485 	firstName = mgu_slist_longest_entry( listFirst );
486 
487 	/* Format last name */
488 	if( listLast ) {
489 		lastName = listLast->data;
490 	}
491 
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 );
501 				}
502 				else {
503 					fullName = g_strdup_printf( "%s", firstName );
504 				}
505 			}
506 			else {
507 				if( lastName ) {
508 					fullName = g_strdup_printf( "%s", lastName );
509 				}
510 			}
511 			if( fullName ) {
512 				g_strstrip( fullName );
513 				allocated = TRUE;
514 			}
515 		}
516 	}
517 
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 );
526 
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);
545 			}
546 		}
547 	}
548 
549 	addrcache_folder_add_person( cache, ADDRQUERY_FOLDER(qry), person );
550 
551 	qry->entriesRead++;
552 
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);
563 		}*/
564 		listReturn = g_list_append( listReturn, email );
565 		nodeAddress = g_slist_next( nodeAddress );
566 	}
567 
568 	/* Free any allocated memory */
569 	if( allocated ) {
570 		g_free( fullName );
571 	}
572 	fullName = firstName = lastName = NULL;
573 
574 	return listReturn;
575 }
576 
577 /**
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.
584  */
ldapqry_process_single_entry(AddressCache * cache,LdapQuery * qry,LDAP * ld,LDAPMessage * e)585 static GList *ldapqry_process_single_entry(
586 		AddressCache *cache, LdapQuery *qry, LDAP *ld, LDAPMessage *e )
587 {
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;
597 
598 	listReturn = NULL;
599 	ctl = qry->control;
600 	dnEntry = ldap_get_dn( ld, e );
601 	debug_print( "DN: %s\n", dnEntry?dnEntry:"null" );
602 
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 );
608 		}
609 		else if( strcasecmp( attribute, ctl->attribCName ) == 0 ) {
610 			listName = ldapqry_add_list_values( ld, e, attribute );
611 		}
612 		else if( strcasecmp( attribute, ctl->attribFName ) == 0 ) {
613 			listFirst = ldapqry_add_list_values( ld, e, attribute );
614 		}
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 			UserAttribute *attrib = addritem_create_attribute();
622 			const gchar *attvalue = attlist?((gchar *)attlist->data):NULL;
623 			if (attvalue) {
624 				addritem_attrib_set_name( attrib, attribute );
625 				addritem_attrib_set_value( attrib, attvalue );
626 				other_attrs = g_slist_prepend(other_attrs, attrib);
627 			}
628 			g_slist_free_full(attlist, g_free);
629 		}
630 		/* Free memory used to store attribute */
631 		ldap_memfree( attribute );
632 	}
633 
634 	/* Format and add items to cache */
635 	listReturn = ldapqry_build_items_fl(
636 		cache, qry, dnEntry, listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
637 
638 	/* Free up */
639 	ldapqry_free_lists( listName, listAddress, listFirst, listLast, listDisplay, other_attrs );
640 	listName = listAddress = listFirst = listLast = listDisplay = other_attrs = NULL;
641 
642 	if( ber != NULL ) {
643 		ber_free( ber, 0 );
644 	}
645 	ldap_memfree( dnEntry );
646 
647 	return listReturn;
648 }
649 
650 /**
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.
655  */
ldapqry_check_search(LdapQuery * qry)656 gboolean ldapqry_check_search( LdapQuery *qry ) {
657 	LdapControl *ctl;
658 	ADDRQUERY_RETVAL(qry) = LDAPRC_CRITERIA;
659 
660 	/* Test for control data */
661 	ctl = qry->control;
662 	if( ctl == NULL ) {
663 		return FALSE;
664 	}
665 
666 	/* Test for search value */
667 	if( ADDRQUERY_SEARCHVALUE(qry) == NULL ) {
668 		return FALSE;
669 	}
670 	if( strlen( ADDRQUERY_SEARCHVALUE(qry) ) < 1 ) {
671 		return FALSE;
672 	}
673 	ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
674 	return TRUE;
675 }
676 
677 /**
678  * Touch the query. This nudges the touch time with the current time.
679  * \param qry Query object to process.
680  */
ldapqry_touch(LdapQuery * qry)681 void ldapqry_touch( LdapQuery *qry ) {
682 	qry->touchTime = time( NULL );
683 	qry->agedFlag = FALSE;
684 }
685 
686 /**
687  * Connect to LDAP server.
688  * \param  qry Query object to process.
689  * \return Error/status code.
690  */
ldapqry_connect(LdapQuery * qry)691 static gint ldapqry_connect( LdapQuery *qry ) {
692 	LdapControl *ctl;
693 	LDAP *ld = NULL;
694 
695 	/* Initialize connection */
696 	if (debug_get_mode()) {
697 		debug_print("===ldapqry_connect===\n");
698 		/*ldapqry_print(qry, stdout);*/
699 	}
700 	ctl = qry->control;
701 	/*if (debug_get_mode()) {
702 		ldapctl_print(ctl, stdout);
703 		debug_print("======\n");
704 	}*/
705 	ldapqry_touch( qry );
706 	qry->startTime = qry->touchTime;
707 	qry->elapsedTime = -1;
708 	ADDRQUERY_RETVAL(qry) = LDAPRC_INIT;
709 
710 	ld = ldapsvr_connect(ctl);
711 
712 	if (ld == NULL)
713 		return ADDRQUERY_RETVAL(qry);
714 
715 	qry->ldap = ld;
716 	ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
717 	if( ldapqry_get_stop_flag( qry ) ) {
718 		return ADDRQUERY_RETVAL(qry);
719 	}
720 	ldapqry_touch( qry );
721 
722 	debug_print("connected to LDAP host %s on port %d\n",
723 			ctl->hostName?ctl->hostName:"null", ctl->port);
724 
725 	ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
726 	if( ldapqry_get_stop_flag( qry ) ) {
727 		return ADDRQUERY_RETVAL(qry);
728 	}
729 	ldapqry_touch( qry );
730 
731 	ADDRQUERY_RETVAL(qry) = LDAP_SUCCESS;
732 
733 	return ADDRQUERY_RETVAL(qry);
734 }
735 
736 /**
737  * Connect to LDAP server.
738  * \param  qry Query object to process.
739  * \return Error/status code.
740  */
ldapqry_disconnect(LdapQuery * qry)741 static gint ldapqry_disconnect( LdapQuery *qry ) {
742 	gint rc;
743 	/* Disconnect */
744 	if( qry->ldap ) {
745 		rc = ldap_unbind_ext( qry->ldap, NULL, NULL );
746 		if (rc != LDAP_SUCCESS) {
747 			log_error(LOG_PROTOCOL, _("LDAP error (unbind): %d (%s)\n"),
748 					rc, ldaputil_get_error(qry->ldap));
749 		} else {
750 			log_print(LOG_PROTOCOL, _("LDAP (unbind): successful\n"));
751 		}
752 	}
753 	qry->ldap = NULL;
754 
755 	ldapqry_touch( qry );
756 	qry->elapsedTime = qry->touchTime - qry->startTime;
757 
758 	return ADDRQUERY_RETVAL(qry);
759 }
760 
761 /**
762  * Perform the LDAP search, reading LDAP entries into cache.
763  * Note that one LDAP entry can have multiple values for many of its
764  * attributes. If these attributes are E-Mail addresses; these are
765  * broken out into separate address items. For any other attribute,
766  * only the first occurrence is read.
767  *
768  * \param  qry Query object to process.
769  * \return Error/status code.
770  */
ldapqry_search_retrieve(LdapQuery * qry)771 static gint ldapqry_search_retrieve( LdapQuery *qry ) {
772 	LdapControl *ctl;
773 	LDAP *ld;
774 	LDAPMessage *result = NULL, *e = NULL;
775 	char **attribs;
776 	gchar *criteria;
777 	gboolean searchFlag;
778 	gboolean entriesFound;
779 	gboolean first;
780 	struct timeval timeout;
781 	gint rc;
782 	AddressCache *cache;
783 	GList *listEMail;
784 
785 	/* Initialize some variables */
786 	ld = qry->ldap;
787 	ctl = qry->control;
788 	cache = qry->server->addressCache;
789 	timeout.tv_sec = ctl->timeOut;
790 	timeout.tv_usec = 0L;
791 	entriesFound = FALSE;
792 	ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
793 
794 	/* Define all attributes we are interested in. */
795 	attribs = ldapctl_full_attribute_array( ctl );
796 
797 	/* Create LDAP search string */
798 	criteria = ldapctl_format_criteria( ctl, ADDRQUERY_SEARCHVALUE(qry) );
799 	debug_print("Search criteria ::%s::\n", criteria?criteria:"null");
800 
801 	/*
802 	 * Execute the search - this step may take some time to complete
803 	 * depending on network traffic and server response time.
804 	 */
805 	ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
806 	rc = ldap_search_ext_s( ld, ctl->baseDN, LDAP_SCOPE_SUBTREE, criteria,
807 		attribs, 0, NULL, NULL, &timeout, 0, &result );
808 	debug_print("LDAP ldap_search_ext_s: %d (%s)\n", rc, ldaputil_get_error(ld));
809 	ldapctl_free_attribute_array( attribs );
810 	g_free( criteria );
811 	criteria = NULL;
812 	if( rc == LDAP_TIMEOUT ) {
813 		log_warning(LOG_PROTOCOL, _("LDAP (search): timeout\n"));
814 		return ADDRQUERY_RETVAL(qry);
815 	}
816 	ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
817 
818 	/* Test valid returns */
819 	searchFlag = FALSE;
820 	if( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
821 		log_warning(LOG_PROTOCOL, _("LDAP (search): server limits exceeded\n"));
822 		searchFlag = TRUE;
823 	}
824 	else if( rc == LDAP_SUCCESS ) {
825 		log_print(LOG_PROTOCOL, _("LDAP (search): successful\n"));
826 		searchFlag = TRUE;
827 	}
828 	else if( rc == LDAP_PARTIAL_RESULTS || (result && ldap_count_entries(ld, result) > 0) ) {
829 		log_print(LOG_PROTOCOL, _("LDAP (search): successful (partial results)\n"));
830 		searchFlag = TRUE;
831 	}
832 	else {
833 		log_error(LOG_PROTOCOL, _("LDAP error (search): %d (%s)\n"), rc, ldaputil_get_error(ld));
834 		return ADDRQUERY_RETVAL(qry);
835 	}
836 	ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
837 
838 #ifdef G_OS_WIN32
839 	debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
840 #else
841 	debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
842 #endif
843 
844 	/* Process results */
845 	first = TRUE;
846 	while( searchFlag ) {
847 		ldapqry_touch( qry );
848 		if( qry->entriesRead >= ctl->maxEntries ) break;
849 
850 		/* Test for stop */
851 		if( ldapqry_get_stop_flag( qry ) ) {
852 			break;
853 		}
854 
855 		/* Retrieve entry */
856 		if( first ) {
857 			first = FALSE;
858 			e = ldap_first_entry( ld, result );
859 		}
860 		else {
861 			e = ldap_next_entry( ld, e );
862 		}
863 		if( e == NULL ) break;
864 		entriesFound = TRUE;
865 
866 		/* Setup a critical section here */
867 		pthread_mutex_lock( qry->mutexEntry );
868 
869 		/* Process entry */
870 		listEMail = ldapqry_process_single_entry( cache, qry, ld, e );
871 
872 		/* Process callback */
873 		if( qry->callBackEntry )
874 			qry->callBackEntry( qry, ADDRQUERY_ID(qry), listEMail, qry->data );
875 		else
876 			g_list_free( listEMail );
877 		pthread_mutex_unlock( qry->mutexEntry );
878 	}
879 
880 	/* Free up and disconnect */
881 	ldap_msgfree( result );
882 
883 	if( searchFlag ) {
884 		if( entriesFound ) {
885 			ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
886 		}
887 		else {
888 			ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
889 		}
890 	}
891 
892 	return ADDRQUERY_RETVAL(qry);
893 }
894 
895 /**
896  * Connection, perform search and disconnect.
897  * \param  qry Query object to process.
898  * \return Error/status code.
899  */
ldapqry_perform_search(LdapQuery * qry)900 static gint ldapqry_perform_search( LdapQuery *qry ) {
901 	/* Check search criteria */
902 	if( ! ldapqry_check_search( qry ) ) {
903 		return ADDRQUERY_RETVAL(qry);
904 	}
905 
906 	/* Connect */
907 	qry->ldap = NULL;
908 	ldapqry_connect( qry );
909 	if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
910 		/* Perform search */
911 		ldapqry_search_retrieve( qry );
912 	}
913 	/* Disconnect */
914 	ldapqry_disconnect( qry );
915 	qry->ldap = NULL;
916 
917 	return ADDRQUERY_RETVAL(qry);
918 }
919 
920 static gint ldapqry_perform_locate( LdapQuery *qry );
921 
922 /**
923  * Wrapper around search.
924  * \param  qry Query object to process.
925  * \return Error/status code.
926  */
ldapqry_search(LdapQuery * qry)927 static gint ldapqry_search( LdapQuery *qry ) {
928 	gint retVal;
929 
930 	cm_return_val_if_fail( qry != NULL, -1 );
931 	cm_return_val_if_fail( qry->control != NULL, -1 );
932 
933 	ldapqry_touch( qry );
934 	qry->completed = FALSE;
935 
936 	/* Setup pointer to thread specific area */
937 	pthread_setspecific( _queryThreadKey_, qry );
938 
939 	pthread_detach( pthread_self() );
940 
941 	/* Now perform the search */
942 	qry->entriesRead = 0;
943 	ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
944 	ldapqry_set_busy_flag( qry, TRUE );
945 	ldapqry_set_stop_flag( qry, FALSE );
946 	if( ADDRQUERY_SEARCHTYPE(qry) == ADDRSEARCH_LOCATE ) {
947 		retVal = ldapqry_perform_locate( qry );
948 	}
949 	else {
950 		retVal = ldapqry_perform_search( qry );
951 	}
952 	if( retVal == LDAPRC_SUCCESS ) {
953 		qry->server->addressCache->dataRead = TRUE;
954 		qry->server->addressCache->accessFlag = FALSE;
955 		if( ldapqry_get_stop_flag( qry ) ) {
956 			debug_print("Search was terminated prematurely\n");
957 		}
958 		else {
959 			ldapqry_touch( qry );
960 			qry->completed = TRUE;
961 			debug_print("Search ran to completion\n");
962 		}
963 	}
964 	ldapqry_set_stop_flag( qry, TRUE );
965 	ldapqry_set_busy_flag( qry, FALSE );
966 
967 	/* Process callback */
968 	if( qry->callBackEnd ) {
969 		g_timeout_add(0, callbackend, qry);
970 	}
971 
972 	return ADDRQUERY_RETVAL(qry);
973 }
974 
975 /**
976  * Read data into list using a background thread. Callback function will be
977  * notified when search is complete.
978  * \param  qry Query object to process.
979  * \return Error/status code.
980  */
ldapqry_read_data_th(LdapQuery * qry)981 gint ldapqry_read_data_th( LdapQuery *qry ) {
982 	cm_return_val_if_fail( qry != NULL, -1 );
983 	cm_return_val_if_fail( qry->control != NULL, -1 );
984 
985 	ldapqry_set_stop_flag( qry, FALSE );
986 	ldapqry_touch( qry );
987 	if( ldapqry_check_search( qry ) ) {
988 		if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
989 			debug_print("Starting LDAP search thread\n");
990 			ldapqry_set_busy_flag( qry, TRUE );
991 			qry->thread = g_malloc0( sizeof( pthread_t ) );
992 
993 			/* Setup thread */
994 			if (pthread_create( qry->thread, NULL,
995 				(void *) ldapqry_search, (void *) qry ) != 0) {
996 				g_free(qry->thread);
997 				qry->thread = NULL;
998 				ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
999 			}
1000 		}
1001 	}
1002 	return ADDRQUERY_RETVAL(qry);
1003 }
1004 
1005 /**
1006  * Cleanup LDAP thread data. This function will be called when each thread
1007  * exits. Note that the thread object will be freed by the kernel.
1008  * \param ptr Pointer to object being destroyed (a query object in this case).
1009  */
ldapqry_destroyer(void * ptr)1010 static void ldapqry_destroyer( void * ptr ) {
1011 	LdapQuery *qry;
1012 
1013 	qry = ( LdapQuery * ) ptr;
1014 	cm_return_if_fail( qry != NULL );
1015 
1016 	/* Perform any destruction here */
1017 	if( qry->control != NULL ) {
1018 		ldapctl_free( qry->control );
1019 	}
1020 	qry->control = NULL;
1021 	qry->thread = NULL;
1022 	ldapqry_set_busy_flag( qry, FALSE );
1023 }
1024 
1025 /**
1026  * Cancel thread associated with query.
1027  * \param qry Query object to process.
1028  */
ldapqry_cancel(LdapQuery * qry)1029 void ldapqry_cancel( LdapQuery *qry ) {
1030 	cm_return_if_fail( qry != NULL );
1031 
1032 	if( ldapqry_get_busy_flag( qry ) ) {
1033 		if( qry->thread ) {
1034 			debug_print("calling pthread_cancel\n");
1035 			pthread_cancel( * qry->thread );
1036 		}
1037 	}
1038 }
1039 
1040 /**
1041  * Initialize LDAP query. This function should be called once before executing
1042  * any LDAP queries to initialize thread specific data.
1043  */
ldapqry_initialize(void)1044 void ldapqry_initialize( void ) {
1045 	debug_print("ldapqry_initialize...\n");
1046 	if( ! _queryThreadInit_ ) {
1047 		debug_print("ldapqry_initialize::creating thread specific area\n");
1048 		pthread_key_create( &_queryThreadKey_, ldapqry_destroyer );
1049 		_queryThreadInit_ = TRUE;
1050 	}
1051 	debug_print("ldapqry_initialize... done!\n");
1052 }
1053 
1054 /**
1055  * Age the query based on LDAP control parameters.
1056  * \param qry    Query object to process.
1057  * \param maxAge Maximum age of query (in seconds).
1058  */
ldapqry_age(LdapQuery * qry,gint maxAge)1059 void ldapqry_age( LdapQuery *qry, gint maxAge ) {
1060 	gint age;
1061 
1062 	cm_return_if_fail( qry != NULL );
1063 
1064 	/* Limit the time that queries can hang around */
1065 	if( maxAge < 1 ) maxAge = LDAPCTL_MAX_QUERY_AGE;
1066 
1067 	/* Check age of query */
1068 	age = time( NULL ) - qry->touchTime;
1069 	if( age > maxAge ) {
1070 		qry->agedFlag = TRUE;
1071 	}
1072 }
1073 
1074 /**
1075  * Delete folder associated with query results.
1076  * \param qry Query object to process.
1077  */
ldapqry_delete_folder(LdapQuery * qry)1078 void ldapqry_delete_folder( LdapQuery *qry ) {
1079 	AddressCache *cache;
1080 	ItemFolder *folder;
1081 
1082 	cm_return_if_fail( qry != NULL );
1083 
1084 	folder = ADDRQUERY_FOLDER(qry);
1085 	if( folder ) {
1086 		cache = qry->server->addressCache;
1087 		folder = addrcache_remove_folder_delete( cache, folder );
1088 		if( folder ) {
1089 			addritem_free_item_folder( folder );
1090 		}
1091 		ADDRQUERY_FOLDER(qry) = NULL;
1092 	}
1093 }
1094 
1095 /**
1096  * Create a name/value pair object.
1097  * \param n Name.
1098  * \param v Value.
1099  * \return Initialized object.
1100  */
ldapqry_create_name_value(const gchar * n,const gchar * v)1101 static NameValuePair *ldapqry_create_name_value( const gchar *n, const gchar *v ) {
1102 	NameValuePair *nvp = g_new0( NameValuePair, 1 );
1103 
1104 	nvp->name = g_strdup( n );
1105 	nvp->value = g_strdup( v );
1106 	return nvp;
1107 }
1108 
1109 /**
1110  * Free up name/value pair object.
1111  * \param nvp Name/value object.
1112  */
ldapqry_free_name_value(NameValuePair * nvp)1113 void ldapqry_free_name_value( NameValuePair *nvp ) {
1114 	if( nvp ) {
1115 		g_free( nvp->name );
1116 		g_free( nvp->value );
1117 		nvp->name = nvp->value = NULL;
1118 		g_free( nvp );
1119 	}
1120 }
1121 
1122 /**
1123  * Free up a list name/value pair objects.
1124  * \param list List of name/value objects.
1125  */
ldapqry_free_list_name_value(GList * list)1126 void ldapqry_free_list_name_value( GList *list ) {
1127 	GList *node;
1128 
1129 	node = list;
1130 	while( node ) {
1131 		NameValuePair *nvp = ( NameValuePair * ) node->data;
1132 		ldapqry_free_name_value( nvp );
1133 		node->data = NULL;
1134 		node = g_list_next( node );
1135 	}
1136 	g_list_free( list );
1137 }
1138 
1139 /**
1140  * Load a list of name/value pairs from LDAP attributes.
1141  * \param  ld          LDAP handle.
1142  * \param  e          LDAP message.
1143  * \param  attr       Attribute name.
1144  * \param  listValues List to populate.
1145  * \return List of attribute name/value pairs.
1146  */
ldapqry_load_attrib_values(LDAP * ld,LDAPMessage * entry,char * attr,GList * listValues)1147 static GList *ldapqry_load_attrib_values(
1148 		LDAP *ld, LDAPMessage *entry, char *attr,
1149 		GList *listValues )
1150 {
1151 	GList *list = NULL;
1152 	gint i;
1153 	struct berval **vals;
1154 	NameValuePair *nvp;
1155 
1156 	list = listValues;
1157 	if( ( vals = ldap_get_values_len( ld, entry, attr ) ) != NULL ) {
1158 		for( i = 0; vals[i] != NULL; i++ ) {
1159 			gchar *tmp = g_strndup( vals[i]->bv_val, vals[i]->bv_len);
1160 			nvp = ldapqry_create_name_value( attr, tmp );
1161 			g_free(tmp);
1162 			list = g_list_append( list, nvp );
1163 		}
1164 	}
1165 	ldap_value_free_len( vals );
1166 	return list;
1167 }
1168 
1169 /**
1170  * Fetch a list of all attributes.
1171  * \param  ld    LDAP handle.
1172  * \param  e     LDAP message.
1173  * \return List of attribute name/value pairs.
1174  */
ldapqry_fetch_attribs(LDAP * ld,LDAPMessage * e)1175 static GList *ldapqry_fetch_attribs( LDAP *ld, LDAPMessage *e )
1176 {
1177 	char *attribute;
1178 	BerElement *ber;
1179 	GList *listValues = NULL;
1180 
1181 	/* Process all attributes */
1182 	for( attribute = ldap_first_attribute( ld, e, &ber ); attribute != NULL;
1183 		attribute = ldap_next_attribute( ld, e, ber ) ) {
1184 		listValues = ldapqry_load_attrib_values( ld, e, attribute, listValues );
1185 		ldap_memfree( attribute );
1186 	}
1187 
1188 	/* Free up */
1189 	if( ber != NULL ) {
1190 		ber_free( ber, 0 );
1191 	}
1192 	return listValues;
1193 }
1194 
1195 #define CRITERIA_SINGLE "(objectclass=*)"
1196 
1197 /**
1198  * Perform the data retrieval for a specific LDAP record.
1199  *
1200  * \param  qry Query object to process.
1201  * \return Error/status code.
1202  */
ldapqry_locate_retrieve(LdapQuery * qry)1203 static gint ldapqry_locate_retrieve( LdapQuery *qry ) {
1204 	LdapControl *ctl;
1205 	LDAP *ld;
1206 	LDAPMessage *result, *e = NULL;
1207 	gboolean entriesFound;
1208 	gboolean first;
1209 	struct timeval timeout;
1210 	gint rc;
1211 	gchar *dn;
1212 	GList *listValues;
1213 
1214 	/* Initialize some variables */
1215 	ld = qry->ldap;
1216 	ctl = qry->control;
1217 	dn = ADDRQUERY_SEARCHVALUE(qry);
1218 	timeout.tv_sec = ctl->timeOut;
1219 	timeout.tv_usec = 0L;
1220 	entriesFound = FALSE;
1221 
1222 	/*
1223 	 * Execute the search - this step may take some time to complete
1224 	 * depending on network traffic and server response time.
1225 	 */
1226 	ADDRQUERY_RETVAL(qry) = LDAPRC_TIMEOUT;
1227 	rc = ldap_search_ext_s( ld, dn, LDAP_SCOPE_BASE, CRITERIA_SINGLE,
1228 		NULL, 0, NULL, NULL, &timeout, 0, &result );
1229 	if( rc == LDAP_TIMEOUT ) {
1230 		return ADDRQUERY_RETVAL(qry);
1231 	}
1232 	ADDRQUERY_RETVAL(qry) = LDAPRC_SEARCH;
1233 	if( rc != LDAP_SUCCESS ) {
1234 		log_error(LOG_PROTOCOL, _("LDAP error (search): %d (%s)\n"),
1235 				rc, ldaputil_get_error(ld));
1236 		debug_print("LDAP Error: ldap_search_ext_s: %d (%s)\n",
1237 				rc, ldaputil_get_error(ld));
1238 		return ADDRQUERY_RETVAL(qry);
1239 	} else {
1240 		log_print(LOG_PROTOCOL, _("LDAP (search): successful\n"));
1241 	}
1242 
1243 #ifdef G_OS_WIN32
1244 	debug_print("Total results are: %lu\n", ldap_count_entries(ld, result));
1245 #else
1246 	debug_print("Total results are: %d\n", ldap_count_entries(ld, result));
1247 #endif
1248 
1249 	/* Process results */
1250 	ADDRQUERY_RETVAL(qry) = LDAPRC_STOP_FLAG;
1251 	first = TRUE;
1252 	while( TRUE ) {
1253 		ldapqry_touch( qry );
1254 		if( qry->entriesRead >= ctl->maxEntries ) break;
1255 
1256 		/* Test for stop */
1257 		if( ldapqry_get_stop_flag( qry ) ) {
1258 			break;
1259 		}
1260 
1261 		/* Retrieve entry */
1262 		if( first ) {
1263 			first = FALSE;
1264 			e = ldap_first_entry( ld, result );
1265 		}
1266 		else {
1267 			e = ldap_next_entry( ld, e );
1268 		}
1269 		if( e == NULL ) break;
1270 
1271 		entriesFound = TRUE;
1272 
1273 		/* Setup a critical section here */
1274 		pthread_mutex_lock( qry->mutexEntry );
1275 
1276 		/* Process entry */
1277 		listValues = ldapqry_fetch_attribs( ld, e );
1278 
1279 		/* Process callback */
1280 		if( qry->callBackEntry ) {
1281 			qry->callBackEntry( qry, ADDRQUERY_ID(qry), listValues, qry->data );
1282 		}
1283 		ldapqry_free_list_name_value( listValues );
1284 		listValues = NULL;
1285 
1286 		pthread_mutex_unlock( qry->mutexEntry );
1287 	}
1288 
1289 	/* Free up and disconnect */
1290 	ldap_msgfree( result );
1291 
1292 	if( entriesFound ) {
1293 		ADDRQUERY_RETVAL(qry) = LDAPRC_SUCCESS;
1294 	}
1295 	else {
1296 		ADDRQUERY_RETVAL(qry) = LDAPRC_NOENTRIES;
1297 	}
1298 
1299 	return ADDRQUERY_RETVAL(qry);
1300 }
1301 
1302 /**
1303  * Perform the search to locate a specific LDAP record identified by
1304  * distinguished name (dn).
1305  *
1306  * \param  qry Query object to process.
1307  * \return Error/status code.
1308  */
ldapqry_perform_locate(LdapQuery * qry)1309 static gint ldapqry_perform_locate( LdapQuery *qry ) {
1310 	/* Connect */
1311 	qry->ldap = NULL;
1312 	ldapqry_connect( qry );
1313 	if( ADDRQUERY_RETVAL(qry) == LDAPRC_SUCCESS ) {
1314 		/* Perform search */
1315 		ldapqry_locate_retrieve( qry );
1316 	}
1317 	/* Disconnect */
1318 	ldapqry_disconnect( qry );
1319 	qry->ldap = NULL;
1320 
1321 	/* Process callback */
1322 	if( qry->callBackEnd ) {
1323 		g_timeout_add(0, callbackend, qry);
1324 	}
1325 
1326 	return ADDRQUERY_RETVAL(qry);
1327 }
1328 
1329 /**
1330  * Remove results (folder and data) for specified LDAP query.
1331  * \param  qry Query object to process.
1332  * \return TRUE if folder deleted successfully.
1333  */
ldapquery_remove_results(LdapQuery * qry)1334 gboolean ldapquery_remove_results( LdapQuery *qry ) {
1335 	gboolean retVal = FALSE;
1336 
1337 	ldapqry_set_aged_flag( qry, TRUE );
1338 
1339 	if( ldapqry_get_busy_flag( qry ) ) {
1340 		ldapqry_set_stop_flag( qry, TRUE );
1341 	}
1342 	else {
1343 		LdapServer *server = qry->server;
1344 		server->listQuery = g_list_remove(server->listQuery, qry);
1345 
1346 		retVal = TRUE;
1347 	}
1348 	return retVal;
1349 }
1350 
ldapqry_print(LdapQuery * qry,FILE * stream)1351 void ldapqry_print(LdapQuery *qry, FILE *stream) {
1352 	cm_return_if_fail( qry != NULL );
1353 
1354 	ldapsvr_print_data(qry->server, stream);
1355 	ldapctl_print(qry->control, stream);
1356 	fprintf(stream, "entriesRead: %d\n", qry->entriesRead);
1357 	fprintf(stream, "elapsedTime: %d\n", qry->elapsedTime);
1358 	fprintf(stream, "stopFlag: %d\n", qry->stopFlag);
1359 	fprintf(stream, "busyFlag: %d\n", qry->busyFlag);
1360 	fprintf(stream, "agedFlag: %d\n", qry->agedFlag);
1361 	fprintf(stream, "completed: %d\n", qry->completed);
1362 	fprintf(stream, "startTime: %d\n", (int) qry->startTime);
1363 	fprintf(stream, "touchTime: %d\n", (int) qry->touchTime);
1364 	fprintf(stream, "data: %s\n", qry->data?(gchar *)qry->data:"null");
1365 }
1366 
1367 #endif	/* USE_LDAP */
1368 
1369 /*
1370  * End of Source.
1371  */
1372 
1373 
1374